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 airport_gui.cpp The GUI for airports. */
11 #include "economy_func.h"
12 #include "window_gui.h"
13 #include "station_gui.h"
14 #include "terraform_gui.h"
15 #include "sound_func.h"
16 #include "window_func.h"
17 #include "strings_func.h"
18 #include "viewport_func.h"
19 #include "company_func.h"
20 #include "tilehighlight_func.h"
21 #include "company_base.h"
22 #include "station_type.h"
23 #include "newgrf_airport.h"
24 #include "newgrf_callbacks.h"
25 #include "dropdown_type.h"
26 #include "dropdown_func.h"
27 #include "core/geometry_func.hpp"
29 #include "vehicle_func.h"
31 #include "command_func.h"
32 #include "airport_cmd.h"
33 #include "station_cmd.h"
34 #include "zoom_func.h"
35 #include "timer/timer.h"
36 #include "timer/timer_game_calendar.h"
38 #include "widgets/airport_widget.h"
40 #include "safeguards.h"
43 static AirportClassID _selected_airport_class
; ///< the currently visible airport class
44 static int _selected_airport_index
; ///< the index of the selected airport in the current class or -1
45 static uint8_t _selected_airport_layout
; ///< selected airport layout number.
47 static void ShowBuildAirportPicker(Window
*parent
);
49 SpriteID
GetCustomAirportSprite(const AirportSpec
*as
, uint8_t layout
);
51 void CcBuildAirport(Commands
, const CommandCost
&result
, TileIndex tile
)
53 if (result
.Failed()) return;
55 if (_settings_client
.sound
.confirm
) SndPlayTileFx(SND_1F_CONSTRUCTION_OTHER
, tile
);
56 if (!_settings_client
.gui
.persistent_buildingtools
) ResetObjectToPlace();
61 * @param tile Position to put the new airport.
63 static void PlaceAirport(TileIndex tile
)
65 if (_selected_airport_index
== -1) return;
67 uint8_t airport_type
= AirportClass::Get(_selected_airport_class
)->GetSpec(_selected_airport_index
)->GetIndex();
68 uint8_t layout
= _selected_airport_layout
;
69 bool adjacent
= _ctrl_pressed
;
71 auto proc
= [=](bool test
, StationID to_join
) -> bool {
73 return Command
<CMD_BUILD_AIRPORT
>::Do(CommandFlagsToDCFlags(GetCommandFlags
<CMD_BUILD_AIRPORT
>()), tile
, airport_type
, layout
, INVALID_STATION
, adjacent
).Succeeded();
75 return Command
<CMD_BUILD_AIRPORT
>::Post(STR_ERROR_CAN_T_BUILD_AIRPORT_HERE
, CcBuildAirport
, tile
, airport_type
, layout
, to_join
, adjacent
);
79 ShowSelectStationIfNeeded(TileArea(tile
, _thd
.size
.x
/ TILE_SIZE
, _thd
.size
.y
/ TILE_SIZE
), proc
);
82 /** Airport build toolbar window handler. */
83 struct BuildAirToolbarWindow
: Window
{
84 int last_user_action
; // Last started user action.
86 BuildAirToolbarWindow(WindowDesc
&desc
, WindowNumber window_number
) : Window(desc
)
88 this->InitNested(window_number
);
89 this->OnInvalidateData();
90 if (_settings_client
.gui
.link_terraform_toolbar
) ShowTerraformToolbar(this);
91 this->last_user_action
= INVALID_WID_AT
;
94 void Close([[maybe_unused
]] int data
= 0) override
96 if (this->IsWidgetLowered(WID_AT_AIRPORT
)) SetViewportCatchmentStation(nullptr, true);
97 if (_settings_client
.gui
.link_terraform_toolbar
) CloseWindowById(WC_SCEN_LAND_GEN
, 0, false);
98 this->Window::Close();
102 * Some data on this window has become invalid.
103 * @param data Information about the changed data.
104 * @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.
106 void OnInvalidateData([[maybe_unused
]] int data
= 0, [[maybe_unused
]] bool gui_scope
= true) override
108 if (!gui_scope
) return;
110 bool can_build
= CanBuildVehicleInfrastructure(VEH_AIRCRAFT
);
111 this->SetWidgetDisabledState(WID_AT_AIRPORT
, !can_build
);
113 CloseWindowById(WC_BUILD_STATION
, TRANSPORT_AIR
);
115 /* Show in the tooltip why this button is disabled. */
116 this->GetWidget
<NWidgetCore
>(WID_AT_AIRPORT
)->SetToolTip(STR_TOOLBAR_DISABLED_NO_VEHICLE_AVAILABLE
);
118 this->GetWidget
<NWidgetCore
>(WID_AT_AIRPORT
)->SetToolTip(STR_TOOLBAR_AIRCRAFT_BUILD_AIRPORT_TOOLTIP
);
122 void OnClick([[maybe_unused
]] Point pt
, WidgetID widget
, [[maybe_unused
]] int click_count
) override
126 if (HandlePlacePushButton(this, WID_AT_AIRPORT
, SPR_CURSOR_AIRPORT
, HT_RECT
)) {
127 ShowBuildAirportPicker(this);
128 this->last_user_action
= widget
;
132 case WID_AT_DEMOLISH
:
133 HandlePlacePushButton(this, WID_AT_DEMOLISH
, ANIMCURSOR_DEMOLISH
, HT_RECT
| HT_DIAGONAL
);
134 this->last_user_action
= widget
;
142 void OnPlaceObject([[maybe_unused
]] Point pt
, TileIndex tile
) override
144 switch (this->last_user_action
) {
149 case WID_AT_DEMOLISH
:
150 PlaceProc_DemolishArea(tile
);
153 default: NOT_REACHED();
157 void OnPlaceDrag(ViewportPlaceMethod select_method
, [[maybe_unused
]] ViewportDragDropSelectionProcess select_proc
, [[maybe_unused
]] Point pt
) override
159 VpSelectTilesWithMethod(pt
.x
, pt
.y
, select_method
);
162 void OnPlaceMouseUp([[maybe_unused
]] ViewportPlaceMethod select_method
, ViewportDragDropSelectionProcess select_proc
, [[maybe_unused
]] Point pt
, TileIndex start_tile
, TileIndex end_tile
) override
164 if (pt
.x
!= -1 && select_proc
== DDSP_DEMOLISH_AREA
) {
165 GUIPlaceProcDragXY(select_proc
, start_tile
, end_tile
);
169 void OnPlaceObjectAbort() override
171 if (this->IsWidgetLowered(WID_AT_AIRPORT
)) SetViewportCatchmentStation(nullptr, true);
173 this->RaiseButtons();
175 CloseWindowById(WC_BUILD_STATION
, TRANSPORT_AIR
);
176 CloseWindowById(WC_SELECT_STATION
, 0);
180 * Handler for global hotkeys of the BuildAirToolbarWindow.
181 * @param hotkey Hotkey
182 * @return ES_HANDLED if hotkey was accepted.
184 static EventState
AirportToolbarGlobalHotkeys(int hotkey
)
186 if (_game_mode
!= GM_NORMAL
) return ES_NOT_HANDLED
;
187 Window
*w
= ShowBuildAirToolbar();
188 if (w
== nullptr) return ES_NOT_HANDLED
;
189 return w
->OnHotkey(hotkey
);
192 static inline HotkeyList hotkeys
{"airtoolbar", {
193 Hotkey('1', "airport", WID_AT_AIRPORT
),
194 Hotkey('2', "demolish", WID_AT_DEMOLISH
),
195 }, AirportToolbarGlobalHotkeys
};
198 static constexpr NWidgetPart _nested_air_toolbar_widgets
[] = {
199 NWidget(NWID_HORIZONTAL
),
200 NWidget(WWT_CLOSEBOX
, COLOUR_DARK_GREEN
),
201 NWidget(WWT_CAPTION
, COLOUR_DARK_GREEN
), SetDataTip(STR_TOOLBAR_AIRCRAFT_CAPTION
, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS
),
202 NWidget(WWT_STICKYBOX
, COLOUR_DARK_GREEN
),
204 NWidget(NWID_HORIZONTAL
),
205 NWidget(WWT_IMGBTN
, COLOUR_DARK_GREEN
, WID_AT_AIRPORT
), SetFill(0, 1), SetMinimalSize(42, 22), SetDataTip(SPR_IMG_AIRPORT
, STR_TOOLBAR_AIRCRAFT_BUILD_AIRPORT_TOOLTIP
),
206 NWidget(WWT_PANEL
, COLOUR_DARK_GREEN
), SetMinimalSize(4, 22), SetFill(1, 1), EndContainer(),
207 NWidget(WWT_IMGBTN
, COLOUR_DARK_GREEN
, WID_AT_DEMOLISH
), SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_DYNAMITE
, STR_TOOLTIP_DEMOLISH_BUILDINGS_ETC
),
211 static WindowDesc
_air_toolbar_desc(
212 WDP_ALIGN_TOOLBAR
, "toolbar_air", 0, 0,
213 WC_BUILD_TOOLBAR
, WC_NONE
,
215 _nested_air_toolbar_widgets
,
216 &BuildAirToolbarWindow::hotkeys
220 * Open the build airport toolbar window
222 * If the terraform toolbar is linked to the toolbar, that window is also opened.
224 * @return newly opened airport toolbar, or nullptr if the toolbar could not be opened.
226 Window
*ShowBuildAirToolbar()
228 if (!Company::IsValidID(_local_company
)) return nullptr;
230 CloseWindowByClass(WC_BUILD_TOOLBAR
);
231 return AllocateWindowDescFront
<BuildAirToolbarWindow
>(_air_toolbar_desc
, TRANSPORT_AIR
);
234 class BuildAirportWindow
: public PickerWindowBase
{
235 SpriteID preview_sprite
; ///< Cached airport preview sprite.
239 /** Build a dropdown list of available airport classes */
240 static DropDownList
BuildAirportClassDropDown()
244 for (const auto &cls
: AirportClass::Classes()) {
245 list
.push_back(MakeDropDownListStringItem(cls
.name
, cls
.Index()));
252 BuildAirportWindow(WindowDesc
&desc
, Window
*parent
) : PickerWindowBase(desc
, parent
)
254 this->CreateNestedTree();
256 this->vscroll
= this->GetScrollbar(WID_AP_SCROLLBAR
);
257 this->vscroll
->SetCapacity(5);
258 this->vscroll
->SetPosition(0);
260 this->FinishInitNested(TRANSPORT_AIR
);
262 this->SetWidgetLoweredState(WID_AP_BTN_DONTHILIGHT
, !_settings_client
.gui
.station_show_coverage
);
263 this->SetWidgetLoweredState(WID_AP_BTN_DOHILIGHT
, _settings_client
.gui
.station_show_coverage
);
264 this->OnInvalidateData();
266 /* Ensure airport class is valid (changing NewGRFs). */
267 _selected_airport_class
= Clamp(_selected_airport_class
, APC_BEGIN
, (AirportClassID
)(AirportClass::GetClassCount() - 1));
268 const AirportClass
*ac
= AirportClass::Get(_selected_airport_class
);
269 this->vscroll
->SetCount(ac
->GetSpecCount());
271 /* Ensure the airport index is valid for this class (changing NewGRFs). */
272 _selected_airport_index
= Clamp(_selected_airport_index
, -1, ac
->GetSpecCount() - 1);
274 /* Only when no valid airport was selected, we want to select the first airport. */
275 bool selectFirstAirport
= true;
276 if (_selected_airport_index
!= -1) {
277 const AirportSpec
*as
= ac
->GetSpec(_selected_airport_index
);
278 if (as
->IsAvailable()) {
279 /* Ensure the airport layout is valid. */
280 _selected_airport_layout
= Clamp(_selected_airport_layout
, 0, static_cast<uint8_t>(as
->layouts
.size() - 1));
281 selectFirstAirport
= false;
282 this->UpdateSelectSize();
286 if (selectFirstAirport
) this->SelectFirstAvailableAirport(true);
289 void Close([[maybe_unused
]] int data
= 0) override
291 CloseWindowById(WC_SELECT_STATION
, 0);
292 this->PickerWindowBase::Close();
295 void SetStringParameters(WidgetID widget
) const override
298 case WID_AP_CLASS_DROPDOWN
:
299 SetDParam(0, AirportClass::Get(_selected_airport_class
)->name
);
302 case WID_AP_LAYOUT_NUM
:
303 SetDParam(0, STR_EMPTY
);
304 if (_selected_airport_index
!= -1) {
305 const AirportSpec
*as
= AirportClass::Get(_selected_airport_class
)->GetSpec(_selected_airport_index
);
306 StringID string
= GetAirportTextCallback(as
, _selected_airport_layout
, CBID_AIRPORT_LAYOUT_NAME
);
307 if (string
!= STR_UNDEFINED
) {
308 SetDParam(0, string
);
309 } else if (as
->layouts
.size() > 1) {
310 SetDParam(0, STR_STATION_BUILD_AIRPORT_LAYOUT_NAME
);
311 SetDParam(1, _selected_airport_layout
+ 1);
320 void UpdateWidgetSize(WidgetID widget
, Dimension
&size
, [[maybe_unused
]] const Dimension
&padding
, [[maybe_unused
]] Dimension
&fill
, [[maybe_unused
]] Dimension
&resize
) override
323 case WID_AP_CLASS_DROPDOWN
: {
324 Dimension d
= {0, 0};
325 for (const auto &cls
: AirportClass::Classes()) {
326 d
= maxdim(d
, GetStringBoundingBox(cls
.name
));
328 d
.width
+= padding
.width
;
329 d
.height
+= padding
.height
;
330 size
= maxdim(size
, d
);
334 case WID_AP_AIRPORT_LIST
: {
335 for (int i
= 0; i
< NUM_AIRPORTS
; i
++) {
336 const AirportSpec
*as
= AirportSpec::Get(i
);
337 if (!as
->enabled
) continue;
339 size
.width
= std::max(size
.width
, GetStringBoundingBox(as
->name
).width
+ padding
.width
);
342 this->line_height
= GetCharacterHeight(FS_NORMAL
) + padding
.height
;
343 size
.height
= 5 * this->line_height
;
347 case WID_AP_AIRPORT_SPRITE
:
348 for (int i
= 0; i
< NUM_AIRPORTS
; i
++) {
349 const AirportSpec
*as
= AirportSpec::Get(i
);
350 if (!as
->enabled
) continue;
351 for (uint8_t layout
= 0; layout
< static_cast<uint8_t>(as
->layouts
.size()); layout
++) {
352 SpriteID sprite
= GetCustomAirportSprite(as
, layout
);
354 Dimension d
= GetSpriteSize(sprite
);
355 d
.width
+= WidgetDimensions::scaled
.framerect
.Horizontal();
356 d
.height
+= WidgetDimensions::scaled
.framerect
.Vertical();
357 size
= maxdim(d
, size
);
363 case WID_AP_EXTRA_TEXT
:
364 for (int i
= NEW_AIRPORT_OFFSET
; i
< NUM_AIRPORTS
; i
++) {
365 const AirportSpec
*as
= AirportSpec::Get(i
);
366 if (!as
->enabled
) continue;
367 for (uint8_t layout
= 0; layout
< static_cast<uint8_t>(as
->layouts
.size()); layout
++) {
368 StringID string
= GetAirportTextCallback(as
, layout
, CBID_AIRPORT_ADDITIONAL_TEXT
);
369 if (string
== STR_UNDEFINED
) continue;
371 Dimension d
= GetStringMultiLineBoundingBox(string
, size
);
372 size
= maxdim(d
, size
);
381 void DrawWidget(const Rect
&r
, WidgetID widget
) const override
384 case WID_AP_AIRPORT_LIST
: {
385 Rect row
= r
.WithHeight(this->line_height
).Shrink(WidgetDimensions::scaled
.bevel
);
386 Rect text
= r
.WithHeight(this->line_height
).Shrink(WidgetDimensions::scaled
.matrix
);
387 const auto specs
= AirportClass::Get(_selected_airport_class
)->Specs();
388 auto [first
, last
] = this->vscroll
->GetVisibleRangeIterators(specs
);
389 for (auto it
= first
; it
!= last
; ++it
) {
390 const AirportSpec
*as
= *it
;
391 if (!as
->IsAvailable()) {
392 GfxFillRect(row
, PC_BLACK
, FILLRECT_CHECKER
);
394 DrawString(text
, as
->name
, (static_cast<int>(as
->index
) == _selected_airport_index
) ? TC_WHITE
: TC_BLACK
);
395 row
= row
.Translate(0, this->line_height
);
396 text
= text
.Translate(0, this->line_height
);
401 case WID_AP_AIRPORT_SPRITE
:
402 if (this->preview_sprite
!= 0) {
403 Dimension d
= GetSpriteSize(this->preview_sprite
);
404 DrawSprite(this->preview_sprite
, COMPANY_SPRITE_COLOUR(_local_company
), CenterBounds(r
.left
, r
.right
, d
.width
), CenterBounds(r
.top
, r
.bottom
, d
.height
));
408 case WID_AP_EXTRA_TEXT
:
409 if (_selected_airport_index
!= -1) {
410 const AirportSpec
*as
= AirportClass::Get(_selected_airport_class
)->GetSpec(_selected_airport_index
);
411 StringID string
= GetAirportTextCallback(as
, _selected_airport_layout
, CBID_AIRPORT_ADDITIONAL_TEXT
);
412 if (string
!= STR_UNDEFINED
) {
413 DrawStringMultiLine(r
.left
, r
.right
, r
.top
, r
.bottom
, string
, TC_BLACK
);
420 void OnPaint() override
424 Rect r
= this->GetWidget
<NWidgetBase
>(WID_AP_ACCEPTANCE
)->GetCurrentRect();
427 if (_selected_airport_index
!= -1) {
428 const AirportSpec
*as
= AirportClass::Get(_selected_airport_class
)->GetSpec(_selected_airport_index
);
429 int rad
= _settings_game
.station
.modified_catchment
? as
->catchment
: (uint
)CA_UNMODIFIED
;
431 /* only show the station (airport) noise, if the noise option is activated */
432 if (_settings_game
.economy
.station_noise_level
) {
433 /* show the noise of the selected airport */
434 SetDParam(0, as
->noise_level
);
435 DrawString(r
.left
, r
.right
, top
, STR_STATION_BUILD_NOISE
);
436 top
+= GetCharacterHeight(FS_NORMAL
) + WidgetDimensions::scaled
.vsep_normal
;
439 if (_settings_game
.economy
.infrastructure_maintenance
) {
440 Money monthly
= _price
[PR_INFRASTRUCTURE_AIRPORT
] * as
->maintenance_cost
>> 3;
441 SetDParam(0, monthly
* 12);
442 DrawString(r
.left
, r
.right
, top
, TimerGameEconomy::UsingWallclockUnits() ? STR_STATION_BUILD_INFRASTRUCTURE_COST_PERIOD
: STR_STATION_BUILD_INFRASTRUCTURE_COST_YEAR
);
443 top
+= GetCharacterHeight(FS_NORMAL
) + WidgetDimensions::scaled
.vsep_normal
;
446 /* strings such as 'Size' and 'Coverage Area' */
447 top
= DrawStationCoverageAreaText(r
.left
, r
.right
, top
, SCT_ALL
, rad
, false) + WidgetDimensions::scaled
.vsep_normal
;
448 top
= DrawStationCoverageAreaText(r
.left
, r
.right
, top
, SCT_ALL
, rad
, true);
451 /* Resize background if the window is too small.
452 * Never make the window smaller to avoid oscillating if the size change affects the acceptance.
453 * (This is the case, if making the window bigger moves the mouse into the window.) */
454 if (top
> r
.bottom
) {
455 ResizeWindow(this, 0, top
- r
.bottom
, false);
459 void SelectOtherAirport(int airport_index
)
461 _selected_airport_index
= airport_index
;
462 _selected_airport_layout
= 0;
464 this->UpdateSelectSize();
468 void UpdateSelectSize()
470 if (_selected_airport_index
== -1) {
471 SetTileSelectSize(1, 1);
472 this->DisableWidget(WID_AP_LAYOUT_DECREASE
);
473 this->DisableWidget(WID_AP_LAYOUT_INCREASE
);
475 const AirportSpec
*as
= AirportClass::Get(_selected_airport_class
)->GetSpec(_selected_airport_index
);
478 Direction rotation
= as
->layouts
[_selected_airport_layout
].rotation
;
479 if (rotation
== DIR_E
|| rotation
== DIR_W
) Swap(w
, h
);
480 SetTileSelectSize(w
, h
);
482 this->preview_sprite
= GetCustomAirportSprite(as
, _selected_airport_layout
);
484 this->SetWidgetDisabledState(WID_AP_LAYOUT_DECREASE
, _selected_airport_layout
== 0);
485 this->SetWidgetDisabledState(WID_AP_LAYOUT_INCREASE
, _selected_airport_layout
+ 1U >= as
->layouts
.size());
487 int rad
= _settings_game
.station
.modified_catchment
? as
->catchment
: (uint
)CA_UNMODIFIED
;
488 if (_settings_client
.gui
.station_show_coverage
) SetTileSelectBigSize(-rad
, -rad
, 2 * rad
, 2 * rad
);
492 void OnClick([[maybe_unused
]] Point pt
, WidgetID widget
, [[maybe_unused
]] int click_count
) override
495 case WID_AP_CLASS_DROPDOWN
:
496 ShowDropDownList(this, BuildAirportClassDropDown(), _selected_airport_class
, WID_AP_CLASS_DROPDOWN
);
499 case WID_AP_AIRPORT_LIST
: {
500 int32_t num_clicked
= this->vscroll
->GetScrolledRowFromWidget(pt
.y
, this, widget
, 0, this->line_height
);
501 if (num_clicked
== INT32_MAX
) break;
502 const AirportSpec
*as
= AirportClass::Get(_selected_airport_class
)->GetSpec(num_clicked
);
503 if (as
->IsAvailable()) this->SelectOtherAirport(num_clicked
);
507 case WID_AP_BTN_DONTHILIGHT
: case WID_AP_BTN_DOHILIGHT
:
508 _settings_client
.gui
.station_show_coverage
= (widget
!= WID_AP_BTN_DONTHILIGHT
);
509 this->SetWidgetLoweredState(WID_AP_BTN_DONTHILIGHT
, !_settings_client
.gui
.station_show_coverage
);
510 this->SetWidgetLoweredState(WID_AP_BTN_DOHILIGHT
, _settings_client
.gui
.station_show_coverage
);
512 if (_settings_client
.sound
.click_beep
) SndPlayFx(SND_15_BEEP
);
513 this->UpdateSelectSize();
514 SetViewportCatchmentStation(nullptr, true);
517 case WID_AP_LAYOUT_DECREASE
:
518 _selected_airport_layout
--;
519 this->UpdateSelectSize();
523 case WID_AP_LAYOUT_INCREASE
:
524 _selected_airport_layout
++;
525 this->UpdateSelectSize();
532 * Select the first available airport.
533 * @param change_class If true, change the class if no airport in the current
534 * class is available.
536 void SelectFirstAvailableAirport(bool change_class
)
538 /* First try to select an airport in the selected class. */
539 AirportClass
*sel_apclass
= AirportClass::Get(_selected_airport_class
);
540 for (const AirportSpec
*as
: sel_apclass
->Specs()) {
541 if (as
->IsAvailable()) {
542 this->SelectOtherAirport(as
->index
);
547 /* If that fails, select the first available airport
548 * from the first class where airports are available. */
549 for (const auto &cls
: AirportClass::Classes()) {
550 for (const auto &as
: cls
.Specs()) {
551 if (as
->IsAvailable()) {
552 _selected_airport_class
= cls
.Index();
553 this->vscroll
->SetCount(cls
.GetSpecCount());
554 this->SelectOtherAirport(as
->index
);
560 /* If all airports are unavailable, select nothing. */
561 this->SelectOtherAirport(-1);
564 void OnDropdownSelect(WidgetID widget
, int index
) override
566 if (widget
== WID_AP_CLASS_DROPDOWN
) {
567 _selected_airport_class
= (AirportClassID
)index
;
568 this->vscroll
->SetCount(AirportClass::Get(_selected_airport_class
)->GetSpecCount());
569 this->SelectFirstAvailableAirport(false);
573 void OnRealtimeTick([[maybe_unused
]] uint delta_ms
) override
575 CheckRedrawStationCoverage(this);
578 IntervalTimer
<TimerGameCalendar
> yearly_interval
= {{TimerGameCalendar::YEAR
, TimerGameCalendar::Priority::NONE
}, [this](auto) {
579 this->InvalidateData();
583 static constexpr NWidgetPart _nested_build_airport_widgets
[] = {
584 NWidget(NWID_HORIZONTAL
),
585 NWidget(WWT_CLOSEBOX
, COLOUR_DARK_GREEN
),
586 NWidget(WWT_CAPTION
, COLOUR_DARK_GREEN
), SetDataTip(STR_STATION_BUILD_AIRPORT_CAPTION
, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS
),
588 NWidget(WWT_PANEL
, COLOUR_DARK_GREEN
),
589 NWidget(NWID_VERTICAL
), SetPIP(0, WidgetDimensions::unscaled
.vsep_normal
, 0), SetPadding(WidgetDimensions::unscaled
.picker
),
590 NWidget(NWID_VERTICAL
), SetPIP(0, WidgetDimensions::unscaled
.vsep_picker
, 0),
591 NWidget(WWT_LABEL
, COLOUR_DARK_GREEN
), SetDataTip(STR_STATION_BUILD_AIRPORT_CLASS_LABEL
, STR_NULL
), SetFill(1, 0),
592 NWidget(WWT_DROPDOWN
, COLOUR_GREY
, WID_AP_CLASS_DROPDOWN
), SetFill(1, 0), SetDataTip(STR_JUST_STRING
, STR_STATION_BUILD_AIRPORT_TOOLTIP
),
593 NWidget(WWT_EMPTY
, COLOUR_DARK_GREEN
, WID_AP_AIRPORT_SPRITE
), SetFill(1, 0),
594 NWidget(NWID_HORIZONTAL
),
595 NWidget(WWT_MATRIX
, COLOUR_GREY
, WID_AP_AIRPORT_LIST
), SetFill(1, 0), SetMatrixDataTip(1, 5, STR_STATION_BUILD_AIRPORT_TOOLTIP
), SetScrollbar(WID_AP_SCROLLBAR
),
596 NWidget(NWID_VSCROLLBAR
, COLOUR_GREY
, WID_AP_SCROLLBAR
),
598 NWidget(WWT_LABEL
, COLOUR_DARK_GREEN
), SetDataTip(STR_STATION_BUILD_ORIENTATION
, STR_NULL
), SetFill(1, 0),
599 NWidget(NWID_HORIZONTAL
),
600 NWidget(WWT_PUSHARROWBTN
, COLOUR_GREY
, WID_AP_LAYOUT_DECREASE
), SetMinimalSize(12, 0), SetDataTip(AWV_DECREASE
, STR_NULL
),
601 NWidget(WWT_LABEL
, COLOUR_GREY
, WID_AP_LAYOUT_NUM
), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_JUST_STRING1
, STR_NULL
),
602 NWidget(WWT_PUSHARROWBTN
, COLOUR_GREY
, WID_AP_LAYOUT_INCREASE
), SetMinimalSize(12, 0), SetDataTip(AWV_INCREASE
, STR_NULL
),
604 NWidget(WWT_EMPTY
, COLOUR_DARK_GREEN
, WID_AP_EXTRA_TEXT
), SetFill(1, 0), SetMinimalSize(150, 0),
605 NWidget(WWT_LABEL
, COLOUR_DARK_GREEN
), SetDataTip(STR_STATION_BUILD_COVERAGE_AREA_TITLE
, STR_NULL
), SetFill(1, 0),
606 NWidget(NWID_HORIZONTAL
), SetPIP(14, 0, 14), SetPIPRatio(1, 0, 1),
607 NWidget(NWID_HORIZONTAL
, NC_EQUALSIZE
),
608 NWidget(WWT_TEXTBTN
, COLOUR_GREY
, WID_AP_BTN_DONTHILIGHT
), SetMinimalSize(60, 12), SetFill(1, 0),
609 SetDataTip(STR_STATION_BUILD_COVERAGE_OFF
, STR_STATION_BUILD_COVERAGE_AREA_OFF_TOOLTIP
),
610 NWidget(WWT_TEXTBTN
, COLOUR_GREY
, WID_AP_BTN_DOHILIGHT
), SetMinimalSize(60, 12), SetFill(1, 0),
611 SetDataTip(STR_STATION_BUILD_COVERAGE_ON
, STR_STATION_BUILD_COVERAGE_AREA_ON_TOOLTIP
),
615 NWidget(WWT_EMPTY
, COLOUR_DARK_GREEN
, WID_AP_ACCEPTANCE
), SetResize(0, 1), SetFill(1, 0), SetMinimalTextLines(2, WidgetDimensions::unscaled
.vsep_normal
),
620 static WindowDesc
_build_airport_desc(
621 WDP_AUTO
, nullptr, 0, 0,
622 WC_BUILD_STATION
, WC_BUILD_TOOLBAR
,
624 _nested_build_airport_widgets
627 static void ShowBuildAirportPicker(Window
*parent
)
629 new BuildAirportWindow(_build_airport_desc
, parent
);
632 void InitializeAirportGui()
634 _selected_airport_class
= APC_BEGIN
;
635 _selected_airport_index
= -1;