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