OpenTTD Source: src/graph_gui.cpp Source File

OpenTTD Source 15.0-RC2

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 <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
6 */
7
10#include "stdafx.h"
11#include <ranges>
12#include "misc/history_func.hpp"
13#include "graph_gui.h"
14#include "window_gui.h"
15#include "company_base.h"
16#include "company_gui.h"
17#include "economy_func.h"
18#include "cargotype.h"
19#include "strings_func.h"
20#include "window_func.h"
21#include "sound_func.h"
22#include "gfx_func.h"
23#include "core/geometry_func.hpp"
24#include "currency.h"
25#include "timer/timer.h"
26#include "timer/timer_window.h"
27#include "timer/timer_game_tick.h"
28#include "timer/timer_game_economy.h"
29#include "zoom_func.h"
30#include "industry.h"
31#include "town.h"
32
33#include "widgets/graph_widget.h"
34
35#include "table/strings.h"
36#include "table/sprites.h"
37
38#include "safeguards.h"
39
40/* Bitmasks of company and cargo indices that shouldn't be drawn. */
41static CompanyMask _legend_excluded_companies;
42
43/* Apparently these don't play well with enums. */
44static const OverflowSafeInt64 INVALID_DATAPOINT(INT64_MAX); // Value used for a datapoint that shouldn't be drawn.
45static const uint INVALID_DATAPOINT_POS = UINT_MAX; // Used to determine if the previous point was drawn.
46
47 constexpr double INT64_MAX_IN_DOUBLE = static_cast<double>(INT64_MAX - 512);
48static_assert(static_cast<int64_t>(INT64_MAX_IN_DOUBLE) < INT64_MAX);
49
50/****************/
51/* GRAPH LEGEND */
52/****************/
53
54 struct GraphLegendWindow : Window {
55 GraphLegendWindow(WindowDesc &desc, WindowNumber window_number) : Window(desc)
56 {
57 this->InitNested(window_number);
58
59 for (CompanyID c = CompanyID::Begin(); c < MAX_COMPANIES; ++c) {
60 if (!_legend_excluded_companies.Test(c)) this->LowerWidget(WID_GL_FIRST_COMPANY + c);
61
62 this->OnInvalidateData(c.base());
63 }
64 }
65
66 void DrawWidget(const Rect &r, WidgetID widget) const override
67 {
68 if (!IsInsideMM(widget, WID_GL_FIRST_COMPANY, WID_GL_FIRST_COMPANY + MAX_COMPANIES)) return;
69
70 CompanyID cid = (CompanyID)(widget - WID_GL_FIRST_COMPANY);
71
72 if (!Company::IsValidID(cid)) return;
73
74 bool rtl = _current_text_dir == TD_RTL;
75
76 const Rect ir = r.Shrink(WidgetDimensions::scaled.framerect);
77 Dimension d = GetSpriteSize(SPR_COMPANY_ICON);
78 DrawCompanyIcon(cid, rtl ? ir.right - d.width : ir.left, CentreBounds(ir.top, ir.bottom, d.height));
79
80 const Rect tr = ir.Indent(d.width + WidgetDimensions::scaled.hsep_normal, rtl);
81 DrawString(tr.left, tr.right, CentreBounds(tr.top, tr.bottom, GetCharacterHeight(FS_NORMAL)), GetString(STR_COMPANY_NAME_COMPANY_NUM, cid, cid), _legend_excluded_companies.Test(cid) ? TC_BLACK : TC_WHITE);
82 }
83
84 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
85 {
86 if (!IsInsideMM(widget, WID_GL_FIRST_COMPANY, WID_GL_FIRST_COMPANY + MAX_COMPANIES)) return;
87
88 _legend_excluded_companies.Flip(static_cast<CompanyID>(widget - WID_GL_FIRST_COMPANY));
89 this->ToggleWidgetLoweredState(widget);
90 this->SetDirty();
91 InvalidateWindowData(WC_INCOME_GRAPH, 0);
92 InvalidateWindowData(WC_OPERATING_PROFIT, 0);
93 InvalidateWindowData(WC_DELIVERED_CARGO, 0);
94 InvalidateWindowData(WC_PERFORMANCE_HISTORY, 0);
95 InvalidateWindowData(WC_COMPANY_VALUE, 0);
96
97 SndClickBeep();
98 }
99
105 void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
106 {
107 if (!gui_scope) return;
108 if (Company::IsValidID(data)) return;
109
110 _legend_excluded_companies.Set(static_cast<CompanyID>(data));
111 this->RaiseWidget(data + WID_GL_FIRST_COMPANY);
112 }
113};
114
119 static std::unique_ptr<NWidgetBase> MakeNWidgetCompanyLines()
120{
121 auto vert = std::make_unique<NWidgetVertical>(NWidContainerFlag::EqualSize);
122 vert->SetPadding(2, 2, 2, 2);
123 uint sprite_height = GetSpriteSize(SPR_COMPANY_ICON, nullptr, ZoomLevel::Normal).height;
124
125 for (WidgetID widnum = WID_GL_FIRST_COMPANY; widnum <= WID_GL_LAST_COMPANY; widnum++) {
126 auto panel = std::make_unique<NWidgetBackground>(WWT_PANEL, COLOUR_BROWN, widnum);
127 panel->SetMinimalSize(246, sprite_height + WidgetDimensions::unscaled.framerect.Vertical());
128 panel->SetMinimalTextLines(1, WidgetDimensions::unscaled.framerect.Vertical(), FS_NORMAL);
129 panel->SetFill(1, 1);
130 panel->SetToolTip(STR_GRAPH_KEY_COMPANY_SELECTION_TOOLTIP);
131 vert->Add(std::move(panel));
132 }
133 return vert;
134}
135
136static constexpr std::initializer_list<NWidgetPart> _nested_graph_legend_widgets = {
137 NWidget(NWID_HORIZONTAL),
138 NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
139 NWidget(WWT_CAPTION, COLOUR_BROWN), SetStringTip(STR_GRAPH_KEY_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
140 NWidget(WWT_SHADEBOX, COLOUR_BROWN),
141 NWidget(WWT_STICKYBOX, COLOUR_BROWN),
142 EndContainer(),
143 NWidget(WWT_PANEL, COLOUR_BROWN, WID_GL_BACKGROUND),
144 NWidgetFunction(MakeNWidgetCompanyLines),
145 EndContainer(),
146};
147
148static WindowDesc _graph_legend_desc(
149 WDP_AUTO, "graph_legend", 0, 0,
150 WC_GRAPH_LEGEND, WC_NONE,
151 {},
152 _nested_graph_legend_widgets
153);
154
155static void ShowGraphLegend()
156{
157 AllocateWindowDescFront<GraphLegendWindow>(_graph_legend_desc, 0);
158}
159
161 struct ValuesInterval {
162 OverflowSafeInt64 highest;
163 OverflowSafeInt64 lowest;
164};
165
166/******************/
167/* BASE OF GRAPHS */
168/*****************/
169
170 struct BaseGraphWindow : Window {
171protected:
172 static const int GRAPH_MAX_DATASETS = 64;
173 static constexpr PixelColour GRAPH_BASE_COLOUR = GREY_SCALE(2);
174 static constexpr PixelColour GRAPH_GRID_COLOUR = GREY_SCALE(3);
175 static constexpr PixelColour GRAPH_AXIS_LINE_COLOUR = GREY_SCALE(1);
176 static constexpr PixelColour GRAPH_ZERO_LINE_COLOUR = GREY_SCALE(8);
177 static constexpr PixelColour GRAPH_YEAR_LINE_COLOUR = GREY_SCALE(5);
178 static const int GRAPH_NUM_MONTHS = 24;
179 static const int GRAPH_PAYMENT_RATE_STEPS = 20;
180 static const int PAYMENT_GRAPH_X_STEP_DAYS = 10;
181 static const int PAYMENT_GRAPH_X_STEP_SECONDS = 20;
182 static const int ECONOMY_YEAR_MINUTES = 12;
183 static const int ECONOMY_QUARTER_MINUTES = 3;
184 static const int ECONOMY_MONTH_MINUTES = 1;
185
186 static const TextColour GRAPH_AXIS_LABEL_COLOUR = TC_BLACK;
187
188 static const int MIN_GRAPH_NUM_LINES_Y = 9;
189 static const int MIN_GRID_PIXEL_SIZE = 20;
190
191 struct GraphScale {
192 StringID label = STR_NULL;
193 uint8_t month_increment = 0;
194 int16_t x_values_increment = 0;
195 const HistoryRange *history_range = nullptr;
196 };
197
198 static inline constexpr GraphScale MONTHLY_SCALE_WALLCLOCK[] = {
199 {STR_GRAPH_LAST_24_MINUTES_TIME_LABEL, HISTORY_MONTH.total_division, ECONOMY_MONTH_MINUTES, &HISTORY_MONTH},
200 {STR_GRAPH_LAST_72_MINUTES_TIME_LABEL, HISTORY_QUARTER.total_division, ECONOMY_QUARTER_MINUTES, &HISTORY_QUARTER},
201 {STR_GRAPH_LAST_288_MINUTES_TIME_LABEL, HISTORY_YEAR.total_division, ECONOMY_YEAR_MINUTES, &HISTORY_YEAR},
202 };
203
204 static inline constexpr GraphScale MONTHLY_SCALE_CALENDAR[] = {
205 {STR_GRAPH_LAST_24_MONTHS, HISTORY_MONTH.total_division, ECONOMY_MONTH_MINUTES, &HISTORY_MONTH},
206 {STR_GRAPH_LAST_24_QUARTERS, HISTORY_QUARTER.total_division, ECONOMY_QUARTER_MINUTES, &HISTORY_QUARTER},
207 {STR_GRAPH_LAST_24_YEARS, HISTORY_YEAR.total_division, ECONOMY_YEAR_MINUTES, &HISTORY_YEAR},
208 };
209
210 uint64_t excluded_data = 0;
211 uint64_t excluded_range = 0;
212 uint64_t masked_range = 0;
213 uint8_t num_on_x_axis = 0;
214 uint8_t num_vert_lines = GRAPH_NUM_MONTHS;
215
216 /* The starting month and year that values are plotted against. */
217 TimerGameEconomy::Month month{};
218 TimerGameEconomy::Year year{};
219 uint8_t month_increment = 3;
220
221 bool draw_dates = true;
222
223 /* These values are used if the graph is being plotted against values
224 * rather than the dates specified by month and year. */
225 bool x_values_reversed = true;
226 int16_t x_values_increment = ECONOMY_QUARTER_MINUTES;
227
228 StringID format_str_y_axis{};
229
230 struct DataSet {
231 std::array<OverflowSafeInt64, GRAPH_NUM_MONTHS> values;
232 PixelColour colour;
233 uint8_t exclude_bit;
234 uint8_t range_bit;
235 uint8_t dash;
236 };
237 std::vector<DataSet> data{};
238
239 std::span<const StringID> ranges{};
240 std::span<const GraphScale> scales{};
241 uint8_t selected_scale = 0;
242
243 uint8_t highlight_data = UINT8_MAX;
244 uint8_t highlight_range = UINT8_MAX;
245 bool highlight_state = false;
246
247 struct BaseFiller {
248 DataSet &dataset;
249
250 inline void MakeZero(uint i) const { this->dataset.values[i] = 0; }
251 inline void MakeInvalid(uint i) const { this->dataset.values[i] = INVALID_DATAPOINT; }
252 };
253
254 template <typename Tprojection>
255 struct Filler : BaseFiller {
256 Tprojection proj;
257
258 inline void Fill(uint i, const auto &data) const { this->dataset.values[i] = std::invoke(this->proj, data); }
259 };
260
266 std::span<const OverflowSafeInt64> GetDataSetRange(const DataSet &dataset) const
267 {
268 return {std::begin(dataset.values), std::begin(dataset.values) + this->num_on_x_axis};
269 }
270
277 ValuesInterval GetValuesInterval(int num_hori_lines) const
278 {
279 assert(num_hori_lines > 0);
280
281 ValuesInterval current_interval;
282 current_interval.highest = INT64_MIN;
283 current_interval.lowest = INT64_MAX;
284
285 for (const DataSet &dataset : this->data) {
286 if (HasBit(this->excluded_data, dataset.exclude_bit)) continue;
287 if (HasBit(this->excluded_range, dataset.range_bit)) continue;
288
289 for (const OverflowSafeInt64 &datapoint : this->GetDataSetRange(dataset)) {
290 if (datapoint != INVALID_DATAPOINT) {
291 current_interval.highest = std::max(current_interval.highest, datapoint);
292 current_interval.lowest = std::min(current_interval.lowest, datapoint);
293 }
294 }
295 }
296
297 /* Always include zero in the shown range. */
298 double abs_lower = (current_interval.lowest > 0) ? 0 : (double)abs(current_interval.lowest);
299 double abs_higher = (current_interval.highest < 0) ? 0 : (double)current_interval.highest;
300
301 /* Prevent showing values too close to the graph limits. */
302 abs_higher = (11.0 * abs_higher) / 10.0;
303 abs_lower = (11.0 * abs_lower) / 10.0;
304
305 int num_pos_grids;
306 OverflowSafeInt64 grid_size;
307
308 if (abs_lower != 0 || abs_higher != 0) {
309 /* The number of grids to reserve for the positive part is: */
310 num_pos_grids = (int)floor(0.5 + num_hori_lines * abs_higher / (abs_higher + abs_lower));
311
312 /* If there are any positive or negative values, force that they have at least one grid. */
313 if (num_pos_grids == 0 && abs_higher != 0) num_pos_grids++;
314 if (num_pos_grids == num_hori_lines && abs_lower != 0) num_pos_grids--;
315
316 /* Get the required grid size for each side and use the maximum one. */
317
318 OverflowSafeInt64 grid_size_higher = 0;
319 if (abs_higher > 0) {
320 grid_size_higher = abs_higher > INT64_MAX_IN_DOUBLE ? INT64_MAX : static_cast<int64_t>(abs_higher);
321 grid_size_higher = (grid_size_higher + num_pos_grids - 1) / num_pos_grids;
322 }
323
324 OverflowSafeInt64 grid_size_lower = 0;
325 if (abs_lower > 0) {
326 grid_size_lower = abs_lower > INT64_MAX_IN_DOUBLE ? INT64_MAX : static_cast<int64_t>(abs_lower);
327 grid_size_lower = (grid_size_lower + num_hori_lines - num_pos_grids - 1) / (num_hori_lines - num_pos_grids);
328 }
329
330 grid_size = std::max(grid_size_higher, grid_size_lower);
331 } else {
332 /* If both values are zero, show an empty graph. */
333 num_pos_grids = num_hori_lines / 2;
334 grid_size = 1;
335 }
336
337 current_interval.highest = num_pos_grids * grid_size;
338 current_interval.lowest = -(num_hori_lines - num_pos_grids) * grid_size;
339 return current_interval;
340 }
341
347 uint GetYLabelWidth(ValuesInterval current_interval, int num_hori_lines) const
348 {
349 /* draw text strings on the y axis */
350 int64_t y_label = current_interval.highest;
351 int64_t y_label_separation = (current_interval.highest - current_interval.lowest) / num_hori_lines;
352
353 uint max_width = 0;
354
355 for (int i = 0; i < (num_hori_lines + 1); i++) {
356 Dimension d = GetStringBoundingBox(GetString(STR_GRAPH_Y_LABEL, this->format_str_y_axis, y_label));
357 if (d.width > max_width) max_width = d.width;
358
359 y_label -= y_label_separation;
360 }
361
362 return max_width;
363 }
364
369 void DrawGraph(Rect r) const
370 {
371 uint x, y;
372 ValuesInterval interval;
373 int x_axis_offset;
374
375 /* the colours and cost array of GraphDrawer must accommodate
376 * both values for cargo and companies. So if any are higher, quit */
377 static_assert(GRAPH_MAX_DATASETS >= (int)NUM_CARGO && GRAPH_MAX_DATASETS >= (int)MAX_COMPANIES);
378 assert(this->num_vert_lines > 0);
379
380 bool rtl = _current_text_dir == TD_RTL;
381
382 /* Rect r will be adjusted to contain just the graph, with labels being
383 * placed outside the area. */
384 r.top += ScaleGUITrad(5) + GetCharacterHeight(FS_SMALL) / 2;
385 r.bottom -= (this->draw_dates ? 2 : 1) * GetCharacterHeight(FS_SMALL) + ScaleGUITrad(4);
386 r.left += ScaleGUITrad(rtl ? 5 : 9);
387 r.right -= ScaleGUITrad(rtl ? 9 : 5);
388
389 /* Initial number of horizontal lines. */
390 int num_hori_lines = 160 / ScaleGUITrad(MIN_GRID_PIXEL_SIZE);
391 /* For the rest of the height, the number of horizontal lines will increase more slowly. */
392 int resize = (r.bottom - r.top - 160) / (2 * ScaleGUITrad(MIN_GRID_PIXEL_SIZE));
393 if (resize > 0) num_hori_lines += resize;
394
395 interval = GetValuesInterval(num_hori_lines);
396
397 int label_width = GetYLabelWidth(interval, num_hori_lines);
398
399 if (rtl) {
400 r.right -= label_width;
401 } else {
402 r.left += label_width;
403 }
404
405 int x_sep = (r.right - r.left) / this->num_vert_lines;
406 int y_sep = (r.bottom - r.top) / num_hori_lines;
407
408 /* Redetermine right and bottom edge of graph to fit with the integer
409 * separation values. */
410 if (rtl) {
411 r.left = r.right - x_sep * this->num_vert_lines;
412 } else {
413 r.right = r.left + x_sep * this->num_vert_lines;
414 }
415 r.bottom = r.top + y_sep * num_hori_lines;
416
417 OverflowSafeInt64 interval_size = interval.highest + abs(interval.lowest);
418 /* Where to draw the X axis. Use floating point to avoid overflowing and results of zero. */
419 x_axis_offset = (int)((r.bottom - r.top) * (double)interval.highest / (double)interval_size);
420
421 /* Draw the background of the graph itself. */
422 GfxFillRect(r.left, r.top, r.right, r.bottom, GRAPH_BASE_COLOUR);
423
424 /* Draw the grid lines. */
425 int gridline_width = WidgetDimensions::scaled.bevel.top;
426 PixelColour grid_colour = GRAPH_GRID_COLOUR;
427
428 /* Don't draw the first line, as that's where the axis will be. */
429 if (rtl) {
430 x_sep = -x_sep;
431 x = r.right + x_sep;
432 } else {
433 x = r.left + x_sep;
434 }
435
436 for (int i = 1; i < this->num_vert_lines + 1; i++) {
437 /* If using wallclock units, we separate periods with a lighter line. */
438 if (TimerGameEconomy::UsingWallclockUnits()) {
439 grid_colour = (i % 4 == 0) ? GRAPH_YEAR_LINE_COLOUR : GRAPH_GRID_COLOUR;
440 }
441 GfxFillRect(x, r.top, x + gridline_width - 1, r.bottom, grid_colour);
442 x += x_sep;
443 }
444
445 /* Draw the horizontal grid lines. */
446 y = r.bottom;
447
448 for (int i = 0; i < (num_hori_lines + 1); i++) {
449 if (rtl) {
450 GfxFillRect(r.right + 1, y, r.right + ScaleGUITrad(3), y + gridline_width - 1, GRAPH_AXIS_LINE_COLOUR);
451 } else {
452 GfxFillRect(r.left - ScaleGUITrad(3), y, r.left - 1, y + gridline_width - 1, GRAPH_AXIS_LINE_COLOUR);
453 }
454 GfxFillRect(r.left, y, r.right + gridline_width - 1, y + gridline_width - 1, GRAPH_GRID_COLOUR);
455 y -= y_sep;
456 }
457
458 /* Draw the y axis. */
459 GfxFillRect(r.left, r.top, r.left + gridline_width - 1, r.bottom + gridline_width - 1, GRAPH_AXIS_LINE_COLOUR);
460
461 /* Draw the x axis. */
462 y = x_axis_offset + r.top;
463 GfxFillRect(r.left, y, r.right + gridline_width - 1, y + gridline_width - 1, GRAPH_ZERO_LINE_COLOUR);
464
465 /* Find the largest value that will be drawn. */
466 if (this->num_on_x_axis == 0) return;
467
468 assert(this->num_on_x_axis > 0);
469
470 /* draw text strings on the y axis */
471 int64_t y_label = interval.highest;
472 int64_t y_label_separation = abs(interval.highest - interval.lowest) / num_hori_lines;
473
474 y = r.top - GetCharacterHeight(FS_SMALL) / 2;
475
476 for (int i = 0; i < (num_hori_lines + 1); i++) {
477 if (rtl) {
478 DrawString(r.right + ScaleGUITrad(4), r.right + label_width + ScaleGUITrad(4), y,
479 GetString(STR_GRAPH_Y_LABEL, this->format_str_y_axis, y_label),
480 GRAPH_AXIS_LABEL_COLOUR, SA_RIGHT | SA_FORCE);
481 } else {
482 DrawString(r.left - label_width - ScaleGUITrad(4), r.left - ScaleGUITrad(4), y,
483 GetString(STR_GRAPH_Y_LABEL, this->format_str_y_axis, y_label),
484 GRAPH_AXIS_LABEL_COLOUR, SA_RIGHT | SA_FORCE);
485 }
486
487 y_label -= y_label_separation;
488 y += y_sep;
489 }
490
491 x = rtl ? r.right : r.left;
492 y = r.bottom + ScaleGUITrad(2);
493
494 /* if there are not enough datapoints to fill the graph, align to the right */
495 x += (this->num_vert_lines - this->num_on_x_axis) * x_sep;
496
497 /* Draw x-axis labels and markings for graphs based on financial quarters and years. */
498 if (this->draw_dates) {
499 TimerGameEconomy::Month month = this->month;
500 TimerGameEconomy::Year year = this->year;
501 for (int i = 0; i < this->num_on_x_axis; i++) {
502 if (rtl) {
503 DrawStringMultiLineWithClipping(x + x_sep, x, y, this->height,
504 GetString(month == 0 ? STR_GRAPH_X_LABEL_MONTH_YEAR : STR_GRAPH_X_LABEL_MONTH, STR_MONTH_ABBREV_JAN + month, year),
505 GRAPH_AXIS_LABEL_COLOUR, SA_LEFT);
506 } else {
507 DrawStringMultiLineWithClipping(x, x + x_sep, y, this->height,
508 GetString(month == 0 ? STR_GRAPH_X_LABEL_MONTH_YEAR : STR_GRAPH_X_LABEL_MONTH, STR_MONTH_ABBREV_JAN + month, year),
509 GRAPH_AXIS_LABEL_COLOUR, SA_LEFT);
510 }
511
512 month += this->month_increment;
513 if (month >= 12) {
514 month = 0;
515 year++;
516
517 /* Draw a lighter grid line between years. Top and bottom adjustments ensure we don't draw over top and bottom horizontal grid lines. */
518 GfxFillRect(x + x_sep, r.top + gridline_width, x + x_sep + gridline_width - 1, r.bottom - 1, GRAPH_YEAR_LINE_COLOUR);
519 }
520 x += x_sep;
521 }
522 } else {
523 /* Draw x-axis labels for graphs not based on quarterly performance (cargo payment rates, and all graphs when using wallclock units). */
524 int16_t iterator;
525 uint16_t label;
526 if (this->x_values_reversed) {
527 label = this->x_values_increment * this->num_on_x_axis;
528 iterator = -this->x_values_increment;
529 } else {
530 label = this->x_values_increment;
531 iterator = this->x_values_increment;
532 }
533
534 for (int i = 0; i < this->num_on_x_axis; i++) {
535 if (rtl) {
536 DrawString(x + x_sep + 1, x - 1, y, GetString(STR_GRAPH_Y_LABEL_NUMBER, label), GRAPH_AXIS_LABEL_COLOUR, SA_HOR_CENTER);
537 } else {
538 DrawString(x + 1, x + x_sep - 1, y, GetString(STR_GRAPH_Y_LABEL_NUMBER, label), GRAPH_AXIS_LABEL_COLOUR, SA_HOR_CENTER);
539 }
540
541 label += iterator;
542 x += x_sep;
543 }
544 }
545
546 /* Draw lines and dots. */
547 uint linewidth = ScaleGUITrad(_settings_client.gui.graph_line_thickness);
548 uint pointwidth = ScaleGUITrad(_settings_client.gui.graph_line_thickness + 1);
549 uint pointoffs1 = pointwidth / 2;
550 uint pointoffs2 = pointwidth - pointoffs1;
551
552 auto draw_dataset = [&](const DataSet &dataset, PixelColour colour) {
553 if (HasBit(this->excluded_data, dataset.exclude_bit)) return;
554 if (HasBit(this->excluded_range, dataset.range_bit)) return;
555
556 /* Centre the dot between the grid lines. */
557 if (rtl) {
558 x = r.right + (x_sep / 2);
559 } else {
560 x = r.left + (x_sep / 2);
561 }
562
563 /* if there are not enough datapoints to fill the graph, align to the right */
564 x += (this->num_vert_lines - this->num_on_x_axis) * x_sep;
565
566 uint prev_x = INVALID_DATAPOINT_POS;
567 uint prev_y = INVALID_DATAPOINT_POS;
568
569 const uint dash = ScaleGUITrad(dataset.dash);
570 for (OverflowSafeInt64 datapoint : this->GetDataSetRange(dataset)) {
571 if (datapoint != INVALID_DATAPOINT) {
572 /*
573 * Check whether we need to reduce the 'accuracy' of the
574 * datapoint value and the highest value to split overflows.
575 * And when 'drawing' 'one million' or 'one million and one'
576 * there is no significant difference, so the least
577 * significant bits can just be removed.
578 *
579 * If there are more bits needed than would fit in a 32 bits
580 * integer, so at about 31 bits because of the sign bit, the
581 * least significant bits are removed.
582 */
583 int mult_range = FindLastBit<uint32_t>(x_axis_offset) + FindLastBit<uint64_t>(abs(datapoint));
584 int reduce_range = std::max(mult_range - 31, 0);
585
586 /* Handle negative values differently (don't shift sign) */
587 if (datapoint < 0) {
588 datapoint = -(abs(datapoint) >> reduce_range);
589 } else {
590 datapoint >>= reduce_range;
591 }
592 y = r.top + x_axis_offset - ((r.bottom - r.top) * datapoint) / (interval_size >> reduce_range);
593
594 /* Draw the point. */
595 GfxFillRect(x - pointoffs1, y - pointoffs1, x + pointoffs2, y + pointoffs2, colour);
596
597 /* Draw the line connected to the previous point. */
598 if (prev_x != INVALID_DATAPOINT_POS) GfxDrawLine(prev_x, prev_y, x, y, colour, linewidth, dash);
599
600 prev_x = x;
601 prev_y = y;
602 } else {
603 prev_x = INVALID_DATAPOINT_POS;
604 prev_y = INVALID_DATAPOINT_POS;
605 }
606
607 x += x_sep;
608 }
609 };
610
611 /* Draw unhighlighted datasets. */
612 for (const DataSet &dataset : this->data) {
613 if (dataset.exclude_bit != this->highlight_data && dataset.range_bit != this->highlight_range) {
614 draw_dataset(dataset, dataset.colour);
615 }
616 }
617
618 /* If any dataset or range is highlighted, draw separately after the rest so they appear on top of all other
619 * data. Highlighted data is only drawn when highlight_state is set, otherwise it is invisible. */
620 if (this->highlight_state && (this->highlight_data != UINT8_MAX || this->highlight_range != UINT8_MAX)) {
621 for (const DataSet &dataset : this->data) {
622 if (dataset.exclude_bit == this->highlight_data || dataset.range_bit == this->highlight_range) {
623 draw_dataset(dataset, PC_WHITE);
624 }
625 }
626 }
627 }
628
629 BaseGraphWindow(WindowDesc &desc, StringID format_str_y_axis) :
630 Window(desc),
631 format_str_y_axis(format_str_y_axis)
632 {
633 SetWindowDirty(WC_GRAPH_LEGEND, 0);
634 }
635
636 const IntervalTimer<TimerWindow> blink_interval = {TIMER_BLINK_INTERVAL, [this](auto) {
637 /* If nothing is highlighted then no redraw is needed. */
638 if (this->highlight_data == UINT8_MAX && this->highlight_range == UINT8_MAX) return;
639
640 /* Toggle the highlight state and redraw. */
641 this->highlight_state = !this->highlight_state;
642 this->SetDirty();
643 }};
644
645 void UpdateMatrixSize(WidgetID widget, Dimension &size, Dimension &resize, auto labels)
646 {
647 size = {};
648 for (const StringID &str : labels) {
649 size = maxdim(size, GetStringBoundingBox(str, FS_SMALL));
650 }
651
652 size.width += WidgetDimensions::scaled.framerect.Horizontal();
653 size.height += WidgetDimensions::scaled.framerect.Vertical();
654
655 /* Set fixed height for number of ranges. */
656 size.height *= static_cast<uint>(std::size(labels));
657
658 resize.width = 0;
659 resize.height = 0;
660 this->GetWidget<NWidgetCore>(widget)->SetMatrixDimension(1, ClampTo<uint32_t>(std::size(labels)));
661 }
662
663public:
664 void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
665 {
666 switch (widget) {
667 case WID_GRAPH_RANGE_MATRIX:
668 this->UpdateMatrixSize(widget, size, resize, this->ranges);
669 break;
670
671 case WID_GRAPH_SCALE_MATRIX:
672 this->UpdateMatrixSize(widget, size, resize, this->scales | std::views::transform(&GraphScale::label));
673 break;
674
675 case WID_GRAPH_GRAPH: {
676 uint x_label_width = 0;
677
678 /* Draw x-axis labels and markings for graphs based on financial quarters and years. */
679 if (this->draw_dates) {
680 uint year = GetParamMaxValue(this->year.base(), 4, FS_SMALL);
681 for (uint month = 0; month < 12; ++month) {
682 x_label_width = std::max(x_label_width, GetStringBoundingBox(GetString(month == 0 ? STR_GRAPH_X_LABEL_MONTH_YEAR : STR_GRAPH_X_LABEL_MONTH, STR_MONTH_ABBREV_JAN + month, year)).width);
683 }
684 } else {
685 /* Draw x-axis labels for graphs not based on quarterly performance (cargo payment rates). */
686 uint64_t max_value = GetParamMaxValue((this->num_on_x_axis + 1) * this->x_values_increment, 0, FS_SMALL);
687 x_label_width = GetStringBoundingBox(GetString(STR_GRAPH_Y_LABEL_NUMBER, max_value)).width;
688 }
689
690 uint y_label_width = GetStringBoundingBox(GetString(STR_GRAPH_Y_LABEL, this->format_str_y_axis, INT64_MAX)).width;
691
692 size.width = std::max<uint>(size.width, ScaleGUITrad(5) + y_label_width + this->num_vert_lines * (x_label_width + ScaleGUITrad(5)) + ScaleGUITrad(9));
693 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));
694 size.height = std::max<uint>(size.height, size.width / 3);
695 break;
696 }
697
698 default: break;
699 }
700 }
701
702 void DrawWidget(const Rect &r, WidgetID widget) const override
703 {
704 switch (widget) {
705 case WID_GRAPH_GRAPH:
706 this->DrawGraph(r);
707 break;
708
709 case WID_GRAPH_RANGE_MATRIX: {
710 uint line_height = GetCharacterHeight(FS_SMALL) + WidgetDimensions::scaled.framerect.Vertical();
711 uint index = 0;
712 Rect line = r.WithHeight(line_height);
713 for (const auto &str : this->ranges) {
714 bool lowered = !HasBit(this->excluded_range, index) && !HasBit(this->masked_range, index);
715
716 /* Redraw frame if lowered */
717 if (lowered) DrawFrameRect(line, COLOUR_BROWN, FrameFlag::Lowered);
718
719 const Rect text = line.Shrink(WidgetDimensions::scaled.framerect);
720 DrawString(text, str, (this->highlight_state && this->highlight_range == index) ? TC_WHITE : TC_BLACK, SA_CENTER, false, FS_SMALL);
721
722 if (HasBit(this->masked_range, index)) {
723 GfxFillRect(line.Shrink(WidgetDimensions::scaled.bevel), GetColourGradient(COLOUR_BROWN, SHADE_DARKER), FILLRECT_CHECKER);
724 }
725
726 line = line.Translate(0, line_height);
727 ++index;
728 }
729 break;
730 }
731
732 case WID_GRAPH_SCALE_MATRIX: {
733 uint line_height = GetCharacterHeight(FS_SMALL) + WidgetDimensions::scaled.framerect.Vertical();
734 uint8_t selected_month_increment = this->scales[this->selected_scale].month_increment;
735 Rect line = r.WithHeight(line_height);
736 for (const auto &scale : this->scales) {
737 /* Redraw frame if selected */
738 if (selected_month_increment == scale.month_increment) DrawFrameRect(line, COLOUR_BROWN, FrameFlag::Lowered);
739
740 DrawString(line.Shrink(WidgetDimensions::scaled.framerect), scale.label, TC_BLACK, SA_CENTER, false, FS_SMALL);
741
742 line = line.Translate(0, line_height);
743 }
744 break;
745 }
746
747 default: break;
748 }
749 }
750
751 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
752 {
753 /* Clicked on legend? */
754 switch (widget) {
755 case WID_GRAPH_KEY_BUTTON:
756 ShowGraphLegend();
757 break;
758
759 case WID_GRAPH_RANGE_MATRIX: {
760 int row = GetRowFromWidget(pt.y, widget, 0, GetCharacterHeight(FS_SMALL) + WidgetDimensions::scaled.framerect.Vertical());
761
762 if (HasBit(this->masked_range, row)) break;
763 ToggleBit(this->excluded_range, row);
764 SndClickBeep();
765 this->SetDirty();
766 break;
767 }
768
769 case WID_GRAPH_SCALE_MATRIX: {
770 int row = GetRowFromWidget(pt.y, widget, 0, GetCharacterHeight(FS_SMALL) + WidgetDimensions::scaled.framerect.Vertical());
771 const auto &scale = this->scales[row];
772 if (this->selected_scale != row) {
773 this->selected_scale = row;
774 this->month_increment = scale.month_increment;
775 this->x_values_increment = scale.x_values_increment;
776 this->InvalidateData();
777 }
778 break;
779 }
780
781 default: break;
782 }
783 }
784
785 void OnMouseOver(Point pt, WidgetID widget) override
786 {
787 /* Test if a range should be highlighted. */
788 uint8_t new_highlight_range = UINT8_MAX;
789 if (widget == WID_GRAPH_RANGE_MATRIX) {
790 int row = GetRowFromWidget(pt.y, widget, 0, GetCharacterHeight(FS_SMALL) + WidgetDimensions::scaled.framerect.Vertical());
791 if (!HasBit(this->excluded_range, row)) new_highlight_range = static_cast<uint8_t>(row);
792 }
793
794 /* Test if a dataset should be highlighted. */
795 uint8_t new_highlight_data = UINT8_MAX;
796 if (widget == WID_GRAPH_MATRIX) {
797 auto dataset_index = this->GetDatasetIndex(pt.y);
798 if (dataset_index.has_value() && !HasBit(this->excluded_data, *dataset_index)) new_highlight_data = *dataset_index;
799 }
800
801 if (this->highlight_data == new_highlight_data && this->highlight_range == new_highlight_range) return;
802
803 /* Range or data set highlight has changed, set and redraw. */
804 this->highlight_data = new_highlight_data;
805 this->highlight_range = new_highlight_range;
806 this->highlight_state = true;
807 this->SetDirty();
808 }
809
810 void OnGameTick() override
811 {
812 this->UpdateStatistics(false);
813 }
814
820 void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
821 {
822 if (!gui_scope) return;
823 this->UpdateStatistics(true);
824 }
825
826 virtual void UpdateStatistics(bool initialize) = 0;
827
828 virtual std::optional<uint8_t> GetDatasetIndex(int) { return std::nullopt; }
829};
830
831 class BaseCompanyGraphWindow : public BaseGraphWindow {
832public:
833 BaseCompanyGraphWindow(WindowDesc &desc, StringID format_str_y_axis) : BaseGraphWindow(desc, format_str_y_axis) {}
834
835 void InitializeWindow(WindowNumber number)
836 {
837 /* Initialise the dataset */
838 this->UpdateStatistics(true);
839
840 this->CreateNestedTree();
841
842 auto *wid = this->GetWidget<NWidgetCore>(WID_GRAPH_FOOTER);
843 wid->SetString(TimerGameEconomy::UsingWallclockUnits() ? STR_GRAPH_LAST_72_MINUTES_TIME_LABEL : STR_EMPTY);
844
845 this->FinishInitNested(number);
846 }
847
852 void UpdateStatistics(bool initialize) override
853 {
854 CompanyMask excluded_companies = _legend_excluded_companies;
855
856 /* Exclude the companies which aren't valid */
857 for (CompanyID c = CompanyID::Begin(); c < MAX_COMPANIES; ++c) {
858 if (!Company::IsValidID(c)) excluded_companies.Set(c);
859 }
860
861 uint8_t nums = 0;
862 for (const Company *c : Company::Iterate()) {
863 nums = std::min(this->num_vert_lines, std::max(nums, c->num_valid_stat_ent));
864 }
865
866 int mo = (TimerGameEconomy::month / this->month_increment - nums) * this->month_increment;
867 auto yr = TimerGameEconomy::year;
868 while (mo < 0) {
869 yr--;
870 mo += 12;
871 }
872
873 if (!initialize && this->excluded_data == excluded_companies.base() && this->num_on_x_axis == nums &&
874 this->year == yr && this->month == mo) {
875 /* There's no reason to get new stats */
876 return;
877 }
878
879 this->excluded_data = excluded_companies.base();
880 this->num_on_x_axis = nums;
881 this->year = yr;
882 this->month = mo;
883
884 this->data.clear();
885 for (CompanyID k = CompanyID::Begin(); k < MAX_COMPANIES; ++k) {
886 const Company *c = Company::GetIfValid(k);
887 if (c == nullptr) continue;
888
889 DataSet &dataset = this->data.emplace_back();
890 dataset.colour = GetColourGradient(c->colour, SHADE_LIGHTER);
891 dataset.exclude_bit = k.base();
892
893 for (int j = this->num_on_x_axis, i = 0; --j >= 0;) {
894 if (j >= c->num_valid_stat_ent) {
895 dataset.values[i] = INVALID_DATAPOINT;
896 } else {
897 /* Ensure we never assign INVALID_DATAPOINT, as that has another meaning.
898 * Instead, use the value just under it. Hopefully nobody will notice. */
899 dataset.values[i] = std::min(GetGraphData(c, j), INVALID_DATAPOINT - 1);
900 }
901 i++;
902 }
903 }
904 }
905
906 virtual OverflowSafeInt64 GetGraphData(const Company *, int) = 0;
907};
908
909
910/********************/
911/* OPERATING PROFIT */
912/********************/
913
914 struct OperatingProfitGraphWindow : BaseCompanyGraphWindow {
915 OperatingProfitGraphWindow(WindowDesc &desc, WindowNumber window_number) :
916 BaseCompanyGraphWindow(desc, STR_JUST_CURRENCY_SHORT)
917 {
918 this->num_on_x_axis = GRAPH_NUM_MONTHS;
919 this->num_vert_lines = GRAPH_NUM_MONTHS;
920 this->draw_dates = !TimerGameEconomy::UsingWallclockUnits();
921
922 this->InitializeWindow(window_number);
923 }
924
925 OverflowSafeInt64 GetGraphData(const Company *c, int j) override
926 {
927 return c->old_economy[j].income + c->old_economy[j].expenses;
928 }
929};
930
931static constexpr std::initializer_list<NWidgetPart> _nested_operating_profit_widgets = {
932 NWidget(NWID_HORIZONTAL),
933 NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
934 NWidget(WWT_CAPTION, COLOUR_BROWN), SetStringTip(STR_GRAPH_OPERATING_PROFIT_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
935 NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_GRAPH_KEY_BUTTON), SetMinimalSize(50, 0), SetStringTip(STR_GRAPH_KEY_BUTTON, STR_GRAPH_KEY_TOOLTIP),
936 NWidget(WWT_SHADEBOX, COLOUR_BROWN),
937 NWidget(WWT_DEFSIZEBOX, COLOUR_BROWN),
938 NWidget(WWT_STICKYBOX, COLOUR_BROWN),
939 EndContainer(),
940 NWidget(WWT_PANEL, COLOUR_BROWN, WID_GRAPH_BACKGROUND),
941 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_GRAPH_GRAPH), SetMinimalSize(576, 160), SetFill(1, 1), SetResize(1, 1),
942 NWidget(NWID_HORIZONTAL),
943 NWidget(WWT_TEXT, INVALID_COLOUR, WID_GRAPH_FOOTER), SetFill(1, 0), SetResize(1, 0), SetPadding(2, 0, 2, 0), SetTextStyle(TC_BLACK, FS_SMALL), SetAlignment(SA_CENTER),
944 NWidget(WWT_RESIZEBOX, COLOUR_BROWN, WID_GRAPH_RESIZE), SetResizeWidgetTypeTip(RWV_HIDE_BEVEL, STR_TOOLTIP_RESIZE),
945 EndContainer(),
946 EndContainer(),
947};
948
949static WindowDesc _operating_profit_desc(
950 WDP_AUTO, "graph_operating_profit", 0, 0,
951 WC_OPERATING_PROFIT, WC_NONE,
952 {},
953 _nested_operating_profit_widgets
954);
955
956
957void ShowOperatingProfitGraph()
958{
959 AllocateWindowDescFront<OperatingProfitGraphWindow>(_operating_profit_desc, 0);
960}
961
962
963/****************/
964/* INCOME GRAPH */
965/****************/
966
967 struct IncomeGraphWindow : BaseCompanyGraphWindow {
968 IncomeGraphWindow(WindowDesc &desc, WindowNumber window_number) :
969 BaseCompanyGraphWindow(desc, STR_JUST_CURRENCY_SHORT)
970 {
971 this->num_on_x_axis = GRAPH_NUM_MONTHS;
972 this->num_vert_lines = GRAPH_NUM_MONTHS;
973 this->draw_dates = !TimerGameEconomy::UsingWallclockUnits();
974
975 this->InitializeWindow(window_number);
976 }
977
978 OverflowSafeInt64 GetGraphData(const Company *c, int j) override
979 {
980 return c->old_economy[j].income;
981 }
982};
983
984static constexpr std::initializer_list<NWidgetPart> _nested_income_graph_widgets = {
985 NWidget(NWID_HORIZONTAL),
986 NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
987 NWidget(WWT_CAPTION, COLOUR_BROWN), SetStringTip(STR_GRAPH_INCOME_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
988 NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_GRAPH_KEY_BUTTON), SetMinimalSize(50, 0), SetStringTip(STR_GRAPH_KEY_BUTTON, STR_GRAPH_KEY_TOOLTIP),
989 NWidget(WWT_SHADEBOX, COLOUR_BROWN),
990 NWidget(WWT_DEFSIZEBOX, COLOUR_BROWN),
991 NWidget(WWT_STICKYBOX, COLOUR_BROWN),
992 EndContainer(),
993 NWidget(WWT_PANEL, COLOUR_BROWN, WID_GRAPH_BACKGROUND),
994 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_GRAPH_GRAPH), SetMinimalSize(576, 128), SetFill(1, 1), SetResize(1, 1),
995 NWidget(NWID_HORIZONTAL),
996 NWidget(WWT_TEXT, INVALID_COLOUR, WID_GRAPH_FOOTER), SetFill(1, 0), SetResize(1, 0), SetPadding(2, 0, 2, 0), SetTextStyle(TC_BLACK, FS_SMALL), SetAlignment(SA_CENTER),
997 NWidget(WWT_RESIZEBOX, COLOUR_BROWN, WID_GRAPH_RESIZE), SetResizeWidgetTypeTip(RWV_HIDE_BEVEL, STR_TOOLTIP_RESIZE),
998 EndContainer(),
999 EndContainer(),
1000};
1001
1002static WindowDesc _income_graph_desc(
1003 WDP_AUTO, "graph_income", 0, 0,
1004 WC_INCOME_GRAPH, WC_NONE,
1005 {},
1006 _nested_income_graph_widgets
1007);
1008
1009void ShowIncomeGraph()
1010{
1011 AllocateWindowDescFront<IncomeGraphWindow>(_income_graph_desc, 0);
1012}
1013
1014/*******************/
1015/* DELIVERED CARGO */
1016/*******************/
1017
1018 struct DeliveredCargoGraphWindow : BaseCompanyGraphWindow {
1019 DeliveredCargoGraphWindow(WindowDesc &desc, WindowNumber window_number) :
1020 BaseCompanyGraphWindow(desc, STR_JUST_COMMA)
1021 {
1022 this->num_on_x_axis = GRAPH_NUM_MONTHS;
1023 this->num_vert_lines = GRAPH_NUM_MONTHS;
1024 this->draw_dates = !TimerGameEconomy::UsingWallclockUnits();
1025
1026 this->InitializeWindow(window_number);
1027 }
1028
1029 OverflowSafeInt64 GetGraphData(const Company *c, int j) override
1030 {
1031 return c->old_economy[j].delivered_cargo.GetSum<OverflowSafeInt64>();
1032 }
1033};
1034
1035static constexpr std::initializer_list<NWidgetPart> _nested_delivered_cargo_graph_widgets = {
1036 NWidget(NWID_HORIZONTAL),
1037 NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
1038 NWidget(WWT_CAPTION, COLOUR_BROWN), SetStringTip(STR_GRAPH_CARGO_DELIVERED_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
1039 NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_GRAPH_KEY_BUTTON), SetMinimalSize(50, 0), SetStringTip(STR_GRAPH_KEY_BUTTON, STR_GRAPH_KEY_TOOLTIP),
1040 NWidget(WWT_SHADEBOX, COLOUR_BROWN),
1041 NWidget(WWT_DEFSIZEBOX, COLOUR_BROWN),
1042 NWidget(WWT_STICKYBOX, COLOUR_BROWN),
1043 EndContainer(),
1044 NWidget(WWT_PANEL, COLOUR_BROWN, WID_GRAPH_BACKGROUND),
1045 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_GRAPH_GRAPH), SetMinimalSize(576, 128), SetFill(1, 1), SetResize(1, 1),
1046 NWidget(NWID_HORIZONTAL),
1047 NWidget(WWT_TEXT, INVALID_COLOUR, WID_GRAPH_FOOTER), SetFill(1, 0), SetResize(1, 0), SetPadding(2, 0, 2, 0), SetTextStyle(TC_BLACK, FS_SMALL), SetAlignment(SA_CENTER),
1048 NWidget(WWT_RESIZEBOX, COLOUR_BROWN, WID_GRAPH_RESIZE), SetResizeWidgetTypeTip(RWV_HIDE_BEVEL, STR_TOOLTIP_RESIZE),
1049 EndContainer(),
1050 EndContainer(),
1051};
1052
1053static WindowDesc _delivered_cargo_graph_desc(
1054 WDP_AUTO, "graph_delivered_cargo", 0, 0,
1055 WC_DELIVERED_CARGO, WC_NONE,
1056 {},
1057 _nested_delivered_cargo_graph_widgets
1058);
1059
1060void ShowDeliveredCargoGraph()
1061{
1062 AllocateWindowDescFront<DeliveredCargoGraphWindow>(_delivered_cargo_graph_desc, 0);
1063}
1064
1065/***********************/
1066/* PERFORMANCE HISTORY */
1067/***********************/
1068
1069 struct PerformanceHistoryGraphWindow : BaseCompanyGraphWindow {
1070 PerformanceHistoryGraphWindow(WindowDesc &desc, WindowNumber window_number) :
1071 BaseCompanyGraphWindow(desc, STR_JUST_COMMA)
1072 {
1073 this->num_on_x_axis = GRAPH_NUM_MONTHS;
1074 this->num_vert_lines = GRAPH_NUM_MONTHS;
1075 this->draw_dates = !TimerGameEconomy::UsingWallclockUnits();
1076
1077 this->InitializeWindow(window_number);
1078 }
1079
1080 OverflowSafeInt64 GetGraphData(const Company *c, int j) override
1081 {
1082 return c->old_economy[j].performance_history;
1083 }
1084
1085 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
1086 {
1087 if (widget == WID_PHG_DETAILED_PERFORMANCE) ShowPerformanceRatingDetail();
1088 this->BaseGraphWindow::OnClick(pt, widget, click_count);
1089 }
1090};
1091
1092static constexpr std::initializer_list<NWidgetPart> _nested_performance_history_widgets = {
1093 NWidget(NWID_HORIZONTAL),
1094 NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
1095 NWidget(WWT_CAPTION, COLOUR_BROWN), SetStringTip(STR_GRAPH_COMPANY_PERFORMANCE_RATINGS_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
1096 NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_PHG_DETAILED_PERFORMANCE), SetMinimalSize(50, 0), SetStringTip(STR_PERFORMANCE_DETAIL_KEY, STR_GRAPH_PERFORMANCE_DETAIL_TOOLTIP),
1097 NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_GRAPH_KEY_BUTTON), SetMinimalSize(50, 0), SetStringTip(STR_GRAPH_KEY_BUTTON, STR_GRAPH_KEY_TOOLTIP),
1098 NWidget(WWT_SHADEBOX, COLOUR_BROWN),
1099 NWidget(WWT_DEFSIZEBOX, COLOUR_BROWN),
1100 NWidget(WWT_STICKYBOX, COLOUR_BROWN),
1101 EndContainer(),
1102 NWidget(WWT_PANEL, COLOUR_BROWN, WID_GRAPH_BACKGROUND),
1103 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_GRAPH_GRAPH), SetMinimalSize(576, 224), SetFill(1, 1), SetResize(1, 1),
1104 NWidget(NWID_HORIZONTAL),
1105 NWidget(WWT_TEXT, INVALID_COLOUR, WID_GRAPH_FOOTER), SetFill(1, 0), SetResize(1, 0), SetPadding(2, 0, 2, 0), SetTextStyle(TC_BLACK, FS_SMALL), SetAlignment(SA_CENTER),
1106 NWidget(WWT_RESIZEBOX, COLOUR_BROWN, WID_GRAPH_RESIZE), SetResizeWidgetTypeTip(RWV_HIDE_BEVEL, STR_TOOLTIP_RESIZE),
1107 EndContainer(),
1108 EndContainer(),
1109};
1110
1111static WindowDesc _performance_history_desc(
1112 WDP_AUTO, "graph_performance", 0, 0,
1113 WC_PERFORMANCE_HISTORY, WC_NONE,
1114 {},
1115 _nested_performance_history_widgets
1116);
1117
1118void ShowPerformanceHistoryGraph()
1119{
1120 AllocateWindowDescFront<PerformanceHistoryGraphWindow>(_performance_history_desc, 0);
1121}
1122
1123/*****************/
1124/* COMPANY VALUE */
1125/*****************/
1126
1127 struct CompanyValueGraphWindow : BaseCompanyGraphWindow {
1128 CompanyValueGraphWindow(WindowDesc &desc, WindowNumber window_number) :
1129 BaseCompanyGraphWindow(desc, STR_JUST_CURRENCY_SHORT)
1130 {
1131 this->num_on_x_axis = GRAPH_NUM_MONTHS;
1132 this->num_vert_lines = GRAPH_NUM_MONTHS;
1133 this->draw_dates = !TimerGameEconomy::UsingWallclockUnits();
1134
1135 this->InitializeWindow(window_number);
1136 }
1137
1138 OverflowSafeInt64 GetGraphData(const Company *c, int j) override
1139 {
1140 return c->old_economy[j].company_value;
1141 }
1142};
1143
1144static constexpr std::initializer_list<NWidgetPart> _nested_company_value_graph_widgets = {
1145 NWidget(NWID_HORIZONTAL),
1146 NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
1147 NWidget(WWT_CAPTION, COLOUR_BROWN), SetStringTip(STR_GRAPH_COMPANY_VALUES_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
1148 NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_GRAPH_KEY_BUTTON), SetMinimalSize(50, 0), SetStringTip(STR_GRAPH_KEY_BUTTON, STR_GRAPH_KEY_TOOLTIP),
1149 NWidget(WWT_SHADEBOX, COLOUR_BROWN),
1150 NWidget(WWT_DEFSIZEBOX, COLOUR_BROWN),
1151 NWidget(WWT_STICKYBOX, COLOUR_BROWN),
1152 EndContainer(),
1153 NWidget(WWT_PANEL, COLOUR_BROWN, WID_GRAPH_BACKGROUND),
1154 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_GRAPH_GRAPH), SetMinimalSize(576, 224), SetFill(1, 1), SetResize(1, 1),
1155 NWidget(NWID_HORIZONTAL),
1156 NWidget(WWT_TEXT, INVALID_COLOUR, WID_GRAPH_FOOTER), SetFill(1, 0), SetResize(1, 0), SetPadding(2, 0, 2, 0), SetTextStyle(TC_BLACK, FS_SMALL), SetAlignment(SA_CENTER),
1157 NWidget(WWT_RESIZEBOX, COLOUR_BROWN, WID_GRAPH_RESIZE), SetResizeWidgetTypeTip(RWV_HIDE_BEVEL, STR_TOOLTIP_RESIZE),
1158 EndContainer(),
1159 EndContainer(),
1160};
1161
1162static WindowDesc _company_value_graph_desc(
1163 WDP_AUTO, "graph_company_value", 0, 0,
1164 WC_COMPANY_VALUE, WC_NONE,
1165 {},
1166 _nested_company_value_graph_widgets
1167);
1168
1169void ShowCompanyValueGraph()
1170{
1171 AllocateWindowDescFront<CompanyValueGraphWindow>(_company_value_graph_desc, 0);
1172}
1173
1174 struct BaseCargoGraphWindow : BaseGraphWindow {
1175 Scrollbar *vscroll = nullptr;
1176 uint line_height = 0;
1177 uint legend_width = 0;
1178
1179 CargoTypes cargo_types{};
1180
1181 BaseCargoGraphWindow(WindowDesc &desc, StringID format_str_y_axis) : BaseGraphWindow(desc, format_str_y_axis) {}
1182
1183 void InitializeWindow(WindowNumber number, StringID footer_wallclock = STR_NULL, StringID footer_calendar = STR_NULL)
1184 {
1185 this->CreateNestedTree();
1186
1187 this->excluded_range = this->masked_range;
1188 this->cargo_types = this->GetCargoTypes(number);
1189
1190 this->vscroll = this->GetScrollbar(WID_GRAPH_MATRIX_SCROLLBAR);
1191 this->vscroll->SetCount(CountBits(this->cargo_types));
1192
1193 auto *wid = this->GetWidget<NWidgetCore>(WID_GRAPH_FOOTER);
1194 wid->SetString(TimerGameEconomy::UsingWallclockUnits() ? footer_wallclock : footer_calendar);
1195
1196 this->FinishInitNested(number);
1197
1198 /* Initialise the dataset */
1199 this->InvalidateData();
1200 }
1201
1202 virtual CargoTypes GetCargoTypes(WindowNumber number) const = 0;
1203 virtual CargoTypes &GetExcludedCargoTypes() const = 0;
1204
1205 std::optional<uint8_t> GetDatasetIndex(int y) override
1206 {
1207 int row = this->vscroll->GetScrolledRowFromWidget(y, this, WID_GRAPH_MATRIX);
1208 if (row >= this->vscroll->GetCount()) return std::nullopt;
1209
1210 for (const CargoSpec *cs : _sorted_cargo_specs) {
1211 if (!HasBit(this->cargo_types, cs->Index())) continue;
1212 if (row-- > 0) continue;
1213
1214 return cs->Index();
1215 }
1216
1217 return std::nullopt;
1218 }
1219
1220 void OnInit() override
1221 {
1222 /* Width of the legend blob. */
1223 this->legend_width = GetCharacterHeight(FS_SMALL) * 9 / 6;
1224 }
1225
1226 void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
1227 {
1228 if (widget != WID_GRAPH_MATRIX) {
1229 BaseGraphWindow::UpdateWidgetSize(widget, size, padding, fill, resize);
1230 return;
1231 }
1232
1233 size.height = GetCharacterHeight(FS_SMALL) + WidgetDimensions::scaled.framerect.Vertical();
1234
1235 for (CargoType cargo_type : SetCargoBitIterator(this->cargo_types)) {
1236 const CargoSpec *cs = CargoSpec::Get(cargo_type);
1237
1238 Dimension d = GetStringBoundingBox(GetString(STR_GRAPH_CARGO_PAYMENT_CARGO, cs->name));
1239 d.width += this->legend_width + WidgetDimensions::scaled.hsep_normal; // colour field
1240 d.width += WidgetDimensions::scaled.framerect.Horizontal();
1241 d.height += WidgetDimensions::scaled.framerect.Vertical();
1242 size = maxdim(d, size);
1243 }
1244
1245 this->line_height = size.height;
1246 size.height = this->line_height * 11; /* Default number of cargo types in most climates. */
1247 resize.width = 0;
1248 fill.height = resize.height = this->line_height;
1249 }
1250
1251 void DrawWidget(const Rect &r, WidgetID widget) const override
1252 {
1253 if (widget != WID_GRAPH_MATRIX) {
1254 BaseGraphWindow::DrawWidget(r, widget);
1255 return;
1256 }
1257
1258 bool rtl = _current_text_dir == TD_RTL;
1259
1260 int pos = this->vscroll->GetPosition();
1261 int max = pos + this->vscroll->GetCapacity();
1262
1263 Rect line = r.WithHeight(this->line_height);
1264
1265 for (const CargoSpec *cs : _sorted_cargo_specs) {
1266 if (!HasBit(this->cargo_types, cs->Index())) continue;
1267
1268 if (pos-- > 0) continue;
1269 if (--max < 0) break;
1270
1271 bool lowered = !HasBit(this->excluded_data, cs->Index());
1272
1273 /* Redraw frame if lowered */
1274 if (lowered) DrawFrameRect(line, COLOUR_BROWN, FrameFlag::Lowered);
1275
1276 const Rect text = line.Shrink(WidgetDimensions::scaled.framerect);
1277
1278 /* Cargo-colour box with outline */
1279 const Rect cargo = text.WithWidth(this->legend_width, rtl);
1280 GfxFillRect(cargo, PC_BLACK);
1281 PixelColour pc = cs->legend_colour;
1282 if (this->highlight_data == cs->Index()) pc = this->highlight_state ? PC_WHITE : PC_BLACK;
1283 GfxFillRect(cargo.Shrink(WidgetDimensions::scaled.bevel), pc);
1284
1285 /* Cargo name */
1286 DrawString(text.Indent(this->legend_width + WidgetDimensions::scaled.hsep_normal, rtl), GetString(STR_GRAPH_CARGO_PAYMENT_CARGO, cs->name));
1287
1288 line = line.Translate(0, this->line_height);
1289 }
1290 }
1291
1292 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
1293 {
1294 switch (widget) {
1295 case WID_GRAPH_ENABLE_CARGOES:
1296 /* Remove all cargoes from the excluded lists. */
1297 this->GetExcludedCargoTypes() = {};
1298 this->excluded_data = this->GetExcludedCargoTypes();
1299 this->SetDirty();
1300 break;
1301
1302 case WID_GRAPH_DISABLE_CARGOES: {
1303 /* Add all cargoes to the excluded lists. */
1304 this->GetExcludedCargoTypes() = this->cargo_types;
1305 this->excluded_data = this->GetExcludedCargoTypes();
1306 this->SetDirty();
1307 break;
1308 }
1309
1310 case WID_GRAPH_MATRIX: {
1311 int row = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_GRAPH_MATRIX);
1312 if (row >= this->vscroll->GetCount()) return;
1313
1314 SndClickBeep();
1315
1316 for (const CargoSpec *cs : _sorted_cargo_specs) {
1317 if (!HasBit(this->cargo_types, cs->Index())) continue;
1318 if (row-- > 0) continue;
1319
1320 ToggleBit(this->GetExcludedCargoTypes(), cs->Index());
1321 this->excluded_data = this->GetExcludedCargoTypes();
1322 this->SetDirty();
1323 break;
1324 }
1325 break;
1326 }
1327
1328 default:
1329 this->BaseGraphWindow::OnClick(pt, widget, click_count);
1330 break;
1331 }
1332 }
1333
1334 void OnResize() override
1335 {
1336 this->vscroll->SetCapacityFromWidget(this, WID_GRAPH_MATRIX);
1337 }
1338};
1339
1340/*****************/
1341/* PAYMENT RATES */
1342/*****************/
1343
1344 struct PaymentRatesGraphWindow : BaseCargoGraphWindow {
1345 static inline CargoTypes excluded_cargo_types{};
1346
1347 PaymentRatesGraphWindow(WindowDesc &desc, WindowNumber window_number) : BaseCargoGraphWindow(desc, STR_JUST_CURRENCY_SHORT)
1348 {
1349 this->num_on_x_axis = GRAPH_PAYMENT_RATE_STEPS;
1350 this->num_vert_lines = GRAPH_PAYMENT_RATE_STEPS;
1351 this->draw_dates = false;
1352
1353 this->x_values_reversed = false;
1354 /* The x-axis is labeled in either seconds or days. A day is two seconds, so we adjust the label if needed. */
1355 this->x_values_increment = (TimerGameEconomy::UsingWallclockUnits() ? PAYMENT_GRAPH_X_STEP_SECONDS : PAYMENT_GRAPH_X_STEP_DAYS);
1356
1357 this->InitializeWindow(window_number, STR_GRAPH_CARGO_PAYMENT_RATES_SECONDS, STR_GRAPH_CARGO_PAYMENT_RATES_DAYS);
1358 }
1359
1360 CargoTypes GetCargoTypes(WindowNumber) const override
1361 {
1362 return _standard_cargo_mask;
1363 }
1364
1365 CargoTypes &GetExcludedCargoTypes() const override
1366 {
1367 return PaymentRatesGraphWindow::excluded_cargo_types;
1368 }
1369
1375 void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
1376 {
1377 if (!gui_scope) return;
1378 this->UpdatePaymentRates();
1379 }
1380
1382 const IntervalTimer<TimerWindow> update_payment_interval = {std::chrono::seconds(3), [this](auto) {
1383 this->UpdatePaymentRates();
1384 }};
1385
1386 void UpdateStatistics(bool) override {}
1387
1391 void UpdatePaymentRates()
1392 {
1393 this->excluded_data = this->GetExcludedCargoTypes();
1394
1395 this->data.clear();
1396 for (const CargoSpec *cs : _sorted_standard_cargo_specs) {
1397 DataSet &dataset = this->data.emplace_back();
1398 dataset.colour = cs->legend_colour;
1399 dataset.exclude_bit = cs->Index();
1400
1401 for (uint j = 0; j != this->num_on_x_axis; j++) {
1402 dataset.values[j] = GetTransportedGoodsIncome(10, 20, j * 4 + 4, cs->Index());
1403 }
1404 }
1405 }
1406};
1407
1408static constexpr std::initializer_list<NWidgetPart> _nested_cargo_payment_rates_widgets = {
1409 NWidget(NWID_HORIZONTAL),
1410 NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
1411 NWidget(WWT_CAPTION, COLOUR_BROWN), SetStringTip(STR_GRAPH_CARGO_PAYMENT_RATES_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
1412 NWidget(WWT_SHADEBOX, COLOUR_BROWN),
1413 NWidget(WWT_DEFSIZEBOX, COLOUR_BROWN),
1414 NWidget(WWT_STICKYBOX, COLOUR_BROWN),
1415 EndContainer(),
1416 NWidget(WWT_PANEL, COLOUR_BROWN, WID_GRAPH_BACKGROUND), SetMinimalSize(568, 128),
1417 NWidget(WWT_TEXT, INVALID_COLOUR, WID_GRAPH_HEADER), SetFill(1, 0), SetResize(1, 0), SetPadding(2, 0, 2, 0), SetStringTip(STR_GRAPH_CARGO_PAYMENT_RATES_TITLE), SetTextStyle(TC_BLACK, FS_SMALL), SetAlignment(SA_CENTER),
1418 NWidget(NWID_HORIZONTAL),
1419 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_GRAPH_GRAPH), SetMinimalSize(495, 0), SetFill(1, 1), SetResize(1, 1),
1420 NWidget(NWID_VERTICAL),
1421 NWidget(NWID_SPACER), SetMinimalSize(0, 24), SetFill(0, 1),
1422 NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_GRAPH_ENABLE_CARGOES), SetStringTip(STR_GRAPH_CARGO_ENABLE_ALL, STR_GRAPH_CARGO_TOOLTIP_ENABLE_ALL), SetFill(1, 0),
1423 NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_GRAPH_DISABLE_CARGOES), SetStringTip(STR_GRAPH_CARGO_DISABLE_ALL, STR_GRAPH_CARGO_TOOLTIP_DISABLE_ALL), SetFill(1, 0),
1424 NWidget(NWID_SPACER), SetMinimalSize(0, 4),
1425 NWidget(NWID_HORIZONTAL),
1426 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),
1427 NWidget(NWID_VSCROLLBAR, COLOUR_BROWN, WID_GRAPH_MATRIX_SCROLLBAR),
1428 EndContainer(),
1429 NWidget(NWID_SPACER), SetMinimalSize(0, 24), SetFill(0, 1),
1430 EndContainer(),
1431 NWidget(NWID_SPACER), SetMinimalSize(5, 0), SetFill(0, 1), SetResize(0, 1),
1432 EndContainer(),
1433 NWidget(NWID_HORIZONTAL),
1434 NWidget(WWT_TEXT, INVALID_COLOUR, WID_GRAPH_FOOTER), SetFill(1, 0), SetResize(1, 0), SetPadding(2, 0, 2, 0), SetTextStyle(TC_BLACK, FS_SMALL), SetAlignment(SA_CENTER),
1435 NWidget(WWT_RESIZEBOX, COLOUR_BROWN, WID_GRAPH_RESIZE), SetResizeWidgetTypeTip(RWV_HIDE_BEVEL, STR_TOOLTIP_RESIZE),
1436 EndContainer(),
1437 EndContainer(),
1438};
1439
1440static WindowDesc _cargo_payment_rates_desc(
1441 WDP_AUTO, "graph_cargo_payment_rates", 0, 0,
1442 WC_PAYMENT_RATES, WC_NONE,
1443 {},
1444 _nested_cargo_payment_rates_widgets
1445);
1446
1447
1448void ShowCargoPaymentRates()
1449{
1450 AllocateWindowDescFront<PaymentRatesGraphWindow>(_cargo_payment_rates_desc, 0);
1451}
1452
1453/*****************************/
1454/* PERFORMANCE RATING DETAIL */
1455/*****************************/
1456
1457 struct PerformanceRatingDetailWindow : Window {
1458 static CompanyID company;
1459 int timeout = 0;
1460 uint score_info_left = 0;
1461 uint score_info_right = 0;
1462 uint bar_left = 0;
1463 uint bar_right = 0;
1464 uint bar_width = 0;
1465 uint bar_height = 0;
1466 uint score_detail_left = 0;
1467 uint score_detail_right = 0;
1468
1469 PerformanceRatingDetailWindow(WindowDesc &desc, WindowNumber window_number) : Window(desc)
1470 {
1471 this->UpdateCompanyStats();
1472
1473 this->InitNested(window_number);
1474 this->OnInvalidateData(CompanyID::Invalid().base());
1475 }
1476
1477 void UpdateCompanyStats()
1478 {
1479 /* Update all company stats with the current data
1480 * (this is because _score_info is not saved to a savegame) */
1481 for (Company *c : Company::Iterate()) {
1482 UpdateCompanyRatingAndValue(c, false);
1483 }
1484
1485 this->timeout = Ticks::DAY_TICKS * 5;
1486 }
1487
1488 void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
1489 {
1490 switch (widget) {
1491 case WID_PRD_SCORE_FIRST:
1492 this->bar_height = GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.fullbevel.Vertical();
1493 size.height = this->bar_height + WidgetDimensions::scaled.matrix.Vertical();
1494
1495 uint score_info_width = 0;
1496 for (uint i = SCORE_BEGIN; i < SCORE_END; i++) {
1497 score_info_width = std::max(score_info_width, GetStringBoundingBox(STR_PERFORMANCE_DETAIL_VEHICLES + i).width);
1498 }
1499 score_info_width += GetStringBoundingBox(GetString(STR_JUST_COMMA, GetParamMaxValue(1000))).width + WidgetDimensions::scaled.hsep_wide;
1500
1501 this->bar_width = GetStringBoundingBox(GetString(STR_PERFORMANCE_DETAIL_PERCENT, GetParamMaxValue(100))).width + WidgetDimensions::scaled.hsep_indent * 2; // Wide bars!
1502
1503 /* At this number we are roughly at the max; it can become wider,
1504 * but then you need at 1000 times more money. At that time you're
1505 * not that interested anymore in the last few digits anyway.
1506 * The 500 is because 999 999 500 to 999 999 999 are rounded to
1507 * 1 000 M, and not 999 999 k. Use negative numbers to account for
1508 * the negative income/amount of money etc. as well. */
1509 int max = -(999999999 - 500);
1510
1511 /* Scale max for the display currency. Prior to rendering the value
1512 * is converted into the display currency, which may cause it to
1513 * raise significantly. We need to compensate for that since {{CURRCOMPACT}}
1514 * is used, which can produce quite short renderings of very large
1515 * values. Otherwise the calculated width could be too narrow.
1516 * Note that it doesn't work if there was a currency with an exchange
1517 * rate greater than max.
1518 * When the currency rate is more than 1000, the 999 999 k becomes at
1519 * least 999 999 M which roughly is equally long. Furthermore if the
1520 * exchange rate is that high, 999 999 k is usually not enough anymore
1521 * to show the different currency numbers. */
1522 if (GetCurrency().rate < 1000) max /= GetCurrency().rate;
1523 uint score_detail_width = GetStringBoundingBox(GetString(STR_PERFORMANCE_DETAIL_AMOUNT_CURRENCY, max, max)).width;
1524
1525 size.width = WidgetDimensions::scaled.frametext.Horizontal() + score_info_width + WidgetDimensions::scaled.hsep_wide + this->bar_width + WidgetDimensions::scaled.hsep_wide + score_detail_width;
1526 uint left = WidgetDimensions::scaled.frametext.left;
1527 uint right = size.width - WidgetDimensions::scaled.frametext.right;
1528
1529 bool rtl = _current_text_dir == TD_RTL;
1530 this->score_info_left = rtl ? right - score_info_width : left;
1531 this->score_info_right = rtl ? right : left + score_info_width;
1532
1533 this->score_detail_left = rtl ? left : right - score_detail_width;
1534 this->score_detail_right = rtl ? left + score_detail_width : right;
1535
1536 this->bar_left = left + (rtl ? score_detail_width : score_info_width) + WidgetDimensions::scaled.hsep_wide;
1537 this->bar_right = this->bar_left + this->bar_width - 1;
1538 break;
1539 }
1540 }
1541
1542 void DrawWidget(const Rect &r, WidgetID widget) const override
1543 {
1544 /* No need to draw when there's nothing to draw */
1545 if (this->company == CompanyID::Invalid()) return;
1546
1547 if (IsInsideMM(widget, WID_PRD_COMPANY_FIRST, WID_PRD_COMPANY_LAST + 1)) {
1548 if (this->IsWidgetDisabled(widget)) return;
1549 CompanyID cid = (CompanyID)(widget - WID_PRD_COMPANY_FIRST);
1550 Dimension sprite_size = GetSpriteSize(SPR_COMPANY_ICON);
1551 DrawCompanyIcon(cid, CentreBounds(r.left, r.right, sprite_size.width), CentreBounds(r.top, r.bottom, sprite_size.height));
1552 return;
1553 }
1554
1555 if (!IsInsideMM(widget, WID_PRD_SCORE_FIRST, WID_PRD_SCORE_LAST + 1)) return;
1556
1557 ScoreID score_type = (ScoreID)(widget - WID_PRD_SCORE_FIRST);
1558
1559 /* The colours used to show how the progress is going */
1560 PixelColour colour_done = GetColourGradient(COLOUR_GREEN, SHADE_NORMAL);
1561 PixelColour colour_notdone = GetColourGradient(COLOUR_RED, SHADE_NORMAL);
1562
1563 /* Draw all the score parts */
1564 int64_t val = _score_part[company][score_type];
1565 int64_t needed = _score_info[score_type].needed;
1566 int score = _score_info[score_type].score;
1567
1568 /* SCORE_TOTAL has its own rules ;) */
1569 if (score_type == SCORE_TOTAL) {
1570 for (ScoreID i = SCORE_BEGIN; i < SCORE_END; i++) score += _score_info[i].score;
1571 needed = SCORE_MAX;
1572 }
1573
1574 uint bar_top = CentreBounds(r.top, r.bottom, this->bar_height);
1575 uint text_top = CentreBounds(r.top, r.bottom, GetCharacterHeight(FS_NORMAL));
1576
1577 DrawString(this->score_info_left, this->score_info_right, text_top, STR_PERFORMANCE_DETAIL_VEHICLES + score_type);
1578
1579 /* Draw the score */
1580 DrawString(this->score_info_left, this->score_info_right, text_top, GetString(STR_JUST_COMMA, score), TC_BLACK, SA_RIGHT);
1581
1582 /* Calculate the %-bar */
1583 uint x = Clamp<int64_t>(val, 0, needed) * this->bar_width / needed;
1584 bool rtl = _current_text_dir == TD_RTL;
1585 if (rtl) {
1586 x = this->bar_right - x;
1587 } else {
1588 x = this->bar_left + x;
1589 }
1590
1591 /* Draw the bar */
1592 if (x != this->bar_left) GfxFillRect(this->bar_left, bar_top, x, bar_top + this->bar_height - 1, rtl ? colour_notdone : colour_done);
1593 if (x != this->bar_right) GfxFillRect(x, bar_top, this->bar_right, bar_top + this->bar_height - 1, rtl ? colour_done : colour_notdone);
1594
1595 /* Draw it */
1596 DrawString(this->bar_left, this->bar_right, text_top, GetString(STR_PERFORMANCE_DETAIL_PERCENT, Clamp<int64_t>(val, 0, needed) * 100 / needed), TC_FROMSTRING, SA_HOR_CENTER);
1597
1598 /* SCORE_LOAN is inverted */
1599 if (score_type == SCORE_LOAN) val = needed - val;
1600
1601 /* Draw the amount we have against what is needed
1602 * For some of them it is in currency format */
1603 switch (score_type) {
1604 case SCORE_MIN_PROFIT:
1605 case SCORE_MIN_INCOME:
1606 case SCORE_MAX_INCOME:
1607 case SCORE_MONEY:
1608 case SCORE_LOAN:
1609 DrawString(this->score_detail_left, this->score_detail_right, text_top, GetString(STR_PERFORMANCE_DETAIL_AMOUNT_CURRENCY, val, needed));
1610 break;
1611 default:
1612 DrawString(this->score_detail_left, this->score_detail_right, text_top, GetString(STR_PERFORMANCE_DETAIL_AMOUNT_INT, val, needed));
1613 break;
1614 }
1615 }
1616
1617 void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
1618 {
1619 /* Check which button is clicked */
1620 if (IsInsideMM(widget, WID_PRD_COMPANY_FIRST, WID_PRD_COMPANY_LAST + 1)) {
1621 /* Is it no on disable? */
1622 if (!this->IsWidgetDisabled(widget)) {
1623 this->RaiseWidget(WID_PRD_COMPANY_FIRST + this->company);
1624 this->company = (CompanyID)(widget - WID_PRD_COMPANY_FIRST);
1625 this->LowerWidget(WID_PRD_COMPANY_FIRST + this->company);
1626 this->SetDirty();
1627 }
1628 }
1629 }
1630
1631 void OnGameTick() override
1632 {
1633 /* Update the company score every 5 days */
1634 if (--this->timeout == 0) {
1635 this->UpdateCompanyStats();
1636 this->SetDirty();
1637 }
1638 }
1639
1645 void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
1646 {
1647 if (!gui_scope) return;
1648 /* Disable the companies who are not active */
1649 for (CompanyID i = CompanyID::Begin(); i < MAX_COMPANIES; ++i) {
1650 this->SetWidgetDisabledState(WID_PRD_COMPANY_FIRST + i, !Company::IsValidID(i));
1651 }
1652
1653 /* Check if the currently selected company is still active. */
1654 if (this->company != CompanyID::Invalid() && !Company::IsValidID(this->company)) {
1655 /* Raise the widget for the previous selection. */
1656 this->RaiseWidget(WID_PRD_COMPANY_FIRST + this->company);
1657 this->company = CompanyID::Invalid();
1658 }
1659
1660 if (this->company == CompanyID::Invalid()) {
1661 for (const Company *c : Company::Iterate()) {
1662 this->company = c->index;
1663 break;
1664 }
1665 }
1666
1667 /* Make sure the widget is lowered */
1668 if (this->company != CompanyID::Invalid()) {
1669 this->LowerWidget(WID_PRD_COMPANY_FIRST + this->company);
1670 }
1671 }
1672};
1673
1674 CompanyID PerformanceRatingDetailWindow::company = CompanyID::Invalid();
1675
1676/*******************************/
1677/* INDUSTRY PRODUCTION HISTORY */
1678/*******************************/
1679
1680 struct IndustryProductionGraphWindow : BaseCargoGraphWindow {
1681 static inline constexpr StringID RANGE_LABELS[] = {
1682 STR_GRAPH_INDUSTRY_RANGE_PRODUCED,
1683 STR_GRAPH_INDUSTRY_RANGE_TRANSPORTED,
1684 STR_GRAPH_INDUSTRY_RANGE_DELIVERED,
1685 STR_GRAPH_INDUSTRY_RANGE_WAITING,
1686 };
1687
1688 static inline CargoTypes excluded_cargo_types{};
1689
1690 IndustryProductionGraphWindow(WindowDesc &desc, WindowNumber window_number) :
1691 BaseCargoGraphWindow(desc, STR_JUST_COMMA)
1692 {
1693 this->num_on_x_axis = GRAPH_NUM_MONTHS;
1694 this->num_vert_lines = GRAPH_NUM_MONTHS;
1695 this->month_increment = 1;
1696 this->x_values_increment = ECONOMY_MONTH_MINUTES;
1697 this->draw_dates = !TimerGameEconomy::UsingWallclockUnits();
1698 this->ranges = RANGE_LABELS;
1699
1700 const Industry *i = Industry::Get(window_number);
1701 if (!i->IsCargoProduced()) this->masked_range = (1U << 0) | (1U << 1);
1702 if (!i->IsCargoAccepted()) this->masked_range = (1U << 2) | (1U << 3);
1703
1704 this->InitializeWindow(window_number);
1705 }
1706
1707 void OnInit() override
1708 {
1709 this->BaseCargoGraphWindow::OnInit();
1710
1711 this->scales = TimerGameEconomy::UsingWallclockUnits() ? MONTHLY_SCALE_WALLCLOCK : MONTHLY_SCALE_CALENDAR;
1712 }
1713
1714 CargoTypes GetCargoTypes(WindowNumber window_number) const override
1715 {
1716 CargoTypes cargo_types{};
1717 const Industry *i = Industry::Get(window_number);
1718 for (const auto &a : i->accepted) {
1719 if (IsValidCargoType(a.cargo)) SetBit(cargo_types, a.cargo);
1720 }
1721 for (const auto &p : i->produced) {
1722 if (IsValidCargoType(p.cargo)) SetBit(cargo_types, p.cargo);
1723 }
1724 return cargo_types;
1725 }
1726
1727 CargoTypes &GetExcludedCargoTypes() const override
1728 {
1729 return IndustryProductionGraphWindow::excluded_cargo_types;
1730 }
1731
1732 std::string GetWidgetString(WidgetID widget, StringID stringid) const override
1733 {
1734 if (widget == WID_GRAPH_CAPTION) return GetString(STR_GRAPH_INDUSTRY_CAPTION, this->window_number);
1735
1736 return this->Window::GetWidgetString(widget, stringid);
1737 }
1738
1739 void UpdateStatistics(bool initialize) override
1740 {
1741 int mo = (TimerGameEconomy::month / this->month_increment - this->num_vert_lines) * this->month_increment;
1742 auto yr = TimerGameEconomy::year;
1743 while (mo < 0) {
1744 yr--;
1745 mo += 12;
1746 }
1747
1748 if (!initialize && this->excluded_data == this->GetExcludedCargoTypes() && this->num_on_x_axis == this->num_vert_lines && this->year == yr && this->month == mo) {
1749 /* There's no reason to get new stats */
1750 return;
1751 }
1752
1753 this->excluded_data = this->GetExcludedCargoTypes();
1754 this->year = yr;
1755 this->month = mo;
1756
1757 const Industry *i = Industry::Get(this->window_number);
1758
1759 this->data.clear();
1760 for (const auto &p : i->produced) {
1761 if (!IsValidCargoType(p.cargo)) continue;
1762 const CargoSpec *cs = CargoSpec::Get(p.cargo);
1763
1764 this->data.reserve(this->data.size() + 2);
1765
1766 DataSet &produced = this->data.emplace_back();
1767 produced.colour = cs->legend_colour;
1768 produced.exclude_bit = cs->Index();
1769 produced.range_bit = 0;
1770
1771 DataSet &transported = this->data.emplace_back();
1772 transported.colour = cs->legend_colour;
1773 transported.exclude_bit = cs->Index();
1774 transported.range_bit = 1;
1775 transported.dash = 2;
1776
1777 FillFromHistory<GRAPH_NUM_MONTHS>(p.history, i->valid_history, *this->scales[this->selected_scale].history_range,
1778 Filler{{produced}, &Industry::ProducedHistory::production},
1779 Filler{{transported}, &Industry::ProducedHistory::transported});
1780 }
1781
1782 for (const auto &a : i->accepted) {
1783 if (!IsValidCargoType(a.cargo)) continue;
1784 const CargoSpec *cs = CargoSpec::Get(a.cargo);
1785
1786 this->data.reserve(this->data.size() + 2);
1787
1788 DataSet &accepted = this->data.emplace_back();
1789 accepted.colour = cs->legend_colour;
1790 accepted.exclude_bit = cs->Index();
1791 accepted.range_bit = 2;
1792 accepted.dash = 1;
1793
1794 DataSet &waiting = this->data.emplace_back();
1795 waiting.colour = cs->legend_colour;
1796 waiting.exclude_bit = cs->Index();
1797 waiting.range_bit = 3;
1798 waiting.dash = 4;
1799
1800 FillFromHistory<GRAPH_NUM_MONTHS>(a.history.get(), i->valid_history, *this->scales[this->selected_scale].history_range,
1801 Filler{{accepted}, &Industry::AcceptedHistory::accepted},
1802 Filler{{waiting}, &Industry::AcceptedHistory::waiting});
1803 }
1804
1805 this->SetDirty();
1806 }
1807};
1808
1809static constexpr std::initializer_list<NWidgetPart> _nested_industry_production_widgets = {
1810 NWidget(NWID_HORIZONTAL),
1811 NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
1812 NWidget(WWT_CAPTION, COLOUR_BROWN, WID_GRAPH_CAPTION),
1813 NWidget(WWT_SHADEBOX, COLOUR_BROWN),
1814 NWidget(WWT_DEFSIZEBOX, COLOUR_BROWN),
1815 NWidget(WWT_STICKYBOX, COLOUR_BROWN),
1816 EndContainer(),
1817 NWidget(WWT_PANEL, COLOUR_BROWN, WID_GRAPH_BACKGROUND), SetMinimalSize(568, 128),
1818 NWidget(NWID_HORIZONTAL),
1819 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_GRAPH_GRAPH), SetMinimalSize(495, 0), SetFill(1, 1), SetResize(1, 1),
1820 NWidget(NWID_VERTICAL),
1821 NWidget(NWID_SPACER), SetMinimalSize(0, 24), SetFill(0, 1),
1822 NWidget(WWT_MATRIX, COLOUR_BROWN, WID_GRAPH_RANGE_MATRIX), SetFill(1, 0), SetResize(0, 0), SetMatrixDataTip(1, 0, STR_GRAPH_TOGGLE_RANGE),
1823 NWidget(NWID_SPACER), SetMinimalSize(0, 4),
1824 NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_GRAPH_ENABLE_CARGOES), SetStringTip(STR_GRAPH_CARGO_ENABLE_ALL, STR_GRAPH_CARGO_TOOLTIP_ENABLE_ALL), SetFill(1, 0),
1825 NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_GRAPH_DISABLE_CARGOES), SetStringTip(STR_GRAPH_CARGO_DISABLE_ALL, STR_GRAPH_CARGO_TOOLTIP_DISABLE_ALL), SetFill(1, 0),
1826 NWidget(NWID_SPACER), SetMinimalSize(0, 4),
1827 NWidget(NWID_HORIZONTAL),
1828 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),
1829 NWidget(NWID_VSCROLLBAR, COLOUR_BROWN, WID_GRAPH_MATRIX_SCROLLBAR),
1830 EndContainer(),
1831 NWidget(NWID_SPACER), SetMinimalSize(0, 4),
1832 NWidget(WWT_MATRIX, COLOUR_BROWN, WID_GRAPH_SCALE_MATRIX), SetFill(1, 0), SetResize(0, 0), SetMatrixDataTip(1, 0, STR_GRAPH_SELECT_SCALE),
1833 NWidget(NWID_SPACER), SetMinimalSize(0, 24), SetFill(0, 1),
1834 EndContainer(),
1835 NWidget(NWID_SPACER), SetMinimalSize(5, 0), SetFill(0, 1), SetResize(0, 1),
1836 EndContainer(),
1837 NWidget(NWID_HORIZONTAL),
1838 NWidget(WWT_TEXT, INVALID_COLOUR, WID_GRAPH_FOOTER), SetFill(1, 0), SetResize(1, 0), SetPadding(2, 0, 2, 0), SetTextStyle(TC_BLACK, FS_SMALL), SetAlignment(SA_CENTER),
1839 NWidget(WWT_RESIZEBOX, COLOUR_BROWN, WID_GRAPH_RESIZE), SetResizeWidgetTypeTip(RWV_HIDE_BEVEL, STR_TOOLTIP_RESIZE),
1840 EndContainer(),
1841 EndContainer(),
1842};
1843
1844static WindowDesc _industry_production_desc(
1845 WDP_AUTO, "graph_industry_production", 0, 0,
1846 WC_INDUSTRY_PRODUCTION, WC_INDUSTRY_VIEW,
1847 {},
1848 _nested_industry_production_widgets
1849);
1850
1851void ShowIndustryProductionGraph(WindowNumber window_number)
1852{
1853 AllocateWindowDescFront<IndustryProductionGraphWindow>(_industry_production_desc, window_number);
1854}
1855
1856 struct TownCargoGraphWindow : BaseCargoGraphWindow {
1857 static inline constexpr StringID RANGE_LABELS[] = {
1858 STR_GRAPH_TOWN_RANGE_PRODUCED,
1859 STR_GRAPH_TOWN_RANGE_TRANSPORTED,
1860 };
1861
1862 static inline CargoTypes excluded_cargo_types{};
1863
1864 TownCargoGraphWindow(WindowDesc &desc, WindowNumber window_number) : BaseCargoGraphWindow(desc, STR_JUST_COMMA)
1865 {
1866 this->num_on_x_axis = GRAPH_NUM_MONTHS;
1867 this->num_vert_lines = GRAPH_NUM_MONTHS;
1868 this->month_increment = 1;
1869 this->x_values_reversed = true;
1870 this->x_values_increment = ECONOMY_MONTH_MINUTES;
1871 this->draw_dates = !TimerGameEconomy::UsingWallclockUnits();
1872 this->ranges = RANGE_LABELS;
1873
1874 this->InitializeWindow(window_number);
1875 }
1876
1877 void OnInit() override
1878 {
1879 this->BaseCargoGraphWindow::OnInit();
1880
1881 this->scales = TimerGameEconomy::UsingWallclockUnits() ? MONTHLY_SCALE_WALLCLOCK : MONTHLY_SCALE_CALENDAR;
1882 }
1883
1884 CargoTypes GetCargoTypes(WindowNumber window_number) const override
1885 {
1886 CargoTypes cargo_types{};
1887 const Town *t = Town::Get(window_number);
1888 for (const auto &s : t->supplied) {
1889 if (IsValidCargoType(s.cargo)) SetBit(cargo_types, s.cargo);
1890 }
1891 return cargo_types;
1892 }
1893
1894 CargoTypes &GetExcludedCargoTypes() const override
1895 {
1896 return TownCargoGraphWindow::excluded_cargo_types;
1897 }
1898
1899 std::string GetWidgetString(WidgetID widget, StringID stringid) const override
1900 {
1901 if (widget == WID_GRAPH_CAPTION) return GetString(STR_GRAPH_TOWN_CARGO_CAPTION, this->window_number);
1902
1903 return this->Window::GetWidgetString(widget, stringid);
1904 }
1905
1906 void UpdateStatistics(bool initialize) override
1907 {
1908 int mo = TimerGameEconomy::month - this->num_vert_lines;
1909 auto yr = TimerGameEconomy::year;
1910 while (mo < 0) {
1911 yr--;
1912 mo += 12;
1913 }
1914
1915 if (!initialize && this->excluded_data == this->GetExcludedCargoTypes() && this->num_on_x_axis == this->num_vert_lines && this->year == yr && this->month == mo) {
1916 /* There's no reason to get new stats */
1917 return;
1918 }
1919
1920 this->excluded_data = this->GetExcludedCargoTypes();
1921 this->year = yr;
1922 this->month = mo;
1923
1924 const Town *t = Town::Get(this->window_number);
1925
1926 this->data.clear();
1927 for (const auto &s : t->supplied) {
1928 if (!IsValidCargoType(s.cargo)) continue;
1929 const CargoSpec *cs = CargoSpec::Get(s.cargo);
1930
1931 this->data.reserve(this->data.size() + 2);
1932
1933 DataSet &produced = this->data.emplace_back();
1934 produced.colour = cs->legend_colour;
1935 produced.exclude_bit = cs->Index();
1936 produced.range_bit = 0;
1937
1938 DataSet &transported = this->data.emplace_back();
1939 transported.colour = cs->legend_colour;
1940 transported.exclude_bit = cs->Index();
1941 transported.range_bit = 1;
1942 transported.dash = 2;
1943
1944 FillFromHistory<GRAPH_NUM_MONTHS>(s.history, t->valid_history, *this->scales[this->selected_scale].history_range,
1945 Filler{{produced}, &Town::SuppliedHistory::production},
1946 Filler{{transported}, &Town::SuppliedHistory::transported});
1947 }
1948
1949 this->SetDirty();
1950 }
1951};
1952
1953static constexpr std::initializer_list<NWidgetPart> _nested_town_cargo_graph_widgets = {
1954 NWidget(NWID_HORIZONTAL),
1955 NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
1956 NWidget(WWT_CAPTION, COLOUR_BROWN, WID_GRAPH_CAPTION),
1957 NWidget(WWT_SHADEBOX, COLOUR_BROWN),
1958 NWidget(WWT_DEFSIZEBOX, COLOUR_BROWN),
1959 NWidget(WWT_STICKYBOX, COLOUR_BROWN),
1960 EndContainer(),
1961 NWidget(WWT_PANEL, COLOUR_BROWN, WID_GRAPH_BACKGROUND), SetMinimalSize(568, 128),
1962 NWidget(NWID_HORIZONTAL),
1963 NWidget(WWT_EMPTY, INVALID_COLOUR, WID_GRAPH_GRAPH), SetMinimalSize(495, 0), SetFill(1, 1), SetResize(1, 1),
1964 NWidget(NWID_VERTICAL),
1965 NWidget(NWID_SPACER), SetMinimalSize(0, 24), SetFill(0, 1),
1966 NWidget(WWT_MATRIX, COLOUR_BROWN, WID_GRAPH_RANGE_MATRIX), SetFill(1, 0), SetResize(0, 0), SetMatrixDataTip(1, 0, STR_GRAPH_CARGO_PAYMENT_TOGGLE_CARGO),
1967 NWidget(NWID_SPACER), SetMinimalSize(0, 4),
1968 NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_GRAPH_ENABLE_CARGOES), SetStringTip(STR_GRAPH_CARGO_ENABLE_ALL, STR_GRAPH_CARGO_TOOLTIP_ENABLE_ALL), SetFill(1, 0),
1969 NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_GRAPH_DISABLE_CARGOES), SetStringTip(STR_GRAPH_CARGO_DISABLE_ALL, STR_GRAPH_CARGO_TOOLTIP_DISABLE_ALL), SetFill(1, 0),
1970 NWidget(NWID_SPACER), SetMinimalSize(0, 4),
1971 NWidget(NWID_HORIZONTAL),
1972 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),
1973 NWidget(NWID_VSCROLLBAR, COLOUR_BROWN, WID_GRAPH_MATRIX_SCROLLBAR),
1974 EndContainer(),
1975 NWidget(NWID_SPACER), SetMinimalSize(0, 4),
1976 NWidget(WWT_MATRIX, COLOUR_BROWN, WID_GRAPH_SCALE_MATRIX), SetFill(1, 0), SetResize(0, 0), SetMatrixDataTip(1, 0, STR_GRAPH_CARGO_PAYMENT_TOGGLE_CARGO),
1977 NWidget(NWID_SPACER), SetMinimalSize(0, 24), SetFill(0, 1),
1978 EndContainer(),
1979 NWidget(NWID_SPACER), SetMinimalSize(5, 0), SetFill(0, 1), SetResize(0, 1),
1980 EndContainer(),
1981 NWidget(NWID_HORIZONTAL),
1982 NWidget(WWT_TEXT, INVALID_COLOUR, WID_GRAPH_FOOTER), SetFill(1, 0), SetResize(1, 0), SetPadding(2, 0, 2, 0), SetTextStyle(TC_BLACK, FS_SMALL), SetAlignment(SA_CENTER),
1983 NWidget(WWT_RESIZEBOX, COLOUR_BROWN, WID_GRAPH_RESIZE), SetResizeWidgetTypeTip(RWV_HIDE_BEVEL, STR_TOOLTIP_RESIZE),
1984 EndContainer(),
1985 EndContainer(),
1986};
1987
1988static WindowDesc _town_cargo_graph_desc(
1989 WDP_AUTO, "graph_town_cargo", 0, 0,
1990 WC_TOWN_CARGO_GRAPH, WC_TOWN_VIEW,
1991 {},
1992 _nested_town_cargo_graph_widgets
1993);
1994
1995void ShowTownCargoGraph(WindowNumber window_number)
1996{
1997 AllocateWindowDescFront<TownCargoGraphWindow>(_town_cargo_graph_desc, window_number);
1998}
1999
2004 static std::unique_ptr<NWidgetBase> MakePerformanceDetailPanels()
2005{
2006 auto realtime = TimerGameEconomy::UsingWallclockUnits();
2007 const StringID performance_tips[] = {
2008 realtime ? STR_PERFORMANCE_DETAIL_VEHICLES_TOOLTIP_PERIODS : STR_PERFORMANCE_DETAIL_VEHICLES_TOOLTIP_YEARS,
2009 STR_PERFORMANCE_DETAIL_STATIONS_TOOLTIP,
2010 realtime ? STR_PERFORMANCE_DETAIL_MIN_PROFIT_TOOLTIP_PERIODS : STR_PERFORMANCE_DETAIL_MIN_PROFIT_TOOLTIP_YEARS,
2011 STR_PERFORMANCE_DETAIL_MIN_INCOME_TOOLTIP,
2012 STR_PERFORMANCE_DETAIL_MAX_INCOME_TOOLTIP,
2013 STR_PERFORMANCE_DETAIL_DELIVERED_TOOLTIP,
2014 STR_PERFORMANCE_DETAIL_CARGO_TOOLTIP,
2015 STR_PERFORMANCE_DETAIL_MONEY_TOOLTIP,
2016 STR_PERFORMANCE_DETAIL_LOAN_TOOLTIP,
2017 STR_PERFORMANCE_DETAIL_TOTAL_TOOLTIP,
2018 };
2019
2020 static_assert(lengthof(performance_tips) == SCORE_END - SCORE_BEGIN);
2021
2022 auto vert = std::make_unique<NWidgetVertical>(NWidContainerFlag::EqualSize);
2023 for (WidgetID widnum = WID_PRD_SCORE_FIRST; widnum <= WID_PRD_SCORE_LAST; widnum++) {
2024 auto panel = std::make_unique<NWidgetBackground>(WWT_PANEL, COLOUR_BROWN, widnum);
2025 panel->SetFill(1, 1);
2026 panel->SetToolTip(performance_tips[widnum - WID_PRD_SCORE_FIRST]);
2027 vert->Add(std::move(panel));
2028 }
2029 return vert;
2030}
2031
2033std::unique_ptr<NWidgetBase> MakeCompanyButtonRowsGraphGUI()
2034{
2035 return MakeCompanyButtonRows(WID_PRD_COMPANY_FIRST, WID_PRD_COMPANY_LAST, COLOUR_BROWN, 8, STR_PERFORMANCE_DETAIL_SELECT_COMPANY_TOOLTIP);
2036}
2037
2038static constexpr std::initializer_list<NWidgetPart> _nested_performance_rating_detail_widgets = {
2039 NWidget(NWID_HORIZONTAL),
2040 NWidget(WWT_CLOSEBOX, COLOUR_BROWN),
2041 NWidget(WWT_CAPTION, COLOUR_BROWN), SetStringTip(STR_PERFORMANCE_DETAIL, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
2042 NWidget(WWT_SHADEBOX, COLOUR_BROWN),
2043 NWidget(WWT_STICKYBOX, COLOUR_BROWN),
2044 EndContainer(),
2045 NWidget(WWT_PANEL, COLOUR_BROWN),
2046 NWidgetFunction(MakeCompanyButtonRowsGraphGUI), SetPadding(2),
2047 EndContainer(),
2048 NWidgetFunction(MakePerformanceDetailPanels),
2049};
2050
2051static WindowDesc _performance_rating_detail_desc(
2052 WDP_AUTO, "league_details", 0, 0,
2053 WC_PERFORMANCE_DETAIL, WC_NONE,
2054 {},
2055 _nested_performance_rating_detail_widgets
2056);
2057
2058void ShowPerformanceRatingDetail()
2059{
2060 AllocateWindowDescFront<PerformanceRatingDetailWindow>(_performance_rating_detail_desc, 0);
2061}
2062
2063void InitializeGraphGui()
2064{
2065 _legend_excluded_companies = CompanyMask{};
2066 PaymentRatesGraphWindow::excluded_cargo_types = {};
2067 IndustryProductionGraphWindow::excluded_cargo_types = {};
2068}
SetBit
constexpr T SetBit(T &x, const uint8_t y)
Set a bit in a variable.
Definition bitmath_func.hpp:121
CountBits
constexpr uint CountBits(T value)
Counts the number of set bits in a variable.
Definition bitmath_func.hpp:238
HasBit
constexpr bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
Definition bitmath_func.hpp:103
ToggleBit
constexpr T ToggleBit(T &x, const uint8_t y)
Toggles a bit in a variable.
Definition bitmath_func.hpp:157
CargoType
uint8_t CargoType
Cargo slots to indicate a cargo type within a game.
Definition cargo_type.h:21
IsValidCargoType
bool IsValidCargoType(CargoType cargo)
Test whether cargo type is not INVALID_CARGO.
Definition cargo_type.h:104
NUM_CARGO
static const CargoType NUM_CARGO
Maximum number of cargo types in a game.
Definition cargo_type.h:73
_sorted_standard_cargo_specs
std::span< const CargoSpec * > _sorted_standard_cargo_specs
Standard cargo specifications sorted alphabetically by name.
Definition cargotype.cpp:191
_standard_cargo_mask
CargoTypes _standard_cargo_mask
Bitmask of real cargo types available.
Definition cargotype.cpp:36
_sorted_cargo_specs
std::vector< const CargoSpec * > _sorted_cargo_specs
Cargo specifications sorted alphabetically by name.
Definition cargotype.cpp:190
cargotype.h
Types/functions related to cargoes.
BaseBitSet::Test
constexpr bool Test(Tvalue_type value) const
Test if the value-th bit is set.
Definition base_bitset_type.hpp:141
BaseBitSet::base
constexpr Tstorage base() const noexcept
Retrieve the raw value behind this bit set.
Definition base_bitset_type.hpp:219
BaseBitSet::Flip
constexpr Timpl & Flip(Tvalue_type value)
Flip the value-th bit.
Definition base_bitset_type.hpp:116
BaseBitSet::Set
constexpr Timpl & Set()
Set all bits.
Definition base_bitset_type.hpp:40
BaseCompanyGraphWindow
Definition graph_gui.cpp:831
BaseCompanyGraphWindow::UpdateStatistics
void UpdateStatistics(bool initialize) override
Update the statistics.
Definition graph_gui.cpp:852
CompanyMask
Definition company_type.h:43
IntervalTimer
An interval timer will fire every interval, and will continue to fire until it is deleted.
Definition timer.h:76
OverflowSafeInt< int64_t >
Scrollbar
Scrollbar data structure.
Definition widget_type.h:708
Scrollbar::GetCapacity
size_type GetCapacity() const
Gets the number of visible elements of the scrollbar.
Definition widget_type.h:743
Scrollbar::SetCount
void SetCount(size_t num)
Sets the number of elements in the list.
Definition widget_type.h:792
Scrollbar::GetScrolledRowFromWidget
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:2425
Scrollbar::SetCapacityFromWidget
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:2499
Scrollbar::GetCount
size_type GetCount() const
Gets the number of elements in the list.
Definition widget_type.h:734
Scrollbar::GetPosition
size_type GetPosition() const
Gets the position of the first visible element in the list.
Definition widget_type.h:752
Ticks::DAY_TICKS
static constexpr TimerGameTick::Ticks DAY_TICKS
1 day is 74 ticks; TimerGameCalendar::date_fract used to be uint16_t and incremented by 885.
Definition timer_game_tick.h:75
TimerGameEconomy::year
static Year year
Current year, starting at 0.
Definition timer_game_economy.h:36
TimerGameEconomy::month
static Month month
Current month (0..11).
Definition timer_game_economy.h:37
TimerGameEconomy::UsingWallclockUnits
static bool UsingWallclockUnits(bool newgame=false)
Check if we are using wallclock units.
Definition timer_game_economy.cpp:98
TimerGame< struct Economy >::Month
uint8_t Month
Type for the month, note: 0 based, i.e.
Definition timer_game_common.h:44
WidgetDimensions::framerect
RectPadding framerect
Standard padding inside many panels.
Definition window_gui.h:40
WidgetDimensions::frametext
RectPadding frametext
Padding inside frame with text.
Definition window_gui.h:41
WidgetDimensions::scaled
static WidgetDimensions scaled
Widget dimensions scaled for current zoom level.
Definition window_gui.h:30
WidgetDimensions::hsep_wide
int hsep_wide
Wide horizontal spacing.
Definition window_gui.h:62
WidgetDimensions::fullbevel
RectPadding fullbevel
Always-scaled bevel thickness.
Definition window_gui.h:39
WidgetDimensions::unscaled
static const WidgetDimensions unscaled
Unscaled widget dimensions.
Definition window_gui.h:93
WidgetDimensions::matrix
RectPadding matrix
Padding of WWT_MATRIX items.
Definition window_gui.h:42
WidgetDimensions::hsep_normal
int hsep_normal
Normal horizontal spacing.
Definition window_gui.h:61
WidgetDimensions::bevel
RectPadding bevel
Bevel thickness, affected by "scaled bevels" game option.
Definition window_gui.h:38
WidgetDimensions::hsep_indent
int hsep_indent
Width of indentation for tree layouts.
Definition window_gui.h:63
company_base.h
Definition of stuff that is very close to a company, like the company struct itself.
DrawCompanyIcon
void DrawCompanyIcon(CompanyID c, int x, int y)
Draw the icon of a company.
Definition company_cmd.cpp:175
company_gui.h
GUI Functions related to companies.
currency.h
Functions to handle different currencies.
GetCurrency
const CurrencySpec & GetCurrency()
Get the currently selected currency.
Definition currency.h:119
UpdateCompanyRatingAndValue
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:202
_score_info
const ScoreInfo _score_info[]
Score info, values used for computing the detailed performance rating.
Definition economy.cpp:91
economy_func.h
Functions related to the economy.
ScoreID
ScoreID
Score categories in the detailed performance rating.
Definition economy_type.h:61
SCORE_END
@ SCORE_END
How many scores are there..
Definition economy_type.h:73
SCORE_TOTAL
@ SCORE_TOTAL
This must always be the last entry.
Definition economy_type.h:72
SCORE_MAX
static constexpr int SCORE_MAX
The max score that can be in the performance history.
Definition economy_type.h:83
GetCharacterHeight
int GetCharacterHeight(FontSize size)
Get height of a character for a given font size.
Definition fontcache.cpp:87
maxdim
Dimension maxdim(const Dimension &d1, const Dimension &d2)
Compute bounding box of both dimensions.
Definition geometry_func.cpp:22
geometry_func.hpp
Geometry functions.
CentreBounds
int CentreBounds(int min, int max, int size)
Determine where to position a centred object.
Definition geometry_type.hpp:26
GetSpriteSize
Dimension GetSpriteSize(SpriteID sprid, Point *offset, ZoomLevel zoom)
Get the size of a sprite.
Definition gfx.cpp:965
GetStringBoundingBox
Dimension GetStringBoundingBox(std::string_view str, FontSize start_fontsize)
Return the string dimension in pixels.
Definition gfx.cpp:894
DrawString
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:665
DrawStringMultiLineWithClipping
bool DrawStringMultiLineWithClipping(int left, int right, int top, int bottom, std::string_view str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
Draw a multiline string, possibly over multiple lines, if the region is within the current display cl...
Definition gfx.cpp:867
GfxFillRect
void GfxFillRect(int left, int top, int right, int bottom, const std::variant< PixelColour, PaletteID > &colour, FillRectMode mode)
Applies a certain FillRectMode-operation to a rectangle [left, right] x [top, bottom] on the screen.
Definition gfx.cpp:115
gfx_func.h
Functions related to the gfx engine.
FS_SMALL
@ FS_SMALL
Index of the small font in the font tables.
Definition gfx_type.h:250
FS_NORMAL
@ FS_NORMAL
Index of the normal font in the font tables.
Definition gfx_type.h:249
SA_LEFT
@ SA_LEFT
Left align the text.
Definition gfx_type.h:388
SA_RIGHT
@ SA_RIGHT
Right align the text (must be a single bit).
Definition gfx_type.h:390
SA_HOR_CENTER
@ SA_HOR_CENTER
Horizontally center the text.
Definition gfx_type.h:389
SA_FORCE
@ SA_FORCE
Force the alignment, i.e. don't swap for RTL languages.
Definition gfx_type.h:400
SA_CENTER
@ SA_CENTER
Center both horizontally and vertically.
Definition gfx_type.h:398
TextColour
TextColour
Colour of the strings, see _string_colourmap in table/string_colours.h or docs/ottd-colourtext-palett...
Definition gfx_type.h:307
FILLRECT_CHECKER
@ FILLRECT_CHECKER
Draw only every second pixel, used for greying-out.
Definition gfx_type.h:346
INT64_MAX_IN_DOUBLE
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:47
MakeNWidgetCompanyLines
static std::unique_ptr< NWidgetBase > MakeNWidgetCompanyLines()
Construct a vertical list of buttons, one for each company.
Definition graph_gui.cpp:119
MakeCompanyButtonRowsGraphGUI
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:2033
MakePerformanceDetailPanels
static std::unique_ptr< NWidgetBase > MakePerformanceDetailPanels()
Make a vertical list of panels for outputting score details.
Definition graph_gui.cpp:2004
graph_gui.h
Graph GUI functions.
graph_widget.h
Types related to the graph widgets.
WID_GRAPH_FOOTER
@ WID_GRAPH_FOOTER
Footer.
Definition graph_widget.h:32
WID_GRAPH_RESIZE
@ WID_GRAPH_RESIZE
Resize button.
Definition graph_widget.h:30
WID_GRAPH_BACKGROUND
@ WID_GRAPH_BACKGROUND
Background of the window.
Definition graph_widget.h:27
WID_GRAPH_SCALE_MATRIX
@ WID_GRAPH_SCALE_MATRIX
Horizontal axis scale list.
Definition graph_widget.h:40
WID_GRAPH_GRAPH
@ WID_GRAPH_GRAPH
Graph itself.
Definition graph_widget.h:29
WID_PHG_DETAILED_PERFORMANCE
@ WID_PHG_DETAILED_PERFORMANCE
Detailed performance.
Definition graph_widget.h:42
WID_GRAPH_HEADER
@ WID_GRAPH_HEADER
Header.
Definition graph_widget.h:31
WID_GRAPH_MATRIX_SCROLLBAR
@ WID_GRAPH_MATRIX_SCROLLBAR
Cargo list scrollbar.
Definition graph_widget.h:37
WID_GRAPH_DISABLE_CARGOES
@ WID_GRAPH_DISABLE_CARGOES
Disable cargoes button.
Definition graph_widget.h:35
WID_GRAPH_CAPTION
@ WID_GRAPH_CAPTION
Caption.
Definition graph_widget.h:28
WID_GRAPH_KEY_BUTTON
@ WID_GRAPH_KEY_BUTTON
Key button.
Definition graph_widget.h:26
WID_GRAPH_MATRIX
@ WID_GRAPH_MATRIX
Cargo list.
Definition graph_widget.h:36
WID_GRAPH_ENABLE_CARGOES
@ WID_GRAPH_ENABLE_CARGOES
Enable cargoes button.
Definition graph_widget.h:34
WID_GRAPH_RANGE_MATRIX
@ WID_GRAPH_RANGE_MATRIX
Range list.
Definition graph_widget.h:39
WID_GL_FIRST_COMPANY
@ WID_GL_FIRST_COMPANY
First company in the legend.
Definition graph_widget.h:20
WID_GL_LAST_COMPANY
@ WID_GL_LAST_COMPANY
Last company in the legend.
Definition graph_widget.h:21
WID_GL_BACKGROUND
@ WID_GL_BACKGROUND
Background of the window.
Definition graph_widget.h:18
WID_PRD_COMPANY_FIRST
@ WID_PRD_COMPANY_FIRST
First company.
Definition graph_widget.h:50
WID_PRD_SCORE_FIRST
@ WID_PRD_SCORE_FIRST
First entry in the score list.
Definition graph_widget.h:47
WID_PRD_SCORE_LAST
@ WID_PRD_SCORE_LAST
Last entry in the score list.
Definition graph_widget.h:48
WID_PRD_COMPANY_LAST
@ WID_PRD_COMPANY_LAST
Last company.
Definition graph_widget.h:51
SetMatrixDataTip
constexpr NWidgetPart SetMatrixDataTip(uint32_t cols, uint32_t rows, StringID tip={})
Widget part function for setting the data and tooltip of WWT_MATRIX widgets.
Definition widget_type.h:1308
NWidgetFunction
constexpr NWidgetPart NWidgetFunction(NWidgetFunctionType *func_ptr)
Obtain a nested widget (sub)tree from an external source.
Definition widget_type.h:1445
SetFill
constexpr NWidgetPart SetFill(uint16_t fill_x, uint16_t fill_y)
Widget part function for setting filling.
Definition widget_type.h:1219
SetScrollbar
constexpr NWidgetPart SetScrollbar(WidgetID index)
Attach a scrollbar to a widget.
Definition widget_type.h:1399
SetPadding
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:1332
SetStringTip
constexpr NWidgetPart SetStringTip(StringID string, StringID tip={})
Widget part function for setting the string and tooltip.
Definition widget_type.h:1240
SetTextStyle
constexpr NWidgetPart SetTextStyle(TextColour colour, FontSize size=FS_NORMAL)
Widget part function for setting the text style.
Definition widget_type.h:1198
SetMinimalSize
constexpr NWidgetPart SetMinimalSize(int16_t x, int16_t y)
Widget part function for setting the minimal size.
Definition widget_type.h:1156
SetResizeWidgetTypeTip
constexpr NWidgetPart SetResizeWidgetTypeTip(ResizeWidgetValues widget_type, StringID tip)
Widget part function for setting the resize widget type and tooltip.
Definition widget_type.h:1285
EndContainer
constexpr NWidgetPart EndContainer()
Widget part function for denoting the end of a container (horizontal, vertical, WWT_FRAME,...
Definition widget_type.h:1229
NWidget
constexpr NWidgetPart NWidget(WidgetType tp, Colours col, WidgetID idx=INVALID_WIDGET)
Widget part function for starting a new 'real' widget.
Definition widget_type.h:1424
SetAlignment
constexpr NWidgetPart SetAlignment(StringAlignment align)
Widget part function for setting the alignment of text/images.
Definition widget_type.h:1208
SetResize
constexpr NWidgetPart SetResize(int16_t dx, int16_t dy)
Widget part function for setting the resize step.
Definition widget_type.h:1145
Window::SetDirty
void SetDirty() const
Mark entire window as dirty (in need of re-paint)
Definition window.cpp:966
history_func.hpp
Functions for storing historical data.
industry.h
Base of all industries.
IsInsideMM
constexpr bool IsInsideMM(const size_t x, const size_t min, const size_t max) noexcept
Checks if a value is in an interval.
Definition math_func.hpp:267
abs
constexpr T abs(const T a)
Returns the absolute value of (scalar) variable.
Definition math_func.hpp:23
GetColourGradient
PixelColour GetColourGradient(Colours colour, ColourShade shade)
Get colour gradient palette index.
Definition palette.cpp:388
GREY_SCALE
constexpr PixelColour GREY_SCALE(uint8_t level)
Return the colour for a particular greyscale level.
Definition palette_func.h:94
PC_BLACK
static constexpr PixelColour PC_BLACK
Black palette colour.
Definition palette_func.h:96
PC_WHITE
static constexpr PixelColour PC_WHITE
White palette colour.
Definition palette_func.h:99
safeguards.h
A number of safeguards to prevent using unsafe methods.
_settings_client
ClientSettings _settings_client
The current settings for this game.
Definition settings.cpp:60
SndClickBeep
void SndClickBeep()
Play a beep sound for a click event if enabled in settings.
Definition sound.cpp:253
sound_func.h
Functions related to sound.
sprites.h
This file contains all sprite-related enums and defines.
stdafx.h
Definition of base types and functions in a cross-platform compatible way.
lengthof
#define lengthof(array)
Return the length of an fixed size array.
Definition stdafx.h:271
GetParamMaxValue
uint64_t GetParamMaxValue(uint64_t max_value, uint min_count, FontSize size)
Get some number that is suitable for string size computations.
Definition strings.cpp:236
GetString
std::string GetString(StringID string)
Resolve the given StringID into a std::string with formatting but no parameters.
Definition strings.cpp:424
_current_text_dir
TextDirection _current_text_dir
Text direction of the currently selected language.
Definition strings.cpp:56
strings_func.h
Functions related to OTTD's strings.
StringID
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
Definition strings_type.h:19
TD_RTL
@ TD_RTL
Text is written right-to-left by default.
Definition strings_type.h:27
BaseCargoGraphWindow
Definition graph_gui.cpp:1174
BaseCargoGraphWindow::legend_width
uint legend_width
Width of legend 'blob'.
Definition graph_gui.cpp:1177
BaseCargoGraphWindow::OnClick
void OnClick(Point pt, WidgetID widget, int click_count) override
A click with the left mouse button has been made on the window.
Definition graph_gui.cpp:1292
BaseCargoGraphWindow::OnInit
void OnInit() override
Notification that the nested widget tree gets initialized.
Definition graph_gui.cpp:1220
BaseCargoGraphWindow::UpdateWidgetSize
void UpdateWidgetSize(WidgetID widget, Dimension &size, const Dimension &padding, Dimension &fill, Dimension &resize) override
Update size and resize step of a widget in the window.
Definition graph_gui.cpp:1226
BaseCargoGraphWindow::DrawWidget
void DrawWidget(const Rect &r, WidgetID widget) const override
Draw the contents of a nested widget.
Definition graph_gui.cpp:1251
BaseCargoGraphWindow::cargo_types
CargoTypes cargo_types
Cargo types that can be selected.
Definition graph_gui.cpp:1179
BaseCargoGraphWindow::OnResize
void OnResize() override
Called after the window got resized.
Definition graph_gui.cpp:1334
BaseCargoGraphWindow::vscroll
Scrollbar * vscroll
Cargo list scrollbar.
Definition graph_gui.cpp:1175
BaseCargoGraphWindow::line_height
uint line_height
Pixel height of each cargo type row.
Definition graph_gui.cpp:1176
BaseGraphWindow::BaseFiller
Definition graph_gui.cpp:247
BaseGraphWindow::BaseFiller::dataset
DataSet & dataset
Dataset to fill.
Definition graph_gui.cpp:248
BaseGraphWindow::DataSet
Definition graph_gui.cpp:230
BaseGraphWindow::Filler
Definition graph_gui.cpp:255
BaseGraphWindow::Filler::proj
Tprojection proj
Projection to apply.
Definition graph_gui.cpp:256
BaseGraphWindow::GraphScale
Definition graph_gui.cpp:191
BaseGraphWindow
Definition graph_gui.cpp:170
BaseGraphWindow::GetYLabelWidth
uint GetYLabelWidth(ValuesInterval current_interval, int num_hori_lines) const
Get width for Y labels.
Definition graph_gui.cpp:347
BaseGraphWindow::OnGameTick
void OnGameTick() override
Called once per (game) tick.
Definition graph_gui.cpp:810
BaseGraphWindow::MIN_GRID_PIXEL_SIZE
static const int MIN_GRID_PIXEL_SIZE
Minimum distance between graph lines.
Definition graph_gui.cpp:189
BaseGraphWindow::GRAPH_NUM_MONTHS
static const int GRAPH_NUM_MONTHS
Number of months displayed in the graph.
Definition graph_gui.cpp:178
BaseGraphWindow::DrawWidget
void DrawWidget(const Rect &r, WidgetID widget) const override
Draw the contents of a nested widget.
Definition graph_gui.cpp:702
BaseGraphWindow::ECONOMY_QUARTER_MINUTES
static const int ECONOMY_QUARTER_MINUTES
Minutes per economic quarter.
Definition graph_gui.cpp:183
BaseGraphWindow::GRAPH_PAYMENT_RATE_STEPS
static const int GRAPH_PAYMENT_RATE_STEPS
Number of steps on Payment rate graph.
Definition graph_gui.cpp:179
BaseGraphWindow::OnMouseOver
void OnMouseOver(Point pt, WidgetID widget) override
The mouse is currently moving over the window or has just moved outside of the window.
Definition graph_gui.cpp:785
BaseGraphWindow::GetDataSetRange
std::span< const OverflowSafeInt64 > GetDataSetRange(const DataSet &dataset) const
Get appropriate part of dataset values for the current number of horizontal points.
Definition graph_gui.cpp:266
BaseGraphWindow::OnClick
void OnClick(Point pt, WidgetID widget, int click_count) override
A click with the left mouse button has been made on the window.
Definition graph_gui.cpp:751
BaseGraphWindow::ECONOMY_MONTH_MINUTES
static const int ECONOMY_MONTH_MINUTES
Minutes per economic month.
Definition graph_gui.cpp:184
BaseGraphWindow::excluded_data
uint64_t excluded_data
bitmask of datasets hidden by the player.
Definition graph_gui.cpp:210
BaseGraphWindow::draw_dates
bool draw_dates
Should we draw months and years on the time axis?
Definition graph_gui.cpp:221
BaseGraphWindow::MIN_GRAPH_NUM_LINES_Y
static const int MIN_GRAPH_NUM_LINES_Y
Minimal number of horizontal lines to draw.
Definition graph_gui.cpp:188
BaseGraphWindow::excluded_range
uint64_t excluded_range
bitmask of ranges hidden by the player.
Definition graph_gui.cpp:211
BaseGraphWindow::highlight_range
uint8_t highlight_range
Data range that should be highlighted, or UINT8_MAX for none.
Definition graph_gui.cpp:244
BaseGraphWindow::highlight_state
bool highlight_state
Current state of highlight, toggled every TIMER_BLINK_INTERVAL period.
Definition graph_gui.cpp:245
BaseGraphWindow::month_increment
uint8_t month_increment
month increment between vertical lines. must be divisor of 12.
Definition graph_gui.cpp:219
BaseGraphWindow::PAYMENT_GRAPH_X_STEP_DAYS
static const int PAYMENT_GRAPH_X_STEP_DAYS
X-axis step label for cargo payment rates "Days in transit".
Definition graph_gui.cpp:180
BaseGraphWindow::ECONOMY_YEAR_MINUTES
static const int ECONOMY_YEAR_MINUTES
Minutes per economic year.
Definition graph_gui.cpp:182
BaseGraphWindow::GetValuesInterval
ValuesInterval GetValuesInterval(int num_hori_lines) const
Get the interval that contains the graph's data.
Definition graph_gui.cpp:277
BaseGraphWindow::masked_range
uint64_t masked_range
bitmask of ranges that are not available for the current data.
Definition graph_gui.cpp:212
BaseGraphWindow::GRAPH_AXIS_LABEL_COLOUR
static const TextColour GRAPH_AXIS_LABEL_COLOUR
colour of the graph axis label.
Definition graph_gui.cpp:186
BaseGraphWindow::OnInvalidateData
void OnInvalidateData(int data=0, bool gui_scope=true) override
Some data on this window has become invalid.
Definition graph_gui.cpp:820
BaseGraphWindow::DrawGraph
void DrawGraph(Rect r) const
Actually draw the graph.
Definition graph_gui.cpp:369
BaseGraphWindow::PAYMENT_GRAPH_X_STEP_SECONDS
static const int PAYMENT_GRAPH_X_STEP_SECONDS
X-axis step label for cargo payment rates "Seconds in transit".
Definition graph_gui.cpp:181
BaseGraphWindow::UpdateWidgetSize
void UpdateWidgetSize(WidgetID widget, Dimension &size, const Dimension &padding, Dimension &fill, Dimension &resize) override
Update size and resize step of a widget in the window.
Definition graph_gui.cpp:664
BaseGraphWindow::highlight_data
uint8_t highlight_data
Data set that should be highlighted, or UINT8_MAX for none.
Definition graph_gui.cpp:243
CargoSpec
Specification of a cargo type.
Definition cargotype.h:74
CargoSpec::Get
static CargoSpec * Get(size_t index)
Retrieve cargo details for the given cargo type.
Definition cargotype.h:137
CargoSpec::Index
CargoType Index() const
Determines index of this cargospec.
Definition cargotype.h:108
CargoSpec::name
StringID name
Name of this type of cargo.
Definition cargotype.h:91
ClientSettings::gui
GUISettings gui
settings related to the GUI
Definition settings_type.h:650
CompanyProperties::colour
Colours colour
Company colour.
Definition company_base.h:89
CompanyProperties::old_economy
std::array< CompanyEconomyEntry, MAX_HISTORY_QUARTERS > old_economy
Economic data of the company of the last MAX_HISTORY_QUARTERS quarters.
Definition company_base.h:118
CompanyProperties::num_valid_stat_ent
uint8_t num_valid_stat_ent
Number of valid statistical entries in old_economy.
Definition company_base.h:119
CompanyValueGraphWindow
Definition graph_gui.cpp:1127
Company
Definition company_base.h:127
Coord2D< int >
Coord2D::y
T y
Y coordinate.
Definition geometry_type.hpp:35
CurrencySpec::rate
uint16_t rate
The conversion rate compared to the base currency.
Definition currency.h:78
DeliveredCargoGraphWindow
Definition graph_gui.cpp:1018
Dimension
Dimensions (a width and height) of a rectangle in 2D.
Definition geometry_type.hpp:56
GUISettings::graph_line_thickness
uint8_t graph_line_thickness
the thickness of the lines in the various graph guis
Definition settings_type.h:229
GraphLegendWindow
Definition graph_gui.cpp:54
GraphLegendWindow::OnClick
void OnClick(Point pt, WidgetID widget, int click_count) override
A click with the left mouse button has been made on the window.
Definition graph_gui.cpp:84
GraphLegendWindow::DrawWidget
void DrawWidget(const Rect &r, WidgetID widget) const override
Draw the contents of a nested widget.
Definition graph_gui.cpp:66
GraphLegendWindow::OnInvalidateData
void OnInvalidateData(int data=0, bool gui_scope=true) override
Some data on this window has become invalid.
Definition graph_gui.cpp:105
HistoryRange
Definition history_type.hpp:13
IncomeGraphWindow
Definition graph_gui.cpp:967
IndustryProductionGraphWindow
Definition graph_gui.cpp:1680
IndustryProductionGraphWindow::GetWidgetString
std::string GetWidgetString(WidgetID widget, StringID stringid) const override
Get the raw string for a widget.
Definition graph_gui.cpp:1732
IndustryProductionGraphWindow::OnInit
void OnInit() override
Notification that the nested widget tree gets initialized.
Definition graph_gui.cpp:1707
Industry::AcceptedHistory::waiting
uint16_t waiting
Total accepted.
Definition industry.h:82
Industry::ProducedHistory::transported
uint16_t transported
Total transported.
Definition industry.h:65
Industry::ProducedHistory::production
uint16_t production
Total produced.
Definition industry.h:64
Industry
Defines the internal data of a functional industry.
Definition industry.h:62
Industry::IsCargoAccepted
bool IsCargoAccepted() const
Test if this industry accepts any cargo.
Definition industry.h:223
Industry::valid_history
ValidHistoryMask valid_history
Mask of valid history records.
Definition industry.h:109
Industry::IsCargoProduced
bool IsCargoProduced() const
Test if this industry produces any cargo.
Definition industry.h:229
OperatingProfitGraphWindow
Definition graph_gui.cpp:914
PaymentRatesGraphWindow
Definition graph_gui.cpp:1344
PaymentRatesGraphWindow::UpdatePaymentRates
void UpdatePaymentRates()
Update the payment rates according to the latest information.
Definition graph_gui.cpp:1391
PaymentRatesGraphWindow::OnInvalidateData
void OnInvalidateData(int data=0, bool gui_scope=true) override
Some data on this window has become invalid.
Definition graph_gui.cpp:1375
PaymentRatesGraphWindow::update_payment_interval
const IntervalTimer< TimerWindow > update_payment_interval
Update the payment rates on a regular interval.
Definition graph_gui.cpp:1382
PerformanceHistoryGraphWindow
Definition graph_gui.cpp:1069
PerformanceHistoryGraphWindow::OnClick
void OnClick(Point pt, WidgetID widget, int click_count) override
A click with the left mouse button has been made on the window.
Definition graph_gui.cpp:1085
PerformanceRatingDetailWindow
Definition graph_gui.cpp:1457
PerformanceRatingDetailWindow::OnGameTick
void OnGameTick() override
Called once per (game) tick.
Definition graph_gui.cpp:1631
PerformanceRatingDetailWindow::OnInvalidateData
void OnInvalidateData(int data=0, bool gui_scope=true) override
Some data on this window has become invalid.
Definition graph_gui.cpp:1645
PerformanceRatingDetailWindow::DrawWidget
void DrawWidget(const Rect &r, WidgetID widget) const override
Draw the contents of a nested widget.
Definition graph_gui.cpp:1542
PerformanceRatingDetailWindow::UpdateWidgetSize
void UpdateWidgetSize(WidgetID widget, Dimension &size, const Dimension &padding, Dimension &fill, Dimension &resize) override
Update size and resize step of a widget in the window.
Definition graph_gui.cpp:1488
PerformanceRatingDetailWindow::OnClick
void OnClick(Point pt, WidgetID widget, int click_count) override
A click with the left mouse button has been made on the window.
Definition graph_gui.cpp:1617
PixelColour
Colour for pixel/line drawing.
Definition gfx_type.h:405
PoolID< uint8_t, struct CompanyIDTag, 0xF, 0xFF >
Pool::PoolItem<&_company_pool >::Iterate
static Pool::IterateWrapper< Titem > Iterate(size_t from=0)
Returns an iterable ensemble of all valid Titem.
Definition pool_type.hpp:436
Pool::PoolItem<&_industry_pool >::Get
static Titem * Get(auto index)
Returns Titem with given index.
Definition pool_type.hpp:387
Pool::PoolItem<&_company_pool >::IsValidID
static bool IsValidID(auto index)
Tests whether given index can be used to get valid (non-nullptr) Titem.
Definition pool_type.hpp:376
Pool::PoolItem<&_company_pool >::GetIfValid
static Titem * GetIfValid(auto index)
Returns Titem with given index.
Definition pool_type.hpp:398
RectPadding::Horizontal
constexpr uint Horizontal() const
Get total horizontal padding of RectPadding.
Definition geometry_type.hpp:89
RectPadding::Vertical
constexpr uint Vertical() const
Get total vertical padding of RectPadding.
Definition geometry_type.hpp:95
Rect
Specification of a rectangle with absolute coordinates of all edges.
Definition geometry_type.hpp:101
Rect::WithWidth
Rect WithWidth(int width, bool end) const
Copy Rect and set its width.
Definition geometry_type.hpp:211
Rect::Shrink
Rect Shrink(int s) const
Copy and shrink Rect by s pixels.
Definition geometry_type.hpp:124
Rect::WithHeight
Rect WithHeight(int height, bool end=false) const
Copy Rect and set its height.
Definition geometry_type.hpp:237
Rect::Indent
Rect Indent(int indent, bool end) const
Copy Rect and indent it from its position.
Definition geometry_type.hpp:224
Rect::Translate
Rect Translate(int x, int y) const
Copy and translate Rect by x,y pixels.
Definition geometry_type.hpp:200
ScoreInfo::needed
int needed
How much you need to get the perfect score.
Definition economy_type.h:87
ScoreInfo::score
int score
How much score it will give.
Definition economy_type.h:88
SetBitIterator
Iterable ensemble of each set bit in a value.
Definition bitmath_func.hpp:277
StrongType::Typedef
Templated helper to make a type-safe 'typedef' representing a single POD value.
Definition strong_typedef_type.hpp:130
TownCargoGraphWindow
Definition graph_gui.cpp:1856
TownCargoGraphWindow::GetWidgetString
std::string GetWidgetString(WidgetID widget, StringID stringid) const override
Get the raw string for a widget.
Definition graph_gui.cpp:1899
TownCargoGraphWindow::OnInit
void OnInit() override
Notification that the nested widget tree gets initialized.
Definition graph_gui.cpp:1877
Town::SuppliedHistory::transported
uint32_t transported
Total transported.
Definition town.h:90
Town::SuppliedHistory::production
uint32_t production
Total produced.
Definition town.h:89
Town
Town data structure.
Definition town.h:63
Town::valid_history
ValidHistoryMask valid_history
Mask of valid history records.
Definition town.h:112
ValuesInterval
Contains the interval of a graph's data.
Definition graph_gui.cpp:161
ValuesInterval::lowest
OverflowSafeInt64 lowest
Lowest value of this interval. Must be zero or less.
Definition graph_gui.cpp:163
ValuesInterval::highest
OverflowSafeInt64 highest
Highest value of this interval. Must be zero or greater.
Definition graph_gui.cpp:162
WindowDesc
High level window description.
Definition window_gui.h:168
WindowNumber
Number to differentiate different windows of the same class.
Definition window_type.h:760
Window
Data structure for an opened window.
Definition window_gui.h:274
Window::FinishInitNested
void FinishInitNested(WindowNumber window_number=0)
Perform the second part of the initialization of a nested widget tree.
Definition window.cpp:1807
Window::InvalidateData
void InvalidateData(int data=0, bool gui_scope=true)
Mark this window's data as invalid (in need of re-computing)
Definition window.cpp:3234
Window::RaiseWidget
void RaiseWidget(WidgetID widget_index)
Marks a widget as raised.
Definition window_gui.h:470
Window::GetWidgetString
virtual std::string GetWidgetString(WidgetID widget, StringID stringid) const
Get the raw string for a widget.
Definition window.cpp:504
Window::resize
ResizeInfo resize
Resize information.
Definition window_gui.h:315
Window::scale
int scale
Scale of this window – used to determine how to resize.
Definition window_gui.h:305
Window::CreateNestedTree
void CreateNestedTree()
Perform the first part of the initialization of a nested widget tree.
Definition window.cpp:1797
Window::IsWidgetDisabled
bool IsWidgetDisabled(WidgetID widget_index) const
Gets the enabled/disabled status of a widget.
Definition window_gui.h:411
Window::left
int left
x position of left edge of the window
Definition window_gui.h:310
Window::GetRowFromWidget
int GetRowFromWidget(int clickpos, WidgetID widget, int padding, int line_height=-1) const
Compute the row of a widget that a user clicked in.
Definition window.cpp:212
Window::LowerWidget
void LowerWidget(WidgetID widget_index)
Marks a widget as lowered.
Definition window_gui.h:461
Window::InitNested
void InitNested(WindowNumber number=0)
Perform complete initialization of the Window with nested widgets, to allow use.
Definition window.cpp:1820
Window::GetScrollbar
const Scrollbar * GetScrollbar(WidgetID widnum) const
Return the Scrollbar to a widget index.
Definition window.cpp:313
Window::SetWidgetDisabledState
void SetWidgetDisabledState(WidgetID widget_index, bool disab_stat)
Sets the enabled/disabled status of a widget.
Definition window_gui.h:382
Window::height
int height
Height of the window (number of pixels down in y direction)
Definition window_gui.h:313
Window::width
int width
width of the window (number of pixels to the right in x direction)
Definition window_gui.h:312
Window::ToggleWidgetLoweredState
void ToggleWidgetLoweredState(WidgetID widget_index)
Invert the lowered/raised status of a widget.
Definition window_gui.h:451
Window::window_number
WindowNumber window_number
Window number within the window class.
Definition window_gui.h:303
timer.h
Definition of Interval and OneShot timers.
timer_game_economy.h
Definition of the game-economy-timer.
timer_game_tick.h
Definition of the tick-based game-timer.
timer_window.h
Definition of the Window system.
TIMER_BLINK_INTERVAL
static constexpr std::chrono::milliseconds TIMER_BLINK_INTERVAL
Interval used by blinking interface elements.
Definition timer_window.h:36
town.h
Base of the town class.
DrawFrameRect
void DrawFrameRect(int left, int top, int right, int bottom, Colours colour, FrameFlags flags)
Draw frame rectangle.
Definition widget.cpp:289
ScaleGUITrad
static RectPadding ScaleGUITrad(const RectPadding &r)
Scale a RectPadding to GUI zoom level.
Definition widget.cpp:49
MakeCompanyButtonRows
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:3435
WWT_PUSHTXTBTN
@ WWT_PUSHTXTBTN
Normal push-button (no toggle button) with text caption.
Definition widget_type.h:104
NWID_SPACER
@ NWID_SPACER
Invisible widget that takes some space.
Definition widget_type.h:70
NWID_HORIZONTAL
@ NWID_HORIZONTAL
Horizontal container.
Definition widget_type.h:66
WWT_PANEL
@ WWT_PANEL
Simple depressed panel.
Definition widget_type.h:39
WWT_STICKYBOX
@ WWT_STICKYBOX
Sticky box (at top-right of a window, after WWT_DEFSIZEBOX)
Definition widget_type.h:57
WWT_MATRIX
@ WWT_MATRIX
Grid of rows and columns.
Definition widget_type.h:50
WWT_SHADEBOX
@ WWT_SHADEBOX
Shade box (at top-right of a window, between WWT_DEBUGBOX and WWT_DEFSIZEBOX)
Definition widget_type.h:55
WWT_CAPTION
@ WWT_CAPTION
Window caption (window title between closebox and stickybox)
Definition widget_type.h:52
NWID_VSCROLLBAR
@ NWID_VSCROLLBAR
Vertical scrollbar.
Definition widget_type.h:76
NWID_VERTICAL
@ NWID_VERTICAL
Vertical container.
Definition widget_type.h:68
WWT_CLOSEBOX
@ WWT_CLOSEBOX
Close box (at top-left of a window)
Definition widget_type.h:60
WWT_EMPTY
@ WWT_EMPTY
Empty widget, place holder to reserve space in widget tree.
Definition widget_type.h:37
WWT_RESIZEBOX
@ WWT_RESIZEBOX
Resize box (normally at bottom-right of a window)
Definition widget_type.h:59
WWT_DEFSIZEBOX
@ WWT_DEFSIZEBOX
Default window size box (at top-right of a window, between WWT_SHADEBOX and WWT_STICKYBOX)
Definition widget_type.h:56
WWT_TEXT
@ WWT_TEXT
Pure simple text.
Definition widget_type.h:49
EqualSize
@ EqualSize
Containers should keep all their (resizing) children equally large.
Definition widget_type.h:541
RWV_HIDE_BEVEL
@ RWV_HIDE_BEVEL
Bevel of resize box is hidden.
Definition widget_type.h:29
InvalidateWindowData
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:3294
SetWindowDirty
void SetWindowDirty(WindowClass cls, WindowNumber number)
Mark window as dirty (in need of repainting)
Definition window.cpp:3176
window_func.h
Window functions not directly related to making/drawing windows.
window_gui.h
Functions, definitions and such used only by the GUI.
FrameFlag::Lowered
@ Lowered
If set the frame is lowered and the background colour brighter (ie. buttons when pressed)
WDP_AUTO
@ WDP_AUTO
Find a place automatically.
Definition window_gui.h:144
WidgetID
int WidgetID
Widget ID.
Definition window_type.h:20
WC_PERFORMANCE_HISTORY
@ WC_PERFORMANCE_HISTORY
Performance history graph; Window numbers:
Definition window_type.h:556
WC_PERFORMANCE_DETAIL
@ WC_PERFORMANCE_DETAIL
Performance detail window; Window numbers:
Definition window_type.h:580
WC_PAYMENT_RATES
@ WC_PAYMENT_RATES
Payment rates graph; Window numbers:
Definition window_type.h:574
WC_GRAPH_LEGEND
@ WC_GRAPH_LEGEND
Legend for graphs; Window numbers:
Definition window_type.h:526
WC_NONE
@ WC_NONE
No window, redirects to WC_MAIN_WINDOW.
Definition window_type.h:50
WC_OPERATING_PROFIT
@ WC_OPERATING_PROFIT
Operating profit graph; Window numbers:
Definition window_type.h:544
WC_INDUSTRY_PRODUCTION
@ WC_INDUSTRY_PRODUCTION
Industry production history graph; Window numbers:
Definition window_type.h:586
WC_TOWN_VIEW
@ WC_TOWN_VIEW
Town view; Window numbers:
Definition window_type.h:338
WC_INDUSTRY_VIEW
@ WC_INDUSTRY_VIEW
Industry view; Window numbers:
Definition window_type.h:368
WC_INCOME_GRAPH
@ WC_INCOME_GRAPH
Income graph; Window numbers:
Definition window_type.h:538
WC_DELIVERED_CARGO
@ WC_DELIVERED_CARGO
Delivered cargo graph; Window numbers:
Definition window_type.h:550
WC_COMPANY_VALUE
@ WC_COMPANY_VALUE
Company value graph; Window numbers:
Definition window_type.h:562
WC_TOWN_CARGO_GRAPH
@ WC_TOWN_CARGO_GRAPH
Town cargo history graph; Window numbers:
Definition window_type.h:592
zoom_func.h
Functions related to zooming.
ZoomLevel::Normal
@ Normal
The normal zoom level.