OpenTTD
airport_gui.cpp
Go to the documentation of this file.
1 /* $Id: airport_gui.cpp 27163 2015-02-22 15:26:27Z frosch $ */
2 
3 /*
4  * This file is part of OpenTTD.
5  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
6  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
7  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
8  */
9 
12 #include "stdafx.h"
13 #include "window_gui.h"
14 #include "station_gui.h"
15 #include "terraform_gui.h"
16 #include "sound_func.h"
17 #include "window_func.h"
18 #include "strings_func.h"
19 #include "viewport_func.h"
20 #include "company_func.h"
21 #include "tilehighlight_func.h"
22 #include "company_base.h"
23 #include "station_type.h"
24 #include "newgrf_airport.h"
25 #include "newgrf_callbacks.h"
26 #include "widgets/dropdown_type.h"
27 #include "core/geometry_func.hpp"
28 #include "hotkeys.h"
29 #include "vehicle_func.h"
30 #include "gui.h"
31 
32 #include "widgets/airport_widget.h"
33 
34 #include "safeguards.h"
35 
36 
40 
41 static void ShowBuildAirportPicker(Window *parent);
42 
43 SpriteID GetCustomAirportSprite(const AirportSpec *as, byte layout);
44 
45 void CcBuildAirport(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2)
46 {
47  if (result.Failed()) return;
48 
49  if (_settings_client.sound.confirm) SndPlayTileFx(SND_1F_SPLAT_OTHER, tile);
51 }
52 
57 static void PlaceAirport(TileIndex tile)
58 {
59  if (_selected_airport_index == -1) return;
60  uint32 p2 = _ctrl_pressed;
61  SB(p2, 16, 16, INVALID_STATION); // no station to join
62 
63  uint32 p1 = AirportClass::Get(_selected_airport_class)->GetSpec(_selected_airport_index)->GetIndex();
64  p1 |= _selected_airport_layout << 8;
65  CommandContainer cmdcont = { tile, p1, p2, CMD_BUILD_AIRPORT | CMD_MSG(STR_ERROR_CAN_T_BUILD_AIRPORT_HERE), CcBuildAirport, "" };
66  ShowSelectStationIfNeeded(cmdcont, TileArea(tile, _thd.size.x / TILE_SIZE, _thd.size.y / TILE_SIZE));
67 }
68 
71  int last_user_action; // Last started user action.
72 
74  {
75  this->InitNested(window_number);
77  this->last_user_action = WIDGET_LIST_END;
78  }
79 
81  {
83  }
84 
90  virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
91  {
92  if (!gui_scope) return;
93 
95  }
96 
97  virtual void OnClick(Point pt, int widget, int click_count)
98  {
99  switch (widget) {
100  case WID_AT_AIRPORT:
101  if (HandlePlacePushButton(this, WID_AT_AIRPORT, SPR_CURSOR_AIRPORT, HT_RECT)) {
102  ShowBuildAirportPicker(this);
103  this->last_user_action = widget;
104  }
105  break;
106 
107  case WID_AT_DEMOLISH:
109  this->last_user_action = widget;
110  break;
111 
112  default: break;
113  }
114  }
115 
116 
117  virtual void OnPlaceObject(Point pt, TileIndex tile)
118  {
119  switch (this->last_user_action) {
120  case WID_AT_AIRPORT:
121  PlaceAirport(tile);
122  break;
123 
124  case WID_AT_DEMOLISH:
126  break;
127 
128  default: NOT_REACHED();
129  }
130  }
131 
132  virtual void OnPlaceDrag(ViewportPlaceMethod select_method, ViewportDragDropSelectionProcess select_proc, Point pt)
133  {
134  VpSelectTilesWithMethod(pt.x, pt.y, select_method);
135  }
136 
137  virtual void OnPlaceMouseUp(ViewportPlaceMethod select_method, ViewportDragDropSelectionProcess select_proc, Point pt, TileIndex start_tile, TileIndex end_tile)
138  {
139  if (pt.x != -1 && select_proc == DDSP_DEMOLISH_AREA) {
140  GUIPlaceProcDragXY(select_proc, start_tile, end_tile);
141  }
142  }
143 
144  virtual void OnPlaceObjectAbort()
145  {
146  this->RaiseButtons();
147 
150  }
151 
152  static HotkeyList hotkeys;
153 };
154 
161 {
162  if (_game_mode != GM_NORMAL || !CanBuildVehicleInfrastructure(VEH_AIRCRAFT)) return ES_NOT_HANDLED;
164  if (w == NULL) return ES_NOT_HANDLED;
165  return w->OnHotkey(hotkey);
166 }
167 
168 static Hotkey airtoolbar_hotkeys[] = {
169  Hotkey('1', "airport", WID_AT_AIRPORT),
170  Hotkey('2', "demolish", WID_AT_DEMOLISH),
171  HOTKEY_LIST_END
172 };
173 HotkeyList BuildAirToolbarWindow::hotkeys("airtoolbar", airtoolbar_hotkeys, AirportToolbarGlobalHotkeys);
174 
175 static const NWidgetPart _nested_air_toolbar_widgets[] = {
177  NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN),
178  NWidget(WWT_CAPTION, COLOUR_DARK_GREEN), SetDataTip(STR_TOOLBAR_AIRCRAFT_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
179  NWidget(WWT_STICKYBOX, COLOUR_DARK_GREEN),
180  EndContainer(),
182  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),
183  NWidget(WWT_PANEL, COLOUR_DARK_GREEN), SetMinimalSize(4, 22), SetFill(1, 1), EndContainer(),
184  NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_AT_DEMOLISH), SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_DYNAMITE, STR_TOOLTIP_DEMOLISH_BUILDINGS_ETC),
185  EndContainer(),
186 };
187 
188 static WindowDesc _air_toolbar_desc(
189  WDP_ALIGN_TOOLBAR, "toolbar_air", 0, 0,
192  _nested_air_toolbar_widgets, lengthof(_nested_air_toolbar_widgets),
193  &BuildAirToolbarWindow::hotkeys
194 );
195 
204 {
205  if (!Company::IsValidID(_local_company)) return NULL;
206 
208  return AllocateWindowDescFront<BuildAirToolbarWindow>(&_air_toolbar_desc, TRANSPORT_AIR);
209 }
210 
213  int line_height;
214  Scrollbar *vscroll;
215 
218  {
219  DropDownList *list = new DropDownList();
220 
221  for (uint i = 0; i < AirportClass::GetClassCount(); i++) {
222  *list->Append() = new DropDownListStringItem(AirportClass::Get((AirportClassID)i)->name, i, false);
223  }
224 
225  return list;
226  }
227 
228 public:
230  {
231  this->CreateNestedTree();
232 
233  this->vscroll = this->GetScrollbar(WID_AP_SCROLLBAR);
234  this->vscroll->SetCapacity(5);
235  this->vscroll->SetPosition(0);
236 
238 
241  this->OnInvalidateData();
242 
243  /* Ensure airport class is valid (changing NewGRFs). */
244  _selected_airport_class = Clamp(_selected_airport_class, APC_BEGIN, (AirportClassID)(AirportClass::GetClassCount() - 1));
246  this->vscroll->SetCount(ac->GetSpecCount());
247 
248  /* Ensure the airport index is valid for this class (changing NewGRFs). */
250 
251  /* Only when no valid airport was selected, we want to select the first airport. */
252  bool selectFirstAirport = true;
253  if (_selected_airport_index != -1) {
254  const AirportSpec *as = ac->GetSpec(_selected_airport_index);
255  if (as->IsAvailable()) {
256  /* Ensure the airport layout is valid. */
258  selectFirstAirport = false;
259  this->UpdateSelectSize();
260  }
261  }
262 
263  if (selectFirstAirport) this->SelectFirstAvailableAirport(true);
264  }
265 
266  virtual ~BuildAirportWindow()
267  {
269  }
270 
271  virtual void SetStringParameters(int widget) const
272  {
273  switch (widget) {
276  break;
277 
278  case WID_AP_LAYOUT_NUM:
279  SetDParam(0, STR_EMPTY);
280  if (_selected_airport_index != -1) {
283  if (string != STR_UNDEFINED) {
284  SetDParam(0, string);
285  } else if (as->num_table > 1) {
286  SetDParam(0, STR_STATION_BUILD_AIRPORT_LAYOUT_NAME);
288  }
289  }
290  break;
291 
292  default: break;
293  }
294  }
295 
296  virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
297  {
298  switch (widget) {
299  case WID_AP_CLASS_DROPDOWN: {
300  Dimension d = {0, 0};
301  for (uint i = 0; i < AirportClass::GetClassCount(); i++) {
303  d = maxdim(d, GetStringBoundingBox(STR_BLACK_STRING));
304  }
305  d.width += padding.width;
306  d.height += padding.height;
307  *size = maxdim(*size, d);
308  break;
309  }
310 
311  case WID_AP_AIRPORT_LIST: {
312  for (int i = 0; i < NUM_AIRPORTS; i++) {
313  const AirportSpec *as = AirportSpec::Get(i);
314  if (!as->enabled) continue;
315 
316  size->width = max(size->width, GetStringBoundingBox(as->name).width);
317  }
318 
319  this->line_height = FONT_HEIGHT_NORMAL + WD_MATRIX_TOP + WD_MATRIX_BOTTOM;
320  size->height = 5 * this->line_height;
321  break;
322  }
323 
325  for (int i = 0; i < NUM_AIRPORTS; i++) {
326  const AirportSpec *as = AirportSpec::Get(i);
327  if (!as->enabled) continue;
328  for (byte layout = 0; layout < as->num_table; layout++) {
329  SpriteID sprite = GetCustomAirportSprite(as, layout);
330  if (sprite != 0) {
331  Dimension d = GetSpriteSize(sprite);
334  *size = maxdim(d, *size);
335  }
336  }
337  }
338  break;
339 
340  case WID_AP_EXTRA_TEXT:
341  for (int i = NEW_AIRPORT_OFFSET; i < NUM_AIRPORTS; i++) {
342  const AirportSpec *as = AirportSpec::Get(i);
343  if (!as->enabled) continue;
344  for (byte layout = 0; layout < as->num_table; layout++) {
346  if (string == STR_UNDEFINED) continue;
347 
348  /* STR_BLACK_STRING is used to start the string with {BLACK} */
349  SetDParam(0, string);
350  Dimension d = GetStringMultiLineBoundingBox(STR_BLACK_STRING, *size);
351  *size = maxdim(d, *size);
352  }
353  }
354  break;
355 
356  default: break;
357  }
358  }
359 
360  virtual void DrawWidget(const Rect &r, int widget) const
361  {
362  switch (widget) {
363  case WID_AP_AIRPORT_LIST: {
364  int y = r.top;
366  for (uint i = this->vscroll->GetPosition(); this->vscroll->IsVisible(i) && i < apclass->GetSpecCount(); i++) {
367  const AirportSpec *as = apclass->GetSpec(i);
368  if (!as->IsAvailable()) {
369  GfxFillRect(r.left + 1, y + 1, r.right - 1, y + this->line_height - 2, PC_BLACK, FILLRECT_CHECKER);
370  }
371  DrawString(r.left + WD_MATRIX_LEFT, r.right - WD_MATRIX_RIGHT, y + WD_MATRIX_TOP, as->name, ((int)i == _selected_airport_index) ? TC_WHITE : TC_BLACK);
372  y += this->line_height;
373  }
374  break;
375  }
376 
378  if (this->preview_sprite != 0) {
380  DrawSprite(this->preview_sprite, COMPANY_SPRITE_COLOUR(_local_company), (r.left + r.right - d.width) / 2, (r.top + r.bottom - d.height) / 2);
381  }
382  break;
383 
384  case WID_AP_EXTRA_TEXT:
385  if (_selected_airport_index != -1) {
388  if (string != STR_UNDEFINED) {
389  SetDParam(0, string);
390  DrawStringMultiLine(r.left, r.right, r.top, r.bottom, STR_BLACK_STRING);
391  }
392  }
393  break;
394  }
395  }
396 
397  virtual void OnPaint()
398  {
399  this->DrawWidgets();
400 
401  uint16 top = this->GetWidget<NWidgetBase>(WID_AP_BTN_DOHILIGHT)->pos_y + this->GetWidget<NWidgetBase>(WID_AP_BTN_DOHILIGHT)->current_y + WD_PAR_VSEP_NORMAL;
402  NWidgetBase *panel_nwi = this->GetWidget<NWidgetBase>(WID_AP_BOTTOMPANEL);
403 
404  int right = panel_nwi->pos_x + panel_nwi->current_x;
405  int bottom = panel_nwi->pos_y + panel_nwi->current_y;
406 
407  if (_selected_airport_index != -1) {
410 
411  /* only show the station (airport) noise, if the noise option is activated */
413  /* show the noise of the selected airport */
414  SetDParam(0, as->noise_level);
415  DrawString(panel_nwi->pos_x + WD_FRAMERECT_LEFT, right - WD_FRAMERECT_RIGHT, top, STR_STATION_BUILD_NOISE);
417  }
418 
419  /* strings such as 'Size' and 'Coverage Area' */
420  top = DrawStationCoverageAreaText(panel_nwi->pos_x + WD_FRAMERECT_LEFT, right - WD_FRAMERECT_RIGHT, top, SCT_ALL, rad, false) + WD_PAR_VSEP_NORMAL;
422  }
423 
424  /* Resize background if the window is too small.
425  * Never make the window smaller to avoid oscillating if the size change affects the acceptance.
426  * (This is the case, if making the window bigger moves the mouse into the window.) */
427  if (top > bottom) {
428  ResizeWindow(this, 0, top - bottom, false);
429  }
430  }
431 
432  void SelectOtherAirport(int airport_index)
433  {
434  _selected_airport_index = airport_index;
436 
437  this->UpdateSelectSize();
438  this->SetDirty();
439  }
440 
441  void UpdateSelectSize()
442  {
443  if (_selected_airport_index == -1) {
444  SetTileSelectSize(1, 1);
447  } else {
449  int w = as->size_x;
450  int h = as->size_y;
452  if (rotation == DIR_E || rotation == DIR_W) Swap(w, h);
453  SetTileSelectSize(w, h);
454 
455  this->preview_sprite = GetCustomAirportSprite(as, _selected_airport_layout);
456 
459 
461  if (_settings_client.gui.station_show_coverage) SetTileSelectBigSize(-rad, -rad, 2 * rad, 2 * rad);
462  }
463  }
464 
465  virtual void OnClick(Point pt, int widget, int click_count)
466  {
467  switch (widget) {
470  break;
471 
472  case WID_AP_AIRPORT_LIST: {
473  int num_clicked = this->vscroll->GetPosition() + (pt.y - this->nested_array[widget]->pos_y) / this->line_height;
474  if (num_clicked >= this->vscroll->GetCount()) break;
475  const AirportSpec *as = AirportClass::Get(_selected_airport_class)->GetSpec(num_clicked);
476  if (as->IsAvailable()) this->SelectOtherAirport(num_clicked);
477  break;
478  }
479 
484  this->SetDirty();
485  if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
486  this->UpdateSelectSize();
487  break;
488 
491  this->UpdateSelectSize();
492  this->SetDirty();
493  break;
494 
497  this->UpdateSelectSize();
498  this->SetDirty();
499  break;
500  }
501  }
502 
508  void SelectFirstAvailableAirport(bool change_class)
509  {
510  /* First try to select an airport in the selected class. */
512  for (uint i = 0; i < sel_apclass->GetSpecCount(); i++) {
513  const AirportSpec *as = sel_apclass->GetSpec(i);
514  if (as->IsAvailable()) {
515  this->SelectOtherAirport(i);
516  return;
517  }
518  }
519  if (change_class) {
520  /* If that fails, select the first available airport
521  * from a random class. */
522  for (AirportClassID j = APC_BEGIN; j < APC_MAX; j++) {
523  AirportClass *apclass = AirportClass::Get(j);
524  for (uint i = 0; i < apclass->GetSpecCount(); i++) {
525  const AirportSpec *as = apclass->GetSpec(i);
526  if (as->IsAvailable()) {
528  this->SelectOtherAirport(i);
529  return;
530  }
531  }
532  }
533  }
534  /* If all airports are unavailable, select nothing. */
535  this->SelectOtherAirport(-1);
536  }
537 
538  virtual void OnDropdownSelect(int widget, int index)
539  {
540  assert(widget == WID_AP_CLASS_DROPDOWN);
542  this->vscroll->SetCount(AirportClass::Get(_selected_airport_class)->GetSpecCount());
543  this->SelectFirstAvailableAirport(false);
544  }
545 
546  virtual void OnTick()
547  {
549  }
550 };
551 
552 static const NWidgetPart _nested_build_airport_widgets[] = {
554  NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN),
555  NWidget(WWT_CAPTION, COLOUR_DARK_GREEN), SetDataTip(STR_STATION_BUILD_AIRPORT_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
556  EndContainer(),
557  NWidget(WWT_PANEL, COLOUR_DARK_GREEN), SetFill(1, 0), SetPIP(2, 0, 2),
558  NWidget(WWT_LABEL, COLOUR_DARK_GREEN), SetDataTip(STR_STATION_BUILD_AIRPORT_CLASS_LABEL, STR_NULL), SetFill(1, 0),
559  NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_AP_CLASS_DROPDOWN), SetFill(1, 0), SetDataTip(STR_BLACK_STRING, STR_STATION_BUILD_AIRPORT_TOOLTIP),
560  NWidget(WWT_EMPTY, COLOUR_DARK_GREEN, WID_AP_AIRPORT_SPRITE), SetFill(1, 0),
562  NWidget(WWT_MATRIX, COLOUR_GREY, WID_AP_AIRPORT_LIST), SetFill(1, 0), SetMatrixDataTip(1, 5, STR_STATION_BUILD_AIRPORT_TOOLTIP), SetScrollbar(WID_AP_SCROLLBAR),
564  EndContainer(),
567  NWidget(WWT_LABEL, COLOUR_GREY, WID_AP_LAYOUT_NUM), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_BLACK_STRING, STR_NULL),
569  EndContainer(),
570  NWidget(WWT_EMPTY, COLOUR_DARK_GREEN, WID_AP_EXTRA_TEXT), SetFill(1, 0), SetMinimalSize(150, 0),
571  EndContainer(),
572  /* Bottom panel. */
573  NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_AP_BOTTOMPANEL), SetPIP(2, 2, 2),
574  NWidget(WWT_LABEL, COLOUR_DARK_GREEN), SetDataTip(STR_STATION_BUILD_COVERAGE_AREA_TITLE, STR_NULL), SetFill(1, 0),
576  NWidget(NWID_SPACER), SetMinimalSize(14, 0), SetFill(1, 0),
578  NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_AP_BTN_DONTHILIGHT), SetMinimalSize(60, 12), SetFill(1, 0),
579  SetDataTip(STR_STATION_BUILD_COVERAGE_OFF, STR_STATION_BUILD_COVERAGE_AREA_OFF_TOOLTIP),
580  NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_AP_BTN_DOHILIGHT), SetMinimalSize(60, 12), SetFill(1, 0),
581  SetDataTip(STR_STATION_BUILD_COVERAGE_ON, STR_STATION_BUILD_COVERAGE_AREA_ON_TOOLTIP),
582  EndContainer(),
583  NWidget(NWID_SPACER), SetMinimalSize(14, 0), SetFill(1, 0),
584  EndContainer(),
585  NWidget(NWID_SPACER), SetMinimalSize(0, 10), SetResize(0, 1), SetFill(1, 0),
586  EndContainer(),
587 };
588 
589 static WindowDesc _build_airport_desc(
590  WDP_AUTO, "build_station_air", 0, 0,
593  _nested_build_airport_widgets, lengthof(_nested_build_airport_widgets)
594 );
595 
596 static void ShowBuildAirportPicker(Window *parent)
597 {
598  new BuildAirportWindow(&_build_airport_desc, parent);
599 }
600 
601 void InitializeAirportGui()
602 {
605 }