/* * This file is part of OpenTTD. * 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. * 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. * 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 . */ /** @file airport_gui.cpp The GUI for airports. */ #include "stdafx.h" #include "economy_func.h" #include "window_gui.h" #include "station_gui.h" #include "terraform_gui.h" #include "sound_func.h" #include "window_func.h" #include "strings_func.h" #include "viewport_func.h" #include "company_func.h" #include "tilehighlight_func.h" #include "company_base.h" #include "station_type.h" #include "newgrf_airport.h" #include "newgrf_callbacks.h" #include "dropdown_type.h" #include "dropdown_func.h" #include "core/geometry_func.hpp" #include "hotkeys.h" #include "vehicle_func.h" #include "gui.h" #include "command_func.h" #include "airport_cmd.h" #include "station_cmd.h" #include "zoom_func.h" #include "timer/timer.h" #include "timer/timer_game_calendar.h" #include "widgets/airport_widget.h" #include "safeguards.h" static AirportClassID _selected_airport_class; ///< the currently visible airport class static int _selected_airport_index; ///< the index of the selected airport in the current class or -1 static uint8_t _selected_airport_layout; ///< selected airport layout number. static void ShowBuildAirportPicker(Window *parent); SpriteID GetCustomAirportSprite(const AirportSpec *as, uint8_t layout); void CcBuildAirport(Commands, const CommandCost &result, TileIndex tile) { if (result.Failed()) return; if (_settings_client.sound.confirm) SndPlayTileFx(SND_1F_CONSTRUCTION_OTHER, tile); if (!_settings_client.gui.persistent_buildingtools) ResetObjectToPlace(); } /** * Place an airport. * @param tile Position to put the new airport. */ static void PlaceAirport(TileIndex tile) { if (_selected_airport_index == -1) return; uint8_t airport_type = AirportClass::Get(_selected_airport_class)->GetSpec(_selected_airport_index)->GetIndex(); uint8_t layout = _selected_airport_layout; bool adjacent = _ctrl_pressed; auto proc = [=](bool test, StationID to_join) -> bool { if (test) { return Command::Do(CommandFlagsToDCFlags(GetCommandFlags()), tile, airport_type, layout, INVALID_STATION, adjacent).Succeeded(); } else { return Command::Post(STR_ERROR_CAN_T_BUILD_AIRPORT_HERE, CcBuildAirport, tile, airport_type, layout, to_join, adjacent); } }; ShowSelectStationIfNeeded(TileArea(tile, _thd.size.x / TILE_SIZE, _thd.size.y / TILE_SIZE), proc); } /** Airport build toolbar window handler. */ struct BuildAirToolbarWindow : Window { int last_user_action; // Last started user action. BuildAirToolbarWindow(WindowDesc *desc, WindowNumber window_number) : Window(desc) { this->InitNested(window_number); this->OnInvalidateData(); if (_settings_client.gui.link_terraform_toolbar) ShowTerraformToolbar(this); this->last_user_action = INVALID_WID_AT; } void Close([[maybe_unused]] int data = 0) override { if (this->IsWidgetLowered(WID_AT_AIRPORT)) SetViewportCatchmentStation(nullptr, true); if (_settings_client.gui.link_terraform_toolbar) CloseWindowById(WC_SCEN_LAND_GEN, 0, false); this->Window::Close(); } /** * Some data on this window has become invalid. * @param data Information about the changed data. * @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. */ void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override { if (!gui_scope) return; bool can_build = CanBuildVehicleInfrastructure(VEH_AIRCRAFT); this->SetWidgetDisabledState(WID_AT_AIRPORT, !can_build); if (!can_build) { CloseWindowById(WC_BUILD_STATION, TRANSPORT_AIR); /* Show in the tooltip why this button is disabled. */ this->GetWidget(WID_AT_AIRPORT)->SetToolTip(STR_TOOLBAR_DISABLED_NO_VEHICLE_AVAILABLE); } else { this->GetWidget(WID_AT_AIRPORT)->SetToolTip(STR_TOOLBAR_AIRCRAFT_BUILD_AIRPORT_TOOLTIP); } } void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override { switch (widget) { case WID_AT_AIRPORT: if (HandlePlacePushButton(this, WID_AT_AIRPORT, SPR_CURSOR_AIRPORT, HT_RECT)) { ShowBuildAirportPicker(this); this->last_user_action = widget; } break; case WID_AT_DEMOLISH: HandlePlacePushButton(this, WID_AT_DEMOLISH, ANIMCURSOR_DEMOLISH, HT_RECT | HT_DIAGONAL); this->last_user_action = widget; break; default: break; } } void OnPlaceObject([[maybe_unused]] Point pt, TileIndex tile) override { switch (this->last_user_action) { case WID_AT_AIRPORT: PlaceAirport(tile); break; case WID_AT_DEMOLISH: PlaceProc_DemolishArea(tile); break; default: NOT_REACHED(); } } void OnPlaceDrag(ViewportPlaceMethod select_method, [[maybe_unused]] ViewportDragDropSelectionProcess select_proc, [[maybe_unused]] Point pt) override { VpSelectTilesWithMethod(pt.x, pt.y, select_method); } void OnPlaceMouseUp([[maybe_unused]] ViewportPlaceMethod select_method, ViewportDragDropSelectionProcess select_proc, [[maybe_unused]] Point pt, TileIndex start_tile, TileIndex end_tile) override { if (pt.x != -1 && select_proc == DDSP_DEMOLISH_AREA) { GUIPlaceProcDragXY(select_proc, start_tile, end_tile); } } void OnPlaceObjectAbort() override { if (this->IsWidgetLowered(WID_AT_AIRPORT)) SetViewportCatchmentStation(nullptr, true); this->RaiseButtons(); CloseWindowById(WC_BUILD_STATION, TRANSPORT_AIR); CloseWindowById(WC_SELECT_STATION, 0); } /** * Handler for global hotkeys of the BuildAirToolbarWindow. * @param hotkey Hotkey * @return ES_HANDLED if hotkey was accepted. */ static EventState AirportToolbarGlobalHotkeys(int hotkey) { if (_game_mode != GM_NORMAL) return ES_NOT_HANDLED; Window *w = ShowBuildAirToolbar(); if (w == nullptr) return ES_NOT_HANDLED; return w->OnHotkey(hotkey); } static inline HotkeyList hotkeys{"airtoolbar", { Hotkey('1', "airport", WID_AT_AIRPORT), Hotkey('2', "demolish", WID_AT_DEMOLISH), }, AirportToolbarGlobalHotkeys}; }; static constexpr NWidgetPart _nested_air_toolbar_widgets[] = { NWidget(NWID_HORIZONTAL), NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN), NWidget(WWT_CAPTION, COLOUR_DARK_GREEN), SetDataTip(STR_TOOLBAR_AIRCRAFT_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), NWidget(WWT_STICKYBOX, COLOUR_DARK_GREEN), EndContainer(), NWidget(NWID_HORIZONTAL), 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), NWidget(WWT_PANEL, COLOUR_DARK_GREEN), SetMinimalSize(4, 22), SetFill(1, 1), EndContainer(), NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_AT_DEMOLISH), SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_DYNAMITE, STR_TOOLTIP_DEMOLISH_BUILDINGS_ETC), EndContainer(), }; static WindowDesc _air_toolbar_desc( WDP_ALIGN_TOOLBAR, "toolbar_air", 0, 0, WC_BUILD_TOOLBAR, WC_NONE, WDF_CONSTRUCTION, std::begin(_nested_air_toolbar_widgets), std::end(_nested_air_toolbar_widgets), &BuildAirToolbarWindow::hotkeys ); /** * Open the build airport toolbar window * * If the terraform toolbar is linked to the toolbar, that window is also opened. * * @return newly opened airport toolbar, or nullptr if the toolbar could not be opened. */ Window *ShowBuildAirToolbar() { if (!Company::IsValidID(_local_company)) return nullptr; CloseWindowByClass(WC_BUILD_TOOLBAR); return AllocateWindowDescFront(&_air_toolbar_desc, TRANSPORT_AIR); } class BuildAirportWindow : public PickerWindowBase { SpriteID preview_sprite; ///< Cached airport preview sprite. int line_height; Scrollbar *vscroll; /** Build a dropdown list of available airport classes */ static DropDownList BuildAirportClassDropDown() { DropDownList list; for (const auto &cls : AirportClass::Classes()) { list.push_back(MakeDropDownListStringItem(cls.name, cls.Index())); } return list; } public: BuildAirportWindow(WindowDesc *desc, Window *parent) : PickerWindowBase(desc, parent) { this->CreateNestedTree(); this->vscroll = this->GetScrollbar(WID_AP_SCROLLBAR); this->vscroll->SetCapacity(5); this->vscroll->SetPosition(0); this->FinishInitNested(TRANSPORT_AIR); this->SetWidgetLoweredState(WID_AP_BTN_DONTHILIGHT, !_settings_client.gui.station_show_coverage); this->SetWidgetLoweredState(WID_AP_BTN_DOHILIGHT, _settings_client.gui.station_show_coverage); this->OnInvalidateData(); /* Ensure airport class is valid (changing NewGRFs). */ _selected_airport_class = Clamp(_selected_airport_class, APC_BEGIN, (AirportClassID)(AirportClass::GetClassCount() - 1)); const AirportClass *ac = AirportClass::Get(_selected_airport_class); this->vscroll->SetCount(ac->GetSpecCount()); /* Ensure the airport index is valid for this class (changing NewGRFs). */ _selected_airport_index = Clamp(_selected_airport_index, -1, ac->GetSpecCount() - 1); /* Only when no valid airport was selected, we want to select the first airport. */ bool selectFirstAirport = true; if (_selected_airport_index != -1) { const AirportSpec *as = ac->GetSpec(_selected_airport_index); if (as->IsAvailable()) { /* Ensure the airport layout is valid. */ _selected_airport_layout = Clamp(_selected_airport_layout, 0, static_cast(as->layouts.size() - 1)); selectFirstAirport = false; this->UpdateSelectSize(); } } if (selectFirstAirport) this->SelectFirstAvailableAirport(true); } void Close([[maybe_unused]] int data = 0) override { CloseWindowById(WC_SELECT_STATION, 0); this->PickerWindowBase::Close(); } void SetStringParameters(WidgetID widget) const override { switch (widget) { case WID_AP_CLASS_DROPDOWN: SetDParam(0, AirportClass::Get(_selected_airport_class)->name); break; case WID_AP_LAYOUT_NUM: SetDParam(0, STR_EMPTY); if (_selected_airport_index != -1) { const AirportSpec *as = AirportClass::Get(_selected_airport_class)->GetSpec(_selected_airport_index); StringID string = GetAirportTextCallback(as, _selected_airport_layout, CBID_AIRPORT_LAYOUT_NAME); if (string != STR_UNDEFINED) { SetDParam(0, string); } else if (as->layouts.size() > 1) { SetDParam(0, STR_STATION_BUILD_AIRPORT_LAYOUT_NAME); SetDParam(1, _selected_airport_layout + 1); } } break; default: break; } } void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override { switch (widget) { case WID_AP_CLASS_DROPDOWN: { Dimension d = {0, 0}; for (const auto &cls : AirportClass::Classes()) { d = maxdim(d, GetStringBoundingBox(cls.name)); } d.width += padding.width; d.height += padding.height; size = maxdim(size, d); break; } case WID_AP_AIRPORT_LIST: { for (int i = 0; i < NUM_AIRPORTS; i++) { const AirportSpec *as = AirportSpec::Get(i); if (!as->enabled) continue; size.width = std::max(size.width, GetStringBoundingBox(as->name).width + padding.width); } this->line_height = GetCharacterHeight(FS_NORMAL) + padding.height; size.height = 5 * this->line_height; break; } case WID_AP_AIRPORT_SPRITE: for (int i = 0; i < NUM_AIRPORTS; i++) { const AirportSpec *as = AirportSpec::Get(i); if (!as->enabled) continue; for (uint8_t layout = 0; layout < static_cast(as->layouts.size()); layout++) { SpriteID sprite = GetCustomAirportSprite(as, layout); if (sprite != 0) { Dimension d = GetSpriteSize(sprite); d.width += WidgetDimensions::scaled.framerect.Horizontal(); d.height += WidgetDimensions::scaled.framerect.Vertical(); size = maxdim(d, size); } } } break; case WID_AP_EXTRA_TEXT: for (int i = NEW_AIRPORT_OFFSET; i < NUM_AIRPORTS; i++) { const AirportSpec *as = AirportSpec::Get(i); if (!as->enabled) continue; for (uint8_t layout = 0; layout < static_cast(as->layouts.size()); layout++) { StringID string = GetAirportTextCallback(as, layout, CBID_AIRPORT_ADDITIONAL_TEXT); if (string == STR_UNDEFINED) continue; Dimension d = GetStringMultiLineBoundingBox(string, size); size = maxdim(d, size); } } break; default: break; } } void DrawWidget(const Rect &r, WidgetID widget) const override { switch (widget) { case WID_AP_AIRPORT_LIST: { Rect row = r.WithHeight(this->line_height).Shrink(WidgetDimensions::scaled.bevel); Rect text = r.WithHeight(this->line_height).Shrink(WidgetDimensions::scaled.matrix); AirportClass *apclass = AirportClass::Get(_selected_airport_class); for (uint i = this->vscroll->GetPosition(); this->vscroll->IsVisible(i) && i < apclass->GetSpecCount(); i++) { const AirportSpec *as = apclass->GetSpec(i); if (!as->IsAvailable()) { GfxFillRect(row, PC_BLACK, FILLRECT_CHECKER); } DrawString(text, as->name, ((int)i == _selected_airport_index) ? TC_WHITE : TC_BLACK); row = row.Translate(0, this->line_height); text = text.Translate(0, this->line_height); } break; } case WID_AP_AIRPORT_SPRITE: if (this->preview_sprite != 0) { Dimension d = GetSpriteSize(this->preview_sprite); DrawSprite(this->preview_sprite, COMPANY_SPRITE_COLOUR(_local_company), CenterBounds(r.left, r.right, d.width), CenterBounds(r.top, r.bottom, d.height)); } break; case WID_AP_EXTRA_TEXT: if (_selected_airport_index != -1) { const AirportSpec *as = AirportClass::Get(_selected_airport_class)->GetSpec(_selected_airport_index); StringID string = GetAirportTextCallback(as, _selected_airport_layout, CBID_AIRPORT_ADDITIONAL_TEXT); if (string != STR_UNDEFINED) { DrawStringMultiLine(r.left, r.right, r.top, r.bottom, string, TC_BLACK); } } break; } } void OnPaint() override { this->DrawWidgets(); Rect r = this->GetWidget(WID_AP_ACCEPTANCE)->GetCurrentRect(); int top = r.top; if (_selected_airport_index != -1) { const AirportSpec *as = AirportClass::Get(_selected_airport_class)->GetSpec(_selected_airport_index); int rad = _settings_game.station.modified_catchment ? as->catchment : (uint)CA_UNMODIFIED; /* only show the station (airport) noise, if the noise option is activated */ if (_settings_game.economy.station_noise_level) { /* show the noise of the selected airport */ SetDParam(0, as->noise_level); DrawString(r.left, r.right, top, STR_STATION_BUILD_NOISE); top += GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.vsep_normal; } if (_settings_game.economy.infrastructure_maintenance) { Money monthly = _price[PR_INFRASTRUCTURE_AIRPORT] * as->maintenance_cost >> 3; SetDParam(0, monthly * 12); DrawString(r.left, r.right, top, TimerGameEconomy::UsingWallclockUnits() ? STR_STATION_BUILD_INFRASTRUCTURE_COST_PERIOD : STR_STATION_BUILD_INFRASTRUCTURE_COST_YEAR); top += GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.vsep_normal; } /* strings such as 'Size' and 'Coverage Area' */ top = DrawStationCoverageAreaText(r.left, r.right, top, SCT_ALL, rad, false) + WidgetDimensions::scaled.vsep_normal; top = DrawStationCoverageAreaText(r.left, r.right, top, SCT_ALL, rad, true); } /* Resize background if the window is too small. * Never make the window smaller to avoid oscillating if the size change affects the acceptance. * (This is the case, if making the window bigger moves the mouse into the window.) */ if (top > r.bottom) { ResizeWindow(this, 0, top - r.bottom, false); } } void SelectOtherAirport(int airport_index) { _selected_airport_index = airport_index; _selected_airport_layout = 0; this->UpdateSelectSize(); this->SetDirty(); } void UpdateSelectSize() { if (_selected_airport_index == -1) { SetTileSelectSize(1, 1); this->DisableWidget(WID_AP_LAYOUT_DECREASE); this->DisableWidget(WID_AP_LAYOUT_INCREASE); } else { const AirportSpec *as = AirportClass::Get(_selected_airport_class)->GetSpec(_selected_airport_index); int w = as->size_x; int h = as->size_y; Direction rotation = as->layouts[_selected_airport_layout].rotation; if (rotation == DIR_E || rotation == DIR_W) Swap(w, h); SetTileSelectSize(w, h); this->preview_sprite = GetCustomAirportSprite(as, _selected_airport_layout); this->SetWidgetDisabledState(WID_AP_LAYOUT_DECREASE, _selected_airport_layout == 0); this->SetWidgetDisabledState(WID_AP_LAYOUT_INCREASE, _selected_airport_layout + 1U >= as->layouts.size()); int rad = _settings_game.station.modified_catchment ? as->catchment : (uint)CA_UNMODIFIED; if (_settings_client.gui.station_show_coverage) SetTileSelectBigSize(-rad, -rad, 2 * rad, 2 * rad); } } void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override { switch (widget) { case WID_AP_CLASS_DROPDOWN: ShowDropDownList(this, BuildAirportClassDropDown(), _selected_airport_class, WID_AP_CLASS_DROPDOWN); break; case WID_AP_AIRPORT_LIST: { int32_t num_clicked = this->vscroll->GetScrolledRowFromWidget(pt.y, this, widget, 0, this->line_height); if (num_clicked == INT32_MAX) break; const AirportSpec *as = AirportClass::Get(_selected_airport_class)->GetSpec(num_clicked); if (as->IsAvailable()) this->SelectOtherAirport(num_clicked); break; } case WID_AP_BTN_DONTHILIGHT: case WID_AP_BTN_DOHILIGHT: _settings_client.gui.station_show_coverage = (widget != WID_AP_BTN_DONTHILIGHT); this->SetWidgetLoweredState(WID_AP_BTN_DONTHILIGHT, !_settings_client.gui.station_show_coverage); this->SetWidgetLoweredState(WID_AP_BTN_DOHILIGHT, _settings_client.gui.station_show_coverage); this->SetDirty(); if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP); this->UpdateSelectSize(); SetViewportCatchmentStation(nullptr, true); break; case WID_AP_LAYOUT_DECREASE: _selected_airport_layout--; this->UpdateSelectSize(); this->SetDirty(); break; case WID_AP_LAYOUT_INCREASE: _selected_airport_layout++; this->UpdateSelectSize(); this->SetDirty(); break; } } /** * Select the first available airport. * @param change_class If true, change the class if no airport in the current * class is available. */ void SelectFirstAvailableAirport(bool change_class) { /* First try to select an airport in the selected class. */ AirportClass *sel_apclass = AirportClass::Get(_selected_airport_class); for (uint i = 0; i < sel_apclass->GetSpecCount(); i++) { const AirportSpec *as = sel_apclass->GetSpec(i); if (as->IsAvailable()) { this->SelectOtherAirport(i); return; } } if (change_class) { /* If that fails, select the first available airport * from the first class where airports are available. */ for (const auto &cls : AirportClass::Classes()) { for (const auto &as : cls.Specs()) { if (as->IsAvailable()) { _selected_airport_class = cls.Index(); this->vscroll->SetCount(cls.GetSpecCount()); this->SelectOtherAirport(as->GetIndex()); return; } } } } /* If all airports are unavailable, select nothing. */ this->SelectOtherAirport(-1); } void OnDropdownSelect(WidgetID widget, int index) override { if (widget == WID_AP_CLASS_DROPDOWN) { _selected_airport_class = (AirportClassID)index; this->vscroll->SetCount(AirportClass::Get(_selected_airport_class)->GetSpecCount()); this->SelectFirstAvailableAirport(false); } } void OnRealtimeTick([[maybe_unused]] uint delta_ms) override { CheckRedrawStationCoverage(this); } IntervalTimer yearly_interval = {{TimerGameCalendar::YEAR, TimerGameCalendar::Priority::NONE}, [this](auto) { this->InvalidateData(); }}; }; static constexpr NWidgetPart _nested_build_airport_widgets[] = { NWidget(NWID_HORIZONTAL), NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN), NWidget(WWT_CAPTION, COLOUR_DARK_GREEN), SetDataTip(STR_STATION_BUILD_AIRPORT_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), EndContainer(), NWidget(WWT_PANEL, COLOUR_DARK_GREEN), NWidget(NWID_VERTICAL), SetPIP(0, WidgetDimensions::unscaled.vsep_normal, 0), SetPadding(WidgetDimensions::unscaled.picker), NWidget(NWID_VERTICAL), SetPIP(0, WidgetDimensions::unscaled.vsep_picker, 0), NWidget(WWT_LABEL, COLOUR_DARK_GREEN), SetDataTip(STR_STATION_BUILD_AIRPORT_CLASS_LABEL, STR_NULL), SetFill(1, 0), NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_AP_CLASS_DROPDOWN), SetFill(1, 0), SetDataTip(STR_JUST_STRING, STR_STATION_BUILD_AIRPORT_TOOLTIP), NWidget(WWT_EMPTY, COLOUR_DARK_GREEN, WID_AP_AIRPORT_SPRITE), SetFill(1, 0), NWidget(NWID_HORIZONTAL), NWidget(WWT_MATRIX, COLOUR_GREY, WID_AP_AIRPORT_LIST), SetFill(1, 0), SetMatrixDataTip(1, 5, STR_STATION_BUILD_AIRPORT_TOOLTIP), SetScrollbar(WID_AP_SCROLLBAR), NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_AP_SCROLLBAR), EndContainer(), NWidget(WWT_LABEL, COLOUR_DARK_GREEN), SetDataTip(STR_STATION_BUILD_ORIENTATION, STR_NULL), SetFill(1, 0), NWidget(NWID_HORIZONTAL), NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_AP_LAYOUT_DECREASE), SetMinimalSize(12, 0), SetDataTip(AWV_DECREASE, STR_NULL), NWidget(WWT_LABEL, COLOUR_GREY, WID_AP_LAYOUT_NUM), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_JUST_STRING1, STR_NULL), NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_AP_LAYOUT_INCREASE), SetMinimalSize(12, 0), SetDataTip(AWV_INCREASE, STR_NULL), EndContainer(), NWidget(WWT_EMPTY, COLOUR_DARK_GREEN, WID_AP_EXTRA_TEXT), SetFill(1, 0), SetMinimalSize(150, 0), NWidget(WWT_LABEL, COLOUR_DARK_GREEN), SetDataTip(STR_STATION_BUILD_COVERAGE_AREA_TITLE, STR_NULL), SetFill(1, 0), NWidget(NWID_HORIZONTAL), SetPIP(14, 0, 14), SetPIPRatio(1, 0, 1), NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_AP_BTN_DONTHILIGHT), SetMinimalSize(60, 12), SetFill(1, 0), SetDataTip(STR_STATION_BUILD_COVERAGE_OFF, STR_STATION_BUILD_COVERAGE_AREA_OFF_TOOLTIP), NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_AP_BTN_DOHILIGHT), SetMinimalSize(60, 12), SetFill(1, 0), SetDataTip(STR_STATION_BUILD_COVERAGE_ON, STR_STATION_BUILD_COVERAGE_AREA_ON_TOOLTIP), EndContainer(), EndContainer(), EndContainer(), NWidget(WWT_EMPTY, COLOUR_DARK_GREEN, WID_AP_ACCEPTANCE), SetResize(0, 1), SetFill(1, 0), SetMinimalTextLines(2, WidgetDimensions::unscaled.vsep_normal), EndContainer(), EndContainer(), }; static WindowDesc _build_airport_desc( WDP_AUTO, nullptr, 0, 0, WC_BUILD_STATION, WC_BUILD_TOOLBAR, WDF_CONSTRUCTION, std::begin(_nested_build_airport_widgets), std::end(_nested_build_airport_widgets) ); static void ShowBuildAirportPicker(Window *parent) { new BuildAirportWindow(&_build_airport_desc, parent); } void InitializeAirportGui() { _selected_airport_class = APC_BEGIN; _selected_airport_index = -1; }