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/>.
8 /** @file road_gui.cpp GUI for building roads. */
12 #include "window_gui.h"
13 #include "station_gui.h"
14 #include "terraform_gui.h"
15 #include "viewport_func.h"
16 #include "command_func.h"
18 #include "station_func.h"
19 #include "window_func.h"
20 #include "vehicle_func.h"
21 #include "sound_func.h"
22 #include "company_func.h"
23 #include "tunnelbridge.h"
24 #include "tunnelbridge_map.h"
25 #include "tilehighlight_func.h"
26 #include "company_base.h"
29 #include "zoom_func.h"
30 #include "engine_base.h"
31 #include "strings_func.h"
32 #include "core/geometry_func.hpp"
33 #include "station_cmd.h"
35 #include "tunnelbridge_cmd.h"
36 #include "newgrf_roadstop.h"
37 #include "querystring_gui.h"
38 #include "sortlist_type.h"
39 #include "stringfilter_type.h"
40 #include "string_func.h"
41 #include "timer/timer.h"
42 #include "timer/timer_game_calendar.h"
44 #include "widgets/road_widget.h"
46 #include "table/strings.h"
48 #include "safeguards.h"
50 static void ShowRVStationPicker(Window
*parent
, RoadStopType rs
);
51 static void ShowRoadDepotPicker(Window
*parent
);
53 static bool _remove_button_clicked
;
54 static bool _one_way_button_clicked
;
56 static Axis _place_road_dir
;
57 static bool _place_road_start_half_x
;
58 static bool _place_road_start_half_y
;
59 static bool _place_road_end_half
;
61 static RoadType _cur_roadtype
;
63 static DiagDirection _road_depot_orientation
;
65 struct RoadStopGUISettings
{
66 DiagDirection orientation
;
68 RoadStopClassID roadstop_class
;
69 uint16_t roadstop_type
;
70 uint16_t roadstop_count
;
72 static RoadStopGUISettings _roadstop_gui_settings
;
75 * Check whether a road stop type can be built.
76 * @return true if building is allowed.
78 static bool IsRoadStopAvailable(const RoadStopSpec
*roadstopspec
, StationType type
)
80 if (roadstopspec
== nullptr) return true;
82 if (HasBit(roadstopspec
->flags
, RSF_BUILD_MENU_ROAD_ONLY
) && !RoadTypeIsRoad(_cur_roadtype
)) return false;
83 if (HasBit(roadstopspec
->flags
, RSF_BUILD_MENU_TRAM_ONLY
) && !RoadTypeIsTram(_cur_roadtype
)) return false;
85 if (roadstopspec
->stop_type
!= ROADSTOPTYPE_ALL
) {
87 case STATION_BUS
: if (roadstopspec
->stop_type
!= ROADSTOPTYPE_PASSENGER
) return false; break;
88 case STATION_TRUCK
: if (roadstopspec
->stop_type
!= ROADSTOPTYPE_FREIGHT
) return false; break;
93 if (!HasBit(roadstopspec
->callback_mask
, CBM_ROAD_STOP_AVAIL
)) return true;
95 uint16_t cb_res
= GetRoadStopCallback(CBID_STATION_AVAILABILITY
, 0, 0, roadstopspec
, nullptr, INVALID_TILE
, _cur_roadtype
, type
, 0);
96 if (cb_res
== CALLBACK_FAILED
) return true;
98 return Convert8bitBooleanCallback(roadstopspec
->grf_prop
.grffile
, CBID_STATION_AVAILABILITY
, cb_res
);
101 void CcPlaySound_CONSTRUCTION_OTHER(Commands
, const CommandCost
&result
, TileIndex tile
)
103 if (result
.Succeeded() && _settings_client
.sound
.confirm
) SndPlayTileFx(SND_1F_CONSTRUCTION_OTHER
, tile
);
107 * Callback to start placing a bridge.
108 * @param tile Start tile of the bridge.
110 static void PlaceRoad_Bridge(TileIndex tile
, Window
*w
)
112 if (IsBridgeTile(tile
)) {
113 TileIndex other_tile
= GetOtherTunnelBridgeEnd(tile
);
115 w
->OnPlaceMouseUp(VPM_X_OR_Y
, DDSP_BUILD_BRIDGE
, pt
, other_tile
, tile
);
117 VpStartPlaceSizing(tile
, VPM_X_OR_Y
, DDSP_BUILD_BRIDGE
);
122 * Callback executed after a build road tunnel command has been called.
124 * @param result Whether the build succeeded.
125 * @param start_tile Starting tile of the tunnel.
127 void CcBuildRoadTunnel(Commands
, const CommandCost
&result
, TileIndex start_tile
)
129 if (result
.Succeeded()) {
130 if (_settings_client
.sound
.confirm
) SndPlayTileFx(SND_1F_CONSTRUCTION_OTHER
, start_tile
);
131 if (!_settings_client
.gui
.persistent_buildingtools
) ResetObjectToPlace();
133 DiagDirection start_direction
= ReverseDiagDir(GetTunnelBridgeDirection(start_tile
));
134 ConnectRoadToStructure(start_tile
, start_direction
);
136 TileIndex end_tile
= GetOtherTunnelBridgeEnd(start_tile
);
137 DiagDirection end_direction
= ReverseDiagDir(GetTunnelBridgeDirection(end_tile
));
138 ConnectRoadToStructure(end_tile
, end_direction
);
140 SetRedErrorSquare(_build_tunnel_endtile
);
145 * If required, connects a new structure to an existing road or tram by building the missing roadbit.
146 * @param tile Tile containing the structure to connect.
147 * @param direction Direction to check.
149 void ConnectRoadToStructure(TileIndex tile
, DiagDirection direction
)
151 tile
+= TileOffsByDiagDir(direction
);
152 /* if there is a roadpiece just outside of the station entrance, build a connecting route */
153 if (IsNormalRoadTile(tile
)) {
154 if (GetRoadBits(tile
, GetRoadTramType(_cur_roadtype
)) != ROAD_NONE
) {
155 Command
<CMD_BUILD_ROAD
>::Post(tile
, DiagDirToRoadBits(ReverseDiagDir(direction
)), _cur_roadtype
, DRD_NONE
, 0);
160 void CcRoadDepot(Commands
, const CommandCost
&result
, TileIndex tile
, RoadType
, DiagDirection dir
)
162 if (result
.Failed()) return;
164 if (_settings_client
.sound
.confirm
) SndPlayTileFx(SND_1F_CONSTRUCTION_OTHER
, tile
);
165 if (!_settings_client
.gui
.persistent_buildingtools
) ResetObjectToPlace();
166 ConnectRoadToStructure(tile
, dir
);
170 * Command callback for building road stops.
171 * @param result Result of the build road stop command.
172 * @param tile Start tile.
173 * @param width Width of the road stop.
174 * @param length Length of the road stop.
175 * @param is_drive_through False for normal stops, true for drive-through.
176 * @param dir Entrance direction (#DiagDirection) for normal stops. Converted to the axis for drive-through stops.
177 * @param spec_class Road stop spec class.
178 * @param spec_index Road stop spec index.
179 * @see CmdBuildRoadStop
181 void CcRoadStop(Commands
, const CommandCost
&result
, TileIndex tile
, uint8_t width
, uint8_t length
, RoadStopType
, bool is_drive_through
,
182 DiagDirection dir
, RoadType
, RoadStopClassID spec_class
, uint16_t spec_index
, StationID
, bool)
184 if (result
.Failed()) return;
186 if (_settings_client
.sound
.confirm
) SndPlayTileFx(SND_1F_CONSTRUCTION_OTHER
, tile
);
187 if (!_settings_client
.gui
.persistent_buildingtools
) ResetObjectToPlace();
189 bool connect_to_road
= true;
190 if ((uint
)spec_class
< RoadStopClass::GetClassCount() && spec_index
< RoadStopClass::Get(spec_class
)->GetSpecCount()) {
191 const RoadStopSpec
*roadstopspec
= RoadStopClass::Get(spec_class
)->GetSpec(spec_index
);
192 if (roadstopspec
!= nullptr && HasBit(roadstopspec
->flags
, RSF_NO_AUTO_ROAD_CONNECTION
)) connect_to_road
= false;
195 if (connect_to_road
) {
196 TileArea
roadstop_area(tile
, width
, length
);
197 for (TileIndex cur_tile
: roadstop_area
) {
198 ConnectRoadToStructure(cur_tile
, dir
);
199 /* For a drive-through road stop build connecting road for other entrance. */
200 if (is_drive_through
) ConnectRoadToStructure(cur_tile
, ReverseDiagDir(dir
));
206 * Place a new road stop.
207 * @param start_tile First tile of the area.
208 * @param end_tile Last tile of the area.
209 * @param stop_type Type of stop (bus/truck).
210 * @param adjacent Allow stations directly adjacent to other stations.
211 * @param rt The roadtypes.
212 * @param err_msg Error message to show.
215 static void PlaceRoadStop(TileIndex start_tile
, TileIndex end_tile
, RoadStopType stop_type
, bool adjacent
, RoadType rt
, StringID err_msg
)
217 TileArea
ta(start_tile
, end_tile
);
218 DiagDirection ddir
= _roadstop_gui_settings
.orientation
;
219 bool drive_through
= ddir
>= DIAGDIR_END
;
220 if (drive_through
) ddir
= static_cast<DiagDirection
>(ddir
- DIAGDIR_END
); // Adjust picker result to actual direction.
221 RoadStopClassID spec_class
= _roadstop_gui_settings
.roadstop_class
;
222 uint16_t spec_index
= _roadstop_gui_settings
.roadstop_type
;
224 auto proc
= [=](bool test
, StationID to_join
) -> bool {
226 return Command
<CMD_BUILD_ROAD_STOP
>::Do(CommandFlagsToDCFlags(GetCommandFlags
<CMD_BUILD_ROAD_STOP
>()), ta
.tile
, ta
.w
, ta
.h
, stop_type
, drive_through
,
227 ddir
, rt
, spec_class
, spec_index
, INVALID_STATION
, adjacent
).Succeeded();
229 return Command
<CMD_BUILD_ROAD_STOP
>::Post(err_msg
, CcRoadStop
, ta
.tile
, ta
.w
, ta
.h
, stop_type
, drive_through
,
230 ddir
, rt
, spec_class
, spec_index
, to_join
, adjacent
);
234 ShowSelectStationIfNeeded(ta
, proc
);
238 * Callback for placing a bus station.
239 * @param tile Position to place the station.
241 static void PlaceRoad_BusStation(TileIndex tile
)
243 if (_remove_button_clicked
) {
244 VpStartPlaceSizing(tile
, VPM_X_AND_Y
, DDSP_REMOVE_BUSSTOP
);
246 if (_roadstop_gui_settings
.orientation
< DIAGDIR_END
) { // Not a drive-through stop.
247 VpStartPlaceSizing(tile
, (DiagDirToAxis(_roadstop_gui_settings
.orientation
) == AXIS_X
) ? VPM_X_LIMITED
: VPM_Y_LIMITED
, DDSP_BUILD_BUSSTOP
);
249 VpStartPlaceSizing(tile
, VPM_X_AND_Y_LIMITED
, DDSP_BUILD_BUSSTOP
);
251 VpSetPlaceSizingLimit(_settings_game
.station
.station_spread
);
256 * Callback for placing a truck station.
257 * @param tile Position to place the station.
259 static void PlaceRoad_TruckStation(TileIndex tile
)
261 if (_remove_button_clicked
) {
262 VpStartPlaceSizing(tile
, VPM_X_AND_Y
, DDSP_REMOVE_TRUCKSTOP
);
264 if (_roadstop_gui_settings
.orientation
< DIAGDIR_END
) { // Not a drive-through stop.
265 VpStartPlaceSizing(tile
, (DiagDirToAxis(_roadstop_gui_settings
.orientation
) == AXIS_X
) ? VPM_X_LIMITED
: VPM_Y_LIMITED
, DDSP_BUILD_TRUCKSTOP
);
267 VpStartPlaceSizing(tile
, VPM_X_AND_Y_LIMITED
, DDSP_BUILD_TRUCKSTOP
);
269 VpSetPlaceSizingLimit(_settings_game
.station
.station_spread
);
273 typedef void OnButtonClick(Window
*w
);
276 * Toggles state of the Remove button of Build road toolbar
277 * @param w window the button belongs to
279 static void ToggleRoadButton_Remove(Window
*w
)
281 w
->ToggleWidgetLoweredState(WID_ROT_REMOVE
);
282 w
->SetWidgetDirty(WID_ROT_REMOVE
);
283 _remove_button_clicked
= w
->IsWidgetLowered(WID_ROT_REMOVE
);
284 SetSelectionRed(_remove_button_clicked
);
288 * Updates the Remove button because of Ctrl state change
289 * @param w window the button belongs to
290 * @return true iff the remove button was changed
292 static bool RoadToolbar_CtrlChanged(Window
*w
)
294 if (w
->IsWidgetDisabled(WID_ROT_REMOVE
)) return false;
296 /* allow ctrl to switch remove mode only for these widgets */
297 for (WidgetID i
= WID_ROT_ROAD_X
; i
<= WID_ROT_AUTOROAD
; i
++) {
298 if (w
->IsWidgetLowered(i
)) {
299 ToggleRoadButton_Remove(w
);
307 /** Road toolbar window handler. */
308 struct BuildRoadToolbarWindow
: Window
{
309 RoadType roadtype
; ///< Road type to build.
310 const RoadTypeInfo
*rti
; ///< Information about current road type
311 int last_started_action
; ///< Last started user action.
313 BuildRoadToolbarWindow(WindowDesc
*desc
, WindowNumber window_number
) : Window(desc
)
315 this->Initialize(_cur_roadtype
);
316 this->InitNested(window_number
);
317 this->SetupRoadToolbar();
318 this->SetWidgetDisabledState(WID_ROT_REMOVE
, true);
320 if (RoadTypeIsRoad(this->roadtype
)) {
321 this->SetWidgetDisabledState(WID_ROT_ONE_WAY
, true);
324 this->OnInvalidateData();
325 this->last_started_action
= INVALID_WID_ROT
;
327 if (_settings_client
.gui
.link_terraform_toolbar
) ShowTerraformToolbar(this);
330 void Close([[maybe_unused
]] int data
= 0) override
332 if (_game_mode
== GM_NORMAL
&& (this->IsWidgetLowered(WID_ROT_BUS_STATION
) || this->IsWidgetLowered(WID_ROT_TRUCK_STATION
))) SetViewportCatchmentStation(nullptr, true);
333 if (_settings_client
.gui
.link_terraform_toolbar
) CloseWindowById(WC_SCEN_LAND_GEN
, 0, false);
334 this->Window::Close();
338 * Some data on this window has become invalid.
339 * @param data Information about the changed data.
340 * @param gui_scope Whether the call is done from GUI scope. You may not do everything when not in GUI scope. See #InvalidateWindowData() for details.
342 void OnInvalidateData([[maybe_unused
]] int data
= 0, [[maybe_unused
]] bool gui_scope
= true) override
344 if (!gui_scope
) return;
345 RoadTramType rtt
= GetRoadTramType(this->roadtype
);
347 bool can_build
= CanBuildVehicleInfrastructure(VEH_ROAD
, rtt
);
348 this->SetWidgetsDisabledState(!can_build
,
351 WID_ROT_TRUCK_STATION
);
353 CloseWindowById(WC_BUS_STATION
, TRANSPORT_ROAD
);
354 CloseWindowById(WC_TRUCK_STATION
, TRANSPORT_ROAD
);
355 CloseWindowById(WC_BUILD_DEPOT
, TRANSPORT_ROAD
);
358 if (_game_mode
!= GM_EDITOR
) {
360 /* Show in the tooltip why this button is disabled. */
361 this->GetWidget
<NWidgetCore
>(WID_ROT_DEPOT
)->SetToolTip(STR_TOOLBAR_DISABLED_NO_VEHICLE_AVAILABLE
);
362 this->GetWidget
<NWidgetCore
>(WID_ROT_BUS_STATION
)->SetToolTip(STR_TOOLBAR_DISABLED_NO_VEHICLE_AVAILABLE
);
363 this->GetWidget
<NWidgetCore
>(WID_ROT_TRUCK_STATION
)->SetToolTip(STR_TOOLBAR_DISABLED_NO_VEHICLE_AVAILABLE
);
365 this->GetWidget
<NWidgetCore
>(WID_ROT_DEPOT
)->SetToolTip(rtt
== RTT_ROAD
? STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_VEHICLE_DEPOT
: STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAM_VEHICLE_DEPOT
);
366 this->GetWidget
<NWidgetCore
>(WID_ROT_BUS_STATION
)->SetToolTip(rtt
== RTT_ROAD
? STR_ROAD_TOOLBAR_TOOLTIP_BUILD_BUS_STATION
: STR_ROAD_TOOLBAR_TOOLTIP_BUILD_PASSENGER_TRAM_STATION
);
367 this->GetWidget
<NWidgetCore
>(WID_ROT_TRUCK_STATION
)->SetToolTip(rtt
== RTT_ROAD
? STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRUCK_LOADING_BAY
: STR_ROAD_TOOLBAR_TOOLTIP_BUILD_CARGO_TRAM_STATION
);
372 void Initialize(RoadType roadtype
)
374 assert(roadtype
< ROADTYPE_END
);
375 this->roadtype
= roadtype
;
376 this->rti
= GetRoadTypeInfo(this->roadtype
);
380 * Configures the road toolbar for roadtype given
381 * @param roadtype the roadtype to display
383 void SetupRoadToolbar()
385 this->GetWidget
<NWidgetCore
>(WID_ROT_ROAD_X
)->widget_data
= rti
->gui_sprites
.build_x_road
;
386 this->GetWidget
<NWidgetCore
>(WID_ROT_ROAD_Y
)->widget_data
= rti
->gui_sprites
.build_y_road
;
387 this->GetWidget
<NWidgetCore
>(WID_ROT_AUTOROAD
)->widget_data
= rti
->gui_sprites
.auto_road
;
388 if (_game_mode
!= GM_EDITOR
) {
389 this->GetWidget
<NWidgetCore
>(WID_ROT_DEPOT
)->widget_data
= rti
->gui_sprites
.build_depot
;
391 this->GetWidget
<NWidgetCore
>(WID_ROT_CONVERT_ROAD
)->widget_data
= rti
->gui_sprites
.convert_road
;
392 this->GetWidget
<NWidgetCore
>(WID_ROT_BUILD_TUNNEL
)->widget_data
= rti
->gui_sprites
.build_tunnel
;
396 * Switch to another road type.
397 * @param roadtype New road type.
399 void ModifyRoadType(RoadType roadtype
)
401 this->Initialize(roadtype
);
402 this->SetupRoadToolbar();
406 void SetStringParameters(WidgetID widget
) const override
408 if (widget
== WID_ROT_CAPTION
) {
409 if (this->rti
->max_speed
> 0) {
410 SetDParam(0, STR_TOOLBAR_RAILTYPE_VELOCITY
);
411 SetDParam(1, this->rti
->strings
.toolbar_caption
);
412 SetDParam(2, PackVelocity(this->rti
->max_speed
/ 2, VEH_ROAD
));
414 SetDParam(0, this->rti
->strings
.toolbar_caption
);
420 * Update the remove button lowered state of the road toolbar
422 * @param clicked_widget The widget which the client clicked just now
424 void UpdateOptionWidgetStatus(RoadToolbarWidgets clicked_widget
)
426 /* The remove and the one way button state is driven
427 * by the other buttons so they don't act on themselves.
428 * Both are only valid if they are able to apply as options. */
429 switch (clicked_widget
) {
431 if (RoadTypeIsRoad(this->roadtype
)) {
432 this->RaiseWidget(WID_ROT_ONE_WAY
);
433 this->SetWidgetDirty(WID_ROT_ONE_WAY
);
438 case WID_ROT_ONE_WAY
:
439 this->RaiseWidget(WID_ROT_REMOVE
);
440 this->SetWidgetDirty(WID_ROT_REMOVE
);
443 case WID_ROT_BUS_STATION
:
444 case WID_ROT_TRUCK_STATION
:
445 if (RoadTypeIsRoad(this->roadtype
)) this->DisableWidget(WID_ROT_ONE_WAY
);
446 this->SetWidgetDisabledState(WID_ROT_REMOVE
, !this->IsWidgetLowered(clicked_widget
));
451 case WID_ROT_AUTOROAD
:
452 this->SetWidgetDisabledState(WID_ROT_REMOVE
, !this->IsWidgetLowered(clicked_widget
));
453 if (RoadTypeIsRoad(this->roadtype
)) {
454 this->SetWidgetDisabledState(WID_ROT_ONE_WAY
, !this->IsWidgetLowered(clicked_widget
));
459 /* When any other buttons than road/station, raise and
460 * disable the removal button */
461 this->SetWidgetDisabledState(WID_ROT_REMOVE
, true);
462 this->SetWidgetLoweredState(WID_ROT_REMOVE
, false);
464 if (RoadTypeIsRoad(this->roadtype
)) {
465 this->SetWidgetDisabledState(WID_ROT_ONE_WAY
, true);
466 this->SetWidgetLoweredState(WID_ROT_ONE_WAY
, false);
473 void OnClick([[maybe_unused
]] Point pt
, WidgetID widget
, [[maybe_unused
]] int click_count
) override
475 _remove_button_clicked
= false;
476 _one_way_button_clicked
= false;
479 HandlePlacePushButton(this, WID_ROT_ROAD_X
, this->rti
->cursor
.road_nwse
, HT_RECT
);
480 this->last_started_action
= widget
;
484 HandlePlacePushButton(this, WID_ROT_ROAD_Y
, this->rti
->cursor
.road_swne
, HT_RECT
);
485 this->last_started_action
= widget
;
488 case WID_ROT_AUTOROAD
:
489 HandlePlacePushButton(this, WID_ROT_AUTOROAD
, this->rti
->cursor
.autoroad
, HT_RECT
);
490 this->last_started_action
= widget
;
493 case WID_ROT_DEMOLISH
:
494 HandlePlacePushButton(this, WID_ROT_DEMOLISH
, ANIMCURSOR_DEMOLISH
, HT_RECT
| HT_DIAGONAL
);
495 this->last_started_action
= widget
;
499 if (HandlePlacePushButton(this, WID_ROT_DEPOT
, this->rti
->cursor
.depot
, HT_RECT
)) {
500 ShowRoadDepotPicker(this);
501 this->last_started_action
= widget
;
505 case WID_ROT_BUS_STATION
:
506 if (HandlePlacePushButton(this, WID_ROT_BUS_STATION
, SPR_CURSOR_BUS_STATION
, HT_RECT
)) {
507 ShowRVStationPicker(this, ROADSTOP_BUS
);
508 this->last_started_action
= widget
;
512 case WID_ROT_TRUCK_STATION
:
513 if (HandlePlacePushButton(this, WID_ROT_TRUCK_STATION
, SPR_CURSOR_TRUCK_STATION
, HT_RECT
)) {
514 ShowRVStationPicker(this, ROADSTOP_TRUCK
);
515 this->last_started_action
= widget
;
519 case WID_ROT_ONE_WAY
:
520 if (this->IsWidgetDisabled(WID_ROT_ONE_WAY
)) return;
522 this->ToggleWidgetLoweredState(WID_ROT_ONE_WAY
);
523 SetSelectionRed(false);
526 case WID_ROT_BUILD_BRIDGE
:
527 HandlePlacePushButton(this, WID_ROT_BUILD_BRIDGE
, SPR_CURSOR_BRIDGE
, HT_RECT
);
528 this->last_started_action
= widget
;
531 case WID_ROT_BUILD_TUNNEL
:
532 HandlePlacePushButton(this, WID_ROT_BUILD_TUNNEL
, this->rti
->cursor
.tunnel
, HT_SPECIAL
);
533 this->last_started_action
= widget
;
537 if (this->IsWidgetDisabled(WID_ROT_REMOVE
)) return;
539 CloseWindowById(WC_SELECT_STATION
, 0);
540 ToggleRoadButton_Remove(this);
541 if (_settings_client
.sound
.click_beep
) SndPlayFx(SND_15_BEEP
);
544 case WID_ROT_CONVERT_ROAD
:
545 HandlePlacePushButton(this, WID_ROT_CONVERT_ROAD
, this->rti
->cursor
.convert_road
, HT_RECT
);
546 this->last_started_action
= widget
;
549 default: NOT_REACHED();
551 this->UpdateOptionWidgetStatus((RoadToolbarWidgets
)widget
);
552 if (_ctrl_pressed
) RoadToolbar_CtrlChanged(this);
555 EventState
OnHotkey(int hotkey
) override
557 MarkTileDirtyByTile(TileVirtXY(_thd
.pos
.x
, _thd
.pos
.y
)); // redraw tile selection
558 return Window::OnHotkey(hotkey
);
561 void OnPlaceObject([[maybe_unused
]] Point pt
, TileIndex tile
) override
563 _remove_button_clicked
= this->IsWidgetLowered(WID_ROT_REMOVE
);
564 _one_way_button_clicked
= RoadTypeIsRoad(this->roadtype
) ? this->IsWidgetLowered(WID_ROT_ONE_WAY
) : false;
565 switch (this->last_started_action
) {
567 _place_road_dir
= AXIS_X
;
568 _place_road_start_half_x
= _tile_fract_coords
.x
>= 8;
569 VpStartPlaceSizing(tile
, VPM_FIX_Y
, DDSP_PLACE_ROAD_X_DIR
);
573 _place_road_dir
= AXIS_Y
;
574 _place_road_start_half_y
= _tile_fract_coords
.y
>= 8;
575 VpStartPlaceSizing(tile
, VPM_FIX_X
, DDSP_PLACE_ROAD_Y_DIR
);
578 case WID_ROT_AUTOROAD
:
579 _place_road_dir
= INVALID_AXIS
;
580 _place_road_start_half_x
= _tile_fract_coords
.x
>= 8;
581 _place_road_start_half_y
= _tile_fract_coords
.y
>= 8;
582 VpStartPlaceSizing(tile
, VPM_X_OR_Y
, DDSP_PLACE_AUTOROAD
);
585 case WID_ROT_DEMOLISH
:
586 PlaceProc_DemolishArea(tile
);
590 Command
<CMD_BUILD_ROAD_DEPOT
>::Post(this->rti
->strings
.err_depot
, CcRoadDepot
,
591 tile
, _cur_roadtype
, _road_depot_orientation
);
594 case WID_ROT_BUS_STATION
:
595 PlaceRoad_BusStation(tile
);
598 case WID_ROT_TRUCK_STATION
:
599 PlaceRoad_TruckStation(tile
);
602 case WID_ROT_BUILD_BRIDGE
:
603 PlaceRoad_Bridge(tile
, this);
606 case WID_ROT_BUILD_TUNNEL
:
607 Command
<CMD_BUILD_TUNNEL
>::Post(STR_ERROR_CAN_T_BUILD_TUNNEL_HERE
, CcBuildRoadTunnel
,
608 tile
, TRANSPORT_ROAD
, _cur_roadtype
);
611 case WID_ROT_CONVERT_ROAD
:
612 VpStartPlaceSizing(tile
, VPM_X_AND_Y
, DDSP_CONVERT_ROAD
);
615 default: NOT_REACHED();
619 void OnPlaceObjectAbort() override
621 if (_game_mode
!= GM_EDITOR
&& (this->IsWidgetLowered(WID_ROT_BUS_STATION
) || this->IsWidgetLowered(WID_ROT_TRUCK_STATION
))) SetViewportCatchmentStation(nullptr, true);
623 this->RaiseButtons();
624 this->SetWidgetDisabledState(WID_ROT_REMOVE
, true);
625 this->SetWidgetDirty(WID_ROT_REMOVE
);
627 if (RoadTypeIsRoad(this->roadtype
)) {
628 this->SetWidgetDisabledState(WID_ROT_ONE_WAY
, true);
629 this->SetWidgetDirty(WID_ROT_ONE_WAY
);
632 CloseWindowById(WC_BUS_STATION
, TRANSPORT_ROAD
);
633 CloseWindowById(WC_TRUCK_STATION
, TRANSPORT_ROAD
);
634 CloseWindowById(WC_BUILD_DEPOT
, TRANSPORT_ROAD
);
635 CloseWindowById(WC_SELECT_STATION
, 0);
636 CloseWindowByClass(WC_BUILD_BRIDGE
);
639 void OnPlaceDrag(ViewportPlaceMethod select_method
, [[maybe_unused
]] ViewportDragDropSelectionProcess select_proc
, [[maybe_unused
]] Point pt
) override
641 /* Here we update the end tile flags
642 * of the road placement actions.
643 * At first we reset the end halfroad
644 * bits and if needed we set them again. */
645 switch (select_proc
) {
646 case DDSP_PLACE_ROAD_X_DIR
:
647 _place_road_end_half
= pt
.x
& 8;
650 case DDSP_PLACE_ROAD_Y_DIR
:
651 _place_road_end_half
= pt
.y
& 8;
654 case DDSP_PLACE_AUTOROAD
:
655 /* For autoroad we need to update the
656 * direction of the road */
657 if (_thd
.size
.x
> _thd
.size
.y
|| (_thd
.size
.x
== _thd
.size
.y
&&
658 ( (_tile_fract_coords
.x
< _tile_fract_coords
.y
&& (_tile_fract_coords
.x
+ _tile_fract_coords
.y
) < 16) ||
659 (_tile_fract_coords
.x
> _tile_fract_coords
.y
&& (_tile_fract_coords
.x
+ _tile_fract_coords
.y
) > 16) ))) {
661 _place_road_dir
= AXIS_X
;
662 _place_road_end_half
= pt
.x
& 8;
665 _place_road_dir
= AXIS_Y
;
666 _place_road_end_half
= pt
.y
& 8;
675 VpSelectTilesWithMethod(pt
.x
, pt
.y
, select_method
);
678 void OnPlaceMouseUp([[maybe_unused
]] ViewportPlaceMethod select_method
, ViewportDragDropSelectionProcess select_proc
, [[maybe_unused
]] Point pt
, TileIndex start_tile
, TileIndex end_tile
) override
681 switch (select_proc
) {
682 default: NOT_REACHED();
683 case DDSP_BUILD_BRIDGE
:
684 if (!_settings_client
.gui
.persistent_buildingtools
) ResetObjectToPlace();
685 ShowBuildBridgeWindow(start_tile
, end_tile
, TRANSPORT_ROAD
, _cur_roadtype
);
688 case DDSP_DEMOLISH_AREA
:
689 GUIPlaceProcDragXY(select_proc
, start_tile
, end_tile
);
692 case DDSP_PLACE_ROAD_X_DIR
:
693 case DDSP_PLACE_ROAD_Y_DIR
:
694 case DDSP_PLACE_AUTOROAD
: {
695 bool start_half
= _place_road_dir
== AXIS_Y
? _place_road_start_half_y
: _place_road_start_half_x
;
697 if (_remove_button_clicked
) {
698 Command
<CMD_REMOVE_LONG_ROAD
>::Post(this->rti
->strings
.err_remove_road
, CcPlaySound_CONSTRUCTION_OTHER
,
699 end_tile
, start_tile
, _cur_roadtype
, _place_road_dir
, start_half
, _place_road_end_half
);
701 Command
<CMD_BUILD_LONG_ROAD
>::Post(this->rti
->strings
.err_build_road
, CcPlaySound_CONSTRUCTION_OTHER
,
702 end_tile
, start_tile
, _cur_roadtype
, _place_road_dir
, _one_way_button_clicked
? DRD_NORTHBOUND
: DRD_NONE
, start_half
, _place_road_end_half
, false);
707 case DDSP_BUILD_BUSSTOP
:
708 case DDSP_REMOVE_BUSSTOP
:
709 if (this->IsWidgetLowered(WID_ROT_BUS_STATION
) && GetIfClassHasNewStopsByType(RoadStopClass::Get(_roadstop_gui_settings
.roadstop_class
), ROADSTOP_BUS
, _cur_roadtype
)) {
710 if (_remove_button_clicked
) {
711 TileArea
ta(start_tile
, end_tile
);
712 Command
<CMD_REMOVE_ROAD_STOP
>::Post(this->rti
->strings
.err_remove_station
[ROADSTOP_BUS
], CcPlaySound_CONSTRUCTION_OTHER
, ta
.tile
, ta
.w
, ta
.h
, ROADSTOP_BUS
, _ctrl_pressed
);
714 PlaceRoadStop(start_tile
, end_tile
, ROADSTOP_BUS
, _ctrl_pressed
, _cur_roadtype
, this->rti
->strings
.err_build_station
[ROADSTOP_BUS
]);
719 case DDSP_BUILD_TRUCKSTOP
:
720 case DDSP_REMOVE_TRUCKSTOP
:
721 if (this->IsWidgetLowered(WID_ROT_TRUCK_STATION
) && GetIfClassHasNewStopsByType(RoadStopClass::Get(_roadstop_gui_settings
.roadstop_class
), ROADSTOP_TRUCK
, _cur_roadtype
)) {
722 if (_remove_button_clicked
) {
723 TileArea
ta(start_tile
, end_tile
);
724 Command
<CMD_REMOVE_ROAD_STOP
>::Post(this->rti
->strings
.err_remove_station
[ROADSTOP_TRUCK
], CcPlaySound_CONSTRUCTION_OTHER
, ta
.tile
, ta
.w
, ta
.h
, ROADSTOP_TRUCK
, _ctrl_pressed
);
726 PlaceRoadStop(start_tile
, end_tile
, ROADSTOP_TRUCK
, _ctrl_pressed
, _cur_roadtype
, this->rti
->strings
.err_build_station
[ROADSTOP_TRUCK
]);
731 case DDSP_CONVERT_ROAD
:
732 Command
<CMD_CONVERT_ROAD
>::Post(rti
->strings
.err_convert_road
, CcPlaySound_CONSTRUCTION_OTHER
, end_tile
, start_tile
, _cur_roadtype
);
738 void OnPlacePresize([[maybe_unused
]] Point pt
, TileIndex tile
) override
740 Command
<CMD_BUILD_TUNNEL
>::Do(DC_AUTO
, tile
, TRANSPORT_ROAD
, _cur_roadtype
);
741 VpSetPresizeRange(tile
, _build_tunnel_endtile
== 0 ? tile
: _build_tunnel_endtile
);
744 EventState
OnCTRLStateChange() override
746 if (RoadToolbar_CtrlChanged(this)) return ES_HANDLED
;
747 return ES_NOT_HANDLED
;
751 * Handler for global hotkeys of the BuildRoadToolbarWindow.
752 * @param hotkey Hotkey
753 * @param last_build Last build road type
754 * @return ES_HANDLED if hotkey was accepted.
756 static EventState
RoadTramToolbarGlobalHotkeys(int hotkey
, RoadType last_build
, RoadTramType rtt
)
759 switch (_game_mode
) {
761 w
= ShowBuildRoadToolbar(last_build
);
765 if ((GetRoadTypes(true) & ((rtt
== RTT_ROAD
) ? ~_roadtypes_type
: _roadtypes_type
)) == ROADTYPES_NONE
) return ES_NOT_HANDLED
;
766 w
= ShowBuildRoadScenToolbar(last_build
);
773 if (w
== nullptr) return ES_NOT_HANDLED
;
774 return w
->OnHotkey(hotkey
);
777 static EventState
RoadToolbarGlobalHotkeys(int hotkey
)
779 extern RoadType _last_built_roadtype
;
780 return RoadTramToolbarGlobalHotkeys(hotkey
, _last_built_roadtype
, RTT_ROAD
);
783 static EventState
TramToolbarGlobalHotkeys(int hotkey
)
785 extern RoadType _last_built_tramtype
;
786 return RoadTramToolbarGlobalHotkeys(hotkey
, _last_built_tramtype
, RTT_TRAM
);
789 static inline HotkeyList road_hotkeys
{"roadtoolbar", {
790 Hotkey('1', "build_x", WID_ROT_ROAD_X
),
791 Hotkey('2', "build_y", WID_ROT_ROAD_Y
),
792 Hotkey('3', "autoroad", WID_ROT_AUTOROAD
),
793 Hotkey('4', "demolish", WID_ROT_DEMOLISH
),
794 Hotkey('5', "depot", WID_ROT_DEPOT
),
795 Hotkey('6', "bus_station", WID_ROT_BUS_STATION
),
796 Hotkey('7', "truck_station", WID_ROT_TRUCK_STATION
),
797 Hotkey('8', "oneway", WID_ROT_ONE_WAY
),
798 Hotkey('B', "bridge", WID_ROT_BUILD_BRIDGE
),
799 Hotkey('T', "tunnel", WID_ROT_BUILD_TUNNEL
),
800 Hotkey('R', "remove", WID_ROT_REMOVE
),
801 Hotkey('C', "convert", WID_ROT_CONVERT_ROAD
),
802 }, RoadToolbarGlobalHotkeys
};
804 static inline HotkeyList tram_hotkeys
{"tramtoolbar", {
805 Hotkey('1', "build_x", WID_ROT_ROAD_X
),
806 Hotkey('2', "build_y", WID_ROT_ROAD_Y
),
807 Hotkey('3', "autoroad", WID_ROT_AUTOROAD
),
808 Hotkey('4', "demolish", WID_ROT_DEMOLISH
),
809 Hotkey('5', "depot", WID_ROT_DEPOT
),
810 Hotkey('6', "bus_station", WID_ROT_BUS_STATION
),
811 Hotkey('7', "truck_station", WID_ROT_TRUCK_STATION
),
812 Hotkey('B', "bridge", WID_ROT_BUILD_BRIDGE
),
813 Hotkey('T', "tunnel", WID_ROT_BUILD_TUNNEL
),
814 Hotkey('R', "remove", WID_ROT_REMOVE
),
815 Hotkey('C', "convert", WID_ROT_CONVERT_ROAD
),
816 }, TramToolbarGlobalHotkeys
};
819 static constexpr NWidgetPart _nested_build_road_widgets
[] = {
820 NWidget(NWID_HORIZONTAL
),
821 NWidget(WWT_CLOSEBOX
, COLOUR_DARK_GREEN
),
822 NWidget(WWT_CAPTION
, COLOUR_DARK_GREEN
, WID_ROT_CAPTION
), SetDataTip(STR_JUST_STRING2
, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS
), SetTextStyle(TC_WHITE
),
823 NWidget(WWT_STICKYBOX
, COLOUR_DARK_GREEN
),
825 NWidget(NWID_HORIZONTAL
),
826 NWidget(WWT_IMGBTN
, COLOUR_DARK_GREEN
, WID_ROT_ROAD_X
),
827 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_ROAD_X_DIR
, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_SECTION
),
828 NWidget(WWT_IMGBTN
, COLOUR_DARK_GREEN
, WID_ROT_ROAD_Y
),
829 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_ROAD_Y_DIR
, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_SECTION
),
830 NWidget(WWT_IMGBTN
, COLOUR_DARK_GREEN
, WID_ROT_AUTOROAD
),
831 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_AUTOROAD
, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_AUTOROAD
),
832 NWidget(WWT_IMGBTN
, COLOUR_DARK_GREEN
, WID_ROT_DEMOLISH
),
833 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_DYNAMITE
, STR_TOOLTIP_DEMOLISH_BUILDINGS_ETC
),
834 NWidget(WWT_IMGBTN
, COLOUR_DARK_GREEN
, WID_ROT_DEPOT
),
835 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_ROAD_DEPOT
, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_VEHICLE_DEPOT
),
836 NWidget(WWT_IMGBTN
, COLOUR_DARK_GREEN
, WID_ROT_BUS_STATION
),
837 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_BUS_STATION
, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_BUS_STATION
),
838 NWidget(WWT_IMGBTN
, COLOUR_DARK_GREEN
, WID_ROT_TRUCK_STATION
),
839 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_TRUCK_BAY
, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRUCK_LOADING_BAY
),
840 NWidget(WWT_PANEL
, COLOUR_DARK_GREEN
, -1), SetMinimalSize(0, 22), SetFill(1, 1), EndContainer(),
841 NWidget(WWT_IMGBTN
, COLOUR_DARK_GREEN
, WID_ROT_ONE_WAY
),
842 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_ROAD_ONE_WAY
, STR_ROAD_TOOLBAR_TOOLTIP_TOGGLE_ONE_WAY_ROAD
),
843 NWidget(WWT_IMGBTN
, COLOUR_DARK_GREEN
, WID_ROT_BUILD_BRIDGE
),
844 SetFill(0, 1), SetMinimalSize(43, 22), SetDataTip(SPR_IMG_BRIDGE
, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_BRIDGE
),
845 NWidget(WWT_IMGBTN
, COLOUR_DARK_GREEN
, WID_ROT_BUILD_TUNNEL
),
846 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_ROAD_TUNNEL
, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_TUNNEL
),
847 NWidget(WWT_IMGBTN
, COLOUR_DARK_GREEN
, WID_ROT_REMOVE
),
848 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_REMOVE
, STR_ROAD_TOOLBAR_TOOLTIP_TOGGLE_BUILD_REMOVE_FOR_ROAD
),
849 NWidget(WWT_IMGBTN
, COLOUR_DARK_GREEN
, WID_ROT_CONVERT_ROAD
),
850 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_CONVERT_ROAD
, STR_ROAD_TOOLBAR_TOOLTIP_CONVERT_ROAD
),
854 static WindowDesc
_build_road_desc(__FILE__
, __LINE__
,
855 WDP_ALIGN_TOOLBAR
, "toolbar_road", 0, 0,
856 WC_BUILD_TOOLBAR
, WC_NONE
,
858 std::begin(_nested_build_road_widgets
), std::end(_nested_build_road_widgets
),
859 &BuildRoadToolbarWindow::road_hotkeys
862 static constexpr NWidgetPart _nested_build_tramway_widgets
[] = {
863 NWidget(NWID_HORIZONTAL
),
864 NWidget(WWT_CLOSEBOX
, COLOUR_DARK_GREEN
),
865 NWidget(WWT_CAPTION
, COLOUR_DARK_GREEN
, WID_ROT_CAPTION
), SetDataTip(STR_JUST_STRING2
, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS
), SetTextStyle(TC_WHITE
),
866 NWidget(WWT_STICKYBOX
, COLOUR_DARK_GREEN
),
868 NWidget(NWID_HORIZONTAL
),
869 NWidget(WWT_IMGBTN
, COLOUR_DARK_GREEN
, WID_ROT_ROAD_X
),
870 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_TRAMWAY_X_DIR
, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAMWAY_SECTION
),
871 NWidget(WWT_IMGBTN
, COLOUR_DARK_GREEN
, WID_ROT_ROAD_Y
),
872 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_TRAMWAY_Y_DIR
, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAMWAY_SECTION
),
873 NWidget(WWT_IMGBTN
, COLOUR_DARK_GREEN
, WID_ROT_AUTOROAD
),
874 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_AUTOTRAM
, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_AUTOTRAM
),
875 NWidget(WWT_IMGBTN
, COLOUR_DARK_GREEN
, WID_ROT_DEMOLISH
),
876 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_DYNAMITE
, STR_TOOLTIP_DEMOLISH_BUILDINGS_ETC
),
877 NWidget(WWT_IMGBTN
, COLOUR_DARK_GREEN
, WID_ROT_DEPOT
),
878 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_ROAD_DEPOT
, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAM_VEHICLE_DEPOT
),
879 NWidget(WWT_IMGBTN
, COLOUR_DARK_GREEN
, WID_ROT_BUS_STATION
),
880 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_BUS_STATION
, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_PASSENGER_TRAM_STATION
),
881 NWidget(WWT_IMGBTN
, COLOUR_DARK_GREEN
, WID_ROT_TRUCK_STATION
),
882 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_TRUCK_BAY
, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_CARGO_TRAM_STATION
),
883 NWidget(WWT_PANEL
, COLOUR_DARK_GREEN
, -1), SetMinimalSize(0, 22), SetFill(1, 1), EndContainer(),
884 NWidget(WWT_IMGBTN
, COLOUR_DARK_GREEN
, WID_ROT_BUILD_BRIDGE
),
885 SetFill(0, 1), SetMinimalSize(43, 22), SetDataTip(SPR_IMG_BRIDGE
, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAMWAY_BRIDGE
),
886 NWidget(WWT_IMGBTN
, COLOUR_DARK_GREEN
, WID_ROT_BUILD_TUNNEL
),
887 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_ROAD_TUNNEL
, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAMWAY_TUNNEL
),
888 NWidget(WWT_IMGBTN
, COLOUR_DARK_GREEN
, WID_ROT_REMOVE
),
889 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_REMOVE
, STR_ROAD_TOOLBAR_TOOLTIP_TOGGLE_BUILD_REMOVE_FOR_TRAMWAYS
),
890 NWidget(WWT_IMGBTN
, COLOUR_DARK_GREEN
, WID_ROT_CONVERT_ROAD
),
891 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_CONVERT_ROAD
, STR_ROAD_TOOLBAR_TOOLTIP_CONVERT_TRAM
),
895 static WindowDesc
_build_tramway_desc(__FILE__
, __LINE__
,
896 WDP_ALIGN_TOOLBAR
, "toolbar_tramway", 0, 0,
897 WC_BUILD_TOOLBAR
, WC_NONE
,
899 std::begin(_nested_build_tramway_widgets
), std::end(_nested_build_tramway_widgets
),
900 &BuildRoadToolbarWindow::tram_hotkeys
904 * Open the build road toolbar window
906 * If the terraform toolbar is linked to the toolbar, that window is also opened.
908 * @return newly opened road toolbar, or nullptr if the toolbar could not be opened.
910 Window
*ShowBuildRoadToolbar(RoadType roadtype
)
912 if (!Company::IsValidID(_local_company
)) return nullptr;
913 if (!ValParamRoadType(roadtype
)) return nullptr;
915 CloseWindowByClass(WC_BUILD_TOOLBAR
);
916 _cur_roadtype
= roadtype
;
918 return AllocateWindowDescFront
<BuildRoadToolbarWindow
>(RoadTypeIsRoad(_cur_roadtype
) ? &_build_road_desc
: &_build_tramway_desc
, TRANSPORT_ROAD
);
921 static constexpr NWidgetPart _nested_build_road_scen_widgets
[] = {
922 NWidget(NWID_HORIZONTAL
),
923 NWidget(WWT_CLOSEBOX
, COLOUR_DARK_GREEN
),
924 NWidget(WWT_CAPTION
, COLOUR_DARK_GREEN
, WID_ROT_CAPTION
), SetDataTip(STR_JUST_STRING2
, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS
), SetTextStyle(TC_WHITE
),
925 NWidget(WWT_STICKYBOX
, COLOUR_DARK_GREEN
),
927 NWidget(NWID_HORIZONTAL
),
928 NWidget(WWT_IMGBTN
, COLOUR_DARK_GREEN
, WID_ROT_ROAD_X
),
929 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_ROAD_X_DIR
, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_SECTION
),
930 NWidget(WWT_IMGBTN
, COLOUR_DARK_GREEN
, WID_ROT_ROAD_Y
),
931 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_ROAD_Y_DIR
, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_SECTION
),
932 NWidget(WWT_IMGBTN
, COLOUR_DARK_GREEN
, WID_ROT_AUTOROAD
),
933 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_AUTOROAD
, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_AUTOROAD
),
934 NWidget(WWT_IMGBTN
, COLOUR_DARK_GREEN
, WID_ROT_DEMOLISH
),
935 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_DYNAMITE
, STR_TOOLTIP_DEMOLISH_BUILDINGS_ETC
),
936 NWidget(WWT_PANEL
, COLOUR_DARK_GREEN
, -1), SetMinimalSize(0, 22), SetFill(1, 1), EndContainer(),
937 NWidget(WWT_IMGBTN
, COLOUR_DARK_GREEN
, WID_ROT_ONE_WAY
),
938 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_ROAD_ONE_WAY
, STR_ROAD_TOOLBAR_TOOLTIP_TOGGLE_ONE_WAY_ROAD
),
939 NWidget(WWT_IMGBTN
, COLOUR_DARK_GREEN
, WID_ROT_BUILD_BRIDGE
),
940 SetFill(0, 1), SetMinimalSize(43, 22), SetDataTip(SPR_IMG_BRIDGE
, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_BRIDGE
),
941 NWidget(WWT_IMGBTN
, COLOUR_DARK_GREEN
, WID_ROT_BUILD_TUNNEL
),
942 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_ROAD_TUNNEL
, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_TUNNEL
),
943 NWidget(WWT_IMGBTN
, COLOUR_DARK_GREEN
, WID_ROT_REMOVE
),
944 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_REMOVE
, STR_ROAD_TOOLBAR_TOOLTIP_TOGGLE_BUILD_REMOVE_FOR_ROAD
),
945 NWidget(WWT_IMGBTN
, COLOUR_DARK_GREEN
, WID_ROT_CONVERT_ROAD
),
946 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_CONVERT_ROAD
, STR_ROAD_TOOLBAR_TOOLTIP_CONVERT_ROAD
),
950 static WindowDesc
_build_road_scen_desc(__FILE__
, __LINE__
,
951 WDP_AUTO
, "toolbar_road_scen", 0, 0,
952 WC_SCEN_BUILD_TOOLBAR
, WC_NONE
,
954 std::begin(_nested_build_road_scen_widgets
), std::end(_nested_build_road_scen_widgets
),
955 &BuildRoadToolbarWindow::road_hotkeys
958 static constexpr NWidgetPart _nested_build_tramway_scen_widgets
[] = {
959 NWidget(NWID_HORIZONTAL
),
960 NWidget(WWT_CLOSEBOX
, COLOUR_DARK_GREEN
),
961 NWidget(WWT_CAPTION
, COLOUR_DARK_GREEN
, WID_ROT_CAPTION
), SetDataTip(STR_JUST_STRING2
, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS
), SetTextStyle(TC_WHITE
),
962 NWidget(WWT_STICKYBOX
, COLOUR_DARK_GREEN
),
964 NWidget(NWID_HORIZONTAL
),
965 NWidget(WWT_IMGBTN
, COLOUR_DARK_GREEN
, WID_ROT_ROAD_X
),
966 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_TRAMWAY_X_DIR
, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAMWAY_SECTION
),
967 NWidget(WWT_IMGBTN
, COLOUR_DARK_GREEN
, WID_ROT_ROAD_Y
),
968 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_TRAMWAY_Y_DIR
, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAMWAY_SECTION
),
969 NWidget(WWT_IMGBTN
, COLOUR_DARK_GREEN
, WID_ROT_AUTOROAD
),
970 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_AUTOTRAM
, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_AUTOTRAM
),
971 NWidget(WWT_IMGBTN
, COLOUR_DARK_GREEN
, WID_ROT_DEMOLISH
),
972 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_DYNAMITE
, STR_TOOLTIP_DEMOLISH_BUILDINGS_ETC
),
973 NWidget(WWT_PANEL
, COLOUR_DARK_GREEN
, -1), SetMinimalSize(0, 22), SetFill(1, 1), EndContainer(),
974 NWidget(WWT_IMGBTN
, COLOUR_DARK_GREEN
, WID_ROT_BUILD_BRIDGE
),
975 SetFill(0, 1), SetMinimalSize(43, 22), SetDataTip(SPR_IMG_BRIDGE
, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAMWAY_BRIDGE
),
976 NWidget(WWT_IMGBTN
, COLOUR_DARK_GREEN
, WID_ROT_BUILD_TUNNEL
),
977 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_ROAD_TUNNEL
, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAMWAY_TUNNEL
),
978 NWidget(WWT_IMGBTN
, COLOUR_DARK_GREEN
, WID_ROT_REMOVE
),
979 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_REMOVE
, STR_ROAD_TOOLBAR_TOOLTIP_TOGGLE_BUILD_REMOVE_FOR_TRAMWAYS
),
980 NWidget(WWT_IMGBTN
, COLOUR_DARK_GREEN
, WID_ROT_CONVERT_ROAD
),
981 SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_CONVERT_ROAD
, STR_ROAD_TOOLBAR_TOOLTIP_CONVERT_TRAM
),
985 static WindowDesc
_build_tramway_scen_desc(__FILE__
, __LINE__
,
986 WDP_AUTO
, "toolbar_tram_scen", 0, 0,
987 WC_SCEN_BUILD_TOOLBAR
, WC_NONE
,
989 std::begin(_nested_build_tramway_scen_widgets
), std::end(_nested_build_tramway_scen_widgets
),
990 &BuildRoadToolbarWindow::tram_hotkeys
994 * Show the road building toolbar in the scenario editor.
995 * @return The just opened toolbar, or \c nullptr if the toolbar was already open.
997 Window
*ShowBuildRoadScenToolbar(RoadType roadtype
)
999 CloseWindowById(WC_SCEN_BUILD_TOOLBAR
, TRANSPORT_ROAD
);
1000 _cur_roadtype
= roadtype
;
1002 return AllocateWindowDescFront
<BuildRoadToolbarWindow
>(RoadTypeIsRoad(_cur_roadtype
) ? &_build_road_scen_desc
: &_build_tramway_scen_desc
, TRANSPORT_ROAD
);
1005 struct BuildRoadDepotWindow
: public PickerWindowBase
{
1006 BuildRoadDepotWindow(WindowDesc
*desc
, Window
*parent
) : PickerWindowBase(desc
, parent
)
1008 this->CreateNestedTree();
1010 this->LowerWidget(WID_BROD_DEPOT_NE
+ _road_depot_orientation
);
1011 if (RoadTypeIsTram(_cur_roadtype
)) {
1012 this->GetWidget
<NWidgetCore
>(WID_BROD_CAPTION
)->widget_data
= STR_BUILD_DEPOT_TRAM_ORIENTATION_CAPTION
;
1013 for (WidgetID i
= WID_BROD_DEPOT_NE
; i
<= WID_BROD_DEPOT_NW
; i
++) {
1014 this->GetWidget
<NWidgetCore
>(i
)->tool_tip
= STR_BUILD_DEPOT_TRAM_ORIENTATION_SELECT_TOOLTIP
;
1018 this->FinishInitNested(TRANSPORT_ROAD
);
1021 void UpdateWidgetSize(WidgetID widget
, Dimension
*size
, [[maybe_unused
]] const Dimension
&padding
, [[maybe_unused
]] Dimension
*fill
, [[maybe_unused
]] Dimension
*resize
) override
1023 if (!IsInsideMM(widget
, WID_BROD_DEPOT_NE
, WID_BROD_DEPOT_NW
+ 1)) return;
1025 size
->width
= ScaleGUITrad(64) + WidgetDimensions::scaled
.fullbevel
.Horizontal();
1026 size
->height
= ScaleGUITrad(48) + WidgetDimensions::scaled
.fullbevel
.Vertical();
1029 void DrawWidget(const Rect
&r
, WidgetID widget
) const override
1031 if (!IsInsideMM(widget
, WID_BROD_DEPOT_NE
, WID_BROD_DEPOT_NW
+ 1)) return;
1033 DrawPixelInfo tmp_dpi
;
1034 Rect ir
= r
.Shrink(WidgetDimensions::scaled
.bevel
);
1035 if (FillDrawPixelInfo(&tmp_dpi
, ir
)) {
1036 AutoRestoreBackup
dpi_backup(_cur_dpi
, &tmp_dpi
);
1037 int x
= (ir
.Width() - ScaleSpriteTrad(64)) / 2 + ScaleSpriteTrad(31);
1038 int y
= (ir
.Height() + ScaleSpriteTrad(48)) / 2 - ScaleSpriteTrad(31);
1039 DrawRoadDepotSprite(x
, y
, (DiagDirection
)(widget
- WID_BROD_DEPOT_NE
+ DIAGDIR_NE
), _cur_roadtype
);
1043 void OnClick([[maybe_unused
]] Point pt
, WidgetID widget
, [[maybe_unused
]] int click_count
) override
1046 case WID_BROD_DEPOT_NW
:
1047 case WID_BROD_DEPOT_NE
:
1048 case WID_BROD_DEPOT_SW
:
1049 case WID_BROD_DEPOT_SE
:
1050 this->RaiseWidget(WID_BROD_DEPOT_NE
+ _road_depot_orientation
);
1051 _road_depot_orientation
= (DiagDirection
)(widget
- WID_BROD_DEPOT_NE
);
1052 this->LowerWidget(WID_BROD_DEPOT_NE
+ _road_depot_orientation
);
1053 if (_settings_client
.sound
.click_beep
) SndPlayFx(SND_15_BEEP
);
1063 static constexpr NWidgetPart _nested_build_road_depot_widgets
[] = {
1064 NWidget(NWID_HORIZONTAL
),
1065 NWidget(WWT_CLOSEBOX
, COLOUR_DARK_GREEN
),
1066 NWidget(WWT_CAPTION
, COLOUR_DARK_GREEN
, WID_BROD_CAPTION
), SetDataTip(STR_BUILD_DEPOT_ROAD_ORIENTATION_CAPTION
, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS
),
1068 NWidget(WWT_PANEL
, COLOUR_DARK_GREEN
),
1069 NWidget(NWID_HORIZONTAL_LTR
), SetPIP(0, WidgetDimensions::unscaled
.hsep_normal
, 0), SetPIPRatio(1, 0, 1), SetPadding(WidgetDimensions::unscaled
.picker
),
1070 NWidget(NWID_VERTICAL
), SetPIP(0, WidgetDimensions::unscaled
.vsep_normal
, 0),
1071 NWidget(WWT_TEXTBTN
, COLOUR_GREY
, WID_BROD_DEPOT_NW
), SetMinimalSize(66, 50), SetFill(0, 0), SetDataTip(0x0, STR_BUILD_DEPOT_ROAD_ORIENTATION_SELECT_TOOLTIP
),
1072 NWidget(WWT_TEXTBTN
, COLOUR_GREY
, WID_BROD_DEPOT_SW
), SetMinimalSize(66, 50), SetFill(0, 0), SetDataTip(0x0, STR_BUILD_DEPOT_ROAD_ORIENTATION_SELECT_TOOLTIP
),
1074 NWidget(NWID_VERTICAL
), SetPIP(0, WidgetDimensions::unscaled
.vsep_normal
, 0),
1075 NWidget(WWT_TEXTBTN
, COLOUR_GREY
, WID_BROD_DEPOT_NE
), SetMinimalSize(66, 50), SetFill(0, 0), SetDataTip(0x0, STR_BUILD_DEPOT_ROAD_ORIENTATION_SELECT_TOOLTIP
),
1076 NWidget(WWT_TEXTBTN
, COLOUR_GREY
, WID_BROD_DEPOT_SE
), SetMinimalSize(66, 50), SetFill(0, 0), SetDataTip(0x0, STR_BUILD_DEPOT_ROAD_ORIENTATION_SELECT_TOOLTIP
),
1082 static WindowDesc
_build_road_depot_desc(__FILE__
, __LINE__
,
1083 WDP_AUTO
, nullptr, 0, 0,
1084 WC_BUILD_DEPOT
, WC_BUILD_TOOLBAR
,
1086 std::begin(_nested_build_road_depot_widgets
), std::end(_nested_build_road_depot_widgets
)
1089 static void ShowRoadDepotPicker(Window
*parent
)
1091 new BuildRoadDepotWindow(&_build_road_depot_desc
, parent
);
1094 /** Enum referring to the Hotkeys in the build road stop window */
1095 enum BuildRoadStopHotkeys
{
1096 BROSHK_FOCUS_FILTER_BOX
, ///< Focus the edit box for editing the filter string
1099 struct BuildRoadStationWindow
: public PickerWindowBase
{
1101 RoadStopType roadStopType
; ///< The RoadStopType for this Window.
1102 uint line_height
; ///< Height of a single line in the newstation selection matrix.
1103 uint coverage_height
; ///< Height of the coverage texts.
1104 Scrollbar
*vscrollList
; ///< Vertical scrollbar of the new station list.
1105 Scrollbar
*vscrollMatrix
; ///< Vertical scrollbar of the station picker matrix.
1107 typedef GUIList
<RoadStopClassID
, std::nullptr_t
, StringFilter
&> GUIRoadStopClassList
; ///< Type definition for the list to hold available road stop classes.
1109 static const uint EDITBOX_MAX_SIZE
= 16; ///< The maximum number of characters for the filter edit box.
1111 static Listing last_sorting
; ///< Default sorting of #GUIRoadStopClassList.
1112 static Filtering last_filtering
; ///< Default filtering of #GUIRoadStopClassList.
1113 static GUIRoadStopClassList::SortFunction
* const sorter_funcs
[]; ///< Sort functions of the #GUIRoadStopClassList.
1114 static GUIRoadStopClassList::FilterFunction
* const filter_funcs
[]; ///< Filter functions of the #GUIRoadStopClassList.
1115 GUIRoadStopClassList roadstop_classes
; ///< Available road stop classes.
1116 StringFilter string_filter
; ///< Filter for available road stop classes.
1117 QueryString filter_editbox
; ///< Filter editbox.
1119 void EnsureSelectedClassIsVisible()
1122 for (auto rs_class
: this->roadstop_classes
) {
1123 if (rs_class
== _roadstop_gui_settings
.roadstop_class
) break;
1126 this->vscrollList
->SetCount(this->roadstop_classes
.size());
1127 this->vscrollList
->ScrollTowards(pos
);
1130 void CheckOrientationValid()
1132 const RoadStopSpec
*spec
= RoadStopClass::Get(_roadstop_gui_settings
.roadstop_class
)->GetSpec(_roadstop_gui_settings
.roadstop_type
);
1134 /* Raise and lower to ensure the correct widget is lowered after changing displayed orientation plane. */
1135 this->RaiseWidget(WID_BROS_STATION_NE
+ _roadstop_gui_settings
.orientation
);
1136 this->GetWidget
<NWidgetStacked
>(WID_BROS_AVAILABLE_ORIENTATIONS
)->SetDisplayedPlane((spec
!= nullptr && HasBit(spec
->flags
, RSF_DRIVE_THROUGH_ONLY
)) ? 1 : 0);
1137 this->LowerWidget(WID_BROS_STATION_NE
+ _roadstop_gui_settings
.orientation
);
1139 if (_roadstop_gui_settings
.orientation
>= DIAGDIR_END
) return;
1141 if (spec
!= nullptr && HasBit(spec
->flags
, RSF_DRIVE_THROUGH_ONLY
)) {
1142 this->RaiseWidget(WID_BROS_STATION_NE
+ _roadstop_gui_settings
.orientation
);
1143 _roadstop_gui_settings
.orientation
= DIAGDIR_END
;
1144 this->LowerWidget(WID_BROS_STATION_NE
+ _roadstop_gui_settings
.orientation
);
1146 CloseWindowById(WC_SELECT_STATION
, 0);
1151 BuildRoadStationWindow(WindowDesc
*desc
, Window
*parent
, RoadStopType rs
) : PickerWindowBase(desc
, parent
), filter_editbox(EDITBOX_MAX_SIZE
* MAX_CHAR_LENGTH
, EDITBOX_MAX_SIZE
)
1153 this->coverage_height
= 2 * GetCharacterHeight(FS_NORMAL
) + WidgetDimensions::scaled
.vsep_normal
;
1154 this->vscrollList
= nullptr;
1155 this->vscrollMatrix
= nullptr;
1156 this->roadStopType
= rs
;
1157 bool newstops
= GetIfNewStopsByType(rs
, _cur_roadtype
);
1159 this->CreateNestedTree();
1161 /* Hide the station class filter if no stations other than the default one are available. */
1162 this->GetWidget
<NWidgetStacked
>(WID_BROS_SHOW_NEWST_DEFSIZE
)->SetDisplayedPlane(newstops
? 0 : SZSP_NONE
);
1163 this->GetWidget
<NWidgetStacked
>(WID_BROS_FILTER_CONTAINER
)->SetDisplayedPlane(newstops
? 0 : SZSP_HORIZONTAL
);
1164 this->GetWidget
<NWidgetStacked
>(WID_BROS_SHOW_NEWST_ADDITIONS
)->SetDisplayedPlane(newstops
? 0 : SZSP_HORIZONTAL
);
1165 this->GetWidget
<NWidgetStacked
>(WID_BROS_SHOW_NEWST_ORIENTATION
)->SetDisplayedPlane(newstops
? 0 : SZSP_HORIZONTAL
);
1166 this->GetWidget
<NWidgetStacked
>(WID_BROS_SHOW_NEWST_TYPE_SEL
)->SetDisplayedPlane(newstops
? 0 : SZSP_HORIZONTAL
);
1167 this->GetWidget
<NWidgetStacked
>(WID_BROS_SHOW_NEWST_MATRIX
)->SetDisplayedPlane(newstops
? 0 : SZSP_NONE
);
1168 this->GetWidget
<NWidgetStacked
>(WID_BROS_SHOW_NEWST_RESIZE
)->SetDisplayedPlane(newstops
? 0 : SZSP_NONE
);
1170 this->vscrollList
= this->GetScrollbar(WID_BROS_NEWST_SCROLL
);
1171 this->vscrollMatrix
= this->GetScrollbar(WID_BROS_MATRIX_SCROLL
);
1173 this->querystrings
[WID_BROS_FILTER_EDITBOX
] = &this->filter_editbox
;
1174 this->roadstop_classes
.SetListing(this->last_sorting
);
1175 this->roadstop_classes
.SetFiltering(this->last_filtering
);
1176 this->roadstop_classes
.SetSortFuncs(this->sorter_funcs
);
1177 this->roadstop_classes
.SetFilterFuncs(this->filter_funcs
);
1180 this->roadstop_classes
.ForceRebuild();
1181 BuildRoadStopClassesAvailable();
1183 /* Trams don't have non-drivethrough stations */
1184 if (RoadTypeIsTram(_cur_roadtype
) && _roadstop_gui_settings
.orientation
< DIAGDIR_END
) {
1185 _roadstop_gui_settings
.orientation
= DIAGDIR_END
;
1187 const RoadTypeInfo
*rti
= GetRoadTypeInfo(_cur_roadtype
);
1188 this->GetWidget
<NWidgetCore
>(WID_BROS_CAPTION
)->widget_data
= rti
->strings
.picker_title
[rs
];
1190 for (WidgetID i
= RoadTypeIsTram(_cur_roadtype
) ? WID_BROS_STATION_X
: WID_BROS_STATION_NE
; i
< WID_BROS_LT_OFF
; i
++) {
1191 this->GetWidget
<NWidgetCore
>(i
)->tool_tip
= rti
->strings
.picker_tooltip
[rs
];
1194 this->LowerWidget(WID_BROS_STATION_NE
+ _roadstop_gui_settings
.orientation
);
1195 this->LowerWidget(WID_BROS_LT_OFF
+ _settings_client
.gui
.station_show_coverage
);
1197 this->FinishInitNested(TRANSPORT_ROAD
);
1199 this->window_class
= (rs
== ROADSTOP_BUS
) ? WC_BUS_STATION
: WC_TRUCK_STATION
;
1200 if (!newstops
|| _roadstop_gui_settings
.roadstop_class
>= (int)RoadStopClass::GetClassCount()) {
1201 /* There's no new stops available or the list has reduced in size.
1202 * Now, set the default road stops as selected. */
1203 _roadstop_gui_settings
.roadstop_class
= ROADSTOP_CLASS_DFLT
;
1204 _roadstop_gui_settings
.roadstop_type
= 0;
1207 /* The currently selected class doesn't have any stops for this RoadStopType, reset the selection. */
1208 if (!GetIfClassHasNewStopsByType(RoadStopClass::Get(_roadstop_gui_settings
.roadstop_class
), rs
, _cur_roadtype
)) {
1209 _roadstop_gui_settings
.roadstop_class
= ROADSTOP_CLASS_DFLT
;
1210 _roadstop_gui_settings
.roadstop_type
= 0;
1212 _roadstop_gui_settings
.roadstop_count
= RoadStopClass::Get(_roadstop_gui_settings
.roadstop_class
)->GetSpecCount();
1213 _roadstop_gui_settings
.roadstop_type
= std::min((int)_roadstop_gui_settings
.roadstop_type
, _roadstop_gui_settings
.roadstop_count
- 1);
1215 /* Reset back to default class if the previously selected class is not available for this road stop type. */
1216 if (!GetIfClassHasNewStopsByType(RoadStopClass::Get(_roadstop_gui_settings
.roadstop_class
), roadStopType
, _cur_roadtype
)) {
1217 _roadstop_gui_settings
.roadstop_class
= ROADSTOP_CLASS_DFLT
;
1220 this->SelectFirstAvailableTypeIfUnavailable();
1222 NWidgetMatrix
*matrix
= this->GetWidget
<NWidgetMatrix
>(WID_BROS_MATRIX
);
1223 matrix
->SetScrollbar(this->vscrollMatrix
);
1224 matrix
->SetCount(_roadstop_gui_settings
.roadstop_count
);
1225 matrix
->SetClicked(_roadstop_gui_settings
.roadstop_type
);
1227 this->EnsureSelectedClassIsVisible();
1228 this->CheckOrientationValid();
1232 void Close([[maybe_unused
]] int data
= 0) override
1234 CloseWindowById(WC_SELECT_STATION
, 0);
1235 this->PickerWindowBase::Close();
1238 /** Sort classes by RoadStopClassID. */
1239 static bool RoadStopClassIDSorter(RoadStopClassID
const &a
, RoadStopClassID
const &b
)
1244 /** Filter classes by class name. */
1245 static bool CDECL
TagNameFilter(RoadStopClassID
const *sc
, StringFilter
&filter
)
1247 filter
.ResetState();
1248 filter
.AddLine(GetString(RoadStopClass::Get(*sc
)->name
));
1249 return filter
.GetState();
1252 inline bool ShowNewStops() const
1254 return this->vscrollList
!= nullptr;
1257 void BuildRoadStopClassesAvailable()
1259 if (!this->roadstop_classes
.NeedRebuild()) return;
1261 this->roadstop_classes
.clear();
1263 for (uint i
= 0; i
< RoadStopClass::GetClassCount(); i
++) {
1264 RoadStopClassID rs_id
= (RoadStopClassID
)i
;
1265 if (rs_id
== ROADSTOP_CLASS_WAYP
) {
1269 RoadStopClass
*rs_class
= RoadStopClass::Get(rs_id
);
1270 if (GetIfClassHasNewStopsByType(rs_class
, this->roadStopType
, _cur_roadtype
)) this->roadstop_classes
.push_back(rs_id
);
1273 if (this->ShowNewStops()) {
1274 this->roadstop_classes
.Filter(this->string_filter
);
1275 this->roadstop_classes
.shrink_to_fit();
1276 this->roadstop_classes
.RebuildDone();
1277 this->roadstop_classes
.Sort();
1279 this->vscrollList
->SetCount(this->roadstop_classes
.size());
1283 void SelectFirstAvailableTypeIfUnavailable()
1285 const RoadStopClass
*rs_class
= RoadStopClass::Get(_roadstop_gui_settings
.roadstop_class
);
1286 StationType st
= GetRoadStationTypeByWindowClass(this->window_class
);
1288 if (IsRoadStopAvailable(rs_class
->GetSpec(_roadstop_gui_settings
.roadstop_type
), st
)) return;
1289 for (uint i
= 0; i
< _roadstop_gui_settings
.roadstop_count
; i
++) {
1290 if (IsRoadStopAvailable(rs_class
->GetSpec(i
), st
)) {
1291 _roadstop_gui_settings
.roadstop_type
= i
;
1297 void OnInvalidateData([[maybe_unused
]] int data
= 0, [[maybe_unused
]] bool gui_scope
= true) override
1299 if (!gui_scope
) return;
1301 this->BuildRoadStopClassesAvailable();
1304 EventState
OnHotkey(int hotkey
) override
1306 if (hotkey
== BROSHK_FOCUS_FILTER_BOX
) {
1307 this->SetFocusedWidget(WID_BROS_FILTER_EDITBOX
);
1308 SetFocusedWindow(this); // The user has asked to give focus to the text box, so make sure this window is focused.
1312 return ES_NOT_HANDLED
;
1315 void OnEditboxChanged(WidgetID widget
) override
1317 if (widget
== WID_BROS_FILTER_EDITBOX
) {
1318 string_filter
.SetFilterTerm(this->filter_editbox
.text
.buf
);
1319 this->roadstop_classes
.SetFilterState(!string_filter
.IsEmpty());
1320 this->roadstop_classes
.ForceRebuild();
1321 this->InvalidateData();
1325 void OnPaint() override
1327 this->DrawWidgets();
1329 int rad
= _settings_game
.station
.modified_catchment
? ((this->window_class
== WC_BUS_STATION
) ? CA_BUS
: CA_TRUCK
) : CA_UNMODIFIED
;
1330 if (_settings_client
.gui
.station_show_coverage
) {
1331 SetTileSelectBigSize(-rad
, -rad
, 2 * rad
, 2 * rad
);
1333 SetTileSelectSize(1, 1);
1336 if (this->IsShaded()) return;
1338 /* 'Accepts' and 'Supplies' texts. */
1339 StationCoverageType sct
= (this->window_class
== WC_BUS_STATION
) ? SCT_PASSENGERS_ONLY
: SCT_NON_PASSENGERS_ONLY
;
1340 Rect r
= this->GetWidget
<NWidgetBase
>(WID_BROS_ACCEPTANCE
)->GetCurrentRect();
1342 top
= DrawStationCoverageAreaText(r
.left
, r
.right
, top
, sct
, rad
, false) + WidgetDimensions::scaled
.vsep_normal
;
1343 top
= DrawStationCoverageAreaText(r
.left
, r
.right
, top
, sct
, rad
, true);
1344 /* Resize background if the window is too small.
1345 * Never make the window smaller to avoid oscillating if the size change affects the acceptance.
1346 * (This is the case, if making the window bigger moves the mouse into the window.) */
1347 if (top
> r
.bottom
) {
1348 this->coverage_height
+= top
- r
.bottom
;
1353 void UpdateWidgetSize(WidgetID widget
, Dimension
*size
, [[maybe_unused
]] const Dimension
&padding
, [[maybe_unused
]] Dimension
*fill
, [[maybe_unused
]] Dimension
*resize
) override
1356 case WID_BROS_NEWST_LIST
: {
1357 Dimension d
= { 0, 0 };
1358 for (auto rs_class
: this->roadstop_classes
) {
1359 d
= maxdim(d
, GetStringBoundingBox(RoadStopClass::Get(rs_class
)->name
));
1361 size
->width
= std::max(size
->width
, d
.width
+ padding
.width
);
1362 this->line_height
= GetCharacterHeight(FS_NORMAL
) + WidgetDimensions::scaled
.matrix
.Vertical();
1363 size
->height
= 5 * this->line_height
;
1364 resize
->height
= this->line_height
;
1368 case WID_BROS_SHOW_NEWST_TYPE
: {
1369 Dimension d
= {0, 0};
1370 StringID str
= this->GetWidget
<NWidgetCore
>(widget
)->widget_data
;
1371 for (auto roadstop_class
: this->roadstop_classes
) {
1372 RoadStopClass
*rs_class
= RoadStopClass::Get(roadstop_class
);
1373 for (uint j
= 0; j
< rs_class
->GetSpecCount(); j
++) {
1374 const RoadStopSpec
*roadstopspec
= rs_class
->GetSpec(j
);
1375 SetDParam(0, (roadstopspec
!= nullptr && roadstopspec
->name
!= 0) ? roadstopspec
->name
: STR_STATION_CLASS_DFLT_ROADSTOP
);
1376 d
= maxdim(d
, GetStringBoundingBox(str
));
1379 size
->width
= std::max(size
->width
, d
.width
+ padding
.width
);
1383 case WID_BROS_STATION_NE
:
1384 case WID_BROS_STATION_SE
:
1385 case WID_BROS_STATION_SW
:
1386 case WID_BROS_STATION_NW
:
1387 case WID_BROS_STATION_X
:
1388 case WID_BROS_STATION_Y
:
1389 case WID_BROS_IMAGE
:
1390 size
->width
= ScaleGUITrad(64) + WidgetDimensions::scaled
.fullbevel
.Horizontal();
1391 size
->height
= ScaleGUITrad(48) + WidgetDimensions::scaled
.fullbevel
.Vertical();
1394 case WID_BROS_MATRIX
:
1399 case WID_BROS_ACCEPTANCE
:
1400 size
->height
= this->coverage_height
;
1406 * Simply to have a easier way to get the StationType for bus, truck and trams from the WindowClass.
1408 StationType
GetRoadStationTypeByWindowClass(WindowClass window_class
) const
1410 switch (window_class
) {
1411 case WC_BUS_STATION
: return STATION_BUS
;
1412 case WC_TRUCK_STATION
: return STATION_TRUCK
;
1413 default: NOT_REACHED();
1417 void DrawWidget(const Rect
&r
, WidgetID widget
) const override
1420 case WID_BROS_STATION_NE
:
1421 case WID_BROS_STATION_SE
:
1422 case WID_BROS_STATION_SW
:
1423 case WID_BROS_STATION_NW
:
1424 case WID_BROS_STATION_X
:
1425 case WID_BROS_STATION_Y
: {
1426 StationType st
= GetRoadStationTypeByWindowClass(this->window_class
);
1427 const RoadStopSpec
*spec
= RoadStopClass::Get(_roadstop_gui_settings
.roadstop_class
)->GetSpec(_roadstop_gui_settings
.roadstop_type
);
1428 DrawPixelInfo tmp_dpi
;
1429 Rect ir
= r
.Shrink(WidgetDimensions::scaled
.bevel
);
1430 if (FillDrawPixelInfo(&tmp_dpi
, ir
)) {
1431 AutoRestoreBackup
dpi_backup(_cur_dpi
, &tmp_dpi
);
1432 int x
= (ir
.Width() - ScaleSpriteTrad(64)) / 2 + ScaleSpriteTrad(31);
1433 int y
= (ir
.Height() + ScaleSpriteTrad(48)) / 2 - ScaleSpriteTrad(31);
1434 if (spec
== nullptr) {
1435 StationPickerDrawSprite(x
, y
, st
, INVALID_RAILTYPE
, _cur_roadtype
, widget
- WID_BROS_STATION_NE
);
1437 DrawRoadStopTile(x
, y
, _cur_roadtype
, spec
, st
, widget
- WID_BROS_STATION_NE
);
1443 case WID_BROS_NEWST_LIST
: {
1446 for (auto rs_class
: this->roadstop_classes
) {
1447 if (this->vscrollList
->IsVisible(statclass
)) {
1448 DrawString(r
.left
+ WidgetDimensions::scaled
.matrix
.left
, r
.right
, row
* this->line_height
+ r
.top
+ WidgetDimensions::scaled
.matrix
.top
,
1449 RoadStopClass::Get(rs_class
)->name
,
1450 rs_class
== _roadstop_gui_settings
.roadstop_class
? TC_WHITE
: TC_BLACK
);
1458 case WID_BROS_IMAGE
: {
1459 uint16_t type
= this->GetWidget
<NWidgetBase
>(widget
)->GetParentWidget
<NWidgetMatrix
>()->GetCurrentElement();
1460 assert(type
< _roadstop_gui_settings
.roadstop_count
);
1462 const RoadStopSpec
*spec
= RoadStopClass::Get(_roadstop_gui_settings
.roadstop_class
)->GetSpec(type
);
1463 StationType st
= GetRoadStationTypeByWindowClass(this->window_class
);
1465 /* Set up a clipping area for the sprite preview. */
1466 DrawPixelInfo tmp_dpi
;
1467 Rect ir
= r
.Shrink(WidgetDimensions::scaled
.bevel
);
1468 if (FillDrawPixelInfo(&tmp_dpi
, ir
)) {
1469 AutoRestoreBackup
dpi_backup(_cur_dpi
, &tmp_dpi
);
1470 int x
= (ir
.Width() - ScaleSpriteTrad(64)) / 2 + ScaleSpriteTrad(31);
1471 int y
= (ir
.Height() + ScaleSpriteTrad(48)) / 2 - ScaleSpriteTrad(31);
1472 if (spec
== nullptr) {
1473 StationPickerDrawSprite(x
, y
, st
, INVALID_RAILTYPE
, _cur_roadtype
, _roadstop_gui_settings
.orientation
);
1475 DiagDirection orientation
= _roadstop_gui_settings
.orientation
;
1476 if (orientation
< DIAGDIR_END
&& HasBit(spec
->flags
, RSF_DRIVE_THROUGH_ONLY
)) orientation
= DIAGDIR_END
;
1477 DrawRoadStopTile(x
, y
, _cur_roadtype
, spec
, st
, (uint8_t)orientation
);
1480 if (!IsRoadStopAvailable(spec
, st
)) {
1481 GfxFillRect(ir
, PC_BLACK
, FILLRECT_CHECKER
);
1488 void OnResize() override
1490 if (this->vscrollList
!= nullptr) {
1491 this->vscrollList
->SetCapacityFromWidget(this, WID_BROS_NEWST_LIST
);
1495 void SetStringParameters(WidgetID widget
) const override
1497 if (widget
== WID_BROS_SHOW_NEWST_TYPE
) {
1498 const RoadStopSpec
*roadstopspec
= RoadStopClass::Get(_roadstop_gui_settings
.roadstop_class
)->GetSpec(_roadstop_gui_settings
.roadstop_type
);
1499 SetDParam(0, (roadstopspec
!= nullptr && roadstopspec
->name
!= 0) ? roadstopspec
->name
: STR_STATION_CLASS_DFLT_ROADSTOP
);
1503 void OnClick([[maybe_unused
]] Point pt
, WidgetID widget
, [[maybe_unused
]] int click_count
) override
1506 case WID_BROS_STATION_NE
:
1507 case WID_BROS_STATION_SE
:
1508 case WID_BROS_STATION_SW
:
1509 case WID_BROS_STATION_NW
:
1510 case WID_BROS_STATION_X
:
1511 case WID_BROS_STATION_Y
:
1512 if (widget
< WID_BROS_STATION_X
) {
1513 const RoadStopSpec
*spec
= RoadStopClass::Get(_roadstop_gui_settings
.roadstop_class
)->GetSpec(_roadstop_gui_settings
.roadstop_type
);
1514 if (spec
!= nullptr && HasBit(spec
->flags
, RSF_DRIVE_THROUGH_ONLY
)) return;
1516 this->RaiseWidget(WID_BROS_STATION_NE
+ _roadstop_gui_settings
.orientation
);
1517 _roadstop_gui_settings
.orientation
= (DiagDirection
)(widget
- WID_BROS_STATION_NE
);
1518 this->LowerWidget(WID_BROS_STATION_NE
+ _roadstop_gui_settings
.orientation
);
1519 if (_settings_client
.sound
.click_beep
) SndPlayFx(SND_15_BEEP
);
1521 CloseWindowById(WC_SELECT_STATION
, 0);
1524 case WID_BROS_LT_OFF
:
1525 case WID_BROS_LT_ON
:
1526 this->RaiseWidget(_settings_client
.gui
.station_show_coverage
+ WID_BROS_LT_OFF
);
1527 _settings_client
.gui
.station_show_coverage
= (widget
!= WID_BROS_LT_OFF
);
1528 this->LowerWidget(_settings_client
.gui
.station_show_coverage
+ WID_BROS_LT_OFF
);
1529 if (_settings_client
.sound
.click_beep
) SndPlayFx(SND_15_BEEP
);
1531 SetViewportCatchmentStation(nullptr, true);
1534 case WID_BROS_NEWST_LIST
: {
1535 auto it
= this->vscrollList
->GetScrolledItemFromWidget(this->roadstop_classes
, pt
.y
, this, WID_BROS_NEWST_LIST
);
1536 if (it
== this->roadstop_classes
.end()) return;
1537 RoadStopClassID class_id
= *it
;
1538 if (_roadstop_gui_settings
.roadstop_class
!= class_id
&& GetIfClassHasNewStopsByType(RoadStopClass::Get(class_id
), roadStopType
, _cur_roadtype
)) {
1539 _roadstop_gui_settings
.roadstop_class
= class_id
;
1540 RoadStopClass
*rsclass
= RoadStopClass::Get(_roadstop_gui_settings
.roadstop_class
);
1541 _roadstop_gui_settings
.roadstop_count
= rsclass
->GetSpecCount();
1542 _roadstop_gui_settings
.roadstop_type
= std::min((int)_roadstop_gui_settings
.roadstop_type
, std::max(0, (int)_roadstop_gui_settings
.roadstop_count
- 1));
1543 this->SelectFirstAvailableTypeIfUnavailable();
1545 NWidgetMatrix
*matrix
= this->GetWidget
<NWidgetMatrix
>(WID_BROS_MATRIX
);
1546 matrix
->SetCount(_roadstop_gui_settings
.roadstop_count
);
1547 matrix
->SetClicked(_roadstop_gui_settings
.roadstop_type
);
1549 if (_settings_client
.sound
.click_beep
) SndPlayFx(SND_15_BEEP
);
1551 CloseWindowById(WC_SELECT_STATION
, 0);
1552 this->CheckOrientationValid();
1556 case WID_BROS_IMAGE
: {
1557 uint16_t y
= this->GetWidget
<NWidgetBase
>(widget
)->GetParentWidget
<NWidgetMatrix
>()->GetCurrentElement();
1558 if (y
>= _roadstop_gui_settings
.roadstop_count
) return;
1560 const RoadStopSpec
*spec
= RoadStopClass::Get(_roadstop_gui_settings
.roadstop_class
)->GetSpec(y
);
1561 StationType st
= GetRoadStationTypeByWindowClass(this->window_class
);
1563 if (!IsRoadStopAvailable(spec
, st
)) return;
1565 _roadstop_gui_settings
.roadstop_type
= y
;
1567 this->GetWidget
<NWidgetBase
>(widget
)->GetParentWidget
<NWidgetMatrix
>()->SetClicked(_roadstop_gui_settings
.roadstop_type
);
1569 if (_settings_client
.sound
.click_beep
) SndPlayFx(SND_15_BEEP
);
1571 CloseWindowById(WC_SELECT_STATION
, 0);
1572 this->CheckOrientationValid();
1581 void OnRealtimeTick([[maybe_unused
]] uint delta_ms
) override
1583 CheckRedrawStationCoverage(this);
1586 IntervalTimer
<TimerGameCalendar
> yearly_interval
= {{TimerGameCalendar::YEAR
, TimerGameCalendar::Priority::NONE
}, [this](auto) {
1587 this->InvalidateData();
1590 static inline HotkeyList road_hotkeys
{"buildroadstop", {
1591 Hotkey('F', "focus_filter_box", BROSHK_FOCUS_FILTER_BOX
),
1594 static inline HotkeyList tram_hotkeys
{"buildtramstop", {
1595 Hotkey('F', "focus_filter_box", BROSHK_FOCUS_FILTER_BOX
),
1599 Listing
BuildRoadStationWindow::last_sorting
= { false, 0 };
1600 Filtering
BuildRoadStationWindow::last_filtering
= { false, 0 };
1602 BuildRoadStationWindow::GUIRoadStopClassList::SortFunction
* const BuildRoadStationWindow::sorter_funcs
[] = {
1603 &RoadStopClassIDSorter
,
1606 BuildRoadStationWindow::GUIRoadStopClassList::FilterFunction
* const BuildRoadStationWindow::filter_funcs
[] = {
1610 /** Widget definition of the build road station window */
1611 static constexpr NWidgetPart _nested_road_station_picker_widgets
[] = {
1612 NWidget(NWID_HORIZONTAL
),
1613 NWidget(WWT_CLOSEBOX
, COLOUR_DARK_GREEN
),
1614 NWidget(WWT_CAPTION
, COLOUR_DARK_GREEN
, WID_BROS_CAPTION
),
1615 NWidget(WWT_SHADEBOX
, COLOUR_DARK_GREEN
),
1616 NWidget(NWID_SELECTION
, INVALID_COLOUR
, WID_BROS_SHOW_NEWST_DEFSIZE
),
1617 NWidget(WWT_DEFSIZEBOX
, COLOUR_DARK_GREEN
),
1620 NWidget(NWID_HORIZONTAL
),
1621 NWidget(WWT_PANEL
, COLOUR_DARK_GREEN
),
1622 NWidget(NWID_VERTICAL
), SetPIP(0, WidgetDimensions::unscaled
.vsep_normal
, 0), SetPadding(WidgetDimensions::unscaled
.picker
),
1623 NWidget(NWID_HORIZONTAL
), SetPIP(0, WidgetDimensions::unscaled
.hsep_normal
, 0), SetPIPRatio(1, 0, 1),
1624 NWidget(NWID_VERTICAL
), SetPIP(0, WidgetDimensions::unscaled
.vsep_picker
, 0),
1625 NWidget(NWID_SELECTION
, INVALID_COLOUR
, WID_BROS_FILTER_CONTAINER
),
1626 NWidget(NWID_HORIZONTAL
), SetPIP(0, WidgetDimensions::unscaled
.hsep_normal
, 0),
1627 NWidget(WWT_TEXT
, COLOUR_DARK_GREEN
), SetFill(0, 1), SetDataTip(STR_LIST_FILTER_TITLE
, STR_NULL
),
1628 NWidget(WWT_EDITBOX
, COLOUR_GREY
, WID_BROS_FILTER_EDITBOX
), SetFill(1, 0), SetResize(1, 0),
1629 SetDataTip(STR_LIST_FILTER_OSKTITLE
, STR_LIST_FILTER_TOOLTIP
),
1632 NWidget(NWID_SELECTION
, INVALID_COLOUR
, WID_BROS_SHOW_NEWST_ADDITIONS
),
1633 NWidget(NWID_HORIZONTAL
),
1634 NWidget(WWT_MATRIX
, COLOUR_GREY
, WID_BROS_NEWST_LIST
), SetMinimalSize(122, 71), SetFill(1, 0),
1635 SetMatrixDataTip(1, 0, STR_STATION_BUILD_STATION_CLASS_TOOLTIP
), SetScrollbar(WID_BROS_NEWST_SCROLL
),
1636 NWidget(NWID_VSCROLLBAR
, COLOUR_GREY
, WID_BROS_NEWST_SCROLL
),
1639 NWidget(NWID_SELECTION
, INVALID_COLOUR
, WID_BROS_SHOW_NEWST_ORIENTATION
),
1640 NWidget(WWT_LABEL
, COLOUR_DARK_GREEN
), SetMinimalSize(144, 11), SetDataTip(STR_STATION_BUILD_ORIENTATION
, STR_NULL
), SetFill(1, 0),
1642 NWidget(NWID_SELECTION
, INVALID_COLOUR
, WID_BROS_AVAILABLE_ORIENTATIONS
),
1643 /* 6-orientation plane. */
1644 NWidget(NWID_VERTICAL
), SetPIP(0, WidgetDimensions::unscaled
.vsep_normal
, 0),
1645 NWidget(NWID_HORIZONTAL
), SetPIP(0, WidgetDimensions::unscaled
.hsep_normal
, 0), SetPIPRatio(1, 0, 1),
1646 NWidget(NWID_HORIZONTAL_LTR
), SetPIP(0, WidgetDimensions::unscaled
.hsep_normal
, 0),
1647 NWidget(WWT_PANEL
, COLOUR_GREY
, WID_BROS_STATION_NW
), SetMinimalSize(66, 50), SetFill(0, 0), EndContainer(),
1648 NWidget(WWT_PANEL
, COLOUR_GREY
, WID_BROS_STATION_NE
), SetMinimalSize(66, 50), SetFill(0, 0), EndContainer(),
1650 NWidget(WWT_PANEL
, COLOUR_GREY
, WID_BROS_STATION_X
), SetMinimalSize(66, 50), SetFill(0, 0), EndContainer(),
1652 NWidget(NWID_HORIZONTAL
), SetPIP(0, WidgetDimensions::unscaled
.hsep_normal
, 0), SetPIPRatio(1, 0, 1),
1653 NWidget(NWID_HORIZONTAL_LTR
), SetPIP(0, WidgetDimensions::unscaled
.hsep_normal
, 0),
1654 NWidget(WWT_PANEL
, COLOUR_GREY
, WID_BROS_STATION_SW
), SetMinimalSize(66, 50), SetFill(0, 0), EndContainer(),
1655 NWidget(WWT_PANEL
, COLOUR_GREY
, WID_BROS_STATION_SE
), SetMinimalSize(66, 50), SetFill(0, 0), EndContainer(),
1657 NWidget(WWT_PANEL
, COLOUR_GREY
, WID_BROS_STATION_Y
), SetMinimalSize(66, 50), SetFill(0, 0), EndContainer(),
1660 /* 2-orientation plane. */
1661 NWidget(NWID_VERTICAL
), SetPIPRatio(0, 0, 1),
1662 NWidget(NWID_HORIZONTAL_LTR
), SetPIP(0, WidgetDimensions::unscaled
.hsep_normal
, 0), SetPIPRatio(1, 0, 1),
1663 NWidget(WWT_PANEL
, COLOUR_GREY
, WID_BROS_STATION_X
), SetMinimalSize(66, 50), SetFill(0, 0), EndContainer(),
1664 NWidget(WWT_PANEL
, COLOUR_GREY
, WID_BROS_STATION_Y
), SetMinimalSize(66, 50), SetFill(0, 0), EndContainer(),
1668 NWidget(NWID_SELECTION
, INVALID_COLOUR
, WID_BROS_SHOW_NEWST_TYPE_SEL
),
1669 NWidget(WWT_LABEL
, COLOUR_DARK_GREEN
, WID_BROS_SHOW_NEWST_TYPE
), SetMinimalSize(144, 8), SetDataTip(STR_JUST_STRING
, STR_NULL
), SetTextStyle(TC_ORANGE
), SetFill(1, 0),
1671 NWidget(WWT_LABEL
, COLOUR_DARK_GREEN
), SetDataTip(STR_STATION_BUILD_COVERAGE_AREA_TITLE
, STR_NULL
), SetFill(1, 0),
1672 NWidget(NWID_HORIZONTAL
), SetPIPRatio(1, 0, 1),
1673 NWidget(WWT_TEXTBTN
, COLOUR_GREY
, WID_BROS_LT_OFF
), SetMinimalSize(60, 12),
1674 SetDataTip(STR_STATION_BUILD_COVERAGE_OFF
, STR_STATION_BUILD_COVERAGE_AREA_OFF_TOOLTIP
),
1675 NWidget(WWT_TEXTBTN
, COLOUR_GREY
, WID_BROS_LT_ON
), SetMinimalSize(60, 12),
1676 SetDataTip(STR_STATION_BUILD_COVERAGE_ON
, STR_STATION_BUILD_COVERAGE_AREA_ON_TOOLTIP
),
1679 NWidget(NWID_SELECTION
, INVALID_COLOUR
, WID_BROS_SHOW_NEWST_MATRIX
),
1680 /* Hidden panel as NWID_MATRIX does not support SetScrollbar() */
1681 NWidget(WWT_PANEL
, COLOUR_DARK_GREEN
), SetScrollbar(WID_BROS_MATRIX_SCROLL
),
1682 NWidget(NWID_MATRIX
, COLOUR_DARK_GREEN
, WID_BROS_MATRIX
), SetPIP(0, 2, 0),
1683 NWidget(WWT_PANEL
, COLOUR_DARK_GREEN
, WID_BROS_IMAGE
), SetMinimalSize(66, 60),
1684 SetFill(0, 0), SetResize(0, 0), SetDataTip(0x0, STR_STATION_BUILD_STATION_TYPE_TOOLTIP
), SetScrollbar(WID_BROS_MATRIX_SCROLL
),
1690 NWidget(WWT_EMPTY
, INVALID_COLOUR
, WID_BROS_ACCEPTANCE
), SetFill(1, 1), SetResize(1, 0), SetMinimalTextLines(2, WidgetDimensions::unscaled
.vsep_normal
),
1693 NWidget(NWID_SELECTION
, INVALID_COLOUR
, WID_BROS_SHOW_NEWST_RESIZE
),
1694 NWidget(NWID_VERTICAL
),
1695 NWidget(NWID_VSCROLLBAR
, COLOUR_DARK_GREEN
, WID_BROS_MATRIX_SCROLL
),
1696 NWidget(WWT_RESIZEBOX
, COLOUR_DARK_GREEN
),
1702 static WindowDesc
_road_station_picker_desc(__FILE__
, __LINE__
,
1703 WDP_AUTO
, "build_station_road", 0, 0,
1704 WC_BUS_STATION
, WC_BUILD_TOOLBAR
,
1706 std::begin(_nested_road_station_picker_widgets
), std::end(_nested_road_station_picker_widgets
),
1707 &BuildRoadStationWindow::road_hotkeys
1710 /** Widget definition of the build tram station window */
1711 static constexpr NWidgetPart _nested_tram_station_picker_widgets
[] = {
1712 NWidget(NWID_HORIZONTAL
),
1713 NWidget(WWT_CLOSEBOX
, COLOUR_DARK_GREEN
),
1714 NWidget(WWT_CAPTION
, COLOUR_DARK_GREEN
, WID_BROS_CAPTION
),
1715 NWidget(WWT_SHADEBOX
, COLOUR_DARK_GREEN
),
1716 NWidget(NWID_SELECTION
, INVALID_COLOUR
, WID_BROS_SHOW_NEWST_DEFSIZE
),
1717 NWidget(WWT_DEFSIZEBOX
, COLOUR_DARK_GREEN
),
1720 NWidget(NWID_HORIZONTAL
),
1721 NWidget(WWT_PANEL
, COLOUR_DARK_GREEN
),
1722 NWidget(NWID_VERTICAL
), SetPIP(0, WidgetDimensions::unscaled
.vsep_normal
, 0), SetPadding(WidgetDimensions::unscaled
.picker
),
1723 NWidget(NWID_HORIZONTAL
), SetPIP(0, WidgetDimensions::unscaled
.hsep_normal
, 0), SetPIPRatio(1, 0, 1),
1724 NWidget(NWID_VERTICAL
), SetPIP(0, WidgetDimensions::unscaled
.vsep_picker
, 0),
1725 NWidget(NWID_SELECTION
, INVALID_COLOUR
, WID_BROS_FILTER_CONTAINER
),
1726 NWidget(NWID_HORIZONTAL
), SetPIP(0, WidgetDimensions::unscaled
.hsep_normal
, 0),
1727 NWidget(WWT_TEXT
, COLOUR_DARK_GREEN
), SetFill(0, 1), SetDataTip(STR_LIST_FILTER_TITLE
, STR_NULL
),
1728 NWidget(WWT_EDITBOX
, COLOUR_GREY
, WID_BROS_FILTER_EDITBOX
), SetFill(1, 0), SetResize(1, 0),
1729 SetDataTip(STR_LIST_FILTER_OSKTITLE
, STR_LIST_FILTER_TOOLTIP
),
1732 NWidget(NWID_SELECTION
, INVALID_COLOUR
, WID_BROS_SHOW_NEWST_ADDITIONS
),
1733 NWidget(NWID_HORIZONTAL
),
1734 NWidget(WWT_MATRIX
, COLOUR_GREY
, WID_BROS_NEWST_LIST
), SetMinimalSize(122, 71), SetFill(1, 0),
1735 SetMatrixDataTip(1, 0, STR_STATION_BUILD_STATION_CLASS_TOOLTIP
), SetScrollbar(WID_BROS_NEWST_SCROLL
),
1736 NWidget(NWID_VSCROLLBAR
, COLOUR_GREY
, WID_BROS_NEWST_SCROLL
),
1739 NWidget(NWID_SELECTION
, INVALID_COLOUR
, WID_BROS_SHOW_NEWST_ORIENTATION
),
1740 NWidget(WWT_LABEL
, COLOUR_DARK_GREEN
), SetMinimalSize(144, 11), SetDataTip(STR_STATION_BUILD_ORIENTATION
, STR_NULL
), SetFill(1, 0),
1742 NWidget(NWID_HORIZONTAL_LTR
), SetPIP(0, WidgetDimensions::unscaled
.hsep_normal
, 0), SetPIPRatio(1, 0, 1),
1743 NWidget(WWT_PANEL
, COLOUR_GREY
, WID_BROS_STATION_X
), SetMinimalSize(66, 50), SetFill(0, 0), EndContainer(),
1744 NWidget(WWT_PANEL
, COLOUR_GREY
, WID_BROS_STATION_Y
), SetMinimalSize(66, 50), SetFill(0, 0), EndContainer(),
1746 NWidget(NWID_SELECTION
, INVALID_COLOUR
, WID_BROS_SHOW_NEWST_TYPE_SEL
),
1747 NWidget(WWT_LABEL
, COLOUR_DARK_GREEN
, WID_BROS_SHOW_NEWST_TYPE
), SetMinimalSize(144, 8), SetDataTip(STR_JUST_STRING
, STR_NULL
), SetTextStyle(TC_ORANGE
), SetFill(1, 0),
1749 NWidget(WWT_LABEL
, COLOUR_DARK_GREEN
), SetDataTip(STR_STATION_BUILD_COVERAGE_AREA_TITLE
, STR_NULL
), SetFill(1, 0),
1750 NWidget(NWID_HORIZONTAL
), SetPIPRatio(1, 0, 1),
1751 NWidget(WWT_TEXTBTN
, COLOUR_GREY
, WID_BROS_LT_OFF
), SetMinimalSize(60, 12),
1752 SetDataTip(STR_STATION_BUILD_COVERAGE_OFF
, STR_STATION_BUILD_COVERAGE_AREA_OFF_TOOLTIP
),
1753 NWidget(WWT_TEXTBTN
, COLOUR_GREY
, WID_BROS_LT_ON
), SetMinimalSize(60, 12),
1754 SetDataTip(STR_STATION_BUILD_COVERAGE_ON
, STR_STATION_BUILD_COVERAGE_AREA_ON_TOOLTIP
),
1757 NWidget(NWID_SELECTION
, INVALID_COLOUR
, WID_BROS_SHOW_NEWST_MATRIX
),
1758 /* Hidden panel as NWID_MATRIX does not support SetScrollbar() */
1759 NWidget(WWT_PANEL
, COLOUR_DARK_GREEN
), SetScrollbar(WID_BROS_MATRIX_SCROLL
),
1760 NWidget(NWID_MATRIX
, COLOUR_DARK_GREEN
, WID_BROS_MATRIX
), SetPIP(0, 2, 0),
1761 NWidget(WWT_PANEL
, COLOUR_DARK_GREEN
, WID_BROS_IMAGE
), SetMinimalSize(66, 60),
1762 SetFill(0, 0), SetResize(0, 0), SetDataTip(0x0, STR_STATION_BUILD_STATION_TYPE_TOOLTIP
), SetScrollbar(WID_BROS_MATRIX_SCROLL
),
1768 NWidget(WWT_EMPTY
, INVALID_COLOUR
, WID_BROS_ACCEPTANCE
), SetFill(1, 1), SetResize(1, 0), SetMinimalTextLines(2, WidgetDimensions::unscaled
.vsep_normal
),
1771 NWidget(NWID_SELECTION
, INVALID_COLOUR
, WID_BROS_SHOW_NEWST_RESIZE
),
1772 NWidget(NWID_VERTICAL
),
1773 NWidget(NWID_VSCROLLBAR
, COLOUR_DARK_GREEN
, WID_BROS_MATRIX_SCROLL
),
1774 NWidget(WWT_RESIZEBOX
, COLOUR_DARK_GREEN
),
1780 static WindowDesc
_tram_station_picker_desc(__FILE__
, __LINE__
,
1781 WDP_AUTO
, "build_station_tram", 0, 0,
1782 WC_BUS_STATION
, WC_BUILD_TOOLBAR
,
1784 std::begin(_nested_tram_station_picker_widgets
), std::end(_nested_tram_station_picker_widgets
),
1785 &BuildRoadStationWindow::tram_hotkeys
1788 static void ShowRVStationPicker(Window
*parent
, RoadStopType rs
)
1790 new BuildRoadStationWindow(RoadTypeIsRoad(_cur_roadtype
) ? &_road_station_picker_desc
: &_tram_station_picker_desc
, parent
, rs
);
1793 void InitializeRoadGui()
1795 _road_depot_orientation
= DIAGDIR_NW
;
1796 _roadstop_gui_settings
.orientation
= DIAGDIR_NW
;
1800 * I really don't know why rail_gui.cpp has this too, shouldn't be included in the other one?
1802 void InitializeRoadGUI()
1804 BuildRoadToolbarWindow
*w
= dynamic_cast<BuildRoadToolbarWindow
*>(FindWindowById(WC_BUILD_TOOLBAR
, TRANSPORT_ROAD
));
1805 if (w
!= nullptr) w
->ModifyRoadType(_cur_roadtype
);
1808 DropDownList
GetRoadTypeDropDownList(RoadTramTypes rtts
, bool for_replacement
, bool all_option
)
1810 RoadTypes used_roadtypes
;
1811 RoadTypes avail_roadtypes
;
1813 const Company
*c
= Company::Get(_local_company
);
1815 /* Find the used roadtypes. */
1816 if (for_replacement
) {
1817 avail_roadtypes
= GetCompanyRoadTypes(c
->index
, false);
1818 used_roadtypes
= GetRoadTypes(false);
1820 avail_roadtypes
= c
->avail_roadtypes
;
1821 used_roadtypes
= GetRoadTypes(true);
1824 /* Filter listed road types */
1825 if (!HasBit(rtts
, RTT_ROAD
)) used_roadtypes
&= _roadtypes_type
;
1826 if (!HasBit(rtts
, RTT_TRAM
)) used_roadtypes
&= ~_roadtypes_type
;
1831 list
.push_back(std::make_unique
<DropDownListStringItem
>(STR_REPLACE_ALL_ROADTYPE
, INVALID_ROADTYPE
, false));
1834 Dimension d
= { 0, 0 };
1835 /* Get largest icon size, to ensure text is aligned on each menu item. */
1836 if (!for_replacement
) {
1837 for (const auto &rt
: _sorted_roadtypes
) {
1838 if (!HasBit(used_roadtypes
, rt
)) continue;
1839 const RoadTypeInfo
*rti
= GetRoadTypeInfo(rt
);
1840 d
= maxdim(d
, GetSpriteSize(rti
->gui_sprites
.build_x_road
));
1844 for (const auto &rt
: _sorted_roadtypes
) {
1845 /* If it's not used ever, don't show it to the user. */
1846 if (!HasBit(used_roadtypes
, rt
)) continue;
1848 const RoadTypeInfo
*rti
= GetRoadTypeInfo(rt
);
1850 SetDParam(0, rti
->strings
.menu_text
);
1851 SetDParam(1, rti
->max_speed
/ 2);
1852 if (for_replacement
) {
1853 list
.push_back(std::make_unique
<DropDownListStringItem
>(rti
->strings
.replace_text
, rt
, !HasBit(avail_roadtypes
, rt
)));
1855 StringID str
= rti
->max_speed
> 0 ? STR_TOOLBAR_RAILTYPE_VELOCITY
: STR_JUST_STRING
;
1856 list
.push_back(std::make_unique
<DropDownListIconItem
>(d
, rti
->gui_sprites
.build_x_road
, PAL_NONE
, str
, rt
, !HasBit(avail_roadtypes
, rt
)));
1861 /* Empty dropdowns are not allowed */
1862 list
.push_back(std::make_unique
<DropDownListStringItem
>(STR_NONE
, INVALID_ROADTYPE
, true));
1868 DropDownList
GetScenRoadTypeDropDownList(RoadTramTypes rtts
)
1870 RoadTypes avail_roadtypes
= GetRoadTypes(false);
1871 avail_roadtypes
= AddDateIntroducedRoadTypes(avail_roadtypes
, TimerGameCalendar::date
);
1872 RoadTypes used_roadtypes
= GetRoadTypes(true);
1874 /* Filter listed road types */
1875 if (!HasBit(rtts
, RTT_ROAD
)) used_roadtypes
&= _roadtypes_type
;
1876 if (!HasBit(rtts
, RTT_TRAM
)) used_roadtypes
&= ~_roadtypes_type
;
1880 /* If it's not used ever, don't show it to the user. */
1881 Dimension d
= { 0, 0 };
1882 for (const auto &rt
: _sorted_roadtypes
) {
1883 if (!HasBit(used_roadtypes
, rt
)) continue;
1884 const RoadTypeInfo
*rti
= GetRoadTypeInfo(rt
);
1885 d
= maxdim(d
, GetSpriteSize(rti
->gui_sprites
.build_x_road
));
1887 for (const auto &rt
: _sorted_roadtypes
) {
1888 if (!HasBit(used_roadtypes
, rt
)) continue;
1890 const RoadTypeInfo
*rti
= GetRoadTypeInfo(rt
);
1892 SetDParam(0, rti
->strings
.menu_text
);
1893 SetDParam(1, rti
->max_speed
/ 2);
1894 StringID str
= rti
->max_speed
> 0 ? STR_TOOLBAR_RAILTYPE_VELOCITY
: STR_JUST_STRING
;
1895 list
.push_back(std::make_unique
<DropDownListIconItem
>(d
, rti
->gui_sprites
.build_x_road
, PAL_NONE
, str
, rt
, !HasBit(avail_roadtypes
, rt
)));
1899 /* Empty dropdowns are not allowed */
1900 list
.push_back(std::make_unique
<DropDownListStringItem
>(STR_NONE
, -1, true));