OpenTTD Source  20241120-master-g6d3adc6169
station_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 "debug.h"
12 #include "gui.h"
13 #include "textbuf_gui.h"
14 #include "company_func.h"
15 #include "command_func.h"
16 #include "vehicle_gui.h"
17 #include "cargotype.h"
18 #include "station_gui.h"
19 #include "strings_func.h"
20 #include "string_func.h"
21 #include "window_func.h"
22 #include "viewport_func.h"
23 #include "dropdown_type.h"
24 #include "dropdown_common_type.h"
25 #include "dropdown_func.h"
26 #include "station_base.h"
27 #include "waypoint_base.h"
28 #include "tilehighlight_func.h"
29 #include "company_base.h"
30 #include "sortlist_type.h"
31 #include "core/geometry_func.hpp"
32 #include "vehiclelist.h"
33 #include "town.h"
34 #include "linkgraph/linkgraph.h"
35 #include "zoom_func.h"
36 #include "station_cmd.h"
37 
38 #include "widgets/station_widget.h"
39 
40 #include "table/strings.h"
41 
42 #include "safeguards.h"
43 
45 {
46  using StationType = Station;
47 
48  static bool IsValidID(StationID id) { return Station::IsValidID(id); }
49  static bool IsValidBaseStation(const BaseStation *st) { return Station::IsExpected(st); }
50  static bool IsAcceptableWaypointTile(TileIndex) { return false; }
51  static constexpr bool IsWaypoint() { return false; }
52 };
53 
54 template <bool ROAD, TileType TILE_TYPE>
56 {
57  using StationType = Waypoint;
58 
59  static bool IsValidID(StationID id) { return Waypoint::IsValidID(id) && HasBit(Waypoint::Get(id)->waypoint_flags, WPF_ROAD) == ROAD; }
60  static bool IsValidBaseStation(const BaseStation *st) { return Waypoint::IsExpected(st) && HasBit(Waypoint::From(st)->waypoint_flags, WPF_ROAD) == ROAD; }
61  static bool IsAcceptableWaypointTile(TileIndex tile) { return IsTileType(tile, TILE_TYPE); }
62  static constexpr bool IsWaypoint() { return true; }
63 };
66 
77 int DrawStationCoverageAreaText(int left, int right, int top, StationCoverageType sct, int rad, bool supplies)
78 {
79  TileIndex tile = TileVirtXY(_thd.pos.x, _thd.pos.y);
80  CargoTypes cargo_mask = 0;
81  if (_thd.drawstyle == HT_RECT && tile < Map::Size()) {
82  CargoArray cargoes;
83  if (supplies) {
84  cargoes = GetProductionAroundTiles(tile, _thd.size.x / TILE_SIZE, _thd.size.y / TILE_SIZE, rad);
85  } else {
86  cargoes = GetAcceptanceAroundTiles(tile, _thd.size.x / TILE_SIZE, _thd.size.y / TILE_SIZE, rad);
87  }
88 
89  /* Convert cargo counts to a set of cargo bits, and draw the result. */
90  for (CargoID i = 0; i < NUM_CARGO; i++) {
91  switch (sct) {
92  case SCT_PASSENGERS_ONLY: if (!IsCargoInClass(i, CC_PASSENGERS)) continue; break;
93  case SCT_NON_PASSENGERS_ONLY: if (IsCargoInClass(i, CC_PASSENGERS)) continue; break;
94  case SCT_ALL: break;
95  default: NOT_REACHED();
96  }
97  if (cargoes[i] >= (supplies ? 1U : 8U)) SetBit(cargo_mask, i);
98  }
99  }
100  SetDParam(0, cargo_mask);
101  return DrawStringMultiLine(left, right, top, INT32_MAX, supplies ? STR_STATION_BUILD_SUPPLIES_CARGO : STR_STATION_BUILD_ACCEPTS_CARGO);
102 }
103 
108 template <typename T>
110 {
111  /* With distant join we don't know which station will be selected, so don't show any */
112  if (_ctrl_pressed) {
113  SetViewportCatchmentSpecializedStation<typename T::StationType>(nullptr, true);
114  return;
115  }
116 
117  /* Tile area for TileHighlightData */
118  TileArea location(TileVirtXY(_thd.pos.x, _thd.pos.y), _thd.size.x / TILE_SIZE - 1, _thd.size.y / TILE_SIZE - 1);
119 
120  /* If the current tile is already a station, then it must be the nearest station. */
121  if (IsTileType(location.tile, MP_STATION) && GetTileOwner(location.tile) == _local_company) {
122  typename T::StationType *st = T::StationType::GetByTile(location.tile);
123  if (st != nullptr && T::IsValidBaseStation(st)) {
124  SetViewportCatchmentSpecializedStation<typename T::StationType>(st, true);
125  return;
126  }
127  }
128 
129  /* Extended area by one tile */
130  uint x = TileX(location.tile);
131  uint y = TileY(location.tile);
132 
133  /* Waypoints can only be built on existing rail/road tiles, so don't extend area if not highlighting a rail tile. */
134  int max_c = T::IsWaypoint() && !T::IsAcceptableWaypointTile(location.tile) ? 0 : 1;
135  TileArea ta(TileXY(std::max<int>(0, x - max_c), std::max<int>(0, y - max_c)), TileXY(std::min<int>(Map::MaxX(), x + location.w + max_c), std::min<int>(Map::MaxY(), y + location.h + max_c)));
136 
137  typename T::StationType *adjacent = nullptr;
138 
139  /* Direct loop instead of ForAllStationsAroundTiles as we are not interested in catchment area */
140  for (TileIndex tile : ta) {
141  if (IsTileType(tile, MP_STATION) && GetTileOwner(tile) == _local_company) {
142  typename T::StationType *st = T::StationType::GetByTile(tile);
143  if (st == nullptr || !T::IsValidBaseStation(st)) continue;
144  if (adjacent != nullptr && st != adjacent) {
145  /* Multiple nearby, distant join is required. */
146  adjacent = nullptr;
147  break;
148  }
149  adjacent = st;
150  }
151  }
152  SetViewportCatchmentSpecializedStation<typename T::StationType>(adjacent, true);
153 }
154 
161 {
162  /* Test if ctrl state changed */
163  static bool _last_ctrl_pressed;
164  if (_ctrl_pressed != _last_ctrl_pressed) {
165  _thd.dirty = 0xff;
166  _last_ctrl_pressed = _ctrl_pressed;
167  }
168 
169  if (_thd.dirty & 1) {
170  _thd.dirty &= ~1;
171  w->SetDirty();
172 
174  FindStationsAroundSelection<StationTypeFilter>();
175  }
176  }
177 }
178 
179 template <typename T>
180 void CheckRedrawWaypointCoverage()
181 {
182  /* Test if ctrl state changed */
183  static bool _last_ctrl_pressed;
184  if (_ctrl_pressed != _last_ctrl_pressed) {
185  _thd.dirty = 0xff;
186  _last_ctrl_pressed = _ctrl_pressed;
187  }
188 
189  if (_thd.dirty & 1) {
190  _thd.dirty &= ~1;
191 
192  if (_thd.drawstyle == HT_RECT) {
193  FindStationsAroundSelection<T>();
194  }
195  }
196 }
197 
198 void CheckRedrawRailWaypointCoverage(const Window *)
199 {
200  CheckRedrawWaypointCoverage<RailWaypointTypeFilter>();
201 }
202 
203 void CheckRedrawRoadWaypointCoverage(const Window *)
204 {
205  CheckRedrawWaypointCoverage<RoadWaypointTypeFilter>();
206 }
207 
220 static void StationsWndShowStationRating(int left, int right, int y, CargoID type, uint amount, uint8_t rating)
221 {
222  static const uint units_full = 576;
223  static const uint rating_full = 224;
224 
225  const CargoSpec *cs = CargoSpec::Get(type);
226  if (!cs->IsValid()) return;
227 
228  int padding = ScaleGUITrad(1);
229  int width = right - left;
230  int colour = cs->rating_colour;
231  TextColour tc = GetContrastColour(colour);
232  uint w = std::min(amount + 5, units_full) * width / units_full;
233 
234  int height = GetCharacterHeight(FS_SMALL) + padding - 1;
235 
236  if (amount > 30) {
237  /* Draw total cargo (limited) on station */
238  GfxFillRect(left, y, left + w - 1, y + height, colour);
239  } else {
240  /* Draw a (scaled) one pixel-wide bar of additional cargo meter, useful
241  * for stations with only a small amount (<=30) */
242  uint rest = ScaleGUITrad(amount) / 5;
243  if (rest != 0) {
244  GfxFillRect(left, y + height - rest, left + padding - 1, y + height, colour);
245  }
246  }
247 
248  DrawString(left + padding, right, y, cs->abbrev, tc, SA_CENTER, false, FS_SMALL);
249 
250  /* Draw green/red ratings bar (fits under the waiting bar) */
251  y += height + padding + 1;
252  GfxFillRect(left + padding, y, right - padding - 1, y + padding - 1, PC_RED);
253  w = std::min<uint>(rating, rating_full) * (width - padding - padding) / rating_full;
254  if (w != 0) GfxFillRect(left + padding, y, left + w - 1, y + padding - 1, PC_GREEN);
255 }
256 
258 
263 {
264 protected:
265  /* Runtime saved values */
266  struct FilterState {
267  Listing last_sorting;
268  uint8_t facilities;
270  CargoTypes cargoes;
271  };
272 
273  static inline FilterState initial_state = {
274  {false, 0},
276  true,
277  ALL_CARGOTYPES,
278  };
279 
280  /* Constants for sorting stations */
281  static inline const StringID sorter_names[] = {
282  STR_SORT_BY_NAME,
283  STR_SORT_BY_FACILITY,
284  STR_SORT_BY_WAITING_TOTAL,
285  STR_SORT_BY_WAITING_AVAILABLE,
286  STR_SORT_BY_RATING_MAX,
287  STR_SORT_BY_RATING_MIN,
288  };
289  static const std::initializer_list<GUIStationList::SortFunction * const> sorter_funcs;
290 
291  FilterState filter;
292  GUIStationList stations{filter.cargoes};
293  Scrollbar *vscroll;
294  uint rating_width;
295  bool filter_expanded;
296  std::array<uint16_t, NUM_CARGO> stations_per_cargo_type;
298 
305  {
306  if (!this->stations.NeedRebuild()) return;
307 
308  Debug(misc, 3, "Building station list for company {}", owner);
309 
310  this->stations.clear();
311  this->stations_per_cargo_type.fill(0);
312  this->stations_per_cargo_type_no_rating = 0;
313 
314  for (const Station *st : Station::Iterate()) {
315  if ((this->filter.facilities & st->facilities) != 0) { // only stations with selected facilities
316  if (st->owner == owner || (st->owner == OWNER_NONE && HasStationInUse(st->index, true, owner))) {
317  bool has_rating = false;
318  /* Add to the station/cargo counts. */
319  for (CargoID j = 0; j < NUM_CARGO; j++) {
320  if (st->goods[j].HasRating()) this->stations_per_cargo_type[j]++;
321  }
322  for (CargoID j = 0; j < NUM_CARGO; j++) {
323  if (st->goods[j].HasRating()) {
324  has_rating = true;
325  if (HasBit(this->filter.cargoes, j)) {
326  this->stations.push_back(st);
327  break;
328  }
329  }
330  }
331  /* Stations with no cargo rating. */
332  if (!has_rating) {
333  if (this->filter.include_no_rating) this->stations.push_back(st);
334  this->stations_per_cargo_type_no_rating++;
335  }
336  }
337  }
338  }
339 
340  this->stations.RebuildDone();
341 
342  this->vscroll->SetCount(this->stations.size()); // Update the scrollbar
343  }
344 
346  static bool StationNameSorter(const Station * const &a, const Station * const &b, const CargoTypes &)
347  {
348  int r = StrNaturalCompare(a->GetCachedName(), b->GetCachedName()); // Sort by name (natural sorting).
349  if (r == 0) return a->index < b->index;
350  return r < 0;
351  }
352 
354  static bool StationTypeSorter(const Station * const &a, const Station * const &b, const CargoTypes &)
355  {
356  return a->facilities < b->facilities;
357  }
358 
360  static bool StationWaitingTotalSorter(const Station * const &a, const Station * const &b, const CargoTypes &cargo_filter)
361  {
362  int diff = 0;
363 
364  for (CargoID j : SetCargoBitIterator(cargo_filter)) {
365  diff += a->goods[j].cargo.TotalCount() - b->goods[j].cargo.TotalCount();
366  }
367 
368  return diff < 0;
369  }
370 
372  static bool StationWaitingAvailableSorter(const Station * const &a, const Station * const &b, const CargoTypes &cargo_filter)
373  {
374  int diff = 0;
375 
376  for (CargoID j : SetCargoBitIterator(cargo_filter)) {
377  diff += a->goods[j].cargo.AvailableCount() - b->goods[j].cargo.AvailableCount();
378  }
379 
380  return diff < 0;
381  }
382 
384  static bool StationRatingMaxSorter(const Station * const &a, const Station * const &b, const CargoTypes &cargo_filter)
385  {
386  uint8_t maxr1 = 0;
387  uint8_t maxr2 = 0;
388 
389  for (CargoID j : SetCargoBitIterator(cargo_filter)) {
390  if (a->goods[j].HasRating()) maxr1 = std::max(maxr1, a->goods[j].rating);
391  if (b->goods[j].HasRating()) maxr2 = std::max(maxr2, b->goods[j].rating);
392  }
393 
394  return maxr1 < maxr2;
395  }
396 
398  static bool StationRatingMinSorter(const Station * const &a, const Station * const &b, const CargoTypes &cargo_filter)
399  {
400  uint8_t minr1 = 255;
401  uint8_t minr2 = 255;
402 
403  for (CargoID j : SetCargoBitIterator(cargo_filter)) {
404  if (a->goods[j].HasRating()) minr1 = std::min(minr1, a->goods[j].rating);
405  if (b->goods[j].HasRating()) minr2 = std::min(minr2, b->goods[j].rating);
406  }
407 
408  return minr1 > minr2;
409  }
410 
413  {
414  if (!this->stations.Sort()) return;
415 
416  /* Set the modified widget dirty */
418  }
419 
420 public:
422  {
423  /* Load initial filter state. */
424  this->filter = CompanyStationsWindow::initial_state;
425  if (this->filter.cargoes == ALL_CARGOTYPES) this->filter.cargoes = _cargo_mask;
426 
427  this->stations.SetListing(this->filter.last_sorting);
428  this->stations.SetSortFuncs(CompanyStationsWindow::sorter_funcs);
429  this->stations.ForceRebuild();
430  this->stations.NeedResort();
431  this->SortStationsList();
432 
433  this->CreateNestedTree();
434  this->vscroll = this->GetScrollbar(WID_STL_SCROLLBAR);
435  this->FinishInitNested(window_number);
436  this->owner = (Owner)this->window_number;
437 
438  if (this->filter.cargoes == ALL_CARGOTYPES) this->filter.cargoes = _cargo_mask;
439 
440  for (uint i = 0; i < 5; i++) {
441  if (HasBit(this->filter.facilities, i)) this->LowerWidget(i + WID_STL_TRAIN);
442  }
443 
444  this->GetWidget<NWidgetCore>(WID_STL_SORTDROPBTN)->widget_data = CompanyStationsWindow::sorter_names[this->stations.SortType()];
445  }
446 
448  {
449  /* Save filter state. */
450  this->filter.last_sorting = this->stations.GetListing();
451  CompanyStationsWindow::initial_state = this->filter;
452  }
453 
454  void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
455  {
456  switch (widget) {
457  case WID_STL_SORTBY: {
458  Dimension d = GetStringBoundingBox(this->GetWidget<NWidgetCore>(widget)->widget_data);
459  d.width += padding.width + Window::SortButtonWidth() * 2; // Doubled since the string is centred and it also looks better.
460  d.height += padding.height;
461  size = maxdim(size, d);
462  break;
463  }
464 
465  case WID_STL_SORTDROPBTN: {
466  Dimension d = GetStringListBoundingBox(CompanyStationsWindow::sorter_names);
467  d.width += padding.width;
468  d.height += padding.height;
469  size = maxdim(size, d);
470  break;
471  }
472 
473  case WID_STL_LIST:
475  size.height = padding.height + 5 * resize.height;
476 
477  /* Determine appropriate width for mini station rating graph */
478  this->rating_width = 0;
479  for (const CargoSpec *cs : _sorted_standard_cargo_specs) {
480  this->rating_width = std::max(this->rating_width, GetStringBoundingBox(cs->abbrev, FS_SMALL).width);
481  }
482  /* Approximately match original 16 pixel wide rating bars by multiplying string width by 1.6 */
483  this->rating_width = this->rating_width * 16 / 10;
484  break;
485  }
486  }
487 
488  void OnPaint() override
489  {
490  this->BuildStationsList((Owner)this->window_number);
491  this->SortStationsList();
492 
493  this->DrawWidgets();
494  }
495 
496  void DrawWidget(const Rect &r, WidgetID widget) const override
497  {
498  switch (widget) {
499  case WID_STL_SORTBY:
500  /* draw arrow pointing up/down for ascending/descending sorting */
502  break;
503 
504  case WID_STL_LIST: {
505  bool rtl = _current_text_dir == TD_RTL;
506  Rect tr = r.Shrink(WidgetDimensions::scaled.framerect);
507  uint line_height = this->GetWidget<NWidgetBase>(widget)->resize_y;
508  /* Spacing between station name and first rating graph. */
509  int text_spacing = WidgetDimensions::scaled.hsep_wide;
510  /* Spacing between additional rating graphs. */
511  int rating_spacing = WidgetDimensions::scaled.hsep_normal;
512 
513  auto [first, last] = this->vscroll->GetVisibleRangeIterators(this->stations);
514  for (auto it = first; it != last; ++it) {
515  const Station *st = *it;
516  assert(st->xy != INVALID_TILE);
517 
518  /* Do not do the complex check HasStationInUse here, it may be even false
519  * when the order had been removed and the station list hasn't been removed yet */
520  assert(st->owner == owner || st->owner == OWNER_NONE);
521 
522  SetDParam(0, st->index);
523  SetDParam(1, st->facilities);
524  int x = DrawString(tr.left, tr.right, tr.top + (line_height - GetCharacterHeight(FS_NORMAL)) / 2, STR_STATION_LIST_STATION);
525  x += rtl ? -text_spacing : text_spacing;
526 
527  /* show cargo waiting and station ratings */
528  for (const CargoSpec *cs : _sorted_standard_cargo_specs) {
529  CargoID cid = cs->Index();
530  if (st->goods[cid].HasRating()) {
531  /* For RTL we work in exactly the opposite direction. So
532  * decrement the space needed first, then draw to the left
533  * instead of drawing to the left and then incrementing
534  * the space. */
535  if (rtl) {
536  x -= rating_width + rating_spacing;
537  if (x < tr.left) break;
538  }
539  StationsWndShowStationRating(x, x + rating_width, tr.top, cid, st->goods[cid].cargo.TotalCount(), st->goods[cid].rating);
540  if (!rtl) {
541  x += rating_width + rating_spacing;
542  if (x > tr.right) break;
543  }
544  }
545  }
546  tr.top += line_height;
547  }
548 
549  if (this->vscroll->GetCount() == 0) { // company has no stations
550  DrawString(tr.left, tr.right, tr.top + (line_height - GetCharacterHeight(FS_NORMAL)) / 2, STR_STATION_LIST_NONE);
551  return;
552  }
553  break;
554  }
555  }
556  }
557 
558  void SetStringParameters(WidgetID widget) const override
559  {
560  if (widget == WID_STL_CAPTION) {
561  SetDParam(0, this->window_number);
562  SetDParam(1, this->vscroll->GetCount());
563  }
564 
565  if (widget == WID_STL_CARGODROPDOWN) {
566  if (this->filter.cargoes == 0) {
567  SetDParam(0, this->filter.include_no_rating ? STR_STATION_LIST_CARGO_FILTER_ONLY_NO_RATING : STR_STATION_LIST_CARGO_FILTER_NO_CARGO_TYPES);
568  } else if (this->filter.cargoes == _cargo_mask) {
569  SetDParam(0, this->filter.include_no_rating ? STR_STATION_LIST_CARGO_FILTER_ALL_AND_NO_RATING : STR_CARGO_TYPE_FILTER_ALL);
570  } else if (CountBits(this->filter.cargoes) == 1 && !this->filter.include_no_rating) {
571  SetDParam(0, CargoSpec::Get(FindFirstBit(this->filter.cargoes))->name);
572  } else {
573  SetDParam(0, STR_STATION_LIST_CARGO_FILTER_MULTIPLE);
574  }
575  }
576  }
577 
578  DropDownList BuildCargoDropDownList(bool expanded) const
579  {
580  /* Define a custom item consisting of check mark, count string, icon and name string. */
582 
583  DropDownList list;
584  list.push_back(MakeDropDownListStringItem(STR_STATION_LIST_CARGO_FILTER_SELECT_ALL, CargoFilterCriteria::CF_SELECT_ALL));
585  list.push_back(MakeDropDownListDividerItem());
586 
587  bool any_hidden = false;
588 
589  uint16_t count = this->stations_per_cargo_type_no_rating;
590  if (count == 0 && !expanded) {
591  any_hidden = true;
592  } else {
593  list.push_back(std::make_unique<DropDownString<DropDownListCheckedItem, FS_SMALL, true>>(fmt::format("{}", count), this->filter.include_no_rating, STR_STATION_LIST_CARGO_FILTER_NO_RATING, CargoFilterCriteria::CF_NO_RATING, false, count == 0));
594  }
595 
597  for (const CargoSpec *cs : _sorted_cargo_specs) {
598  count = this->stations_per_cargo_type[cs->Index()];
599  if (count == 0 && !expanded) {
600  any_hidden = true;
601  } else {
602  list.push_back(std::make_unique<DropDownListCargoItem>(HasBit(this->filter.cargoes, cs->Index()), fmt::format("{}", count), d, cs->GetCargoIcon(), PAL_NONE, cs->name, cs->Index(), false, count == 0));
603  }
604  }
605 
606  if (!expanded && any_hidden) {
607  if (list.size() > 2) list.push_back(MakeDropDownListDividerItem());
608  list.push_back(MakeDropDownListStringItem(STR_STATION_LIST_CARGO_FILTER_EXPAND, CargoFilterCriteria::CF_EXPAND_LIST));
609  }
610 
611  return list;
612  }
613 
614  void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
615  {
616  switch (widget) {
617  case WID_STL_LIST: {
618  auto it = this->vscroll->GetScrolledItemFromWidget(this->stations, pt.y, this, WID_STL_LIST, WidgetDimensions::scaled.framerect.top);
619  if (it == this->stations.end()) return; // click out of list bound
620 
621  const Station *st = *it;
622  /* do not check HasStationInUse - it is slow and may be invalid */
623  assert(st->owner == (Owner)this->window_number || st->owner == OWNER_NONE);
624 
625  if (_ctrl_pressed) {
627  } else {
629  }
630  break;
631  }
632 
633  case WID_STL_TRAIN:
634  case WID_STL_TRUCK:
635  case WID_STL_BUS:
636  case WID_STL_AIRPLANE:
637  case WID_STL_SHIP:
638  if (_ctrl_pressed) {
639  ToggleBit(this->filter.facilities, widget - WID_STL_TRAIN);
640  this->ToggleWidgetLoweredState(widget);
641  } else {
642  for (uint i : SetBitIterator(this->filter.facilities)) {
643  this->RaiseWidget(i + WID_STL_TRAIN);
644  }
645  this->filter.facilities = 1 << (widget - WID_STL_TRAIN);
646  this->LowerWidget(widget);
647  }
648  this->stations.ForceRebuild();
649  this->SetDirty();
650  break;
651 
652  case WID_STL_FACILALL:
653  for (WidgetID i = WID_STL_TRAIN; i <= WID_STL_SHIP; i++) {
654  this->LowerWidget(i);
655  }
656 
657  this->filter.facilities = FACIL_TRAIN | FACIL_TRUCK_STOP | FACIL_BUS_STOP | FACIL_AIRPORT | FACIL_DOCK;
658  this->stations.ForceRebuild();
659  this->SetDirty();
660  break;
661 
662  case WID_STL_SORTBY: // flip sorting method asc/desc
663  this->stations.ToggleSortOrder();
664  this->SetDirty();
665  break;
666 
667  case WID_STL_SORTDROPBTN: // select sorting criteria dropdown menu
668  ShowDropDownMenu(this, CompanyStationsWindow::sorter_names, this->stations.SortType(), WID_STL_SORTDROPBTN, 0, 0);
669  break;
670 
672  this->filter_expanded = false;
673  ShowDropDownList(this, this->BuildCargoDropDownList(this->filter_expanded), -1, widget, 0, false, true);
674  break;
675  }
676  }
677 
678  void OnDropdownSelect(int widget, int index) override
679  {
680  if (widget == WID_STL_SORTDROPBTN) {
681  if (this->stations.SortType() != index) {
682  this->stations.SetSortType(index);
683 
684  /* Display the current sort variant */
685  this->GetWidget<NWidgetCore>(WID_STL_SORTDROPBTN)->widget_data = CompanyStationsWindow::sorter_names[this->stations.SortType()];
686 
687  this->SetDirty();
688  }
689  }
690 
691  if (widget == WID_STL_CARGODROPDOWN) {
692  FilterState oldstate = this->filter;
693 
694  if (index >= 0 && index < NUM_CARGO) {
695  if (_ctrl_pressed) {
696  ToggleBit(this->filter.cargoes, index);
697  } else {
698  this->filter.cargoes = 1ULL << index;
699  this->filter.include_no_rating = false;
700  }
701  } else if (index == CargoFilterCriteria::CF_NO_RATING) {
702  if (_ctrl_pressed) {
703  this->filter.include_no_rating = !this->filter.include_no_rating;
704  } else {
705  this->filter.include_no_rating = true;
706  this->filter.cargoes = 0;
707  }
708  } else if (index == CargoFilterCriteria::CF_SELECT_ALL) {
709  this->filter.cargoes = _cargo_mask;
710  this->filter.include_no_rating = true;
711  } else if (index == CargoFilterCriteria::CF_EXPAND_LIST) {
712  this->filter_expanded = true;
713  ReplaceDropDownList(this, this->BuildCargoDropDownList(this->filter_expanded));
714  return;
715  }
716 
717  if (oldstate.cargoes != this->filter.cargoes || oldstate.include_no_rating != this->filter.include_no_rating) {
718  this->stations.ForceRebuild();
719  this->SetDirty();
720 
721  /* Only refresh the list if it's changed. */
722  if (_ctrl_pressed) ReplaceDropDownList(this, this->BuildCargoDropDownList(this->filter_expanded));
723  }
724 
725  /* Always close the list if ctrl is not pressed. */
727  }
728  }
729 
730  void OnGameTick() override
731  {
732  if (this->stations.NeedResort()) {
733  Debug(misc, 3, "Periodic rebuild station list company {}", this->window_number);
734  this->SetDirty();
735  }
736  }
737 
738  void OnResize() override
739  {
740  this->vscroll->SetCapacityFromWidget(this, WID_STL_LIST, WidgetDimensions::scaled.framerect.Vertical());
741  }
742 
748  void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
749  {
750  if (data == 0) {
751  /* This needs to be done in command-scope to enforce rebuilding before resorting invalid data */
752  this->stations.ForceRebuild();
753  } else {
754  this->stations.ForceResort();
755  }
756  }
757 };
758 
759 /* Available station sorting functions */
760 const std::initializer_list<GUIStationList::SortFunction * const> CompanyStationsWindow::sorter_funcs = {
761  &StationNameSorter,
762  &StationTypeSorter,
763  &StationWaitingTotalSorter,
764  &StationWaitingAvailableSorter,
765  &StationRatingMaxSorter,
766  &StationRatingMinSorter
767 };
768 
769 static constexpr NWidgetPart _nested_company_stations_widgets[] = {
771  NWidget(WWT_CLOSEBOX, COLOUR_GREY),
772  NWidget(WWT_CAPTION, COLOUR_GREY, WID_STL_CAPTION), SetDataTip(STR_STATION_LIST_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
773  NWidget(WWT_SHADEBOX, COLOUR_GREY),
774  NWidget(WWT_DEFSIZEBOX, COLOUR_GREY),
775  NWidget(WWT_STICKYBOX, COLOUR_GREY),
776  EndContainer(),
778  NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_STL_TRAIN), SetAspect(WidgetDimensions::ASPECT_VEHICLE_ICON), SetDataTip(STR_TRAIN, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE), SetFill(0, 1),
779  NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_STL_TRUCK), SetAspect(WidgetDimensions::ASPECT_VEHICLE_ICON), SetDataTip(STR_LORRY, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE), SetFill(0, 1),
780  NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_STL_BUS), SetAspect(WidgetDimensions::ASPECT_VEHICLE_ICON), SetDataTip(STR_BUS, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE), SetFill(0, 1),
781  NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_STL_SHIP), SetAspect(WidgetDimensions::ASPECT_VEHICLE_ICON), SetDataTip(STR_SHIP, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE), SetFill(0, 1),
782  NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_STL_AIRPLANE), SetAspect(WidgetDimensions::ASPECT_VEHICLE_ICON), SetDataTip(STR_PLANE, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE), SetFill(0, 1),
783  NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_STL_FACILALL), SetAspect(WidgetDimensions::ASPECT_VEHICLE_ICON), SetDataTip(STR_ABBREV_ALL, STR_STATION_LIST_SELECT_ALL_FACILITIES), SetTextStyle(TC_BLACK, FS_SMALL), SetFill(0, 1),
784  NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalSize(5, 0), SetFill(0, 1), EndContainer(),
785  NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_STL_CARGODROPDOWN), SetFill(1, 0), SetDataTip(STR_JUST_STRING, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE),
786  NWidget(WWT_PANEL, COLOUR_GREY), SetResize(1, 0), SetFill(1, 1), EndContainer(),
787  EndContainer(),
789  NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_STL_SORTBY), SetMinimalSize(81, 12), SetDataTip(STR_BUTTON_SORT_BY, STR_TOOLTIP_SORT_ORDER),
790  NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_STL_SORTDROPBTN), SetMinimalSize(163, 12), SetDataTip(STR_SORT_BY_NAME, STR_TOOLTIP_SORT_CRITERIA), // widget_data gets overwritten.
791  NWidget(WWT_PANEL, COLOUR_GREY), SetResize(1, 0), SetFill(1, 1), EndContainer(),
792  EndContainer(),
794  NWidget(WWT_PANEL, COLOUR_GREY, WID_STL_LIST), SetMinimalSize(346, 125), SetResize(1, 10), SetDataTip(0x0, STR_STATION_LIST_TOOLTIP), SetScrollbar(WID_STL_SCROLLBAR), EndContainer(),
797  NWidget(WWT_RESIZEBOX, COLOUR_GREY),
798  EndContainer(),
799  EndContainer(),
800 };
801 
802 static WindowDesc _company_stations_desc(
803  WDP_AUTO, "list_stations", 358, 162,
805  0,
806  _nested_company_stations_widgets
807 );
808 
815 {
816  if (!Company::IsValidID(company)) return;
817 
818  AllocateWindowDescFront<CompanyStationsWindow>(_company_stations_desc, company);
819 }
820 
821 static constexpr NWidgetPart _nested_station_view_widgets[] = {
823  NWidget(WWT_CLOSEBOX, COLOUR_GREY),
824  NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_SV_RENAME), SetAspect(WidgetDimensions::ASPECT_RENAME), SetDataTip(SPR_RENAME, STR_STATION_VIEW_RENAME_TOOLTIP),
825  NWidget(WWT_CAPTION, COLOUR_GREY, WID_SV_CAPTION), SetDataTip(STR_STATION_VIEW_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
826  NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_SV_LOCATION), SetAspect(WidgetDimensions::ASPECT_LOCATION), SetDataTip(SPR_GOTO_LOCATION, STR_STATION_VIEW_CENTER_TOOLTIP),
827  NWidget(WWT_SHADEBOX, COLOUR_GREY),
828  NWidget(WWT_DEFSIZEBOX, COLOUR_GREY),
829  NWidget(WWT_STICKYBOX, COLOUR_GREY),
830  EndContainer(),
832  NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_SV_GROUP), SetMinimalSize(81, 12), SetFill(1, 1), SetDataTip(STR_STATION_VIEW_GROUP, 0x0),
833  NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_SV_GROUP_BY), SetMinimalSize(168, 12), SetResize(1, 0), SetFill(0, 1), SetDataTip(0x0, STR_TOOLTIP_GROUP_ORDER),
834  EndContainer(),
836  NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SV_SORT_ORDER), SetMinimalSize(81, 12), SetFill(1, 1), SetDataTip(STR_BUTTON_SORT_BY, STR_TOOLTIP_SORT_ORDER),
837  NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_SV_SORT_BY), SetMinimalSize(168, 12), SetResize(1, 0), SetFill(0, 1), SetDataTip(0x0, STR_TOOLTIP_SORT_CRITERIA),
838  EndContainer(),
842  EndContainer(),
845  NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SV_ACCEPTS_RATINGS), SetMinimalSize(46, 12), SetResize(1, 0), SetFill(1, 1),
846  SetDataTip(STR_STATION_VIEW_RATINGS_BUTTON, STR_STATION_VIEW_RATINGS_TOOLTIP),
847  NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_SV_CLOSE_AIRPORT), SetMinimalSize(45, 12), SetResize(1, 0), SetFill(1, 1),
848  SetDataTip(STR_STATION_VIEW_CLOSE_AIRPORT, STR_STATION_VIEW_CLOSE_AIRPORT_TOOLTIP),
849  NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_SV_CATCHMENT), SetMinimalSize(45, 12), SetResize(1, 0), SetFill(1, 1), SetDataTip(STR_BUTTON_CATCHMENT, STR_TOOLTIP_CATCHMENT),
850  NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SV_TRAINS), SetAspect(WidgetDimensions::ASPECT_VEHICLE_ICON), SetFill(0, 1), SetDataTip(STR_TRAIN, STR_STATION_VIEW_SCHEDULED_TRAINS_TOOLTIP),
851  NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SV_ROADVEHS), SetAspect(WidgetDimensions::ASPECT_VEHICLE_ICON), SetFill(0, 1), SetDataTip(STR_LORRY, STR_STATION_VIEW_SCHEDULED_ROAD_VEHICLES_TOOLTIP),
852  NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SV_SHIPS), SetAspect(WidgetDimensions::ASPECT_VEHICLE_ICON), SetFill(0, 1), SetDataTip(STR_SHIP, STR_STATION_VIEW_SCHEDULED_SHIPS_TOOLTIP),
853  NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SV_PLANES), SetAspect(WidgetDimensions::ASPECT_VEHICLE_ICON), SetFill(0, 1), SetDataTip(STR_PLANE, STR_STATION_VIEW_SCHEDULED_AIRCRAFT_TOOLTIP),
854  NWidget(WWT_RESIZEBOX, COLOUR_GREY),
855  EndContainer(),
856 };
857 
867 static void DrawCargoIcons(CargoID i, uint waiting, int left, int right, int y)
868 {
869  int width = ScaleSpriteTrad(10);
870  uint num = std::min<uint>((waiting + (width / 2)) / width, (right - left) / width); // maximum is width / 10 icons so it won't overflow
871  if (num == 0) return;
872 
873  SpriteID sprite = CargoSpec::Get(i)->GetCargoIcon();
874 
875  int x = _current_text_dir == TD_RTL ? left : right - num * width;
876  do {
877  DrawSprite(sprite, PAL_NONE, x, y);
878  x += width;
879  } while (--num);
880 }
881 
882 enum SortOrder {
883  SO_DESCENDING,
884  SO_ASCENDING
885 };
886 
887 class CargoDataEntry;
888 
889 enum class CargoSortType : uint8_t {
890  AsGrouping,
891  Count,
892  StationString,
893  StationID,
894  CargoID,
895 };
896 
897 class CargoSorter {
898 public:
899  CargoSorter(CargoSortType t = CargoSortType::StationID, SortOrder o = SO_ASCENDING) : type(t), order(o) {}
900  CargoSortType GetSortType() {return this->type;}
901  bool operator()(const CargoDataEntry *cd1, const CargoDataEntry *cd2) const;
902 
903 private:
904  CargoSortType type;
905  SortOrder order;
906 
907  template<class Tid>
908  bool SortId(Tid st1, Tid st2) const;
909  bool SortCount(const CargoDataEntry *cd1, const CargoDataEntry *cd2) const;
910  bool SortStation (StationID st1, StationID st2) const;
911 };
912 
913 typedef std::set<CargoDataEntry *, CargoSorter> CargoDataSet;
914 
921 public:
922  CargoDataEntry();
923  ~CargoDataEntry();
924 
931  {
932  return this->InsertOrRetrieve<StationID>(station);
933  }
934 
941  {
942  return this->InsertOrRetrieve<CargoID>(cargo);
943  }
944 
945  void Update(uint count);
946 
951  void Remove(StationID station)
952  {
954  this->Remove(&t);
955  }
956 
962  {
964  this->Remove(&t);
965  }
966 
972  CargoDataEntry *Retrieve(StationID station) const
973  {
975  return this->Retrieve(this->children->find(&t));
976  }
977 
984  {
986  return this->Retrieve(this->children->find(&t));
987  }
988 
989  void Resort(CargoSortType type, SortOrder order);
990 
994  StationID GetStation() const { return this->station; }
995 
999  CargoID GetCargo() const { return this->cargo; }
1000 
1004  uint GetCount() const { return this->count; }
1005 
1009  CargoDataEntry *GetParent() const { return this->parent; }
1010 
1014  uint GetNumChildren() const { return this->num_children; }
1015 
1019  CargoDataSet::iterator Begin() const { return this->children->begin(); }
1020 
1024  CargoDataSet::iterator End() const { return this->children->end(); }
1025 
1029  bool HasTransfers() const { return this->transfers; }
1030 
1034  void SetTransfers(bool value) { this->transfers = value; }
1035 
1036  void Clear();
1037 private:
1038 
1039  CargoDataEntry(StationID st, uint c, CargoDataEntry *p);
1040  CargoDataEntry(CargoID car, uint c, CargoDataEntry *p);
1041  CargoDataEntry(StationID st);
1042  CargoDataEntry(CargoID car);
1043 
1044  CargoDataEntry *Retrieve(CargoDataSet::iterator i) const;
1045 
1046  template<class Tid>
1048 
1049  void Remove(CargoDataEntry *comp);
1050  void IncrementSize();
1051 
1053  const union {
1054  StationID station;
1055  struct {
1057  bool transfers;
1058  };
1059  };
1061  uint count;
1062  CargoDataSet *children;
1063 };
1064 
1065 CargoDataEntry::CargoDataEntry() :
1066  parent(nullptr),
1067  station(INVALID_STATION),
1068  num_children(0),
1069  count(0),
1070  children(new CargoDataSet(CargoSorter(CargoSortType::CargoID)))
1071 {}
1072 
1073 CargoDataEntry::CargoDataEntry(CargoID cargo, uint count, CargoDataEntry *parent) :
1074  parent(parent),
1075  cargo(cargo),
1076  num_children(0),
1077  count(count),
1078  children(new CargoDataSet)
1079 {}
1080 
1081 CargoDataEntry::CargoDataEntry(StationID station, uint count, CargoDataEntry *parent) :
1082  parent(parent),
1083  station(station),
1084  num_children(0),
1085  count(count),
1086  children(new CargoDataSet)
1087 {}
1088 
1089 CargoDataEntry::CargoDataEntry(StationID station) :
1090  parent(nullptr),
1091  station(station),
1092  num_children(0),
1093  count(0),
1094  children(nullptr)
1095 {}
1096 
1097 CargoDataEntry::CargoDataEntry(CargoID cargo) :
1098  parent(nullptr),
1099  cargo(cargo),
1100  num_children(0),
1101  count(0),
1102  children(nullptr)
1103 {}
1104 
1105 CargoDataEntry::~CargoDataEntry()
1106 {
1107  this->Clear();
1108  delete this->children;
1109 }
1110 
1115 {
1116  if (this->children != nullptr) {
1117  for (auto &it : *this->children) {
1118  assert(it != this);
1119  delete it;
1120  }
1121  this->children->clear();
1122  }
1123  if (this->parent != nullptr) this->parent->count -= this->count;
1124  this->count = 0;
1125  this->num_children = 0;
1126 }
1127 
1135 {
1136  CargoDataSet::iterator i = this->children->find(child);
1137  if (i != this->children->end()) {
1138  delete *i;
1139  this->children->erase(i);
1140  }
1141 }
1142 
1149 template<class Tid>
1151 {
1152  CargoDataEntry tmp(child_id);
1153  CargoDataSet::iterator i = this->children->find(&tmp);
1154  if (i == this->children->end()) {
1155  IncrementSize();
1156  return *(this->children->insert(new CargoDataEntry(child_id, 0, this)).first);
1157  } else {
1158  CargoDataEntry *ret = *i;
1159  assert(this->children->value_comp().GetSortType() != CargoSortType::Count);
1160  return ret;
1161  }
1162 }
1163 
1169 void CargoDataEntry::Update(uint count)
1170 {
1171  this->count += count;
1172  if (this->parent != nullptr) this->parent->Update(count);
1173 }
1174 
1179 {
1180  ++this->num_children;
1181  if (this->parent != nullptr) this->parent->IncrementSize();
1182 }
1183 
1184 void CargoDataEntry::Resort(CargoSortType type, SortOrder order)
1185 {
1186  CargoDataSet *new_subs = new CargoDataSet(this->children->begin(), this->children->end(), CargoSorter(type, order));
1187  delete this->children;
1188  this->children = new_subs;
1189 }
1190 
1191 CargoDataEntry *CargoDataEntry::Retrieve(CargoDataSet::iterator i) const
1192 {
1193  if (i == this->children->end()) {
1194  return nullptr;
1195  } else {
1196  assert(this->children->value_comp().GetSortType() != CargoSortType::Count);
1197  return *i;
1198  }
1199 }
1200 
1201 bool CargoSorter::operator()(const CargoDataEntry *cd1, const CargoDataEntry *cd2) const
1202 {
1203  switch (this->type) {
1205  return this->SortId<StationID>(cd1->GetStation(), cd2->GetStation());
1207  return this->SortId<CargoID>(cd1->GetCargo(), cd2->GetCargo());
1208  case CargoSortType::Count:
1209  return this->SortCount(cd1, cd2);
1211  return this->SortStation(cd1->GetStation(), cd2->GetStation());
1212  default:
1213  NOT_REACHED();
1214  }
1215 }
1216 
1217 template<class Tid>
1218 bool CargoSorter::SortId(Tid st1, Tid st2) const
1219 {
1220  return (this->order == SO_ASCENDING) ? st1 < st2 : st2 < st1;
1221 }
1222 
1223 bool CargoSorter::SortCount(const CargoDataEntry *cd1, const CargoDataEntry *cd2) const
1224 {
1225  uint c1 = cd1->GetCount();
1226  uint c2 = cd2->GetCount();
1227  if (c1 == c2) {
1228  return this->SortStation(cd1->GetStation(), cd2->GetStation());
1229  } else if (this->order == SO_ASCENDING) {
1230  return c1 < c2;
1231  } else {
1232  return c2 < c1;
1233  }
1234 }
1235 
1236 bool CargoSorter::SortStation(StationID st1, StationID st2) const
1237 {
1238  if (!Station::IsValidID(st1)) {
1239  return Station::IsValidID(st2) ? this->order == SO_ASCENDING : this->SortId(st1, st2);
1240  } else if (!Station::IsValidID(st2)) {
1241  return order == SO_DESCENDING;
1242  }
1243 
1244  int res = StrNaturalCompare(Station::Get(st1)->GetCachedName(), Station::Get(st2)->GetCachedName()); // Sort by name (natural sorting).
1245  if (res == 0) {
1246  return this->SortId(st1, st2);
1247  } else {
1248  return (this->order == SO_ASCENDING) ? res < 0 : res > 0;
1249  }
1250 }
1251 
1255 struct StationViewWindow : public Window {
1259  struct RowDisplay {
1260  RowDisplay(CargoDataEntry *f, StationID n) : filter(f), next_station(n) {}
1262 
1267  union {
1271  StationID next_station;
1272 
1277  };
1278  };
1279 
1280  typedef std::vector<RowDisplay> CargoDataVector;
1281 
1282  static const int NUM_COLUMNS = 4;
1283 
1288  INV_FLOWS = 0x100,
1289  INV_CARGO = 0x200
1290  };
1291 
1295  enum Grouping {
1300  };
1301 
1305  enum Mode {
1307  MODE_PLANNED
1308  };
1309 
1313  Scrollbar *vscroll;
1314 
1315  /* Height of the #WID_SV_ACCEPT_RATING_LIST widget for different views. */
1316  static constexpr uint RATING_LINES = 13;
1317  static constexpr uint ACCEPTS_LINES = 3;
1318 
1320  static inline const StringID sort_names[] = {
1321  STR_STATION_VIEW_WAITING_STATION,
1322  STR_STATION_VIEW_WAITING_AMOUNT,
1323  STR_STATION_VIEW_PLANNED_STATION,
1324  STR_STATION_VIEW_PLANNED_AMOUNT,
1325  };
1327  static inline const StringID group_names[] = {
1328  STR_STATION_VIEW_GROUP_S_V_D,
1329  STR_STATION_VIEW_GROUP_S_D_V,
1330  STR_STATION_VIEW_GROUP_V_S_D,
1331  STR_STATION_VIEW_GROUP_V_D_S,
1332  STR_STATION_VIEW_GROUP_D_S_V,
1333  STR_STATION_VIEW_GROUP_D_V_S,
1334  };
1335 
1343 
1346 
1351 
1354  CargoDataVector displayed_rows;
1355 
1357  scroll_to_row(INT_MAX), grouping_index(0)
1358  {
1359  this->rating_lines = RATING_LINES;
1360  this->accepts_lines = ACCEPTS_LINES;
1361 
1362  this->CreateNestedTree();
1363  this->vscroll = this->GetScrollbar(WID_SV_SCROLLBAR);
1364  /* Nested widget tree creation is done in two steps to ensure that this->GetWidget<NWidgetCore>(WID_SV_ACCEPTS_RATINGS) exists in UpdateWidgetSize(). */
1365  this->FinishInitNested(window_number);
1366 
1367  this->groupings[0] = GR_CARGO;
1368  this->sortings[0] = CargoSortType::AsGrouping;
1371  this->sort_orders[0] = SO_ASCENDING;
1373  this->owner = Station::Get(window_number)->owner;
1374  }
1375 
1376  void Close([[maybe_unused]] int data = 0) override
1377  {
1378  CloseWindowById(WC_TRAINS_LIST, VehicleListIdentifier(VL_STATION_LIST, VEH_TRAIN, this->owner, this->window_number).Pack(), false);
1379  CloseWindowById(WC_ROADVEH_LIST, VehicleListIdentifier(VL_STATION_LIST, VEH_ROAD, this->owner, this->window_number).Pack(), false);
1380  CloseWindowById(WC_SHIPS_LIST, VehicleListIdentifier(VL_STATION_LIST, VEH_SHIP, this->owner, this->window_number).Pack(), false);
1381  CloseWindowById(WC_AIRCRAFT_LIST, VehicleListIdentifier(VL_STATION_LIST, VEH_AIRCRAFT, this->owner, this->window_number).Pack(), false);
1382 
1383  SetViewportCatchmentStation(Station::Get(this->window_number), false);
1384  this->Window::Close();
1385  }
1386 
1397  void ShowCargo(CargoDataEntry *data, CargoID cargo, StationID source, StationID next, StationID dest, uint count)
1398  {
1399  if (count == 0) return;
1400  bool auto_distributed = _settings_game.linkgraph.GetDistributionType(cargo) != DT_MANUAL;
1401  const CargoDataEntry *expand = &this->expanded_rows;
1402  for (int i = 0; i < NUM_COLUMNS && expand != nullptr; ++i) {
1403  switch (groupings[i]) {
1404  case GR_CARGO:
1405  assert(i == 0);
1406  data = data->InsertOrRetrieve(cargo);
1407  data->SetTransfers(source != this->window_number);
1408  expand = expand->Retrieve(cargo);
1409  break;
1410  case GR_SOURCE:
1411  if (auto_distributed || source != this->window_number) {
1412  data = data->InsertOrRetrieve(source);
1413  expand = expand->Retrieve(source);
1414  }
1415  break;
1416  case GR_NEXT:
1417  if (auto_distributed) {
1418  data = data->InsertOrRetrieve(next);
1419  expand = expand->Retrieve(next);
1420  }
1421  break;
1422  case GR_DESTINATION:
1423  if (auto_distributed) {
1424  data = data->InsertOrRetrieve(dest);
1425  expand = expand->Retrieve(dest);
1426  }
1427  break;
1428  }
1429  }
1430  data->Update(count);
1431  }
1432 
1433  void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
1434  {
1435  switch (widget) {
1436  case WID_SV_WAITING:
1438  size.height = 4 * resize.height + padding.height;
1439  this->expand_shrink_width = std::max(GetStringBoundingBox("-").width, GetStringBoundingBox("+").width);
1440  break;
1441 
1443  size.height = ((this->GetWidget<NWidgetCore>(WID_SV_ACCEPTS_RATINGS)->widget_data == STR_STATION_VIEW_RATINGS_BUTTON) ? this->accepts_lines : this->rating_lines) * GetCharacterHeight(FS_NORMAL) + padding.height;
1444  break;
1445 
1446  case WID_SV_CLOSE_AIRPORT:
1447  if (!(Station::Get(this->window_number)->facilities & FACIL_AIRPORT)) {
1448  /* Hide 'Close Airport' button if no airport present. */
1449  size.width = 0;
1450  resize.width = 0;
1451  fill.width = 0;
1452  }
1453  break;
1454  }
1455  }
1456 
1457  void OnPaint() override
1458  {
1459  const Station *st = Station::Get(this->window_number);
1460  CargoDataEntry cargo;
1461  BuildCargoList(&cargo, st);
1462 
1463  this->vscroll->SetCount(cargo.GetNumChildren()); // update scrollbar
1464 
1465  /* disable some buttons */
1471  this->SetWidgetDisabledState(WID_SV_CLOSE_AIRPORT, !(st->facilities & FACIL_AIRPORT) || st->owner != _local_company || st->owner == OWNER_NONE); // Also consider SE, where _local_company == OWNER_NONE
1473 
1474  extern const Station *_viewport_highlight_station;
1476  this->SetWidgetLoweredState(WID_SV_CATCHMENT, _viewport_highlight_station == st);
1477 
1478  this->DrawWidgets();
1479 
1480  if (!this->IsShaded()) {
1481  /* Draw 'accepted cargo' or 'cargo ratings'. */
1482  const NWidgetBase *wid = this->GetWidget<NWidgetBase>(WID_SV_ACCEPT_RATING_LIST);
1483  const Rect r = wid->GetCurrentRect();
1484  if (this->GetWidget<NWidgetCore>(WID_SV_ACCEPTS_RATINGS)->widget_data == STR_STATION_VIEW_RATINGS_BUTTON) {
1485  int lines = this->DrawAcceptedCargo(r);
1486  if (lines > this->accepts_lines) { // Resize the widget, and perform re-initialization of the window.
1487  this->accepts_lines = lines;
1488  this->ReInit();
1489  return;
1490  }
1491  } else {
1492  int lines = this->DrawCargoRatings(r);
1493  if (lines > this->rating_lines) { // Resize the widget, and perform re-initialization of the window.
1494  this->rating_lines = lines;
1495  this->ReInit();
1496  return;
1497  }
1498  }
1499 
1500  /* Draw arrow pointing up/down for ascending/descending sorting */
1501  this->DrawSortButtonState(WID_SV_SORT_ORDER, sort_orders[1] == SO_ASCENDING ? SBS_UP : SBS_DOWN);
1502 
1503  int pos = this->vscroll->GetPosition();
1504 
1505  int maxrows = this->vscroll->GetCapacity();
1506 
1507  displayed_rows.clear();
1508 
1509  /* Draw waiting cargo. */
1510  NWidgetBase *nwi = this->GetWidget<NWidgetBase>(WID_SV_WAITING);
1511  Rect waiting_rect = nwi->GetCurrentRect().Shrink(WidgetDimensions::scaled.framerect);
1512  this->DrawEntries(&cargo, waiting_rect, pos, maxrows, 0);
1513  scroll_to_row = INT_MAX;
1514  }
1515  }
1516 
1517  void SetStringParameters(WidgetID widget) const override
1518  {
1519  if (widget == WID_SV_CAPTION) {
1520  const Station *st = Station::Get(this->window_number);
1521  SetDParam(0, st->index);
1522  SetDParam(1, st->facilities);
1523  }
1524  }
1525 
1532  {
1533  const Station *st = Station::Get(this->window_number);
1535  cargo_entry->Clear();
1536 
1537  for (const auto &it : st->goods[i].flows) {
1538  StationID from = it.first;
1539  CargoDataEntry *source_entry = cargo_entry->InsertOrRetrieve(from);
1540  uint32_t prev_count = 0;
1541  for (const auto &flow_it : *it.second.GetShares()) {
1542  StationID via = flow_it.second;
1543  CargoDataEntry *via_entry = source_entry->InsertOrRetrieve(via);
1544  if (via == this->window_number) {
1545  via_entry->InsertOrRetrieve(via)->Update(flow_it.first - prev_count);
1546  } else {
1547  EstimateDestinations(i, from, via, flow_it.first - prev_count, via_entry);
1548  }
1549  prev_count = flow_it.first;
1550  }
1551  }
1552  }
1553 
1563  void EstimateDestinations(CargoID cargo, StationID source, StationID next, uint count, CargoDataEntry *dest)
1564  {
1565  if (Station::IsValidID(next) && Station::IsValidID(source)) {
1566  CargoDataEntry tmp;
1567  const FlowStatMap &flowmap = Station::Get(next)->goods[cargo].flows;
1568  FlowStatMap::const_iterator map_it = flowmap.find(source);
1569  if (map_it != flowmap.end()) {
1570  const FlowStat::SharesMap *shares = map_it->second.GetShares();
1571  uint32_t prev_count = 0;
1572  for (FlowStat::SharesMap::const_iterator i = shares->begin(); i != shares->end(); ++i) {
1573  tmp.InsertOrRetrieve(i->second)->Update(i->first - prev_count);
1574  prev_count = i->first;
1575  }
1576  }
1577 
1578  if (tmp.GetCount() == 0) {
1579  dest->InsertOrRetrieve(INVALID_STATION)->Update(count);
1580  } else {
1581  uint sum_estimated = 0;
1582  while (sum_estimated < count) {
1583  for (CargoDataSet::iterator i = tmp.Begin(); i != tmp.End() && sum_estimated < count; ++i) {
1584  CargoDataEntry *child = *i;
1585  uint estimate = DivideApprox(child->GetCount() * count, tmp.GetCount());
1586  if (estimate == 0) estimate = 1;
1587 
1588  sum_estimated += estimate;
1589  if (sum_estimated > count) {
1590  estimate -= sum_estimated - count;
1591  sum_estimated = count;
1592  }
1593 
1594  if (estimate > 0) {
1595  if (child->GetStation() == next) {
1596  dest->InsertOrRetrieve(next)->Update(estimate);
1597  } else {
1598  EstimateDestinations(cargo, source, child->GetStation(), estimate, dest);
1599  }
1600  }
1601  }
1602 
1603  }
1604  }
1605  } else {
1606  dest->InsertOrRetrieve(INVALID_STATION)->Update(count);
1607  }
1608  }
1609 
1616  void BuildFlowList(CargoID i, const FlowStatMap &flows, CargoDataEntry *cargo)
1617  {
1618  const CargoDataEntry *source_dest = this->cached_destinations.Retrieve(i);
1619  for (FlowStatMap::const_iterator it = flows.begin(); it != flows.end(); ++it) {
1620  StationID from = it->first;
1621  const CargoDataEntry *source_entry = source_dest->Retrieve(from);
1622  const FlowStat::SharesMap *shares = it->second.GetShares();
1623  for (FlowStat::SharesMap::const_iterator flow_it = shares->begin(); flow_it != shares->end(); ++flow_it) {
1624  const CargoDataEntry *via_entry = source_entry->Retrieve(flow_it->second);
1625  for (CargoDataSet::iterator dest_it = via_entry->Begin(); dest_it != via_entry->End(); ++dest_it) {
1626  CargoDataEntry *dest_entry = *dest_it;
1627  ShowCargo(cargo, i, from, flow_it->second, dest_entry->GetStation(), dest_entry->GetCount());
1628  }
1629  }
1630  }
1631  }
1632 
1639  void BuildCargoList(CargoID i, const StationCargoList &packets, CargoDataEntry *cargo)
1640  {
1641  const CargoDataEntry *source_dest = this->cached_destinations.Retrieve(i);
1642  for (StationCargoList::ConstIterator it = packets.Packets()->begin(); it != packets.Packets()->end(); it++) {
1643  const CargoPacket *cp = *it;
1644  StationID next = it.GetKey();
1645 
1646  const CargoDataEntry *source_entry = source_dest->Retrieve(cp->GetFirstStation());
1647  if (source_entry == nullptr) {
1648  this->ShowCargo(cargo, i, cp->GetFirstStation(), next, INVALID_STATION, cp->Count());
1649  continue;
1650  }
1651 
1652  const CargoDataEntry *via_entry = source_entry->Retrieve(next);
1653  if (via_entry == nullptr) {
1654  this->ShowCargo(cargo, i, cp->GetFirstStation(), next, INVALID_STATION, cp->Count());
1655  continue;
1656  }
1657 
1658  uint remaining = cp->Count();
1659  for (CargoDataSet::iterator dest_it = via_entry->Begin(); dest_it != via_entry->End();) {
1660  CargoDataEntry *dest_entry = *dest_it;
1661 
1662  /* Advance iterator here instead of in the for statement to test whether this is the last entry */
1663  ++dest_it;
1664 
1665  uint val;
1666  if (dest_it == via_entry->End()) {
1667  /* Allocate all remaining waiting cargo to the last destination to avoid
1668  * waiting cargo being "lost", and the displayed total waiting cargo
1669  * not matching GoodsEntry::TotalCount() */
1670  val = remaining;
1671  } else {
1672  val = std::min<uint>(remaining, DivideApprox(cp->Count() * dest_entry->GetCount(), via_entry->GetCount()));
1673  remaining -= val;
1674  }
1675  this->ShowCargo(cargo, i, cp->GetFirstStation(), next, dest_entry->GetStation(), val);
1676  }
1677  }
1678  this->ShowCargo(cargo, i, NEW_STATION, NEW_STATION, NEW_STATION, packets.ReservedCount());
1679  }
1680 
1686  void BuildCargoList(CargoDataEntry *cargo, const Station *st)
1687  {
1688  for (CargoID i = 0; i < NUM_CARGO; i++) {
1689 
1690  if (this->cached_destinations.Retrieve(i) == nullptr) {
1691  this->RecalcDestinations(i);
1692  }
1693 
1694  if (this->current_mode == MODE_WAITING) {
1695  this->BuildCargoList(i, st->goods[i].cargo, cargo);
1696  } else {
1697  this->BuildFlowList(i, st->goods[i].flows, cargo);
1698  }
1699  }
1700  }
1701 
1707  {
1708  std::list<StationID> stations;
1709  const CargoDataEntry *parent = data->GetParent();
1710  if (parent->GetParent() == nullptr) {
1711  this->displayed_rows.push_back(RowDisplay(&this->expanded_rows, data->GetCargo()));
1712  return;
1713  }
1714 
1715  StationID next = data->GetStation();
1716  while (parent->GetParent()->GetParent() != nullptr) {
1717  stations.push_back(parent->GetStation());
1718  parent = parent->GetParent();
1719  }
1720 
1721  CargoID cargo = parent->GetCargo();
1722  CargoDataEntry *filter = this->expanded_rows.Retrieve(cargo);
1723  while (!stations.empty()) {
1724  filter = filter->Retrieve(stations.back());
1725  stations.pop_back();
1726  }
1727 
1728  this->displayed_rows.push_back(RowDisplay(filter, next));
1729  }
1730 
1739  StringID GetEntryString(StationID station, StringID here, StringID other_station, StringID any)
1740  {
1741  if (station == this->window_number) {
1742  return here;
1743  } else if (station == INVALID_STATION) {
1744  return any;
1745  } else if (station == NEW_STATION) {
1746  return STR_STATION_VIEW_RESERVED;
1747  } else {
1748  SetDParam(2, station);
1749  return other_station;
1750  }
1751  }
1752 
1760  StringID SearchNonStop(CargoDataEntry *cd, StationID station, int column)
1761  {
1762  CargoDataEntry *parent = cd->GetParent();
1763  for (int i = column - 1; i > 0; --i) {
1764  if (this->groupings[i] == GR_DESTINATION) {
1765  if (parent->GetStation() == station) {
1766  return STR_STATION_VIEW_NONSTOP;
1767  } else {
1768  return STR_STATION_VIEW_VIA;
1769  }
1770  }
1771  parent = parent->GetParent();
1772  }
1773 
1774  if (this->groupings[column + 1] == GR_DESTINATION) {
1775  CargoDataSet::iterator begin = cd->Begin();
1776  CargoDataSet::iterator end = cd->End();
1777  if (begin != end && ++(cd->Begin()) == end && (*(begin))->GetStation() == station) {
1778  return STR_STATION_VIEW_NONSTOP;
1779  } else {
1780  return STR_STATION_VIEW_VIA;
1781  }
1782  }
1783 
1784  return STR_STATION_VIEW_VIA;
1785  }
1786 
1797  int DrawEntries(CargoDataEntry *entry, const Rect &r, int pos, int maxrows, int column, CargoID cargo = INVALID_CARGO)
1798  {
1799  if (this->sortings[column] == CargoSortType::AsGrouping) {
1800  if (this->groupings[column] != GR_CARGO) {
1801  entry->Resort(CargoSortType::StationString, this->sort_orders[column]);
1802  }
1803  } else {
1804  entry->Resort(CargoSortType::Count, this->sort_orders[column]);
1805  }
1806  for (CargoDataSet::iterator i = entry->Begin(); i != entry->End(); ++i) {
1807  CargoDataEntry *cd = *i;
1808 
1809  Grouping grouping = this->groupings[column];
1810  if (grouping == GR_CARGO) cargo = cd->GetCargo();
1811  bool auto_distributed = _settings_game.linkgraph.GetDistributionType(cargo) != DT_MANUAL;
1812 
1813  if (pos > -maxrows && pos <= 0) {
1814  StringID str = STR_EMPTY;
1815  int y = r.top - pos * GetCharacterHeight(FS_NORMAL);
1816  SetDParam(0, cargo);
1817  SetDParam(1, cd->GetCount());
1818 
1819  if (this->groupings[column] == GR_CARGO) {
1820  str = STR_STATION_VIEW_WAITING_CARGO;
1821  DrawCargoIcons(cd->GetCargo(), cd->GetCount(), r.left + this->expand_shrink_width, r.right - this->expand_shrink_width, y);
1822  } else {
1823  if (!auto_distributed) grouping = GR_SOURCE;
1824  StationID station = cd->GetStation();
1825 
1826  switch (grouping) {
1827  case GR_SOURCE:
1828  str = this->GetEntryString(station, STR_STATION_VIEW_FROM_HERE, STR_STATION_VIEW_FROM, STR_STATION_VIEW_FROM_ANY);
1829  break;
1830  case GR_NEXT:
1831  str = this->GetEntryString(station, STR_STATION_VIEW_VIA_HERE, STR_STATION_VIEW_VIA, STR_STATION_VIEW_VIA_ANY);
1832  if (str == STR_STATION_VIEW_VIA) str = this->SearchNonStop(cd, station, column);
1833  break;
1834  case GR_DESTINATION:
1835  str = this->GetEntryString(station, STR_STATION_VIEW_TO_HERE, STR_STATION_VIEW_TO, STR_STATION_VIEW_TO_ANY);
1836  break;
1837  default:
1838  NOT_REACHED();
1839  }
1840  if (pos == -this->scroll_to_row && Station::IsValidID(station)) {
1841  ScrollMainWindowToTile(Station::Get(station)->xy);
1842  }
1843  }
1844 
1845  bool rtl = _current_text_dir == TD_RTL;
1846  Rect text = r.Indent(column * WidgetDimensions::scaled.hsep_indent, rtl).Indent(this->expand_shrink_width, !rtl);
1847  Rect shrink = r.WithWidth(this->expand_shrink_width, !rtl);
1848 
1849  DrawString(text.left, text.right, y, str);
1850 
1851  if (column < NUM_COLUMNS - 1) {
1852  const char *sym = nullptr;
1853  if (cd->GetNumChildren() > 0) {
1854  sym = "-";
1855  } else if (auto_distributed && str != STR_STATION_VIEW_RESERVED) {
1856  sym = "+";
1857  } else {
1858  /* Only draw '+' if there is something to be shown. */
1859  const StationCargoList &list = Station::Get(this->window_number)->goods[cargo].cargo;
1860  if (grouping == GR_CARGO && (list.ReservedCount() > 0 || cd->HasTransfers())) {
1861  sym = "+";
1862  }
1863  }
1864  if (sym != nullptr) DrawString(shrink.left, shrink.right, y, sym, TC_YELLOW);
1865  }
1866  this->SetDisplayedRow(cd);
1867  }
1868  --pos;
1869  if (auto_distributed || column == 0) {
1870  pos = this->DrawEntries(cd, r, pos, maxrows, column + 1, cargo);
1871  }
1872  }
1873  return pos;
1874  }
1875 
1881  int DrawAcceptedCargo(const Rect &r) const
1882  {
1883  const Station *st = Station::Get(this->window_number);
1884  Rect tr = r.Shrink(WidgetDimensions::scaled.framerect);
1885 
1886  SetDParam(0, GetAcceptanceMask(st));
1887  int bottom = DrawStringMultiLine(tr.left, tr.right, tr.top, INT32_MAX, STR_STATION_VIEW_ACCEPTS_CARGO);
1889  }
1890 
1896  int DrawCargoRatings(const Rect &r) const
1897  {
1898  const Station *st = Station::Get(this->window_number);
1899  bool rtl = _current_text_dir == TD_RTL;
1900  Rect tr = r.Shrink(WidgetDimensions::scaled.framerect);
1901 
1902  if (st->town->exclusive_counter > 0) {
1903  SetDParam(0, st->town->exclusivity);
1904  tr.top = DrawStringMultiLine(tr, st->town->exclusivity == st->owner ? STR_STATION_VIEW_EXCLUSIVE_RIGHTS_SELF : STR_STATION_VIEW_EXCLUSIVE_RIGHTS_COMPANY);
1906  }
1907 
1908  DrawString(tr, TimerGameEconomy::UsingWallclockUnits() ? STR_STATION_VIEW_SUPPLY_RATINGS_TITLE_MINUTE : STR_STATION_VIEW_SUPPLY_RATINGS_TITLE_MONTH);
1909  tr.top += GetCharacterHeight(FS_NORMAL);
1910 
1911  for (const CargoSpec *cs : _sorted_standard_cargo_specs) {
1912  const GoodsEntry *ge = &st->goods[cs->Index()];
1913  if (!ge->HasRating()) continue;
1914 
1915  const LinkGraph *lg = LinkGraph::GetIfValid(ge->link_graph);
1916  SetDParam(0, cs->name);
1917  SetDParam(1, lg != nullptr ? lg->Monthly((*lg)[ge->node].supply) : 0);
1918  SetDParam(2, STR_CARGO_RATING_APPALLING + (ge->rating >> 5));
1919  SetDParam(3, ToPercent8(ge->rating));
1920  DrawString(tr.Indent(WidgetDimensions::scaled.hsep_indent, rtl), STR_STATION_VIEW_CARGO_SUPPLY_RATING);
1921  tr.top += GetCharacterHeight(FS_NORMAL);
1922  }
1924  }
1925 
1931  template<class Tid>
1932  void HandleCargoWaitingClick(CargoDataEntry *filter, Tid next)
1933  {
1934  if (filter->Retrieve(next) != nullptr) {
1935  filter->Remove(next);
1936  } else {
1937  filter->InsertOrRetrieve(next);
1938  }
1939  }
1940 
1946  {
1947  if (row < 0 || (uint)row >= this->displayed_rows.size()) return;
1948  if (_ctrl_pressed) {
1949  this->scroll_to_row = row;
1950  } else {
1951  RowDisplay &display = this->displayed_rows[row];
1952  if (display.filter == &this->expanded_rows) {
1953  this->HandleCargoWaitingClick<CargoID>(display.filter, display.next_cargo);
1954  } else {
1955  this->HandleCargoWaitingClick<StationID>(display.filter, display.next_station);
1956  }
1957  }
1959  }
1960 
1961  void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
1962  {
1963  switch (widget) {
1964  case WID_SV_WAITING:
1965  this->HandleCargoWaitingClick(this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_SV_WAITING, WidgetDimensions::scaled.framerect.top) - this->vscroll->GetPosition());
1966  break;
1967 
1968  case WID_SV_CATCHMENT:
1970  break;
1971 
1972  case WID_SV_LOCATION:
1973  if (_ctrl_pressed) {
1974  ShowExtraViewportWindow(Station::Get(this->window_number)->xy);
1975  } else {
1976  ScrollMainWindowToTile(Station::Get(this->window_number)->xy);
1977  }
1978  break;
1979 
1980  case WID_SV_ACCEPTS_RATINGS: {
1981  /* Swap between 'accepts' and 'ratings' view. */
1982  int height_change;
1983  NWidgetCore *nwi = this->GetWidget<NWidgetCore>(WID_SV_ACCEPTS_RATINGS);
1984  if (this->GetWidget<NWidgetCore>(WID_SV_ACCEPTS_RATINGS)->widget_data == STR_STATION_VIEW_RATINGS_BUTTON) {
1985  nwi->SetDataTip(STR_STATION_VIEW_ACCEPTS_BUTTON, STR_STATION_VIEW_ACCEPTS_TOOLTIP); // Switch to accepts view.
1986  height_change = this->rating_lines - this->accepts_lines;
1987  } else {
1988  nwi->SetDataTip(STR_STATION_VIEW_RATINGS_BUTTON, STR_STATION_VIEW_RATINGS_TOOLTIP); // Switch to ratings view.
1989  height_change = this->accepts_lines - this->rating_lines;
1990  }
1991  this->ReInit(0, height_change * GetCharacterHeight(FS_NORMAL));
1992  break;
1993  }
1994 
1995  case WID_SV_RENAME:
1996  SetDParam(0, this->window_number);
1997  ShowQueryString(STR_STATION_NAME, STR_STATION_VIEW_RENAME_STATION_CAPTION, MAX_LENGTH_STATION_NAME_CHARS,
1999  break;
2000 
2001  case WID_SV_CLOSE_AIRPORT:
2002  Command<CMD_OPEN_CLOSE_AIRPORT>::Post(this->window_number);
2003  break;
2004 
2005  case WID_SV_TRAINS: // Show list of scheduled trains to this station
2006  case WID_SV_ROADVEHS: // Show list of scheduled road-vehicles to this station
2007  case WID_SV_SHIPS: // Show list of scheduled ships to this station
2008  case WID_SV_PLANES: { // Show list of scheduled aircraft to this station
2009  Owner owner = Station::Get(this->window_number)->owner;
2010  ShowVehicleListWindow(owner, (VehicleType)(widget - WID_SV_TRAINS), (StationID)this->window_number);
2011  break;
2012  }
2013 
2014  case WID_SV_SORT_BY: {
2015  /* The initial selection is composed of current mode and
2016  * sorting criteria for columns 1, 2, and 3. Column 0 is always
2017  * sorted by cargo ID. The others can theoretically be sorted
2018  * by different things but there is no UI for that. */
2020  this->current_mode * 2 + (this->sortings[1] == CargoSortType::Count ? 1 : 0),
2021  WID_SV_SORT_BY, 0, 0);
2022  break;
2023  }
2024 
2025  case WID_SV_GROUP_BY: {
2026  ShowDropDownMenu(this, StationViewWindow::group_names, this->grouping_index, WID_SV_GROUP_BY, 0, 0);
2027  break;
2028  }
2029 
2030  case WID_SV_SORT_ORDER: { // flip sorting method asc/desc
2031  this->SelectSortOrder(this->sort_orders[1] == SO_ASCENDING ? SO_DESCENDING : SO_ASCENDING);
2032  this->SetTimeout();
2034  break;
2035  }
2036  }
2037  }
2038 
2043  void SelectSortOrder(SortOrder order)
2044  {
2045  this->sort_orders[1] = this->sort_orders[2] = this->sort_orders[3] = order;
2046  _settings_client.gui.station_gui_sort_order = this->sort_orders[1];
2047  this->SetDirty();
2048  }
2049 
2054  void SelectSortBy(int index)
2055  {
2057  switch (StationViewWindow::sort_names[index]) {
2058  case STR_STATION_VIEW_WAITING_STATION:
2059  this->current_mode = MODE_WAITING;
2060  this->sortings[1] = this->sortings[2] = this->sortings[3] = CargoSortType::AsGrouping;
2061  break;
2062  case STR_STATION_VIEW_WAITING_AMOUNT:
2063  this->current_mode = MODE_WAITING;
2064  this->sortings[1] = this->sortings[2] = this->sortings[3] = CargoSortType::Count;
2065  break;
2066  case STR_STATION_VIEW_PLANNED_STATION:
2067  this->current_mode = MODE_PLANNED;
2068  this->sortings[1] = this->sortings[2] = this->sortings[3] = CargoSortType::AsGrouping;
2069  break;
2070  case STR_STATION_VIEW_PLANNED_AMOUNT:
2071  this->current_mode = MODE_PLANNED;
2072  this->sortings[1] = this->sortings[2] = this->sortings[3] = CargoSortType::Count;
2073  break;
2074  default:
2075  NOT_REACHED();
2076  }
2077  /* Display the current sort variant */
2078  this->GetWidget<NWidgetCore>(WID_SV_SORT_BY)->widget_data = StationViewWindow::sort_names[index];
2079  this->SetDirty();
2080  }
2081 
2086  void SelectGroupBy(int index)
2087  {
2088  this->grouping_index = index;
2090  this->GetWidget<NWidgetCore>(WID_SV_GROUP_BY)->widget_data = StationViewWindow::group_names[index];
2091  switch (StationViewWindow::group_names[index]) {
2092  case STR_STATION_VIEW_GROUP_S_V_D:
2093  this->groupings[1] = GR_SOURCE;
2094  this->groupings[2] = GR_NEXT;
2095  this->groupings[3] = GR_DESTINATION;
2096  break;
2097  case STR_STATION_VIEW_GROUP_S_D_V:
2098  this->groupings[1] = GR_SOURCE;
2099  this->groupings[2] = GR_DESTINATION;
2100  this->groupings[3] = GR_NEXT;
2101  break;
2102  case STR_STATION_VIEW_GROUP_V_S_D:
2103  this->groupings[1] = GR_NEXT;
2104  this->groupings[2] = GR_SOURCE;
2105  this->groupings[3] = GR_DESTINATION;
2106  break;
2107  case STR_STATION_VIEW_GROUP_V_D_S:
2108  this->groupings[1] = GR_NEXT;
2109  this->groupings[2] = GR_DESTINATION;
2110  this->groupings[3] = GR_SOURCE;
2111  break;
2112  case STR_STATION_VIEW_GROUP_D_S_V:
2113  this->groupings[1] = GR_DESTINATION;
2114  this->groupings[2] = GR_SOURCE;
2115  this->groupings[3] = GR_NEXT;
2116  break;
2117  case STR_STATION_VIEW_GROUP_D_V_S:
2118  this->groupings[1] = GR_DESTINATION;
2119  this->groupings[2] = GR_NEXT;
2120  this->groupings[3] = GR_SOURCE;
2121  break;
2122  }
2123  this->SetDirty();
2124  }
2125 
2126  void OnDropdownSelect(WidgetID widget, int index) override
2127  {
2128  if (widget == WID_SV_SORT_BY) {
2129  this->SelectSortBy(index);
2130  } else {
2131  this->SelectGroupBy(index);
2132  }
2133  }
2134 
2135  void OnQueryTextFinished(std::optional<std::string> str) override
2136  {
2137  if (!str.has_value()) return;
2138 
2139  Command<CMD_RENAME_STATION>::Post(STR_ERROR_CAN_T_RENAME_STATION, this->window_number, *str);
2140  }
2141 
2142  void OnResize() override
2143  {
2144  this->vscroll->SetCapacityFromWidget(this, WID_SV_WAITING, WidgetDimensions::scaled.framerect.Vertical());
2145  }
2146 
2152  void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
2153  {
2154  if (gui_scope) {
2155  if (data >= 0 && data < NUM_CARGO) {
2156  this->cached_destinations.Remove((CargoID)data);
2157  } else {
2158  this->ReInit();
2159  }
2160  }
2161  }
2162 };
2163 
2164 static WindowDesc _station_view_desc(
2165  WDP_AUTO, "view_station", 249, 117,
2167  0,
2168  _nested_station_view_widgets
2169 );
2170 
2176 void ShowStationViewWindow(StationID station)
2177 {
2178  AllocateWindowDescFront<StationViewWindow>(_station_view_desc, station);
2179 }
2180 
2184  StationID station;
2185 };
2186 
2187 static std::vector<TileAndStation> _deleted_stations_nearby;
2188 static std::vector<StationID> _stations_nearby_list;
2189 
2197 template <class T>
2198 static bool AddNearbyStation(TileIndex tile, void *user_data)
2199 {
2200  TileArea *ctx = (TileArea *)user_data;
2201 
2202  /* First check if there were deleted stations here */
2203  for (auto it = _deleted_stations_nearby.begin(); it != _deleted_stations_nearby.end(); /* nothing */) {
2204  if (it->tile == tile) {
2205  _stations_nearby_list.push_back(it->station);
2206  it = _deleted_stations_nearby.erase(it);
2207  } else {
2208  ++it;
2209  }
2210  }
2211 
2212  /* Check if own station and if we stay within station spread */
2213  if (!IsTileType(tile, MP_STATION)) return false;
2214 
2215  StationID sid = GetStationIndex(tile);
2216 
2217  /* This station is (likely) a waypoint */
2218  if (!T::IsValidID(sid)) return false;
2219 
2220  BaseStation *st = BaseStation::Get(sid);
2221  if (st->owner != _local_company || std::find(_stations_nearby_list.begin(), _stations_nearby_list.end(), sid) != _stations_nearby_list.end()) return false;
2222 
2223  if (st->rect.BeforeAddRect(ctx->tile, ctx->w, ctx->h, StationRect::ADD_TEST).Succeeded()) {
2224  _stations_nearby_list.push_back(sid);
2225  }
2226 
2227  return false; // We want to include *all* nearby stations
2228 }
2229 
2239 template <class T>
2240 static const BaseStation *FindStationsNearby(TileArea ta, bool distant_join)
2241 {
2242  TileArea ctx = ta;
2243 
2244  _stations_nearby_list.clear();
2245  _stations_nearby_list.push_back(NEW_STATION);
2246  _deleted_stations_nearby.clear();
2247 
2248  /* Check the inside, to return, if we sit on another station */
2249  for (TileIndex t : ta) {
2250  if (t < Map::Size() && IsTileType(t, MP_STATION) && T::IsValidID(GetStationIndex(t))) return BaseStation::GetByTile(t);
2251  }
2252 
2253  /* Look for deleted stations */
2254  for (const BaseStation *st : BaseStation::Iterate()) {
2255  if (T::IsValidBaseStation(st) && !st->IsInUse() && st->owner == _local_company) {
2256  /* Include only within station spread (yes, it is strictly less than) */
2257  if (std::max(DistanceMax(ta.tile, st->xy), DistanceMax(TileAddXY(ta.tile, ta.w - 1, ta.h - 1), st->xy)) < _settings_game.station.station_spread) {
2258  _deleted_stations_nearby.push_back({st->xy, st->index});
2259 
2260  /* Add the station when it's within where we're going to build */
2261  if (IsInsideBS(TileX(st->xy), TileX(ctx.tile), ctx.w) &&
2262  IsInsideBS(TileY(st->xy), TileY(ctx.tile), ctx.h)) {
2263  AddNearbyStation<T>(st->xy, &ctx);
2264  }
2265  }
2266  }
2267  }
2268 
2269  /* Only search tiles where we have a chance to stay within the station spread.
2270  * The complete check needs to be done in the callback as we don't know the
2271  * extent of the found station, yet. */
2272  if (distant_join && std::min(ta.w, ta.h) >= _settings_game.station.station_spread) return nullptr;
2273  uint max_dist = distant_join ? _settings_game.station.station_spread - std::min(ta.w, ta.h) : 1;
2274 
2275  TileIndex tile = TileAddByDir(ctx.tile, DIR_N);
2276  CircularTileSearch(&tile, max_dist, ta.w, ta.h, AddNearbyStation<T>, &ctx);
2277 
2278  return nullptr;
2279 }
2280 
2281 static constexpr NWidgetPart _nested_select_station_widgets[] = {
2283  NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN),
2284  NWidget(WWT_CAPTION, COLOUR_DARK_GREEN, WID_JS_CAPTION), SetDataTip(STR_JOIN_STATION_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
2285  NWidget(WWT_DEFSIZEBOX, COLOUR_DARK_GREEN),
2286  EndContainer(),
2288  NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_JS_PANEL), SetResize(1, 0), SetScrollbar(WID_JS_SCROLLBAR), EndContainer(),
2290  NWidget(NWID_VSCROLLBAR, COLOUR_DARK_GREEN, WID_JS_SCROLLBAR),
2291  NWidget(WWT_RESIZEBOX, COLOUR_DARK_GREEN),
2292  EndContainer(),
2293  EndContainer(),
2294 };
2295 
2300 template <class T>
2302  StationPickerCmdProc select_station_proc;
2304  Scrollbar *vscroll;
2305 
2306  SelectStationWindow(WindowDesc &desc, TileArea ta, StationPickerCmdProc&& proc) :
2307  Window(desc),
2308  select_station_proc(std::move(proc)),
2309  area(ta)
2310  {
2311  this->CreateNestedTree();
2312  this->vscroll = this->GetScrollbar(WID_JS_SCROLLBAR);
2313  this->GetWidget<NWidgetCore>(WID_JS_CAPTION)->widget_data = T::IsWaypoint() ? STR_JOIN_WAYPOINT_CAPTION : STR_JOIN_STATION_CAPTION;
2314  this->FinishInitNested(0);
2315  this->OnInvalidateData(0);
2316 
2317  _thd.freeze = true;
2318  }
2319 
2320  void Close([[maybe_unused]] int data = 0) override
2321  {
2322  SetViewportCatchmentSpecializedStation<typename T::StationType>(nullptr, true);
2323 
2324  _thd.freeze = false;
2325  this->Window::Close();
2326  }
2327 
2328  void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
2329  {
2330  if (widget != WID_JS_PANEL) return;
2331 
2332  /* Determine the widest string */
2333  Dimension d = GetStringBoundingBox(T::IsWaypoint() ? STR_JOIN_WAYPOINT_CREATE_SPLITTED_WAYPOINT : STR_JOIN_STATION_CREATE_SPLITTED_STATION);
2334  for (const auto &station : _stations_nearby_list) {
2335  if (station == NEW_STATION) continue;
2336  const BaseStation *st = BaseStation::Get(station);
2337  SetDParam(0, st->index);
2338  SetDParam(1, st->facilities);
2339  d = maxdim(d, GetStringBoundingBox(T::IsWaypoint() ? STR_STATION_LIST_WAYPOINT : STR_STATION_LIST_STATION));
2340  }
2341 
2342  resize.height = d.height;
2343  d.height *= 5;
2344  d.width += padding.width;
2345  d.height += padding.height;
2346  size = d;
2347  }
2348 
2349  void DrawWidget(const Rect &r, WidgetID widget) const override
2350  {
2351  if (widget != WID_JS_PANEL) return;
2352 
2353  Rect tr = r.Shrink(WidgetDimensions::scaled.framerect);
2354  auto [first, last] = this->vscroll->GetVisibleRangeIterators(_stations_nearby_list);
2355  for (auto it = first; it != last; ++it, tr.top += this->resize.step_height) {
2356  if (*it == NEW_STATION) {
2357  DrawString(tr, T::IsWaypoint() ? STR_JOIN_WAYPOINT_CREATE_SPLITTED_WAYPOINT : STR_JOIN_STATION_CREATE_SPLITTED_STATION);
2358  } else {
2359  const BaseStation *st = BaseStation::Get(*it);
2360  SetDParam(0, st->index);
2361  SetDParam(1, st->facilities);
2362  DrawString(tr, T::IsWaypoint() ? STR_STATION_LIST_WAYPOINT : STR_STATION_LIST_STATION);
2363  }
2364  }
2365 
2366  }
2367 
2368  void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
2369  {
2370  if (widget != WID_JS_PANEL) return;
2371 
2372  auto it = this->vscroll->GetScrolledItemFromWidget(_stations_nearby_list, pt.y, this, WID_JS_PANEL, WidgetDimensions::scaled.framerect.top);
2373  if (it == _stations_nearby_list.end()) return;
2374 
2375  /* Execute stored Command */
2376  this->select_station_proc(false, *it);
2377 
2378  /* Close Window; this might cause double frees! */
2380  }
2381 
2382  void OnRealtimeTick([[maybe_unused]] uint delta_ms) override
2383  {
2384  if (_thd.dirty & 2) {
2385  _thd.dirty &= ~2;
2386  this->SetDirty();
2387  }
2388  }
2389 
2390  void OnResize() override
2391  {
2392  this->vscroll->SetCapacityFromWidget(this, WID_JS_PANEL, WidgetDimensions::scaled.framerect.Vertical());
2393  }
2394 
2400  void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override
2401  {
2402  if (!gui_scope) return;
2403  FindStationsNearby<T>(this->area, true);
2404  this->vscroll->SetCount(_stations_nearby_list.size());
2405  this->SetDirty();
2406  }
2407 
2408  void OnMouseOver([[maybe_unused]] Point pt, WidgetID widget) override
2409  {
2410  if (widget != WID_JS_PANEL) {
2411  SetViewportCatchmentSpecializedStation<typename T::StationType>(nullptr, true);
2412  return;
2413  }
2414 
2415  /* Show coverage area of station under cursor */
2416  auto it = this->vscroll->GetScrolledItemFromWidget(_stations_nearby_list, pt.y, this, WID_JS_PANEL, WidgetDimensions::scaled.framerect.top);
2417  const typename T::StationType *st = it == _stations_nearby_list.end() || *it == NEW_STATION ? nullptr : T::StationType::Get(*it);
2418  SetViewportCatchmentSpecializedStation<typename T::StationType>(st, true);
2419  }
2420 };
2421 
2422 static WindowDesc _select_station_desc(
2423  WDP_AUTO, "build_station_join", 200, 180,
2426  _nested_select_station_widgets
2427 );
2428 
2429 
2437 template <class T>
2438 static bool StationJoinerNeeded(TileArea ta, const StationPickerCmdProc &proc)
2439 {
2440  /* Only show selection if distant join is enabled in the settings */
2441  if (!_settings_game.station.distant_join_stations) return false;
2442 
2443  /* If a window is already opened and we didn't ctrl-click,
2444  * return true (i.e. just flash the old window) */
2445  Window *selection_window = FindWindowById(WC_SELECT_STATION, 0);
2446  if (selection_window != nullptr) {
2447  /* Abort current distant-join and start new one */
2448  selection_window->Close();
2450  }
2451 
2452  /* only show the popup, if we press ctrl */
2453  if (!_ctrl_pressed) return false;
2454 
2455  /* Now check if we could build there */
2456  if (!proc(true, INVALID_STATION)) return false;
2457 
2458  return FindStationsNearby<T>(ta, false) == nullptr;
2459 }
2460 
2467 template <class T>
2468 void ShowSelectBaseStationIfNeeded(TileArea ta, StationPickerCmdProc&& proc)
2469 {
2470  if (StationJoinerNeeded<T>(ta, proc)) {
2472  new SelectStationWindow<T>(_select_station_desc, ta, std::move(proc));
2473  } else {
2474  proc(false, INVALID_STATION);
2475  }
2476 }
2477 
2483 void ShowSelectStationIfNeeded(TileArea ta, StationPickerCmdProc proc)
2484 {
2485  ShowSelectBaseStationIfNeeded<StationTypeFilter>(ta, std::move(proc));
2486 }
2487 
2493 void ShowSelectRailWaypointIfNeeded(TileArea ta, StationPickerCmdProc proc)
2494 {
2495  ShowSelectBaseStationIfNeeded<RailWaypointTypeFilter>(ta, std::move(proc));
2496 }
2497 
2503 void ShowSelectRoadWaypointIfNeeded(TileArea ta, StationPickerCmdProc proc)
2504 {
2505  ShowSelectBaseStationIfNeeded<RoadWaypointTypeFilter>(ta, std::move(proc));
2506 }
static const uint64_t AIRPORT_CLOSED_block
Dummy block for indicating a closed airport.
Definition: airport.h:128
constexpr debug_inline bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
constexpr T SetBit(T &x, const uint8_t y)
Set a bit in a variable.
constexpr uint8_t FindFirstBit(T x)
Search the first set bit in a value.
constexpr uint CountBits(T value)
Counts the number of set bits in a variable.
constexpr T ToggleBit(T &x, const uint8_t y)
Toggles a bit in a variable.
uint8_t CargoID
Cargo slots to indicate a cargo type within a game.
Definition: cargo_type.h:22
static const CargoID NUM_CARGO
Maximum number of cargo types in a game.
Definition: cargo_type.h:74
Dimension GetLargestCargoIconSize()
Get dimensions of largest cargo icon.
Definition: cargotype.cpp:156
std::span< const CargoSpec * > _sorted_standard_cargo_specs
Standard cargo specifications sorted alphabetically by name.
Definition: cargotype.cpp:184
std::vector< const CargoSpec * > _sorted_cargo_specs
Cargo specifications sorted alphabetically by name.
Definition: cargotype.cpp:183
CargoTypes _cargo_mask
Bitmask of cargo types available.
Definition: cargotype.cpp:31
Types/functions related to cargoes.
bool IsCargoInClass(CargoID c, CargoClass cc)
Does cargo c have cargo class cc?
Definition: cargotype.h:232
@ CC_PASSENGERS
Passengers.
Definition: cargotype.h:50
A cargo data entry representing one possible row in the station view window's top part.
uint count
sum of counts of all children or amount of cargo for this entry.
void Clear()
Delete all subentries, reset count and num_children and adapt parent's count.
StationID GetStation() const
Get the station ID for this entry.
void IncrementSize()
Increment.
CargoDataEntry * InsertOrRetrieve(CargoID cargo)
Insert a new child or retrieve an existing child using a cargo ID as ID.
void SetTransfers(bool value)
Set the transfers state.
CargoDataEntry * Retrieve(CargoID cargo) const
Retrieve a child for the given cargo.
uint num_children
the number of subentries belonging to this entry.
uint GetCount() const
Get the cargo count for this entry.
CargoID GetCargo() const
Get the cargo ID for this entry.
StationID station
ID of the station this entry is associated with.
CargoDataSet * children
the children of this entry.
CargoDataSet::iterator End() const
Get an iterator pointing to the end of the set of children.
void Update(uint count)
Update the count for this entry and propagate the change to the parent entry if there is one.
void Remove(CargoID cargo)
Remove a child associated with the given cargo.
CargoDataEntry * Retrieve(StationID station) const
Retrieve a child for the given station.
CargoID cargo
ID of the cargo this entry is associated with.
bool transfers
If there are transfers for this cargo.
CargoDataSet::iterator Begin() const
Get an iterator pointing to the begin of the set of children.
CargoDataEntry * InsertOrRetrieve(StationID station)
Insert a new child or retrieve an existing child using a station ID as ID.
bool HasTransfers() const
Has this entry transfers.
CargoDataEntry * parent
the parent of this entry.
void Remove(StationID station)
Remove a child associated with the given station.
CargoDataEntry * GetParent() const
Get the parent entry for this entry.
uint GetNumChildren() const
Get the number of children for this entry.
Tcont::const_iterator ConstIterator
The const iterator for our container.
Definition: cargopacket.h:290
const Tcont * Packets() const
Returns a pointer to the cargo packet list (so you can iterate over it etc).
Definition: cargopacket.h:329
bool Succeeded() const
Did this command succeed?
Definition: command_type.h:162
The list of stations per company.
static bool StationRatingMaxSorter(const Station *const &a, const Station *const &b, const CargoTypes &cargo_filter)
Sort stations by their rating.
std::array< uint16_t, NUM_CARGO > stations_per_cargo_type
Number of stations with a rating for each cargo type.
void OnResize() override
Called after the window got resized.
void SortStationsList()
Sort the stations list.
static bool StationRatingMinSorter(const Station *const &a, const Station *const &b, const CargoTypes &cargo_filter)
Sort stations by their rating.
static bool StationWaitingAvailableSorter(const Station *const &a, const Station *const &b, const CargoTypes &cargo_filter)
Sort stations by their available waiting cargo.
void OnPaint() override
The window must be repainted.
static bool StationNameSorter(const Station *const &a, const Station *const &b, const CargoTypes &)
Sort stations by their name.
static bool StationTypeSorter(const Station *const &a, const Station *const &b, const CargoTypes &)
Sort stations by their type.
void OnGameTick() override
Called once per (game) tick.
static bool StationWaitingTotalSorter(const Station *const &a, const Station *const &b, const CargoTypes &cargo_filter)
Sort stations by their waiting cargo.
void BuildStationsList(const Owner owner)
(Re)Build station list
uint16_t stations_per_cargo_type_no_rating
Number of stations without a rating.
void OnInvalidateData([[maybe_unused]] int data=0, [[maybe_unused]] bool gui_scope=true) override
Some data on this window has become invalid.
Drop down checkmark component.
Drop down string component.
Flow descriptions by origin stations.
Definition: station_base.h:148
void RebuildDone()
Notify the sortlist that the rebuild is done.
void SetListing(Listing l)
Import sort conditions.
bool IsDescSortOrder() const
Check if the sort order is descending.
void ToggleSortOrder()
Toggle the sort order Since that is the worst condition for the sort function reverse the list here.
bool NeedRebuild() const
Check if a rebuild is needed.
void ForceRebuild()
Force that a rebuild is needed.
bool Sort(Comp compare)
Sort the list.
void ForceResort()
Force a resort next Sort call Reset the resort timer if used too.
uint8_t SortType() const
Get the sorttype of the list.
Listing GetListing() const
Export current sort conditions.
void SetSortFuncs(std::span< SortFunction *const > n_funcs)
Hand the sort function pointers to the GUIList.
bool NeedResort()
Check if a resort is needed next loop If used the resort timer will decrease every call till 0.
void SetSortType(uint8_t n_type)
Set the sorttype of the list.
A connected component of a link graph.
Definition: linkgraph.h:37
uint Monthly(uint base) const
Scale a value to its monthly equivalent, based on last compression.
Definition: linkgraph.h:249
Baseclass for nested widgets.
Definition: widget_type.h:144
Base class for a 'real' widget.
Definition: widget_type.h:370
void SetDataTip(uint32_t widget_data, StringID tool_tip)
Set data and tool tip of the nested widget.
Definition: widget.cpp:1130
Scrollbar data structure.
Definition: widget_type.h:694
size_type GetCapacity() const
Gets the number of visible elements of the scrollbar.
Definition: widget_type.h:731
void SetCount(size_t num)
Sets the number of elements in the list.
Definition: widget_type.h:780
auto GetScrolledItemFromWidget(Tcontainer &container, int clickpos, const Window *const w, WidgetID widget, int padding=0, int line_height=-1) const
Return an iterator pointing to the element of a scrolled widget that a user clicked in.
Definition: widget_type.h:879
size_type GetScrolledRowFromWidget(int clickpos, const Window *const w, WidgetID widget, int padding=0, int line_height=-1) const
Compute the row of a scrolled widget that a user clicked in.
Definition: widget.cpp:2320
void SetCapacityFromWidget(Window *w, WidgetID widget, int padding=0)
Set capacity of visible elements from the size and resize properties of a widget.
Definition: widget.cpp:2394
size_type GetCount() const
Gets the number of elements in the list.
Definition: widget_type.h:722
auto GetVisibleRangeIterators(Tcontainer &container) const
Get a pair of iterators for the range of visible elements in a container.
Definition: widget_type.h:860
size_type GetPosition() const
Gets the position of the first visible element in the list.
Definition: widget_type.h:740
CargoList that is used for stations.
Definition: cargopacket.h:529
uint TotalCount() const
Returns total count of cargo at the station, including cargo which is already reserved for loading.
Definition: cargopacket.h:607
uint ReservedCount() const
Returns sum of cargo reserved for loading onto vehicles.
Definition: cargopacket.h:597
uint AvailableCount() const
Returns sum of cargo still available for loading at the sation.
Definition: cargopacket.h:588
static bool UsingWallclockUnits(bool newgame=false)
Check if we are using wallclock units.
RectPadding framerect
Standard padding inside many panels.
Definition: window_gui.h:42
static WidgetDimensions scaled
Widget dimensions scaled for current zoom level.
Definition: window_gui.h:68
int vsep_wide
Wide vertical spacing.
Definition: window_gui.h:62
int hsep_wide
Wide horizontal spacing.
Definition: window_gui.h:64
int hsep_normal
Normal horizontal spacing.
Definition: window_gui.h:63
int hsep_indent
Width of identation for tree layouts.
Definition: window_gui.h:65
Functions related to commands.
Definition of stuff that is very close to a company, like the company struct itself.
CompanyID _local_company
Company controlled by the human player at this client. Can also be COMPANY_SPECTATOR.
Definition: company_cmd.cpp:52
Functions related to companies.
Owner
Enum for all companies/owners.
Definition: company_type.h:18
@ OWNER_NONE
The tile has no ownership.
Definition: company_type.h:25
int DivideApprox(int a, int b)
Deterministic approximate division.
Definition: math_func.cpp:22
Functions related to debugging.
#define Debug(category, level, format_string,...)
Ouptut a line of debugging information.
Definition: debug.h:37
@ DIR_N
North.
void ShowDropDownMenu(Window *w, std::span< const StringID > strings, int selected, WidgetID button, uint32_t disabled_mask, uint32_t hidden_mask, uint width)
Show a dropdown menu window near a widget of the parent window.
Definition: dropdown.cpp:441
void ShowDropDownList(Window *w, DropDownList &&list, int selected, WidgetID button, uint width, bool instant_close, bool persist)
Show a drop down list.
Definition: dropdown.cpp:404
Common drop down list components.
Functions related to the drop down widget.
Types related to the drop down widget.
std::vector< std::unique_ptr< const DropDownListItem > > DropDownList
A drop down list is a collection of drop down list items.
Definition: dropdown_type.h:50
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 GetStringBoundingBox(std::string_view str, FontSize start_fontsize)
Return the string dimension in pixels.
Definition: gfx.cpp:851
Dimension GetStringListBoundingBox(std::span< const StringID > list, FontSize fontsize)
Get maximum dimension of a list of strings.
Definition: gfx.cpp:889
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
bool _ctrl_pressed
Is Ctrl pressed?
Definition: gfx.cpp:38
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
void DrawSprite(SpriteID img, PaletteID pal, int x, int y, const SubSprite *sub, ZoomLevel zoom)
Draw a sprite, not in a viewport.
Definition: gfx.cpp:988
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
uint32_t SpriteID
The number of a sprite, without mapping bits and colourtables.
Definition: gfx_type.h:18
@ 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 NWidgetPart SetFill(uint16_t fill_x, uint16_t fill_y)
Widget part function for setting filling.
Definition: widget_type.h:1181
constexpr NWidgetPart SetScrollbar(WidgetID index)
Attach a scrollbar to a widget.
Definition: widget_type.h:1284
constexpr NWidgetPart SetDataTip(uint32_t data, StringID tip)
Widget part function for setting the data and tooltip.
Definition: widget_type.h:1202
constexpr NWidgetPart SetTextStyle(TextColour colour, FontSize size=FS_NORMAL)
Widget part function for setting the text style.
Definition: widget_type.h:1160
constexpr NWidgetPart SetMinimalSize(int16_t x, int16_t y)
Widget part function for setting the minimal size.
Definition: widget_type.h:1137
constexpr NWidgetPart NWidget(WidgetType tp, Colours col, WidgetID idx=-1)
Widget part function for starting a new 'real' widget.
Definition: widget_type.h:1309
constexpr NWidgetPart EndContainer()
Widget part function for denoting the end of a container (horizontal, vertical, WWT_FRAME,...
Definition: widget_type.h:1191
constexpr NWidgetPart SetAspect(float ratio, AspectFlags flags=AspectFlags::ResizeX)
Widget part function for setting the aspect ratio.
Definition: widget_type.h:1295
constexpr NWidgetPart SetResize(int16_t dx, int16_t dy)
Widget part function for setting the resize step.
Definition: widget_type.h:1126
void SetDirty() const
Mark entire window as dirty (in need of re-paint)
Definition: window.cpp:940
GUI functions that shouldn't be here.
void ShowExtraViewportWindow(TileIndex tile=INVALID_TILE)
Show a new Extra Viewport window.
Declaration of link graph classes used for cargo distribution.
@ DT_MANUAL
Manual distribution. No link graph calculations are run.
bool CircularTileSearch(TileIndex *tile, uint size, TestTileOnSearchProc proc, void *user_data)
Function performing a search around a center tile and going outward, thus in circle.
Definition: map.cpp:247
uint DistanceMax(TileIndex t0, TileIndex t1)
Gets the biggest distance component (x or y) between the two given tiles.
Definition: map.cpp:178
TileIndex TileAddXY(TileIndex tile, int x, int y)
Adds a given offset to a tile.
Definition: map_func.h:467
TileIndex TileAddByDir(TileIndex tile, Direction dir)
Adds a Direction to a tile.
Definition: map_func.h:596
static debug_inline TileIndex TileXY(uint x, uint y)
Returns the TileIndex of a coordinate.
Definition: map_func.h:373
static debug_inline uint TileY(TileIndex tile)
Get the Y component of a tile.
Definition: map_func.h:425
static debug_inline uint TileX(TileIndex tile)
Get the X component of a tile.
Definition: map_func.h:415
static debug_inline TileIndex TileVirtXY(uint x, uint y)
Get a tile from the virtual XY-coordinate.
Definition: map_func.h:404
constexpr bool IsInsideBS(const T x, const size_t base, const size_t size)
Checks if a value is between a window started at some base point.
Definition: math_func.hpp:252
constexpr uint CeilDiv(uint a, uint b)
Computes ceil(a / b) for non-negative a and b.
Definition: math_func.hpp:320
constexpr uint ToPercent8(uint i)
Converts a "fract" value 0..255 to "percent" value 0..100.
Definition: math_func.hpp:295
void ShowQueryString(StringID str, StringID caption, uint maxsize, Window *parent, CharSetFilter afilter, QueryStringFlags flags)
Show a query popup window with a textbox in it.
Definition: misc_gui.cpp:1079
static constexpr CargoID CF_NO_RATING
Show items with no rating (station list)
Definition: cargo_type.h:99
static constexpr CargoID CF_EXPAND_LIST
Expand list to show all items (station list)
Definition: cargo_type.h:101
static constexpr CargoID CF_SELECT_ALL
Select all items (station list)
Definition: cargo_type.h:100
TextColour GetContrastColour(uint8_t background, uint8_t threshold)
Determine a contrasty text colour for a coloured background.
Definition: palette.cpp:287
static const uint8_t PC_GREEN
Green palette colour.
Definition: palette_func.h:84
static const uint8_t PC_RED
Red palette colour.
Definition: palette_func.h:74
A number of safeguards to prevent using unsafe methods.
GameSettings _settings_game
Game settings of a running game or the scenario editor.
Definition: settings.cpp:57
ClientSettings _settings_client
The current settings for this game.
Definition: settings.cpp:56
Base types for having sorted lists in GUIs.
Base classes/functions for stations.
CargoArray GetAcceptanceAroundTiles(TileIndex center_tile, int w, int h, int rad, CargoTypes *always_accepted)
Get the acceptance of cargoes around the tile in 1/8.
bool HasStationInUse(StationID station, bool include_company, CompanyID company)
Tests whether the company's vehicles have this station in orders.
CargoTypes GetAcceptanceMask(const Station *st)
Get a mask of the cargo types that the station accepts.
CargoArray GetProductionAroundTiles(TileIndex north_tile, int w, int h, int rad)
Get the cargo types being produced around the tile (in a rectangle).
Command definitions related to stations.
void ShowSelectStationIfNeeded(TileArea ta, StationPickerCmdProc proc)
Show the station selection window when needed.
static void DrawCargoIcons(CargoID i, uint waiting, int left, int right, int y)
Draws icons of waiting cargo in the StationView window.
static bool AddNearbyStation(TileIndex tile, void *user_data)
Add station on this tile to _stations_nearby_list if it's fully within the station spread.
static void StationsWndShowStationRating(int left, int right, int y, CargoID type, uint amount, uint8_t rating)
Draw small boxes of cargo amount and ratings data at the given coordinates.
CargoSortType
@ StationID
by station id
@ AsGrouping
by the same principle the entries are being grouped
@ CargoID
by cargo id
@ StationString
by station name
@ Count
by amount of cargo
void ShowSelectRailWaypointIfNeeded(TileArea ta, StationPickerCmdProc proc)
Show the rail waypoint selection window when needed.
void FindStationsAroundSelection()
Find stations adjacent to the current tile highlight area, so that existing coverage area can be draw...
void ShowStationViewWindow(StationID station)
Opens StationViewWindow for given station.
void CheckRedrawStationCoverage(const Window *w)
Check whether we need to redraw the station coverage text.
void ShowCompanyStations(CompanyID company)
Opens window with list of company's stations.
void ShowSelectBaseStationIfNeeded(TileArea ta, StationPickerCmdProc &&proc)
Show the station selection window when needed.
static const BaseStation * FindStationsNearby(TileArea ta, bool distant_join)
Circulate around the to-be-built station to find stations we could join.
int DrawStationCoverageAreaText(int left, int right, int top, StationCoverageType sct, int rad, bool supplies)
Calculates and draws the accepted or supplied cargo around the selected tile(s)
Definition: station_gui.cpp:77
static bool StationJoinerNeeded(TileArea ta, const StationPickerCmdProc &proc)
Check whether we need to show the station selection window.
void ShowSelectRoadWaypointIfNeeded(TileArea ta, StationPickerCmdProc proc)
Show the road waypoint selection window when needed.
Contains enums and function declarations connected with stations GUI.
StationCoverageType
Types of cargo to display for station coverage.
Definition: station_gui.h:20
@ SCT_NON_PASSENGERS_ONLY
Draw all non-passenger class cargoes.
Definition: station_gui.h:22
@ SCT_PASSENGERS_ONLY
Draw only passenger class cargoes.
Definition: station_gui.h:21
@ SCT_ALL
Draw all cargoes.
Definition: station_gui.h:23
StationID GetStationIndex(Tile t)
Get StationID from a tile.
Definition: station_map.h:28
@ FACIL_DOCK
Station with a dock.
Definition: station_type.h:58
@ FACIL_BUS_STOP
Station with bus stops.
Definition: station_type.h:56
@ FACIL_AIRPORT
Station with an airport.
Definition: station_type.h:57
@ FACIL_TRUCK_STOP
Station with truck stops.
Definition: station_type.h:55
@ FACIL_TRAIN
Station with train station.
Definition: station_type.h:54
@ FACIL_NONE
The station has no facilities at all.
Definition: station_type.h:53
StationType
Station types.
Definition: station_type.h:31
static const uint MAX_LENGTH_STATION_NAME_CHARS
The maximum length of a station name in characters including '\0'.
Definition: station_type.h:87
Types related to the station widgets.
@ WID_SV_CLOSE_AIRPORT
'Close airport' button.
@ WID_SV_SORT_ORDER
'Sort order' button
@ WID_SV_CATCHMENT
Toggle catchment area highlight.
@ WID_SV_ROADVEHS
List of scheduled road vehs button.
@ WID_SV_SCROLLBAR
Scrollbar.
@ WID_SV_CAPTION
Caption of the window.
@ WID_SV_GROUP
label for "group by"
@ WID_SV_RENAME
'Rename' button.
@ WID_SV_SORT_BY
'Sort by' button
@ WID_SV_GROUP_BY
'Group by' button
@ WID_SV_PLANES
List of scheduled planes button.
@ WID_SV_ACCEPT_RATING_LIST
List of accepted cargoes / rating of cargoes.
@ WID_SV_WAITING
List of waiting cargo.
@ WID_SV_SHIPS
List of scheduled ships button.
@ WID_SV_LOCATION
'Location' button.
@ WID_SV_TRAINS
List of scheduled trains button.
@ WID_SV_ACCEPTS_RATINGS
'Accepts' / 'Ratings' button.
@ WID_STL_CAPTION
Caption of the window.
@ WID_STL_TRUCK
'TRUCK' button - list only facilities where is a truck stop.
@ WID_STL_SCROLLBAR
Scrollbar next to the main panel.
@ WID_STL_SORTDROPBTN
Dropdown button.
@ WID_STL_SORTBY
'Sort by' button - reverse sort direction.
@ WID_STL_TRAIN
'TRAIN' button - list only facilities where is a railroad station.
@ WID_STL_BUS
'BUS' button - list only facilities where is a bus stop.
@ WID_STL_FACILALL
'ALL' button - list all facilities.
@ WID_STL_LIST
The main panel, list of stations.
@ WID_STL_SHIP
'SHIP' button - list only facilities where is a dock.
@ WID_STL_CARGODROPDOWN
Cargo type dropdown list.
@ WID_STL_AIRPLANE
'AIRPLANE' button - list only facilities where is an airport.
Definition of base types and functions in a cross-platform compatible way.
int StrNaturalCompare(std::string_view s1, std::string_view s2, bool ignore_garbage_at_front)
Compares two strings using case insensitive natural sort.
Definition: string.cpp:589
Functions related to low-level strings.
@ CS_ALPHANUMERAL
Both numeric and alphabetic and spaces and stuff.
Definition: string_type.h:25
void SetDParam(size_t n, uint64_t v)
Set a string parameter v at index n in the global string parameter array.
Definition: strings.cpp:104
TextDirection _current_text_dir
Text direction of the currently selected language.
Definition: strings.cpp:56
Functions related to OTTD's strings.
@ TD_RTL
Text is written right-to-left by default.
Definition: strings_type.h:24
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
Definition: strings_type.h:16
uint64_t flags
stores which blocks on the airport are taken. was 16 bit earlier on, then 32
Definition: station_base.h:293
Base class for all station-ish types.
StationFacility facilities
The facilities that this station has.
TileIndex xy
Base tile of the station.
Owner owner
The owner of this station.
StationRect rect
NOSAVE: Station spread out rectangle maintained by StationRect::xxx() functions.
bool IsInUse() const
Check whether the base station currently is in use; in use means that it is not scheduled for deletio...
Town * town
The town this station is associated with.
static BaseStation * GetByTile(TileIndex tile)
Get the base station belonging to a specific tile.
Class for storing amounts of cargo.
Definition: cargo_type.h:114
Container for cargo from the same location and time.
Definition: cargopacket.h:40
uint16_t Count() const
Gets the number of 'items' in this packet.
Definition: cargopacket.h:158
StationID GetFirstStation() const
Gets the ID of the station where the cargo was loaded for the first time.
Definition: cargopacket.h:218
Specification of a cargo type.
Definition: cargotype.h:71
StringID abbrev
Two letter abbreviation for this cargo type.
Definition: cargotype.h:92
SpriteID GetCargoIcon() const
Get sprite for showing cargo of this type.
Definition: cargotype.cpp:169
static CargoSpec * Get(size_t index)
Retrieve cargo details for the given cargo ID.
Definition: cargotype.h:134
StringID name
Name of this type of cargo.
Definition: cargotype.h:88
bool IsValid() const
Tests for validity of this cargospec.
Definition: cargotype.h:115
GUISettings gui
settings related to the GUI
CargoTypes cargoes
bitmap of cargo types to include
bool include_no_rating
Whether we should include stations with no cargo rating.
uint8_t facilities
types of stations of interest
Dimensions (a width and height) of a rectangle in 2D.
bool persistent_buildingtools
keep the building tools active after usage
uint8_t station_gui_group_order
the order of grouping cargo entries in the station gui
uint8_t station_gui_sort_order
the sort order of entries in the station gui - ascending or descending
bool station_show_coverage
whether to highlight coverage area
uint8_t station_gui_sort_by
sort cargo entries in the station gui by station name or amount
StationSettings station
settings related to station management
LinkGraphSettings linkgraph
settings for link graph calculations
Stores station stats for a single cargo.
Definition: station_base.h:166
FlowStatMap flows
Planned flows through this station.
Definition: station_base.h:211
bool HasRating() const
Does this cargo have a rating at this station?
Definition: station_base.h:258
NodeID node
ID of node in link graph referring to this goods entry.
Definition: station_base.h:214
StationCargoList cargo
The cargo packets of cargo waiting in this station.
Definition: station_base.h:210
LinkGraphID link_graph
Link graph this station belongs to.
Definition: station_base.h:215
uint8_t rating
Station rating for this cargo.
Definition: station_base.h:226
Data structure describing how to show the list (what sort direction and criteria).
Definition: sortlist_type.h:30
static uint MaxY()
Gets the maximum Y coordinate within the map, including MP_VOID.
Definition: map_func.h:306
static debug_inline uint Size()
Get the size of the map.
Definition: map_func.h:288
static debug_inline uint MaxX()
Gets the maximum X coordinate within the map, including MP_VOID.
Definition: map_func.h:297
Partial widget specification to allow NWidgets to be written nested.
Definition: widget_type.h:1075
Represents the covered area of e.g.
Definition: tilearea_type.h:18
uint16_t w
The width of the area.
Definition: tilearea_type.h:20
TileIndex tile
The base tile of the area.
Definition: tilearea_type.h:19
uint16_t h
The height of the area.
Definition: tilearea_type.h:21
Coordinates of a point in 2D.
Tindex index
Index of this pool item.
Definition: pool_type.hpp:238
static Titem * Get(size_t index)
Returns Titem with given index.
Definition: pool_type.hpp:339
static bool IsValidID(size_t index)
Tests whether given index can be used to get valid (non-nullptr) Titem.
Definition: pool_type.hpp:328
static Titem * GetIfValid(size_t index)
Returns Titem with given index.
Definition: pool_type.hpp:350
static Pool::IterateWrapper< Titem > Iterate(size_t from=0)
Returns an iterable ensemble of all valid Titem.
Definition: pool_type.hpp:388
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 Indent(int indent, bool end) const
Copy Rect and indent it from its position.
Window for selecting stations/waypoints to (distant) join to.
void OnRealtimeTick([[maybe_unused]] uint delta_ms) override
Called periodically.
void OnResize() override
Called after the window got resized.
void OnInvalidateData([[maybe_unused]] int data=0, [[maybe_unused]] bool gui_scope=true) override
Some data on this window has become invalid.
TileArea area
Location of new station.
Iterable ensemble of each set bit in a value.
static Pool::IterateWrapper< Station > Iterate(size_t from=0)
Returns an iterable ensemble of all valid stations of type T.
static bool IsExpected(const BaseStation *st)
Helper for checking whether the given station is of this type.
static Waypoint * Get(size_t index)
Gets station with given index.
static bool IsValidID(size_t index)
Tests whether given index is a valid index for station of this type.
static Waypoint * From(BaseStation *st)
Converts a BaseStation to SpecializedStation with type checking.
uint8_t station_spread
amount a station may spread
bool distant_join_stations
allow to join non-adjacent stations
A row being displayed in the cargo view (as opposed to being "hidden" behind a plus sign).
StationID next_station
ID of the station belonging to the entry actually displayed if it's to/from/via.
CargoID next_cargo
ID of the cargo belonging to the entry actually displayed if it's cargo.
CargoDataEntry * filter
Parent of the cargo entry belonging to the row.
The StationView window.
void HandleCargoWaitingClick(int row)
Handle a click on a specific row in the cargo view.
Invalidation
Type of data invalidation.
@ INV_FLOWS
The planned flows have been recalculated and everything has to be updated.
@ INV_CARGO
Some cargo has been added or removed.
void EstimateDestinations(CargoID cargo, StationID source, StationID next, uint count, CargoDataEntry *dest)
Estimate the amounts of cargo per final destination for a given cargo, source station and next hop an...
void SelectSortBy(int index)
Select a new sort criterium for the cargo view.
void OnResize() override
Called after the window got resized.
int DrawCargoRatings(const Rect &r) const
Draw cargo ratings in the WID_SV_ACCEPT_RATING_LIST widget.
static const int NUM_COLUMNS
Number of "columns" in the cargo view: cargo, from, via, to.
void SetDisplayedRow(const CargoDataEntry *data)
Mark a specific row, characterized by its CargoDataEntry, as expanded.
void BuildFlowList(CargoID i, const FlowStatMap &flows, CargoDataEntry *cargo)
Build up the cargo view for PLANNED mode and a specific cargo.
StringID SearchNonStop(CargoDataEntry *cd, StationID station, int column)
Determine if we need to show the special "non-stop" string.
void RecalcDestinations(CargoID i)
Rebuild the cache for estimated destinations which is used to quickly show the "destination" entries ...
CargoSortType sortings[NUM_COLUMNS]
Sort types of the different 'columns'.
void SelectSortOrder(SortOrder order)
Select a new sort order for the cargo view.
CargoDataEntry expanded_rows
Parent entry of currently expanded rows.
void OnPaint() override
The window must be repainted.
Grouping
Type of grouping used in each of the "columns".
@ GR_SOURCE
Group by source of cargo ("from").
@ GR_NEXT
Group by next station ("via").
@ GR_CARGO
Group by cargo type.
@ GR_DESTINATION
Group by estimated final destination ("to").
static constexpr uint RATING_LINES
Height in lines of the cargo ratings view.
CargoDataVector displayed_rows
Parent entry of currently displayed rows (including collapsed ones).
static const StringID group_names[]
Names of the grouping options in the dropdown.
int grouping_index
Currently selected entry in the grouping drop down.
static constexpr uint ACCEPTS_LINES
Height in lines of the accepted cargo view.
void OnInvalidateData([[maybe_unused]] int data=0, [[maybe_unused]] bool gui_scope=true) override
Some data on this window has become invalid.
void BuildCargoList(CargoDataEntry *cargo, const Station *st)
Build up the cargo view for all cargoes.
int DrawEntries(CargoDataEntry *entry, const Rect &r, int pos, int maxrows, int column, CargoID cargo=INVALID_CARGO)
Draw the given cargo entries in the station GUI.
uint expand_shrink_width
The width allocated to the expand/shrink 'button'.
void ShowCargo(CargoDataEntry *data, CargoID cargo, StationID source, StationID next, StationID dest, uint count)
Show a certain cargo entry characterized by source/next/dest station, cargo ID and amount of cargo at...
void HandleCargoWaitingClick(CargoDataEntry *filter, Tid next)
Expand or collapse a specific row.
int rating_lines
Number of lines in the cargo ratings view.
static const StringID sort_names[]
Names of the sorting options in the dropdown.
CargoDataEntry cached_destinations
Cache for the flows passing through this station.
int scroll_to_row
If set, scroll the main viewport to the station pointed to by this row.
Mode current_mode
Currently selected display mode of cargo view.
int accepts_lines
Number of lines in the accepted cargo view.
Mode
Display mode of the cargo view.
@ MODE_PLANNED
Show cargo planned to pass through the station.
@ MODE_WAITING
Show cargo waiting at the station.
void BuildCargoList(CargoID i, const StationCargoList &packets, CargoDataEntry *cargo)
Build up the cargo view for WAITING mode and a specific cargo.
StringID GetEntryString(StationID station, StringID here, StringID other_station, StringID any)
Select the correct string for an entry referring to the specified station.
SortOrder sort_orders[NUM_COLUMNS]
Sort order (ascending/descending) for the 'columns'.
void SelectGroupBy(int index)
Select a new grouping mode for the cargo view.
Grouping groupings[NUM_COLUMNS]
Grouping modes for the different columns.
int DrawAcceptedCargo(const Rect &r) const
Draw accepted cargo in the WID_SV_ACCEPT_RATING_LIST widget.
Station data structure.
Definition: station_base.h:439
GoodsEntry goods[NUM_CARGO]
Goods at this station.
Definition: station_base.h:468
Airport airport
Tile area the airport covers.
Definition: station_base.h:453
Struct containing TileIndex and StationID.
TileIndex tile
TileIndex.
StationID station
StationID.
HighLightStyle drawstyle
Lower bits 0-3 are reserved for detailed highlight information.
uint8_t dirty
Whether the build station window needs to redraw due to the changed selection.
Point size
Size, in tile "units", of the white/red selection area.
Point pos
Location, in tile "units", of the northern tile of the selected area.
bool freeze
Freeze highlight in place.
CompanyID exclusivity
which company has exclusivity
Definition: town.h:75
uint8_t exclusive_counter
months till the exclusivity expires
Definition: town.h:76
The information about a vehicle list.
Definition: vehiclelist.h:28
Representation of a waypoint.
Definition: waypoint_base.h:23
High level window description.
Definition: window_gui.h:159
Data structure for an opened window.
Definition: window_gui.h:273
void ReInit(int rx=0, int ry=0, bool reposition=false)
Re-initialize a window, and optionally change its size.
Definition: window.cpp:952
virtual void Close(int data=0)
Hide the window and all its child windows, and mark them for a later deletion.
Definition: window.cpp:1047
static int SortButtonWidth()
Get width of up/down arrow of sort button state.
Definition: widget.cpp:780
void FinishInitNested(WindowNumber window_number=0)
Perform the second part of the initialization of a nested widget tree.
Definition: window.cpp:1733
void DrawWidgets() const
Paint all widgets of a window.
Definition: widget.cpp:731
Window * parent
Parent window.
Definition: window_gui.h:328
void RaiseWidget(WidgetID widget_index)
Marks a widget as raised.
Definition: window_gui.h:475
void SetWidgetDirty(WidgetID widget_index) const
Invalidate a widget, i.e.
Definition: window.cpp:551
void DrawSortButtonState(WidgetID widget, SortButtonState state) const
Draw a sort button's up or down arrow symbol.
Definition: widget.cpp:763
void CloseChildWindows(WindowClass wc=WC_INVALID) const
Close all children a window might have in a head-recursive manner.
Definition: window.cpp:1035
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 IsWidgetLowered(WidgetID widget_index) const
Gets the lowered state of a widget.
Definition: window_gui.h:497
Owner owner
The owner of the content shown in this window. Company colour is acquired from this variable.
Definition: window_gui.h:316
void SetWidgetLoweredState(WidgetID widget_index, bool lowered_stat)
Sets the lowered/raised status of a widget.
Definition: window_gui.h:447
bool IsShaded() const
Is window shaded currently?
Definition: window_gui.h:563
void SetTimeout()
Set the timeout flag of the window and initiate the timer.
Definition: window_gui.h:361
void LowerWidget(WidgetID widget_index)
Marks a widget as lowered.
Definition: window_gui.h:466
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 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
Stuff related to the text buffer GUI.
@ QSF_ENABLE_DEFAULT
enable the 'Default' button ("\0" is returned)
Definition: textbuf_gui.h:21
@ QSF_LEN_IN_CHARS
the length of the string is counted in characters
Definition: textbuf_gui.h:22
Owner GetTileOwner(Tile tile)
Returns the owner of a tile.
Definition: tile_map.h:178
static debug_inline bool IsTileType(Tile tile, TileType type)
Checks if a tile is a given tiletype.
Definition: tile_map.h:150
static const uint TILE_SIZE
Tile size in world coordinates.
Definition: tile_type.h:15
constexpr TileIndex INVALID_TILE
The very nice invalid tile marker.
Definition: tile_type.h:95
@ MP_STATION
A tile of a station.
Definition: tile_type.h:53
Functions related to tile highlights.
void ResetObjectToPlace()
Reset the cursor and mouse mode handling back to default (normal cursor, only clicking in windows).
Definition: viewport.cpp:3498
void UpdateTileSelection()
Updates tile highlighting for all cases.
Definition: viewport.cpp:2602
@ HT_RECT
rectangle (stations, depots, ...)
Base of the town class.
Functions related to the vehicle's GUIs.
VehicleType
Available vehicle types.
Definition: vehicle_type.h:21
@ VEH_ROAD
Road vehicle type.
Definition: vehicle_type.h:25
@ VEH_AIRCRAFT
Aircraft vehicle type.
Definition: vehicle_type.h:27
@ VEH_SHIP
Ship vehicle type.
Definition: vehicle_type.h:26
@ VEH_TRAIN
Train vehicle type.
Definition: vehicle_type.h:24
Functions and type for generating vehicle lists.
bool ScrollMainWindowToTile(TileIndex tile, bool instant)
Scrolls the viewport of the main window to a given location.
Definition: viewport.cpp:2515
void SetViewportCatchmentStation(const Station *st, bool sel)
Select or deselect station for coverage area highlight.
Definition: viewport.cpp:3618
const Station * _viewport_highlight_station
Currently selected station for coverage area highlight.
Definition: viewport.cpp:1005
Functions related to (drawing on) viewports.
Base of waypoints.
@ WPF_ROAD
This is a road waypoint.
Definition: waypoint_base.h:19
@ NC_EQUALSIZE
Value of the NCB_EQUALSIZE flag.
Definition: widget_type.h:524
@ WWT_PUSHTXTBTN
Normal push-button (no toggle button) with text caption.
Definition: widget_type.h:112
@ WWT_PUSHIMGBTN
Normal push-button (no toggle button) with image caption.
Definition: widget_type.h:113
@ NWID_HORIZONTAL
Horizontal container.
Definition: widget_type.h:75
@ WWT_TEXTBTN
(Toggle) Button with text
Definition: widget_type.h:55
@ 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_SHADEBOX
Shade box (at top-right of a window, between WWT_DEBUGBOX and WWT_DEFSIZEBOX)
Definition: widget_type.h:64
@ WWT_CAPTION
Window caption (window title between closebox and stickybox)
Definition: widget_type.h:61
@ NWID_VSCROLLBAR
Vertical scrollbar.
Definition: widget_type.h:84
@ NWID_VERTICAL
Vertical container.
Definition: widget_type.h:77
@ WWT_CLOSEBOX
Close box (at top-left of a window)
Definition: widget_type.h:69
@ WWT_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_DROPDOWN
Drop down list.
Definition: widget_type.h:70
void CloseWindowById(WindowClass cls, WindowNumber number, bool force, int data)
Close a window by its class and window number (if it is open).
Definition: window.cpp:1140
Window * FindWindowById(WindowClass cls, WindowNumber number)
Find a window by its class and window number.
Definition: window.cpp:1098
Window functions not directly related to making/drawing windows.
@ SBS_DOWN
Sort ascending.
Definition: window_gui.h:220
@ SBS_UP
Sort descending.
Definition: window_gui.h:221
@ WDF_CONSTRUCTION
This window is used for construction; close it whenever changing company.
Definition: window_gui.h:203
@ WDP_AUTO
Find a place automatically.
Definition: window_gui.h:147
int WidgetID
Widget ID.
Definition: window_type.h:18
int32_t WindowNumber
Number to differentiate different windows of the same class.
Definition: window_type.h:737
@ WC_STATION_LIST
Station list; Window numbers:
Definition: window_type.h:302
@ WC_ROADVEH_LIST
Road vehicle list; Window numbers:
Definition: window_type.h:314
@ WC_NONE
No window, redirects to WC_MAIN_WINDOW.
Definition: window_type.h:45
@ WC_SELECT_STATION
Select station (when joining stations); Window numbers:
Definition: window_type.h:242
@ WC_SHIPS_LIST
Ships list; Window numbers:
Definition: window_type.h:320
@ WC_STATION_VIEW
Station view; Window numbers:
Definition: window_type.h:345
@ WC_TRAINS_LIST
Trains list; Window numbers:
Definition: window_type.h:308
@ WC_DROPDOWN_MENU
Drop down menu; Window numbers:
Definition: window_type.h:156
@ WC_AIRCRAFT_LIST
Aircraft list; Window numbers:
Definition: window_type.h:326
Functions related to zooming.
int ScaleSpriteTrad(int value)
Scale traditional pixel dimensions to GUI zoom level, for drawing sprites.
Definition: zoom_func.h:107
int ScaleGUITrad(int value)
Scale traditional pixel dimensions to GUI zoom level.
Definition: zoom_func.h:117