OpenTTD
rail_gui.cpp
Go to the documentation of this file.
1 /* $Id: rail_gui.cpp 27710 2016-12-25 14:59:53Z frosch $ */
2 
3 /*
4  * This file is part of OpenTTD.
5  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
6  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
7  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
8  */
9 
12 #include "stdafx.h"
13 #include "gui.h"
14 #include "window_gui.h"
15 #include "station_gui.h"
16 #include "terraform_gui.h"
17 #include "viewport_func.h"
18 #include "command_func.h"
19 #include "waypoint_func.h"
20 #include "newgrf_station.h"
21 #include "company_base.h"
22 #include "strings_func.h"
23 #include "window_func.h"
24 #include "date_func.h"
25 #include "sound_func.h"
26 #include "company_func.h"
27 #include "widgets/dropdown_type.h"
28 #include "tunnelbridge.h"
29 #include "tilehighlight_func.h"
30 #include "spritecache.h"
31 #include "core/geometry_func.hpp"
32 #include "hotkeys.h"
33 #include "engine_base.h"
34 #include "vehicle_func.h"
35 #include "zoom_func.h"
36 #include "rail_gui.h"
37 
38 #include "station_map.h"
39 #include "tunnelbridge_map.h"
40 
41 #include "widgets/rail_widget.h"
42 
43 #include "safeguards.h"
44 
45 
49 static byte _waypoint_count = 1;
50 static byte _cur_waypoint_type;
54 
55 /* Map the setting: default_signal_type to the corresponding signal type */
56 static const SignalType _default_signal_type[] = {SIGTYPE_NORMAL, SIGTYPE_PBS, SIGTYPE_PBS_ONEWAY};
57 
60 
61  bool newstations;
63  byte station_type;
65 };
67 
68 
69 static void HandleStationPlacement(TileIndex start, TileIndex end);
70 static void ShowBuildTrainDepotPicker(Window *parent);
71 static void ShowBuildWaypointPicker(Window *parent);
72 static void ShowStationBuilder(Window *parent);
73 static void ShowSignalBuilder(Window *parent);
74 
79 static bool IsStationAvailable(const StationSpec *statspec)
80 {
81  if (statspec == NULL || !HasBit(statspec->callback_mask, CBM_STATION_AVAIL)) return true;
82 
83  uint16 cb_res = GetStationCallback(CBID_STATION_AVAILABILITY, 0, 0, statspec, NULL, INVALID_TILE);
84  if (cb_res == CALLBACK_FAILED) return true;
85 
87 }
88 
89 void CcPlaySound_SPLAT_RAIL(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2)
90 {
91  if (result.Succeeded() && _settings_client.sound.confirm) SndPlayTileFx(SND_20_SPLAT_RAIL, tile);
92 }
93 
94 static void GenericPlaceRail(TileIndex tile, int cmd)
95 {
96  DoCommandP(tile, _cur_railtype, cmd,
98  CMD_REMOVE_SINGLE_RAIL | CMD_MSG(STR_ERROR_CAN_T_REMOVE_RAILROAD_TRACK) :
99  CMD_BUILD_SINGLE_RAIL | CMD_MSG(STR_ERROR_CAN_T_BUILD_RAILROAD_TRACK),
100  CcPlaySound_SPLAT_RAIL);
101 }
102 
110 static void PlaceExtraDepotRail(TileIndex tile, DiagDirection dir, Track track)
111 {
112  if (GetRailTileType(tile) != RAIL_TILE_NORMAL) return;
113  if ((GetTrackBits(tile) & DiagdirReachesTracks(dir)) == 0) return;
114 
116 }
117 
119 static const Track _place_depot_extra_track[12] = {
120  TRACK_LEFT, TRACK_UPPER, TRACK_UPPER, TRACK_RIGHT, // First additional track for directions 0..3
121  TRACK_X, TRACK_Y, TRACK_X, TRACK_Y, // Second additional track
122  TRACK_LOWER, TRACK_LEFT, TRACK_RIGHT, TRACK_LOWER, // Third additional track
123 };
124 
130 };
131 
132 void CcRailDepot(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2)
133 {
134  if (result.Failed()) return;
135 
136  DiagDirection dir = (DiagDirection)p2;
137 
138  if (_settings_client.sound.confirm) SndPlayTileFx(SND_20_SPLAT_RAIL, tile);
140 
141  tile += TileOffsByDiagDir(dir);
142 
143  if (IsTileType(tile, MP_RAILWAY)) {
144  PlaceExtraDepotRail(tile, _place_depot_extra_dir[dir], _place_depot_extra_track[dir]);
145  PlaceExtraDepotRail(tile, _place_depot_extra_dir[dir + 4], _place_depot_extra_track[dir + 4]);
146  PlaceExtraDepotRail(tile, _place_depot_extra_dir[dir + 8], _place_depot_extra_track[dir + 8]);
147  }
148 }
149 
154 static void PlaceRail_Waypoint(TileIndex tile)
155 {
158  return;
159  }
160 
161  Axis axis = GetAxisForNewWaypoint(tile);
162  if (IsValidAxis(axis)) {
163  /* Valid tile for waypoints */
165  VpSetPlaceSizingLimit(_settings_game.station.station_spread);
166  } else {
167  /* Tile where we can't build rail waypoints. This is always going to fail,
168  * but provides the user with a proper error message. */
169  DoCommandP(tile, 1 << 8 | 1 << 16, STAT_CLASS_WAYP | INVALID_STATION << 16, CMD_BUILD_RAIL_WAYPOINT | CMD_MSG(STR_ERROR_CAN_T_BUILD_TRAIN_WAYPOINT));
170  }
171 }
172 
173 void CcStation(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2)
174 {
175  if (result.Failed()) return;
176 
177  if (_settings_client.sound.confirm) SndPlayTileFx(SND_20_SPLAT_RAIL, tile);
178  /* Only close the station builder window if the default station and non persistent building is chosen. */
180 }
181 
186 static void PlaceRail_Station(TileIndex tile)
187 {
190  VpSetPlaceSizingLimit(-1);
193  VpSetPlaceSizingLimit(_settings_game.station.station_spread);
194  } else {
196  uint32 p2 = _railstation.station_class | _railstation.station_type << 8 | INVALID_STATION << 16;
197 
200  if (!_railstation.orientation) Swap(w, h);
201 
202  CommandContainer cmdcont = { tile, p1, p2, CMD_BUILD_RAIL_STATION | CMD_MSG(STR_ERROR_CAN_T_BUILD_RAILROAD_STATION), CcStation, "" };
203  ShowSelectStationIfNeeded(cmdcont, TileArea(tile, w, h));
204  }
205 }
206 
213 {
215 
216  if (trackbits & TRACK_BIT_VERT) { // N-S direction
217  trackbits = (_tile_fract_coords.x <= _tile_fract_coords.y) ? TRACK_BIT_RIGHT : TRACK_BIT_LEFT;
218  }
219 
220  if (trackbits & TRACK_BIT_HORZ) { // E-W direction
221  trackbits = (_tile_fract_coords.x + _tile_fract_coords.y <= 15) ? TRACK_BIT_UPPER : TRACK_BIT_LOWER;
222  }
223 
224  Track track = FindFirstTrack(trackbits);
225 
227  DoCommandP(tile, track, 0, CMD_REMOVE_SIGNALS | CMD_MSG(STR_ERROR_CAN_T_REMOVE_SIGNALS_FROM), CcPlaySound_SPLAT_RAIL);
228  } else {
229  const Window *w = FindWindowById(WC_BUILD_SIGNAL, 0);
230 
231  /* Map the setting cycle_signal_types to the lower and upper allowed signal type. */
232  static const uint cycle_bounds[] = {SIGTYPE_NORMAL | (SIGTYPE_LAST_NOPBS << 3), SIGTYPE_PBS | (SIGTYPE_LAST << 3), SIGTYPE_NORMAL | (SIGTYPE_LAST << 3)};
233 
234  /* various bitstuffed elements for CmdBuildSingleSignal() */
235  uint32 p1 = track;
236 
237  if (w != NULL) {
238  /* signal GUI is used */
239  SB(p1, 3, 1, _ctrl_pressed);
240  SB(p1, 4, 1, _cur_signal_variant);
241  SB(p1, 5, 3, _cur_signal_type);
242  SB(p1, 8, 1, _convert_signal_button);
243  SB(p1, 9, 6, cycle_bounds[_settings_client.gui.cycle_signal_types]);
244  } else {
245  SB(p1, 3, 1, _ctrl_pressed);
247  SB(p1, 5, 3, _default_signal_type[_settings_client.gui.default_signal_type]);
248  SB(p1, 8, 1, 0);
249  SB(p1, 9, 6, cycle_bounds[_settings_client.gui.cycle_signal_types]);
250  }
251 
252  DoCommandP(tile, p1, 0, CMD_BUILD_SIGNALS |
253  CMD_MSG((w != NULL && _convert_signal_button) ? STR_ERROR_SIGNAL_CAN_T_CONVERT_SIGNALS_HERE : STR_ERROR_CAN_T_BUILD_SIGNALS_HERE),
254  CcPlaySound_SPLAT_RAIL);
255  }
256 }
257 
263 static void PlaceRail_Bridge(TileIndex tile, Window *w)
264 {
265  if (IsBridgeTile(tile)) {
266  TileIndex other_tile = GetOtherTunnelBridgeEnd(tile);
267  Point pt = {0, 0};
268  w->OnPlaceMouseUp(VPM_X_OR_Y, DDSP_BUILD_BRIDGE, pt, other_tile, tile);
269  } else {
271  }
272 }
273 
275 void CcBuildRailTunnel(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2)
276 {
277  if (result.Succeeded()) {
278  if (_settings_client.sound.confirm) SndPlayTileFx(SND_20_SPLAT_RAIL, tile);
280  } else {
282  }
283 }
284 
290 {
295  SetSelectionRed(_remove_button_clicked);
296 }
297 
304 {
305  if (w->IsWidgetDisabled(WID_RAT_REMOVE)) return false;
306 
307  /* allow ctrl to switch remove mode only for these widgets */
308  for (uint i = WID_RAT_BUILD_NS; i <= WID_RAT_BUILD_STATION; i++) {
309  if ((i <= WID_RAT_AUTORAIL || i >= WID_RAT_BUILD_WAYPOINT) && w->IsWidgetLowered(i)) {
311  return true;
312  }
313  }
314 
315  return false;
316 }
317 
318 
325 {
326  if (w->IsWidgetDisabled(WID_RAT_REMOVE)) return;
328  if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
329 
330  /* handle station builder */
333  /* starting drag & drop remove */
335  SetTileSelectSize(1, 1);
336  } else {
337  VpSetPlaceSizingLimit(-1);
338  }
339  } else {
340  /* starting station build mode */
344  if (_railstation.orientation == 0) Swap(x, y);
345  SetTileSelectSize(x, y);
346  } else {
347  VpSetPlaceSizingLimit(_settings_game.station.station_spread);
348  }
349  }
350  }
351 }
352 
353 static void DoRailroadTrack(int mode)
354 {
355  DoCommandP(TileVirtXY(_thd.selstart.x, _thd.selstart.y), TileVirtXY(_thd.selend.x, _thd.selend.y), _cur_railtype | (mode << 4),
357  CMD_REMOVE_RAILROAD_TRACK | CMD_MSG(STR_ERROR_CAN_T_REMOVE_RAILROAD_TRACK) :
358  CMD_BUILD_RAILROAD_TRACK | CMD_MSG(STR_ERROR_CAN_T_BUILD_RAILROAD_TRACK),
359  CcPlaySound_SPLAT_RAIL);
360 }
361 
362 static void HandleAutodirPlacement()
363 {
364  int trackstat = _thd.drawstyle & HT_DIR_MASK; // 0..5
365 
366  if (_thd.drawstyle & HT_RAIL) { // one tile case
367  GenericPlaceRail(TileVirtXY(_thd.selend.x, _thd.selend.y), trackstat);
368  return;
369  }
370 
371  DoRailroadTrack(trackstat);
372 }
373 
381 {
382  uint32 p2 = GB(_thd.drawstyle, 0, 3); // 0..5
383 
384  if ((_thd.drawstyle & HT_DRAG_MASK) == HT_RECT) { // one tile case
385  GenericPlaceSignals(TileVirtXY(_thd.selend.x, _thd.selend.y));
386  return;
387  }
388 
389  const Window *w = FindWindowById(WC_BUILD_SIGNAL, 0);
390 
391  if (w != NULL) {
392  /* signal GUI is used */
393  SB(p2, 3, 1, 0);
394  SB(p2, 4, 1, _cur_signal_variant);
395  SB(p2, 6, 1, _ctrl_pressed);
396  SB(p2, 7, 3, _cur_signal_type);
399  } else {
400  SB(p2, 3, 1, 0);
402  SB(p2, 6, 1, _ctrl_pressed);
403  SB(p2, 7, 3, _default_signal_type[_settings_client.gui.default_signal_type]);
406  }
407 
408  /* _settings_client.gui.drag_signals_density is given as a parameter such that each user
409  * in a network game can specify his/her own signal density */
410  DoCommandP(TileVirtXY(_thd.selstart.x, _thd.selstart.y), TileVirtXY(_thd.selend.x, _thd.selend.y), p2,
412  CMD_REMOVE_SIGNAL_TRACK | CMD_MSG(STR_ERROR_CAN_T_REMOVE_SIGNALS_FROM) :
413  CMD_BUILD_SIGNAL_TRACK | CMD_MSG(STR_ERROR_CAN_T_BUILD_SIGNALS_HERE),
414  CcPlaySound_SPLAT_RAIL);
415 }
416 
417 
422 
424  {
425  this->InitNested(TRANSPORT_RAIL);
426  this->SetupRailToolbar(railtype);
429 
431  }
432 
434  {
436  }
437 
443  virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
444  {
445  if (!gui_scope) return;
446 
447  if (!CanBuildVehicleInfrastructure(VEH_TRAIN)) delete this;
448  }
449 
455  {
456  this->railtype = railtype;
457  const RailtypeInfo *rti = GetRailTypeInfo(railtype);
458 
459  assert(railtype < RAILTYPE_END);
460  this->GetWidget<NWidgetCore>(WID_RAT_BUILD_NS)->widget_data = rti->gui_sprites.build_ns_rail;
462  this->GetWidget<NWidgetCore>(WID_RAT_BUILD_EW)->widget_data = rti->gui_sprites.build_ew_rail;
464  this->GetWidget<NWidgetCore>(WID_RAT_AUTORAIL)->widget_data = rti->gui_sprites.auto_rail;
466  this->GetWidget<NWidgetCore>(WID_RAT_CONVERT_RAIL)->widget_data = rti->gui_sprites.convert_rail;
468  }
469 
475  {
476  this->SetupRailToolbar(railtype);
477  this->ReInit();
478  }
479 
480  void UpdateRemoveWidgetStatus(int clicked_widget)
481  {
482  switch (clicked_widget) {
483  case WID_RAT_REMOVE:
484  /* If it is the removal button that has been clicked, do nothing,
485  * as it is up to the other buttons to drive removal status */
486  return;
487 
488  case WID_RAT_BUILD_NS:
489  case WID_RAT_BUILD_X:
490  case WID_RAT_BUILD_EW:
491  case WID_RAT_BUILD_Y:
492  case WID_RAT_AUTORAIL:
496  /* Removal button is enabled only if the rail/signal/waypoint/station
497  * button is still lowered. Once raised, it has to be disabled */
498  this->SetWidgetDisabledState(WID_RAT_REMOVE, !this->IsWidgetLowered(clicked_widget));
499  break;
500 
501  default:
502  /* When any other buttons than rail/signal/waypoint/station, raise and
503  * disable the removal button */
506  break;
507  }
508  }
509 
510  virtual void SetStringParameters(int widget) const
511  {
512  if (widget == WID_RAT_CAPTION) {
513  const RailtypeInfo *rti = GetRailTypeInfo(this->railtype);
514  if (rti->max_speed > 0) {
515  SetDParam(0, STR_TOOLBAR_RAILTYPE_VELOCITY);
517  SetDParam(2, rti->max_speed);
518  } else {
520  }
521  }
522  }
523 
524  virtual void OnClick(Point pt, int widget, int click_count)
525  {
526  if (widget < WID_RAT_BUILD_NS) return;
527 
528  _remove_button_clicked = false;
529  switch (widget) {
530  case WID_RAT_BUILD_NS:
532  this->last_user_action = widget;
533  break;
534 
535  case WID_RAT_BUILD_X:
537  this->last_user_action = widget;
538  break;
539 
540  case WID_RAT_BUILD_EW:
542  this->last_user_action = widget;
543  break;
544 
545  case WID_RAT_BUILD_Y:
547  this->last_user_action = widget;
548  break;
549 
550  case WID_RAT_AUTORAIL:
552  this->last_user_action = widget;
553  break;
554 
555  case WID_RAT_DEMOLISH:
557  this->last_user_action = widget;
558  break;
559 
560  case WID_RAT_BUILD_DEPOT:
562  ShowBuildTrainDepotPicker(this);
563  this->last_user_action = widget;
564  }
565  break;
566 
568  this->last_user_action = widget;
570  if (HandlePlacePushButton(this, WID_RAT_BUILD_WAYPOINT, SPR_CURSOR_WAYPOINT, HT_RECT) && _waypoint_count > 1) {
571  ShowBuildWaypointPicker(this);
572  }
573  break;
574 
576  if (HandlePlacePushButton(this, WID_RAT_BUILD_STATION, SPR_CURSOR_RAIL_STATION, HT_RECT)) {
577  ShowStationBuilder(this);
578  this->last_user_action = widget;
579  }
580  break;
581 
582  case WID_RAT_BUILD_SIGNALS: {
583  this->last_user_action = widget;
586  ShowSignalBuilder(this);
587  }
588  break;
589  }
590 
592  HandlePlacePushButton(this, WID_RAT_BUILD_BRIDGE, SPR_CURSOR_BRIDGE, HT_RECT);
593  this->last_user_action = widget;
594  break;
595 
598  this->last_user_action = widget;
599  break;
600 
601  case WID_RAT_REMOVE:
602  BuildRailClick_Remove(this);
603  break;
604 
607  this->last_user_action = widget;
608  break;
609 
610  default: NOT_REACHED();
611  }
612  this->UpdateRemoveWidgetStatus(widget);
614  }
615 
616  virtual EventState OnHotkey(int hotkey)
617  {
618  MarkTileDirtyByTile(TileVirtXY(_thd.pos.x, _thd.pos.y)); // redraw tile selection
619  return Window::OnHotkey(hotkey);
620  }
621 
622  virtual void OnPlaceObject(Point pt, TileIndex tile)
623  {
624  switch (this->last_user_action) {
625  case WID_RAT_BUILD_NS:
627  break;
628 
629  case WID_RAT_BUILD_X:
631  break;
632 
633  case WID_RAT_BUILD_EW:
635  break;
636 
637  case WID_RAT_BUILD_Y:
639  break;
640 
641  case WID_RAT_AUTORAIL:
643  break;
644 
645  case WID_RAT_DEMOLISH:
647  break;
648 
649  case WID_RAT_BUILD_DEPOT:
651  CMD_BUILD_TRAIN_DEPOT | CMD_MSG(STR_ERROR_CAN_T_BUILD_TRAIN_DEPOT),
652  CcRailDepot);
653  break;
654 
656  PlaceRail_Waypoint(tile);
657  break;
658 
660  PlaceRail_Station(tile);
661  break;
662 
665  break;
666 
668  PlaceRail_Bridge(tile, this);
669  break;
670 
672  DoCommandP(tile, _cur_railtype | (TRANSPORT_RAIL << 8), 0, CMD_BUILD_TUNNEL | CMD_MSG(STR_ERROR_CAN_T_BUILD_TUNNEL_HERE), CcBuildRailTunnel);
673  break;
674 
677  break;
678 
679  default: NOT_REACHED();
680  }
681  }
682 
683  virtual void OnPlaceDrag(ViewportPlaceMethod select_method, ViewportDragDropSelectionProcess select_proc, Point pt)
684  {
685  /* no dragging if you have pressed the convert button */
687 
688  VpSelectTilesWithMethod(pt.x, pt.y, select_method);
689  }
690 
691  virtual void OnPlaceMouseUp(ViewportPlaceMethod select_method, ViewportDragDropSelectionProcess select_proc, Point pt, TileIndex start_tile, TileIndex end_tile)
692  {
693  if (pt.x != -1) {
694  switch (select_proc) {
695  default: NOT_REACHED();
696  case DDSP_BUILD_BRIDGE:
698  ShowBuildBridgeWindow(start_tile, end_tile, TRANSPORT_RAIL, _cur_railtype);
699  break;
700 
701  case DDSP_PLACE_RAIL:
702  HandleAutodirPlacement();
703  break;
704 
705  case DDSP_BUILD_SIGNALS:
707  break;
708 
709  case DDSP_DEMOLISH_AREA:
710  GUIPlaceProcDragXY(select_proc, start_tile, end_tile);
711  break;
712 
713  case DDSP_CONVERT_RAIL:
714  DoCommandP(end_tile, start_tile, _cur_railtype | (_ctrl_pressed ? 0x10 : 0), CMD_CONVERT_RAIL | CMD_MSG(STR_ERROR_CAN_T_CONVERT_RAIL), CcPlaySound_SPLAT_RAIL);
715  break;
716 
717  case DDSP_REMOVE_STATION:
718  case DDSP_BUILD_STATION:
720  /* Station */
722  DoCommandP(end_tile, start_tile, _ctrl_pressed ? 0 : 1, CMD_REMOVE_FROM_RAIL_STATION | CMD_MSG(STR_ERROR_CAN_T_REMOVE_PART_OF_STATION), CcPlaySound_SPLAT_RAIL);
723  } else {
724  HandleStationPlacement(start_tile, end_tile);
725  }
726  } else {
727  /* Waypoint */
729  DoCommandP(end_tile, start_tile, _ctrl_pressed ? 0 : 1, CMD_REMOVE_FROM_RAIL_WAYPOINT | CMD_MSG(STR_ERROR_CAN_T_REMOVE_TRAIN_WAYPOINT), CcPlaySound_SPLAT_RAIL);
730  } else {
731  TileArea ta(start_tile, end_tile);
732  uint32 p1 = _cur_railtype | (select_method == VPM_X_LIMITED ? AXIS_X : AXIS_Y) << 4 | ta.w << 8 | ta.h << 16 | _ctrl_pressed << 24;
733  uint32 p2 = STAT_CLASS_WAYP | _cur_waypoint_type << 8 | INVALID_STATION << 16;
734 
735  CommandContainer cmdcont = { ta.tile, p1, p2, CMD_BUILD_RAIL_WAYPOINT | CMD_MSG(STR_ERROR_CAN_T_BUILD_TRAIN_WAYPOINT), CcPlaySound_SPLAT_RAIL, "" };
736  ShowSelectWaypointIfNeeded(cmdcont, ta);
737  }
738  }
739  break;
740  }
741  }
742  }
743 
744  virtual void OnPlaceObjectAbort()
745  {
746  this->RaiseButtons();
747  this->DisableWidget(WID_RAT_REMOVE);
748  this->SetWidgetDirty(WID_RAT_REMOVE);
749 
756  }
757 
758  virtual void OnPlacePresize(Point pt, TileIndex tile)
759  {
762  }
763 
764  virtual EventState OnCTRLStateChange()
765  {
766  /* do not toggle Remove button by Ctrl when placing station */
767  if (!this->IsWidgetLowered(WID_RAT_BUILD_STATION) && !this->IsWidgetLowered(WID_RAT_BUILD_WAYPOINT) && RailToolbar_CtrlChanged(this)) return ES_HANDLED;
768  return ES_NOT_HANDLED;
769  }
770 
771  static HotkeyList hotkeys;
772 };
773 
780 {
781  if (_game_mode != GM_NORMAL || !CanBuildVehicleInfrastructure(VEH_TRAIN)) return ES_NOT_HANDLED;
782  extern RailType _last_built_railtype;
783  Window *w = ShowBuildRailToolbar(_last_built_railtype);
784  if (w == NULL) return ES_NOT_HANDLED;
785  return w->OnHotkey(hotkey);
786 }
787 
788 const uint16 _railtoolbar_autorail_keys[] = {'5', 'A' | WKC_GLOBAL_HOTKEY, 0};
789 
790 static Hotkey railtoolbar_hotkeys[] = {
791  Hotkey('1', "build_ns", WID_RAT_BUILD_NS),
792  Hotkey('2', "build_x", WID_RAT_BUILD_X),
793  Hotkey('3', "build_ew", WID_RAT_BUILD_EW),
794  Hotkey('4', "build_y", WID_RAT_BUILD_Y),
795  Hotkey(_railtoolbar_autorail_keys, "autorail", WID_RAT_AUTORAIL),
796  Hotkey('6', "demolish", WID_RAT_DEMOLISH),
797  Hotkey('7', "depot", WID_RAT_BUILD_DEPOT),
798  Hotkey('8', "waypoint", WID_RAT_BUILD_WAYPOINT),
799  Hotkey('9', "station", WID_RAT_BUILD_STATION),
800  Hotkey('S', "signal", WID_RAT_BUILD_SIGNALS),
801  Hotkey('B', "bridge", WID_RAT_BUILD_BRIDGE),
802  Hotkey('T', "tunnel", WID_RAT_BUILD_TUNNEL),
803  Hotkey('R', "remove", WID_RAT_REMOVE),
804  Hotkey('C', "convert", WID_RAT_CONVERT_RAIL),
805  HOTKEY_LIST_END
806 };
807 HotkeyList BuildRailToolbarWindow::hotkeys("railtoolbar", railtoolbar_hotkeys, RailToolbarGlobalHotkeys);
808 
809 static const NWidgetPart _nested_build_rail_widgets[] = {
811  NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN),
812  NWidget(WWT_CAPTION, COLOUR_DARK_GREEN, WID_RAT_CAPTION), SetDataTip(STR_WHITE_STRING, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
813  NWidget(WWT_STICKYBOX, COLOUR_DARK_GREEN),
814  EndContainer(),
816  NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_RAT_BUILD_NS),
817  SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_RAIL_NS, STR_RAIL_TOOLBAR_TOOLTIP_BUILD_RAILROAD_TRACK),
818  NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_RAT_BUILD_X),
819  SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_RAIL_NE, STR_RAIL_TOOLBAR_TOOLTIP_BUILD_RAILROAD_TRACK),
820  NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_RAT_BUILD_EW),
821  SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_RAIL_EW, STR_RAIL_TOOLBAR_TOOLTIP_BUILD_RAILROAD_TRACK),
822  NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_RAT_BUILD_Y),
823  SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_RAIL_NW, STR_RAIL_TOOLBAR_TOOLTIP_BUILD_RAILROAD_TRACK),
824  NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_RAT_AUTORAIL),
825  SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_AUTORAIL, STR_RAIL_TOOLBAR_TOOLTIP_BUILD_AUTORAIL),
826 
827  NWidget(WWT_PANEL, COLOUR_DARK_GREEN), SetMinimalSize(4, 22), SetDataTip(0x0, STR_NULL), EndContainer(),
828 
829  NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_RAT_DEMOLISH),
830  SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_DYNAMITE, STR_TOOLTIP_DEMOLISH_BUILDINGS_ETC),
831  NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_RAT_BUILD_DEPOT),
832  SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_DEPOT_RAIL, STR_RAIL_TOOLBAR_TOOLTIP_BUILD_TRAIN_DEPOT_FOR_BUILDING),
833  NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_RAT_BUILD_WAYPOINT),
834  SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_WAYPOINT, STR_RAIL_TOOLBAR_TOOLTIP_CONVERT_RAIL_TO_WAYPOINT),
835  NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_RAT_BUILD_STATION),
836  SetFill(0, 1), SetMinimalSize(42, 22), SetDataTip(SPR_IMG_RAIL_STATION, STR_RAIL_TOOLBAR_TOOLTIP_BUILD_RAILROAD_STATION),
837  NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_RAT_BUILD_SIGNALS),
838  SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_RAIL_SIGNALS, STR_RAIL_TOOLBAR_TOOLTIP_BUILD_RAILROAD_SIGNALS),
839  NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_RAT_BUILD_BRIDGE),
840  SetFill(0, 1), SetMinimalSize(42, 22), SetDataTip(SPR_IMG_BRIDGE, STR_RAIL_TOOLBAR_TOOLTIP_BUILD_RAILROAD_BRIDGE),
841  NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_RAT_BUILD_TUNNEL),
842  SetFill(0, 1), SetMinimalSize(20, 22), SetDataTip(SPR_IMG_TUNNEL_RAIL, STR_RAIL_TOOLBAR_TOOLTIP_BUILD_RAILROAD_TUNNEL),
843  NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_RAT_REMOVE),
844  SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_REMOVE, STR_RAIL_TOOLBAR_TOOLTIP_TOGGLE_BUILD_REMOVE_FOR),
845  NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_RAT_CONVERT_RAIL),
846  SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_CONVERT_RAIL, STR_RAIL_TOOLBAR_TOOLTIP_CONVERT_RAIL),
847  EndContainer(),
848 };
849 
850 static WindowDesc _build_rail_desc(
851  WDP_ALIGN_TOOLBAR, "toolbar_rail", 0, 0,
854  _nested_build_rail_widgets, lengthof(_nested_build_rail_widgets),
855  &BuildRailToolbarWindow::hotkeys
856 );
857 
858 
868 {
869  if (!Company::IsValidID(_local_company)) return NULL;
870  if (!ValParamRailtype(railtype)) return NULL;
871 
873  _cur_railtype = railtype;
874  _remove_button_clicked = false;
875  return new BuildRailToolbarWindow(&_build_rail_desc, railtype);
876 }
877 
878 /* TODO: For custom stations, respect their allowed platforms/lengths bitmasks!
879  * --pasky */
880 
881 static void HandleStationPlacement(TileIndex start, TileIndex end)
882 {
883  TileArea ta(start, end);
884  uint numtracks = ta.w;
885  uint platlength = ta.h;
886 
887  if (_railstation.orientation == AXIS_X) Swap(numtracks, platlength);
888 
889  uint32 p1 = _cur_railtype | _railstation.orientation << 4 | numtracks << 8 | platlength << 16 | _ctrl_pressed << 24;
890  uint32 p2 = _railstation.station_class | _railstation.station_type << 8 | INVALID_STATION << 16;
891 
892  CommandContainer cmdcont = { ta.tile, p1, p2, CMD_BUILD_RAIL_STATION | CMD_MSG(STR_ERROR_CAN_T_BUILD_RAILROAD_STATION), CcStation, "" };
893  ShowSelectStationIfNeeded(cmdcont, ta);
894 }
895 
897 private:
898  uint line_height;
902 
908  void CheckSelectedSize(const StationSpec *statspec)
909  {
910  if (statspec == NULL || _settings_client.gui.station_dragdrop) return;
911 
912  /* If current number of tracks is not allowed, make it as big as possible */
916  if (statspec->disallowed_platforms != UINT8_MAX) {
919  }
921  }
922  }
923 
927  if (statspec->disallowed_lengths != UINT8_MAX) {
930  }
932  }
933  }
934  }
935 
936 public:
937  BuildRailStationWindow(WindowDesc *desc, Window *parent, bool newstation) : PickerWindowBase(desc, parent)
938  {
940  this->vscroll = NULL;
941  _railstation.newstations = newstation;
942 
943  this->CreateNestedTree();
944  NWidgetStacked *newst_additions = this->GetWidget<NWidgetStacked>(WID_BRAS_SHOW_NEWST_ADDITIONS);
945  newst_additions->SetDisplayedPlane(newstation ? 0 : SZSP_NONE);
946  newst_additions = this->GetWidget<NWidgetStacked>(WID_BRAS_SHOW_NEWST_MATRIX);
947  newst_additions->SetDisplayedPlane(newstation ? 0 : SZSP_NONE);
948  newst_additions = this->GetWidget<NWidgetStacked>(WID_BRAS_SHOW_NEWST_DEFSIZE);
949  newst_additions->SetDisplayedPlane(newstation ? 0 : SZSP_NONE);
950  newst_additions = this->GetWidget<NWidgetStacked>(WID_BRAS_SHOW_NEWST_RESIZE);
951  newst_additions->SetDisplayedPlane(newstation ? 0 : SZSP_NONE);
952  if (newstation) {
955  }
957 
958  this->LowerWidget(_railstation.orientation + WID_BRAS_PLATFORM_DIR_X);
961  } else {
964  }
967 
968  if (!newstation || _railstation.station_class >= (int)StationClass::GetClassCount()) {
969  /* New stations are not available or changed, so ensure the default station
970  * type is 'selected'. */
971  _railstation.station_class = STAT_CLASS_DFLT;
972  _railstation.station_type = 0;
973  this->vscroll2 = NULL;
974  }
975  if (newstation) {
976  _railstation.station_count = StationClass::Get(_railstation.station_class)->GetSpecCount();
977  _railstation.station_type = min(_railstation.station_type, _railstation.station_count - 1);
978 
979  int count = 0;
980  for (uint i = 0; i < StationClass::GetClassCount(); i++) {
981  if (i == STAT_CLASS_WAYP) continue;
982  count++;
983  }
984  this->vscroll->SetCount(count);
985  this->vscroll->SetPosition(Clamp(_railstation.station_class - 2, 0, max(this->vscroll->GetCount() - this->vscroll->GetCapacity(), 0)));
986 
987  NWidgetMatrix *matrix = this->GetWidget<NWidgetMatrix>(WID_BRAS_MATRIX);
988  matrix->SetScrollbar(this->vscroll2);
989  matrix->SetCount(_railstation.station_count);
990  matrix->SetClicked(_railstation.station_type);
991  }
992  }
993 
994  virtual ~BuildRailStationWindow()
995  {
997  }
998 
999  virtual void OnPaint()
1000  {
1001  bool newstations = _railstation.newstations;
1002  const StationSpec *statspec = newstations ? StationClass::Get(_railstation.station_class)->GetSpec(_railstation.station_type) : NULL;
1003 
1005  SetTileSelectSize(1, 1);
1006  } else {
1009  if (_railstation.orientation == AXIS_X) Swap(x, y);
1010  if (!_remove_button_clicked) {
1011  SetTileSelectSize(x, y);
1012  }
1013  }
1014 
1016 
1017  if (_settings_client.gui.station_show_coverage) SetTileSelectBigSize(-rad, -rad, 2 * rad, 2 * rad);
1018 
1019  for (uint bits = 0; bits < 7; bits++) {
1020  bool disable = bits >= _settings_game.station.station_spread;
1021  if (statspec == NULL) {
1022  this->SetWidgetDisabledState(bits + WID_BRAS_PLATFORM_NUM_1, disable);
1023  this->SetWidgetDisabledState(bits + WID_BRAS_PLATFORM_LEN_1, disable);
1024  } else {
1025  this->SetWidgetDisabledState(bits + WID_BRAS_PLATFORM_NUM_1, HasBit(statspec->disallowed_platforms, bits) || disable);
1026  this->SetWidgetDisabledState(bits + WID_BRAS_PLATFORM_LEN_1, HasBit(statspec->disallowed_lengths, bits) || disable);
1027  }
1028  }
1029 
1030  this->DrawWidgets();
1031 
1032  /* 'Accepts' and 'Supplies' texts. */
1033  NWidgetBase *cov = this->GetWidget<NWidgetBase>(WID_BRAS_COVERAGE_TEXTS);
1034  int top = cov->pos_y + WD_PAR_VSEP_NORMAL;
1035  int left = cov->pos_x + WD_FRAMERECT_LEFT;
1036  int right = cov->pos_x + cov->current_x - WD_FRAMERECT_RIGHT;
1037  int bottom = cov->pos_y + cov->current_y;
1038  top = DrawStationCoverageAreaText(left, right, top, SCT_ALL, rad, false) + WD_PAR_VSEP_NORMAL;
1039  top = DrawStationCoverageAreaText(left, right, top, SCT_ALL, rad, true) + WD_PAR_VSEP_NORMAL;
1040  /* Resize background if the window is too small.
1041  * Never make the window smaller to avoid oscillating if the size change affects the acceptance.
1042  * (This is the case, if making the window bigger moves the mouse into the window.) */
1043  if (top > bottom) {
1044  this->coverage_height += top - bottom;
1045  this->ReInit();
1046  }
1047  }
1048 
1049  virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
1050  {
1051  switch (widget) {
1052  case WID_BRAS_NEWST_LIST: {
1053  Dimension d = {0, 0};
1054  for (uint i = 0; i < StationClass::GetClassCount(); i++) {
1055  if (i == STAT_CLASS_WAYP) continue;
1057  }
1058  size->width = max(size->width, d.width + padding.width);
1060  size->height = 5 * this->line_height;
1061  resize->height = this->line_height;
1062  break;
1063  }
1064 
1065  case WID_BRAS_SHOW_NEWST_TYPE: {
1066  if (!_railstation.newstations) {
1067  size->width = 0;
1068  size->height = 0;
1069  break;
1070  }
1071 
1072  /* If newstations exist, compute the non-zero minimal size. */
1073  Dimension d = {0, 0};
1074  StringID str = this->GetWidget<NWidgetCore>(widget)->widget_data;
1075  for (StationClassID statclass = STAT_CLASS_BEGIN; statclass < (StationClassID)StationClass::GetClassCount(); statclass++) {
1076  if (statclass == STAT_CLASS_WAYP) continue;
1077  StationClass *stclass = StationClass::Get(statclass);
1078  for (uint16 j = 0; j < stclass->GetSpecCount(); j++) {
1079  const StationSpec *statspec = stclass->GetSpec(j);
1080  SetDParam(0, (statspec != NULL && statspec->name != 0) ? statspec->name : STR_STATION_CLASS_DFLT);
1081  d = maxdim(d, GetStringBoundingBox(str));
1082  }
1083  }
1084  size->width = max(size->width, d.width + padding.width);
1085  break;
1086  }
1087 
1090  case WID_BRAS_IMAGE:
1091  size->width = ScaleGUITrad(64) + 2;
1092  size->height = ScaleGUITrad(58) + 2;
1093  break;
1094 
1096  size->height = this->coverage_height;
1097  break;
1098 
1099  case WID_BRAS_MATRIX:
1100  fill->height = 1;
1101  resize->height = 1;
1102  break;
1103  }
1104  }
1105 
1106  virtual void DrawWidget(const Rect &r, int widget) const
1107  {
1108  DrawPixelInfo tmp_dpi;
1109 
1110  switch (GB(widget, 0, 16)) {
1112  /* Set up a clipping area for the '/' station preview */
1113  if (FillDrawPixelInfo(&tmp_dpi, r.left, r.top, r.right - r.left + 1, r.bottom - r.top + 1)) {
1114  DrawPixelInfo *old_dpi = _cur_dpi;
1115  _cur_dpi = &tmp_dpi;
1116  int x = ScaleGUITrad(31) + 1;
1117  int y = r.bottom - r.top - ScaleGUITrad(31);
1118  if (!DrawStationTile(x, y, _cur_railtype, AXIS_X, _railstation.station_class, _railstation.station_type)) {
1119  StationPickerDrawSprite(x, y, STATION_RAIL, _cur_railtype, INVALID_ROADTYPE, 2);
1120  }
1121  _cur_dpi = old_dpi;
1122  }
1123  break;
1124 
1126  /* Set up a clipping area for the '\' station preview */
1127  if (FillDrawPixelInfo(&tmp_dpi, r.left, r.top, r.right - r.left + 1, r.bottom - r.top + 1)) {
1128  DrawPixelInfo *old_dpi = _cur_dpi;
1129  _cur_dpi = &tmp_dpi;
1130  int x = ScaleGUITrad(31) + 1;
1131  int y = r.bottom - r.top - ScaleGUITrad(31);
1132  if (!DrawStationTile(x, y, _cur_railtype, AXIS_Y, _railstation.station_class, _railstation.station_type)) {
1133  StationPickerDrawSprite(x, y, STATION_RAIL, _cur_railtype, INVALID_ROADTYPE, 3);
1134  }
1135  _cur_dpi = old_dpi;
1136  }
1137  break;
1138 
1139  case WID_BRAS_NEWST_LIST: {
1140  uint statclass = 0;
1141  uint row = 0;
1142  for (uint i = 0; i < StationClass::GetClassCount(); i++) {
1143  if (i == STAT_CLASS_WAYP) continue;
1144  if (this->vscroll->IsVisible(statclass)) {
1145  DrawString(r.left + WD_MATRIX_LEFT, r.right - WD_MATRIX_RIGHT, row * this->line_height + r.top + WD_MATRIX_TOP,
1147  (StationClassID)i == _railstation.station_class ? TC_WHITE : TC_BLACK);
1148  row++;
1149  }
1150  statclass++;
1151  }
1152  break;
1153  }
1154 
1155  case WID_BRAS_IMAGE: {
1156  byte type = GB(widget, 16, 16);
1157  assert(type < _railstation.station_count);
1158  /* Check station availability callback */
1159  const StationSpec *statspec = StationClass::Get(_railstation.station_class)->GetSpec(type);
1160  if (!IsStationAvailable(statspec)) {
1161  GfxFillRect(r.left + 1, r.top + 1, r.right - 1, r.bottom - 1, PC_BLACK, FILLRECT_CHECKER);
1162  }
1163 
1164  /* Set up a clipping area for the station preview. */
1165  if (FillDrawPixelInfo(&tmp_dpi, r.left, r.top, r.right - r.left + 1, r.bottom - r.top + 1)) {
1166  DrawPixelInfo *old_dpi = _cur_dpi;
1167  _cur_dpi = &tmp_dpi;
1168  int x = ScaleGUITrad(31) + 1;
1169  int y = r.bottom - r.top - ScaleGUITrad(31);
1170  if (!DrawStationTile(x, y, _cur_railtype, _railstation.orientation, _railstation.station_class, type)) {
1171  StationPickerDrawSprite(x, y, STATION_RAIL, _cur_railtype, INVALID_ROADTYPE, 2 + _railstation.orientation);
1172  }
1173  _cur_dpi = old_dpi;
1174  }
1175  break;
1176  }
1177  }
1178  }
1179 
1180  virtual void OnResize()
1181  {
1182  if (this->vscroll != NULL) { // New stations available.
1184  }
1185  }
1186 
1187  virtual void SetStringParameters(int widget) const
1188  {
1189  if (widget == WID_BRAS_SHOW_NEWST_TYPE) {
1190  const StationSpec *statspec = StationClass::Get(_railstation.station_class)->GetSpec(_railstation.station_type);
1191  SetDParam(0, (statspec != NULL && statspec->name != 0) ? statspec->name : STR_STATION_CLASS_DFLT);
1192  }
1193  }
1194 
1195  virtual void OnClick(Point pt, int widget, int click_count)
1196  {
1197  switch (GB(widget, 0, 16)) {
1200  this->RaiseWidget(_railstation.orientation + WID_BRAS_PLATFORM_DIR_X);
1201  _railstation.orientation = (Axis)(widget - WID_BRAS_PLATFORM_DIR_X);
1202  this->LowerWidget(_railstation.orientation + WID_BRAS_PLATFORM_DIR_X);
1203  if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
1204  this->SetDirty();
1206  break;
1207 
1214  case WID_BRAS_PLATFORM_NUM_7: {
1217 
1220 
1222 
1223  const StationSpec *statspec = _railstation.newstations ? StationClass::Get(_railstation.station_class)->GetSpec(_railstation.station_type) : NULL;
1224  if (statspec != NULL && HasBit(statspec->disallowed_lengths, _settings_client.gui.station_platlength - 1)) {
1225  /* The previously selected number of platforms in invalid */
1226  for (uint i = 0; i < 7; i++) {
1227  if (!HasBit(statspec->disallowed_lengths, i)) {
1230  break;
1231  }
1232  }
1233  }
1234 
1235  this->LowerWidget(_settings_client.gui.station_numtracks + WID_BRAS_PLATFORM_NUM_BEGIN);
1237  if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
1238  this->SetDirty();
1240  break;
1241  }
1242 
1249  case WID_BRAS_PLATFORM_LEN_7: {
1252 
1255 
1257 
1258  const StationSpec *statspec = _railstation.newstations ? StationClass::Get(_railstation.station_class)->GetSpec(_railstation.station_type) : NULL;
1259  if (statspec != NULL && HasBit(statspec->disallowed_platforms, _settings_client.gui.station_numtracks - 1)) {
1260  /* The previously selected number of tracks in invalid */
1261  for (uint i = 0; i < 7; i++) {
1262  if (!HasBit(statspec->disallowed_platforms, i)) {
1265  break;
1266  }
1267  }
1268  }
1269 
1271  this->LowerWidget(_settings_client.gui.station_platlength + WID_BRAS_PLATFORM_LEN_BEGIN);
1272  if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
1273  this->SetDirty();
1275  break;
1276  }
1277 
1280 
1282 
1283  /* get the first allowed length/number of platforms */
1284  const StationSpec *statspec = _railstation.newstations ? StationClass::Get(_railstation.station_class)->GetSpec(_railstation.station_type) : NULL;
1285  if (statspec != NULL && HasBit(statspec->disallowed_lengths, _settings_client.gui.station_platlength - 1)) {
1286  for (uint i = 0; i < 7; i++) {
1287  if (!HasBit(statspec->disallowed_lengths, i)) {
1290  break;
1291  }
1292  }
1293  }
1294  if (statspec != NULL && HasBit(statspec->disallowed_platforms, _settings_client.gui.station_numtracks - 1)) {
1295  for (uint i = 0; i < 7; i++) {
1296  if (!HasBit(statspec->disallowed_platforms, i)) {
1299  break;
1300  }
1301  }
1302  }
1303 
1306  if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
1307  this->SetDirty();
1309  break;
1310  }
1311 
1313  case WID_BRAS_HIGHLIGHT_ON:
1315 
1318  if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
1319  this->SetDirty();
1320  break;
1321 
1322  case WID_BRAS_NEWST_LIST: {
1323  int y = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_BRAS_NEWST_LIST, 0, this->line_height);
1324  if (y >= (int)StationClass::GetClassCount()) return;
1325  for (uint i = 0; i < StationClass::GetClassCount(); i++) {
1326  if (i == STAT_CLASS_WAYP) continue;
1327  if (y == 0) {
1328  if (_railstation.station_class != (StationClassID)i) {
1329  _railstation.station_class = (StationClassID)i;
1330  StationClass *stclass = StationClass::Get(_railstation.station_class);
1331  _railstation.station_count = stclass->GetSpecCount();
1332  _railstation.station_type = min((int)_railstation.station_type, max(0, (int)_railstation.station_count - 1));
1333 
1334  this->CheckSelectedSize(stclass->GetSpec(_railstation.station_type));
1335 
1336  NWidgetMatrix *matrix = this->GetWidget<NWidgetMatrix>(WID_BRAS_MATRIX);
1337  matrix->SetCount(_railstation.station_count);
1338  matrix->SetClicked(_railstation.station_type);
1339  }
1340  if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
1341  this->SetDirty();
1343  break;
1344  }
1345  y--;
1346  }
1347  break;
1348  }
1349 
1350  case WID_BRAS_IMAGE: {
1351  int y = GB(widget, 16, 16);
1352  if (y >= _railstation.station_count) return;
1353 
1354  /* Check station availability callback */
1355  const StationSpec *statspec = StationClass::Get(_railstation.station_class)->GetSpec(y);
1356  if (!IsStationAvailable(statspec)) return;
1357 
1358  _railstation.station_type = y;
1359 
1360  this->CheckSelectedSize(statspec);
1361  this->GetWidget<NWidgetMatrix>(WID_BRAS_MATRIX)->SetClicked(_railstation.station_type);
1362 
1363  if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
1364  this->SetDirty();
1366  break;
1367  }
1368  }
1369  }
1370 
1371  virtual void OnTick()
1372  {
1374  }
1375 };
1376 
1377 static const NWidgetPart _nested_station_builder_widgets[] = {
1379  NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN),
1380  NWidget(WWT_CAPTION, COLOUR_DARK_GREEN), SetDataTip(STR_STATION_BUILD_RAIL_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
1382  NWidget(WWT_DEFSIZEBOX, COLOUR_DARK_GREEN),
1383  EndContainer(),
1384  EndContainer(),
1385  NWidget(WWT_PANEL, COLOUR_DARK_GREEN),
1389  NWidget(NWID_HORIZONTAL), SetPIP(7, 0, 7), SetPadding(2, 0, 1, 0),
1390  NWidget(WWT_MATRIX, COLOUR_GREY, WID_BRAS_NEWST_LIST), SetMinimalSize(122, 71), SetFill(1, 0),
1391  SetMatrixDataTip(1, 0, STR_STATION_BUILD_STATION_CLASS_TOOLTIP), SetScrollbar(WID_BRAS_NEWST_SCROLL),
1393  EndContainer(),
1394  EndContainer(),
1395  NWidget(WWT_LABEL, COLOUR_DARK_GREEN), SetMinimalSize(144, 11), SetDataTip(STR_STATION_BUILD_ORIENTATION, STR_NULL), SetPadding(1, 2, 0, 2),
1397  NWidget(NWID_SPACER), SetMinimalSize(7, 0), SetFill(1, 0),
1398  NWidget(WWT_PANEL, COLOUR_GREY, WID_BRAS_PLATFORM_DIR_X), SetMinimalSize(66, 60), SetFill(0, 0), SetDataTip(0x0, STR_STATION_BUILD_RAILROAD_ORIENTATION_TOOLTIP), EndContainer(),
1399  NWidget(NWID_SPACER), SetMinimalSize(2, 0), SetFill(1, 0),
1400  NWidget(WWT_PANEL, COLOUR_GREY, WID_BRAS_PLATFORM_DIR_Y), SetMinimalSize(66, 60), SetFill(0, 0), SetDataTip(0x0, STR_STATION_BUILD_RAILROAD_ORIENTATION_TOOLTIP), EndContainer(),
1401  NWidget(NWID_SPACER), SetMinimalSize(7, 0), SetFill(1, 0),
1402  EndContainer(),
1403  NWidget(WWT_LABEL, COLOUR_DARK_GREEN, WID_BRAS_SHOW_NEWST_TYPE), SetMinimalSize(144, 11), SetDataTip(STR_ORANGE_STRING, STR_NULL), SetPadding(1, 2, 4, 2),
1404  NWidget(WWT_LABEL, COLOUR_DARK_GREEN), SetMinimalSize(144, 11), SetDataTip(STR_STATION_BUILD_NUMBER_OF_TRACKS, STR_NULL), SetPadding(0, 2, 0, 2),
1406  NWidget(NWID_SPACER), SetFill(1, 0),
1407  NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BRAS_PLATFORM_NUM_1), SetMinimalSize(15, 12), SetDataTip(STR_BLACK_1, STR_STATION_BUILD_NUMBER_OF_TRACKS_TOOLTIP),
1408  NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BRAS_PLATFORM_NUM_2), SetMinimalSize(15, 12), SetDataTip(STR_BLACK_2, STR_STATION_BUILD_NUMBER_OF_TRACKS_TOOLTIP),
1409  NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BRAS_PLATFORM_NUM_3), SetMinimalSize(15, 12), SetDataTip(STR_BLACK_3, STR_STATION_BUILD_NUMBER_OF_TRACKS_TOOLTIP),
1410  NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BRAS_PLATFORM_NUM_4), SetMinimalSize(15, 12), SetDataTip(STR_BLACK_4, STR_STATION_BUILD_NUMBER_OF_TRACKS_TOOLTIP),
1411  NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BRAS_PLATFORM_NUM_5), SetMinimalSize(15, 12), SetDataTip(STR_BLACK_5, STR_STATION_BUILD_NUMBER_OF_TRACKS_TOOLTIP),
1412  NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BRAS_PLATFORM_NUM_6), SetMinimalSize(15, 12), SetDataTip(STR_BLACK_6, STR_STATION_BUILD_NUMBER_OF_TRACKS_TOOLTIP),
1413  NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BRAS_PLATFORM_NUM_7), SetMinimalSize(15, 12), SetDataTip(STR_BLACK_7, STR_STATION_BUILD_NUMBER_OF_TRACKS_TOOLTIP),
1414  NWidget(NWID_SPACER), SetFill(1, 0),
1415  EndContainer(),
1416  NWidget(WWT_LABEL, COLOUR_DARK_GREEN), SetMinimalSize(144, 11), SetDataTip(STR_STATION_BUILD_PLATFORM_LENGTH, STR_NULL), SetPadding(2, 2, 0, 2),
1418  NWidget(NWID_SPACER), SetFill(1, 0),
1419  NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BRAS_PLATFORM_LEN_1), SetMinimalSize(15, 12), SetDataTip(STR_BLACK_1, STR_STATION_BUILD_PLATFORM_LENGTH_TOOLTIP),
1420  NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BRAS_PLATFORM_LEN_2), SetMinimalSize(15, 12), SetDataTip(STR_BLACK_2, STR_STATION_BUILD_PLATFORM_LENGTH_TOOLTIP),
1421  NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BRAS_PLATFORM_LEN_3), SetMinimalSize(15, 12), SetDataTip(STR_BLACK_3, STR_STATION_BUILD_PLATFORM_LENGTH_TOOLTIP),
1422  NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BRAS_PLATFORM_LEN_4), SetMinimalSize(15, 12), SetDataTip(STR_BLACK_4, STR_STATION_BUILD_PLATFORM_LENGTH_TOOLTIP),
1423  NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BRAS_PLATFORM_LEN_5), SetMinimalSize(15, 12), SetDataTip(STR_BLACK_5, STR_STATION_BUILD_PLATFORM_LENGTH_TOOLTIP),
1424  NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BRAS_PLATFORM_LEN_6), SetMinimalSize(15, 12), SetDataTip(STR_BLACK_6, STR_STATION_BUILD_PLATFORM_LENGTH_TOOLTIP),
1425  NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BRAS_PLATFORM_LEN_7), SetMinimalSize(15, 12), SetDataTip(STR_BLACK_7, STR_STATION_BUILD_PLATFORM_LENGTH_TOOLTIP),
1426  NWidget(NWID_SPACER), SetFill(1, 0),
1427  EndContainer(),
1430  NWidget(NWID_SPACER), SetMinimalSize(2, 0), SetFill(1, 0),
1431  NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BRAS_PLATFORM_DRAG_N_DROP), SetMinimalSize(75, 12), SetDataTip(STR_STATION_BUILD_DRAG_DROP, STR_STATION_BUILD_DRAG_DROP_TOOLTIP),
1432  NWidget(NWID_SPACER), SetMinimalSize(2, 0), SetFill(1, 0),
1433  EndContainer(),
1434  NWidget(WWT_LABEL, COLOUR_DARK_GREEN), SetMinimalSize(144, 11), SetDataTip(STR_STATION_BUILD_COVERAGE_AREA_TITLE, STR_NULL), SetPadding(3, 2, 0, 2),
1436  NWidget(NWID_SPACER), SetMinimalSize(2, 0), SetFill(1, 0),
1437  NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BRAS_HIGHLIGHT_OFF), SetMinimalSize(60, 12),
1438  SetDataTip(STR_STATION_BUILD_COVERAGE_OFF, STR_STATION_BUILD_COVERAGE_AREA_OFF_TOOLTIP),
1439  NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BRAS_HIGHLIGHT_ON), SetMinimalSize(60, 12),
1440  SetDataTip(STR_STATION_BUILD_COVERAGE_ON, STR_STATION_BUILD_COVERAGE_AREA_ON_TOOLTIP),
1441  NWidget(NWID_SPACER), SetMinimalSize(2, 0), SetFill(1, 0),
1442  EndContainer(),
1443  EndContainer(),
1445  /* We need an additional background for the matrix, as the matrix cannot handle the scrollbar due to not being an NWidgetCore. */
1446  NWidget(WWT_PANEL, COLOUR_DARK_GREEN), SetScrollbar(WID_BRAS_MATRIX_SCROLL),
1448  NWidget(NWID_MATRIX, COLOUR_DARK_GREEN, WID_BRAS_MATRIX), SetScrollbar(WID_BRAS_MATRIX_SCROLL), SetPIP(0, 2, 0), SetPadding(2, 0, 0, 0),
1449  NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_BRAS_IMAGE), SetMinimalSize(66, 60),
1450  SetFill(0, 0), SetResize(0, 0), SetDataTip(0x0, STR_STATION_BUILD_STATION_TYPE_TOOLTIP), SetScrollbar(WID_BRAS_MATRIX_SCROLL),
1451  EndContainer(),
1452  EndContainer(),
1453  NWidget(NWID_VSCROLLBAR, COLOUR_DARK_GREEN, WID_BRAS_MATRIX_SCROLL),
1454  EndContainer(),
1455  EndContainer(),
1456  EndContainer(),
1457  EndContainer(),
1459  NWidget(WWT_EMPTY, INVALID_COLOUR, WID_BRAS_COVERAGE_TEXTS), SetFill(1, 1), SetResize(1, 0),
1462  NWidget(WWT_PANEL, COLOUR_DARK_GREEN), SetFill(0, 1), EndContainer(),
1463  NWidget(WWT_RESIZEBOX, COLOUR_DARK_GREEN),
1464  EndContainer(),
1465  EndContainer(),
1466  EndContainer(),
1467  EndContainer(),
1468 };
1469 
1472  WDP_AUTO, "build_station_rail", 350, 0,
1475  _nested_station_builder_widgets, lengthof(_nested_station_builder_widgets)
1476 );
1477 
1479 static void ShowStationBuilder(Window *parent)
1480 {
1481  bool newstations = StationClass::GetClassCount() > 2 || StationClass::Get(STAT_CLASS_DFLT)->GetSpecCount() != 1;
1482  new BuildRailStationWindow(&_station_builder_desc, parent, newstations);
1483 }
1484 
1486 private:
1489 
1497  void DrawSignalSprite(byte widget_index, SpriteID image) const
1498  {
1499  Point offset;
1500  Dimension sprite_size = GetSpriteSize(image, &offset);
1501  const NWidgetBase *widget = this->GetWidget<NWidgetBase>(widget_index);
1502  int x = widget->pos_x - offset.x +
1503  (widget->current_x - sprite_size.width + offset.x) / 2; // centered
1504  int y = widget->pos_y - sig_sprite_bottom_offset + WD_IMGBTN_TOP +
1505  (widget->current_y - WD_IMGBTN_TOP - WD_IMGBTN_BOTTOM + sig_sprite_size.height) / 2; // aligned to bottom
1506 
1507  DrawSprite(image, PAL_NONE,
1508  x + this->IsWidgetLowered(widget_index),
1509  y + this->IsWidgetLowered(widget_index));
1510  }
1511 
1512 public:
1513  BuildSignalWindow(WindowDesc *desc, Window *parent) : PickerWindowBase(desc, parent)
1514  {
1515  this->InitNested(TRANSPORT_RAIL);
1516  this->OnInvalidateData();
1517  }
1518 
1520  {
1521  _convert_signal_button = false;
1522  }
1523 
1524  virtual void OnInit()
1525  {
1526  /* Calculate maximum signal sprite size. */
1527  this->sig_sprite_size.width = 0;
1528  this->sig_sprite_size.height = 0;
1529  this->sig_sprite_bottom_offset = 0;
1531  for (uint type = SIGTYPE_NORMAL; type < SIGTYPE_END; type++) {
1532  for (uint variant = SIG_ELECTRIC; variant <= SIG_SEMAPHORE; variant++) {
1533  for (uint lowered = 0; lowered < 2; lowered++) {
1534  Point offset;
1535  Dimension sprite_size = GetSpriteSize(rti->gui_sprites.signals[type][variant][lowered], &offset);
1536  this->sig_sprite_bottom_offset = max<int>(this->sig_sprite_bottom_offset, sprite_size.height);
1537  this->sig_sprite_size.width = max<int>(this->sig_sprite_size.width, sprite_size.width - offset.x);
1538  this->sig_sprite_size.height = max<int>(this->sig_sprite_size.height, sprite_size.height - offset.y);
1539  }
1540  }
1541  }
1542  }
1543 
1544  virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
1545  {
1546  if (widget == WID_BS_DRAG_SIGNALS_DENSITY_LABEL) {
1547  /* Two digits for signals density. */
1548  size->width = max(size->width, 2 * GetDigitWidth() + padding.width + WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT);
1549  } else if (IsInsideMM(widget, WID_BS_SEMAPHORE_NORM, WID_BS_ELECTRIC_PBS_OWAY + 1)) {
1550  size->width = max(size->width, this->sig_sprite_size.width + WD_IMGBTN_LEFT + WD_IMGBTN_RIGHT);
1551  size->height = max(size->height, this->sig_sprite_size.height + WD_IMGBTN_TOP + WD_IMGBTN_BOTTOM);
1552  }
1553  }
1554 
1555  virtual void SetStringParameters(int widget) const
1556  {
1557  switch (widget) {
1560  break;
1561  }
1562  }
1563 
1564  virtual void DrawWidget(const Rect &r, int widget) const
1565  {
1567  /* Extract signal from widget number. */
1568  int type = (widget - WID_BS_SEMAPHORE_NORM) % SIGTYPE_END;
1569  int var = SIG_SEMAPHORE - (widget - WID_BS_SEMAPHORE_NORM) / SIGTYPE_END; // SignalVariant order is reversed compared to the widgets.
1570  SpriteID sprite = GetRailTypeInfo(_cur_railtype)->gui_sprites.signals[type][var][this->IsWidgetLowered(widget)];
1571 
1572  this->DrawSignalSprite(widget, sprite);
1573  }
1574  }
1575 
1576  virtual void OnClick(Point pt, int widget, int click_count)
1577  {
1578  switch (widget) {
1579  case WID_BS_SEMAPHORE_NORM:
1581  case WID_BS_SEMAPHORE_EXIT:
1583  case WID_BS_SEMAPHORE_PBS:
1585  case WID_BS_ELECTRIC_NORM:
1586  case WID_BS_ELECTRIC_ENTRY:
1587  case WID_BS_ELECTRIC_EXIT:
1588  case WID_BS_ELECTRIC_COMBO:
1589  case WID_BS_ELECTRIC_PBS:
1592 
1593  _cur_signal_type = (SignalType)((uint)((widget - WID_BS_SEMAPHORE_NORM) % (SIGTYPE_LAST + 1)));
1595 
1596  /* If 'remove' button of rail build toolbar is active, disable it. */
1597  if (_remove_button_clicked) {
1599  if (w != NULL) ToggleRailButton_Remove(w);
1600  }
1601 
1602  break;
1603 
1604  case WID_BS_CONVERT:
1606  break;
1607 
1612  }
1613  break;
1614 
1619  }
1620  break;
1621 
1622  default: break;
1623  }
1624 
1625  this->InvalidateData();
1626  }
1627 
1633  virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
1634  {
1635  if (!gui_scope) return;
1637 
1639 
1642  }
1643 };
1644 
1648  NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN),
1649  NWidget(WWT_CAPTION, COLOUR_DARK_GREEN), SetDataTip(STR_BUILD_SIGNAL_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
1650  EndContainer(),
1653  NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_BS_SEMAPHORE_NORM), SetDataTip(STR_NULL, STR_BUILD_SIGNAL_SEMAPHORE_NORM_TOOLTIP), EndContainer(), SetFill(1, 1),
1654  NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_BS_SEMAPHORE_ENTRY), SetDataTip(STR_NULL, STR_BUILD_SIGNAL_SEMAPHORE_ENTRY_TOOLTIP), EndContainer(), SetFill(1, 1),
1655  NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_BS_SEMAPHORE_EXIT), SetDataTip(STR_NULL, STR_BUILD_SIGNAL_SEMAPHORE_EXIT_TOOLTIP), EndContainer(), SetFill(1, 1),
1656  NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_BS_SEMAPHORE_COMBO), SetDataTip(STR_NULL, STR_BUILD_SIGNAL_SEMAPHORE_COMBO_TOOLTIP), EndContainer(), SetFill(1, 1),
1657  NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_BS_SEMAPHORE_PBS), SetDataTip(STR_NULL, STR_BUILD_SIGNAL_SEMAPHORE_PBS_TOOLTIP), EndContainer(), SetFill(1, 1),
1658  NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_BS_SEMAPHORE_PBS_OWAY), SetDataTip(STR_NULL, STR_BUILD_SIGNAL_SEMAPHORE_PBS_OWAY_TOOLTIP), EndContainer(), SetFill(1, 1),
1659  NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_BS_CONVERT), SetDataTip(SPR_IMG_SIGNAL_CONVERT, STR_BUILD_SIGNAL_CONVERT_TOOLTIP), SetFill(1, 1),
1660  EndContainer(),
1662  NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_BS_ELECTRIC_NORM), SetDataTip(STR_NULL, STR_BUILD_SIGNAL_ELECTRIC_NORM_TOOLTIP), EndContainer(), SetFill(1, 1),
1663  NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_BS_ELECTRIC_ENTRY), SetDataTip(STR_NULL, STR_BUILD_SIGNAL_ELECTRIC_ENTRY_TOOLTIP), EndContainer(), SetFill(1, 1),
1664  NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_BS_ELECTRIC_EXIT), SetDataTip(STR_NULL, STR_BUILD_SIGNAL_ELECTRIC_EXIT_TOOLTIP), EndContainer(), SetFill(1, 1),
1665  NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_BS_ELECTRIC_COMBO), SetDataTip(STR_NULL, STR_BUILD_SIGNAL_ELECTRIC_COMBO_TOOLTIP), EndContainer(), SetFill(1, 1),
1666  NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_BS_ELECTRIC_PBS), SetDataTip(STR_NULL, STR_BUILD_SIGNAL_ELECTRIC_PBS_TOOLTIP), EndContainer(), SetFill(1, 1),
1667  NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_BS_ELECTRIC_PBS_OWAY), SetDataTip(STR_NULL, STR_BUILD_SIGNAL_ELECTRIC_PBS_OWAY_TOOLTIP), EndContainer(), SetFill(1, 1),
1668  NWidget(WWT_PANEL, COLOUR_DARK_GREEN), SetDataTip(STR_NULL, STR_BUILD_SIGNAL_DRAG_SIGNALS_DENSITY_TOOLTIP), SetFill(1, 1),
1669  NWidget(WWT_LABEL, COLOUR_DARK_GREEN, WID_BS_DRAG_SIGNALS_DENSITY_LABEL), SetDataTip(STR_ORANGE_INT, STR_BUILD_SIGNAL_DRAG_SIGNALS_DENSITY_TOOLTIP), SetFill(1, 1),
1670  NWidget(NWID_HORIZONTAL), SetPIP(2, 0, 2),
1671  NWidget(NWID_SPACER), SetFill(1, 0),
1672  NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_BS_DRAG_SIGNALS_DENSITY_DECREASE), SetMinimalSize(9, 12), SetDataTip(AWV_DECREASE, STR_BUILD_SIGNAL_DRAG_SIGNALS_DENSITY_DECREASE_TOOLTIP),
1673  NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_BS_DRAG_SIGNALS_DENSITY_INCREASE), SetMinimalSize(9, 12), SetDataTip(AWV_INCREASE, STR_BUILD_SIGNAL_DRAG_SIGNALS_DENSITY_INCREASE_TOOLTIP),
1674  NWidget(NWID_SPACER), SetFill(1, 0),
1675  EndContainer(),
1676  NWidget(NWID_SPACER), SetMinimalSize(0, 2), SetFill(1, 0),
1677  EndContainer(),
1678  EndContainer(),
1679  EndContainer(),
1680 };
1681 
1684  WDP_AUTO, "build_signal", 0, 0,
1687  _nested_signal_builder_widgets, lengthof(_nested_signal_builder_widgets)
1688 );
1689 
1693 static void ShowSignalBuilder(Window *parent)
1694 {
1696 }
1697 
1699  BuildRailDepotWindow(WindowDesc *desc, Window *parent) : PickerWindowBase(desc, parent)
1700  {
1701  this->InitNested(TRANSPORT_RAIL);
1703  }
1704 
1705  virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
1706  {
1707  if (!IsInsideMM(widget, WID_BRAD_DEPOT_NE, WID_BRAD_DEPOT_NW + 1)) return;
1708 
1709  size->width = ScaleGUITrad(64) + 2;
1710  size->height = ScaleGUITrad(48) + 2;
1711  }
1712 
1713  virtual void DrawWidget(const Rect &r, int widget) const
1714  {
1715  if (!IsInsideMM(widget, WID_BRAD_DEPOT_NE, WID_BRAD_DEPOT_NW + 1)) return;
1716 
1717  DrawTrainDepotSprite(r.left + 1 + ScaleGUITrad(31), r.bottom - ScaleGUITrad(31), widget - WID_BRAD_DEPOT_NE + DIAGDIR_NE, _cur_railtype);
1718  }
1719 
1720  virtual void OnClick(Point pt, int widget, int click_count)
1721  {
1722  switch (widget) {
1723  case WID_BRAD_DEPOT_NE:
1724  case WID_BRAD_DEPOT_SE:
1725  case WID_BRAD_DEPOT_SW:
1726  case WID_BRAD_DEPOT_NW:
1730  if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
1731  this->SetDirty();
1732  break;
1733  }
1734  }
1735 };
1736 
1740  NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN),
1741  NWidget(WWT_CAPTION, COLOUR_DARK_GREEN), SetDataTip(STR_BUILD_DEPOT_TRAIN_ORIENTATION_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
1742  EndContainer(),
1743  NWidget(WWT_PANEL, COLOUR_DARK_GREEN),
1746  NWidget(NWID_SPACER), SetMinimalSize(3, 0), SetFill(1, 0),
1748  NWidget(WWT_PANEL, COLOUR_GREY, WID_BRAD_DEPOT_NW), SetMinimalSize(66, 50), SetDataTip(0x0, STR_BUILD_DEPOT_TRAIN_ORIENTATION_TOOLTIP),
1749  EndContainer(),
1751  NWidget(WWT_PANEL, COLOUR_GREY, WID_BRAD_DEPOT_SW), SetMinimalSize(66, 50), SetDataTip(0x0, STR_BUILD_DEPOT_TRAIN_ORIENTATION_TOOLTIP),
1752  EndContainer(),
1753  EndContainer(),
1756  NWidget(WWT_PANEL, COLOUR_GREY, WID_BRAD_DEPOT_NE), SetMinimalSize(66, 50), SetDataTip(0x0, STR_BUILD_DEPOT_TRAIN_ORIENTATION_TOOLTIP),
1757  EndContainer(),
1759  NWidget(WWT_PANEL, COLOUR_GREY, WID_BRAD_DEPOT_SE), SetMinimalSize(66, 50), SetDataTip(0x0, STR_BUILD_DEPOT_TRAIN_ORIENTATION_TOOLTIP),
1760  EndContainer(),
1761  EndContainer(),
1762  NWidget(NWID_SPACER), SetMinimalSize(3, 0), SetFill(1, 0),
1763  EndContainer(),
1765  EndContainer(),
1766 };
1767 
1768 static WindowDesc _build_depot_desc(
1769  WDP_AUTO, NULL, 0, 0,
1772  _nested_build_depot_widgets, lengthof(_nested_build_depot_widgets)
1773 );
1774 
1775 static void ShowBuildTrainDepotPicker(Window *parent)
1776 {
1777  new BuildRailDepotWindow(&_build_depot_desc, parent);
1778 }
1779 
1781  BuildRailWaypointWindow(WindowDesc *desc, Window *parent) : PickerWindowBase(desc, parent)
1782  {
1783  this->CreateNestedTree();
1784 
1785  NWidgetMatrix *matrix = this->GetWidget<NWidgetMatrix>(WID_BRW_WAYPOINT_MATRIX);
1786  matrix->SetScrollbar(this->GetScrollbar(WID_BRW_SCROLL));
1787 
1789 
1790  matrix->SetCount(_waypoint_count);
1791  matrix->SetClicked(_cur_waypoint_type);
1792  }
1793 
1794  virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
1795  {
1796  switch (widget) {
1798  /* Three blobs high and wide. */
1799  size->width += resize->width * 2;
1800  size->height += resize->height * 2;
1801 
1802  /* Resizing in X direction only at blob size, but at pixel level in Y. */
1803  resize->height = 1;
1804  break;
1805 
1806  case WID_BRW_WAYPOINT:
1807  size->width = ScaleGUITrad(64) + 2;
1808  size->height = ScaleGUITrad(58) + 2;
1809  break;
1810  }
1811  }
1812 
1813  virtual void DrawWidget(const Rect &r, int widget) const
1814  {
1815  switch (GB(widget, 0, 16)) {
1816  case WID_BRW_WAYPOINT: {
1817  byte type = GB(widget, 16, 16);
1818  const StationSpec *statspec = StationClass::Get(STAT_CLASS_WAYP)->GetSpec(type);
1819  DrawWaypointSprite(r.left + 1 + ScaleGUITrad(31), r.bottom - ScaleGUITrad(31), type, _cur_railtype);
1820 
1821  if (!IsStationAvailable(statspec)) {
1822  GfxFillRect(r.left + 1, r.top + 1, r.right - 1, r.bottom - 1, PC_BLACK, FILLRECT_CHECKER);
1823  }
1824  }
1825  }
1826  }
1827 
1828  virtual void OnClick(Point pt, int widget, int click_count)
1829  {
1830  switch (GB(widget, 0, 16)) {
1831  case WID_BRW_WAYPOINT: {
1832  byte type = GB(widget, 16, 16);
1833  this->GetWidget<NWidgetMatrix>(WID_BRW_WAYPOINT_MATRIX)->SetClicked(_cur_waypoint_type);
1834 
1835  /* Check station availability callback */
1836  const StationSpec *statspec = StationClass::Get(STAT_CLASS_WAYP)->GetSpec(type);
1837  if (!IsStationAvailable(statspec)) return;
1838 
1839  _cur_waypoint_type = type;
1840  this->GetWidget<NWidgetMatrix>(WID_BRW_WAYPOINT_MATRIX)->SetClicked(_cur_waypoint_type);
1841  if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
1842  this->SetDirty();
1843  break;
1844  }
1845  }
1846  }
1847 };
1848 
1852  NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN),
1853  NWidget(WWT_CAPTION, COLOUR_DARK_GREEN), SetDataTip(STR_WAYPOINT_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
1854  NWidget(WWT_DEFSIZEBOX, COLOUR_DARK_GREEN),
1855  EndContainer(),
1858  NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_BRW_WAYPOINT), SetMinimalSize(66, 60), SetDataTip(0x0, STR_WAYPOINT_GRAPHICS_TOOLTIP), SetScrollbar(WID_BRW_SCROLL), EndContainer(),
1859  EndContainer(),
1861  NWidget(NWID_VSCROLLBAR, COLOUR_DARK_GREEN, WID_BRW_SCROLL),
1862  NWidget(WWT_RESIZEBOX, COLOUR_DARK_GREEN),
1863  EndContainer(),
1864  EndContainer(),
1865 };
1866 
1867 static WindowDesc _build_waypoint_desc(
1868  WDP_AUTO, "build_waypoint", 0, 0,
1871  _nested_build_waypoint_widgets, lengthof(_nested_build_waypoint_widgets)
1872 );
1873 
1874 static void ShowBuildWaypointPicker(Window *parent)
1875 {
1876  new BuildRailWaypointWindow(&_build_waypoint_desc, parent);
1877 }
1878 
1883 {
1885 }
1886 
1891 void ReinitGuiAfterToggleElrail(bool disable)
1892 {
1893  extern RailType _last_built_railtype;
1894  if (disable && _last_built_railtype == RAILTYPE_ELECTRIC) {
1895  _last_built_railtype = _cur_railtype = RAILTYPE_RAIL;
1897  if (w != NULL) w->ModifyRailType(_cur_railtype);
1898  }
1900 }
1901 
1903 static void SetDefaultRailGui()
1904 {
1906 
1907  extern RailType _last_built_railtype;
1909  if (rt == DEF_RAILTYPE_MOST_USED) {
1910  /* Find the most used rail type */
1911  RailType count[RAILTYPE_END];
1912  memset(count, 0, sizeof(count));
1913  for (TileIndex t = 0; t < MapSize(); t++) {
1916  count[GetRailType(t)]++;
1917  }
1918  }
1919 
1920  rt = RAILTYPE_RAIL;
1921  for (RailType r = RAILTYPE_ELECTRIC; r < RAILTYPE_END; r++) {
1922  if (count[r] >= count[rt]) rt = r;
1923  }
1924 
1925  /* No rail, just get the first available one */
1926  if (count[rt] == 0) rt = DEF_RAILTYPE_FIRST;
1927  }
1928  switch (rt) {
1929  case DEF_RAILTYPE_FIRST:
1930  rt = RAILTYPE_RAIL;
1931  while (rt < RAILTYPE_END && !HasRailtypeAvail(_local_company, rt)) rt++;
1932  break;
1933 
1934  case DEF_RAILTYPE_LAST:
1936  break;
1937 
1938  default:
1939  break;
1940  }
1941 
1942  _last_built_railtype = _cur_railtype = rt;
1944  if (w != NULL) w->ModifyRailType(_cur_railtype);
1945 }
1946 
1953 bool ResetSignalVariant(int32 p)
1954 {
1956 
1957  if (new_variant != _cur_signal_variant) {
1959  if (w != NULL) {
1960  w->SetDirty();
1962  }
1963  _cur_signal_variant = new_variant;
1964  }
1965 
1966  return true;
1967 }
1968 
1974 {
1976 
1977  _convert_signal_button = false;
1978  _cur_signal_type = _default_signal_type[_settings_client.gui.default_signal_type];
1980 }
1981 
1988 DropDownList *GetRailTypeDropDownList(bool for_replacement, bool all_option)
1989 {
1990  RailTypes used_railtypes = RAILTYPES_NONE;
1991 
1992  /* Find the used railtypes. */
1993  Engine *e;
1994  FOR_ALL_ENGINES_OF_TYPE(e, VEH_TRAIN) {
1995  if (!HasBit(e->info.climates, _settings_game.game_creation.landscape)) continue;
1996 
1997  used_railtypes |= GetRailTypeInfo(e->u.rail.railtype)->introduces_railtypes;
1998  }
1999 
2000  /* Get the date introduced railtypes as well. */
2001  used_railtypes = AddDateIntroducedRailTypes(used_railtypes, MAX_DAY);
2002 
2003  const Company *c = Company::Get(_local_company);
2004  DropDownList *list = new DropDownList();
2005 
2006  if (all_option) {
2007  DropDownListStringItem *item = new DropDownListStringItem(STR_REPLACE_ALL_RAILTYPE, INVALID_RAILTYPE, false);
2008  *list->Append() = item;
2009  }
2010 
2011  RailType rt;
2013  /* If it's not used ever, don't show it to the user. */
2014  if (!HasBit(used_railtypes, rt)) continue;
2015 
2016  const RailtypeInfo *rti = GetRailTypeInfo(rt);
2017 
2018  StringID str = for_replacement ? rti->strings.replace_text : (rti->max_speed > 0 ? STR_TOOLBAR_RAILTYPE_VELOCITY : STR_JUST_STRING);
2020  item->SetParam(0, rti->strings.menu_text);
2021  item->SetParam(1, rti->max_speed);
2022  *list->Append() = item;
2023  }
2024  return list;
2025 }