OpenTTD
airport_gui.cpp
Go to the documentation of this file.
1 /* $Id: airport_gui.cpp 26996 2014-10-12 10:48:18Z peter1138 $ */
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);
50  if (!_settings_client.gui.persistent_buildingtools) ResetObjectToPlace();
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 
85  virtual void OnClick(Point pt, int widget, int click_count)
86  {
87  switch (widget) {
88  case WID_AT_AIRPORT:
89  if (HandlePlacePushButton(this, WID_AT_AIRPORT, SPR_CURSOR_AIRPORT, HT_RECT)) {
90  ShowBuildAirportPicker(this);
91  this->last_user_action = widget;
92  }
93  break;
94 
95  case WID_AT_DEMOLISH:
97  this->last_user_action = widget;
98  break;
99 
100  default: break;
101  }
102  }
103 
104 
105  virtual void OnPlaceObject(Point pt, TileIndex tile)
106  {
107  switch (this->last_user_action) {
108  case WID_AT_AIRPORT:
109  PlaceAirport(tile);
110  break;
111 
112  case WID_AT_DEMOLISH:
114  break;
115 
116  default: NOT_REACHED();
117  }
118  }
119 
120  virtual void OnPlaceDrag(ViewportPlaceMethod select_method, ViewportDragDropSelectionProcess select_proc, Point pt)
121  {
122  VpSelectTilesWithMethod(pt.x, pt.y, select_method);
123  }
124 
125  virtual void OnPlaceMouseUp(ViewportPlaceMethod select_method, ViewportDragDropSelectionProcess select_proc, Point pt, TileIndex start_tile, TileIndex end_tile)
126  {
127  if (pt.x != -1 && select_proc == DDSP_DEMOLISH_AREA) {
128  GUIPlaceProcDragXY(select_proc, start_tile, end_tile);
129  }
130  }
131 
132  virtual void OnPlaceObjectAbort()
133  {
134  this->RaiseButtons();
135 
138  }
139 
140  static HotkeyList hotkeys;
141 };
142 
149 {
150  if (_game_mode != GM_NORMAL || !CanBuildVehicleInfrastructure(VEH_AIRCRAFT)) return ES_NOT_HANDLED;
152  if (w == NULL) return ES_NOT_HANDLED;
153  return w->OnHotkey(hotkey);
154 }
155 
156 static Hotkey airtoolbar_hotkeys[] = {
157  Hotkey('1', "airport", WID_AT_AIRPORT),
158  Hotkey('2', "demolish", WID_AT_DEMOLISH),
159  HOTKEY_LIST_END
160 };
161 HotkeyList BuildAirToolbarWindow::hotkeys("airtoolbar", airtoolbar_hotkeys, AirportToolbarGlobalHotkeys);
162 
163 static const NWidgetPart _nested_air_toolbar_widgets[] = {
165  NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN),
166  NWidget(WWT_CAPTION, COLOUR_DARK_GREEN), SetDataTip(STR_TOOLBAR_AIRCRAFT_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
167  NWidget(WWT_STICKYBOX, COLOUR_DARK_GREEN),
168  EndContainer(),
170  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),
171  NWidget(WWT_PANEL, COLOUR_DARK_GREEN), SetMinimalSize(4, 22), SetFill(1, 1), EndContainer(),
172  NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_AT_DEMOLISH), SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_DYNAMITE, STR_TOOLTIP_DEMOLISH_BUILDINGS_ETC),
173  EndContainer(),
174 };
175 
176 static WindowDesc _air_toolbar_desc(
177  WDP_ALIGN_TOOLBAR, "toolbar_air", 0, 0,
180  _nested_air_toolbar_widgets, lengthof(_nested_air_toolbar_widgets),
181  &BuildAirToolbarWindow::hotkeys
182 );
183 
192 {
193  if (!Company::IsValidID(_local_company)) return NULL;
194 
196  return AllocateWindowDescFront<BuildAirToolbarWindow>(&_air_toolbar_desc, TRANSPORT_AIR);
197 }
198 
201  int line_height;
202  Scrollbar *vscroll;
203 
206  {
207  DropDownList *list = new DropDownList();
208 
209  for (uint i = 0; i < AirportClass::GetClassCount(); i++) {
210  *list->Append() = new DropDownListStringItem(AirportClass::Get((AirportClassID)i)->name, i, false);
211  }
212 
213  return list;
214  }
215 
216 public:
218  {
219  this->CreateNestedTree();
220 
221  this->vscroll = this->GetScrollbar(WID_AP_SCROLLBAR);
222  this->vscroll->SetCapacity(5);
223  this->vscroll->SetPosition(0);
224 
226 
229  this->OnInvalidateData();
230 
231  /* Ensure airport class is valid (changing NewGRFs). */
232  _selected_airport_class = Clamp(_selected_airport_class, APC_BEGIN, (AirportClassID)(AirportClass::GetClassCount() - 1));
234  this->vscroll->SetCount(ac->GetSpecCount());
235 
236  /* Ensure the airport index is valid for this class (changing NewGRFs). */
238 
239  /* Only when no valid airport was selected, we want to select the first airport. */
240  bool selectFirstAirport = true;
241  if (_selected_airport_index != -1) {
242  const AirportSpec *as = ac->GetSpec(_selected_airport_index);
243  if (as->IsAvailable()) {
244  /* Ensure the airport layout is valid. */
246  selectFirstAirport = false;
247  this->UpdateSelectSize();
248  }
249  }
250 
251  if (selectFirstAirport) this->SelectFirstAvailableAirport(true);
252  }
253 
254  virtual ~BuildAirportWindow()
255  {
257  }
258 
259  virtual void SetStringParameters(int widget) const
260  {
261  switch (widget) {
264  break;
265 
266  case WID_AP_LAYOUT_NUM:
267  SetDParam(0, STR_EMPTY);
268  if (_selected_airport_index != -1) {
271  if (string != STR_UNDEFINED) {
272  SetDParam(0, string);
273  } else if (as->num_table > 1) {
274  SetDParam(0, STR_STATION_BUILD_AIRPORT_LAYOUT_NAME);
276  }
277  }
278  break;
279 
280  default: break;
281  }
282  }
283 
284  virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
285  {
286  switch (widget) {
287  case WID_AP_CLASS_DROPDOWN: {
288  Dimension d = {0, 0};
289  for (uint i = 0; i < AirportClass::GetClassCount(); i++) {
291  d = maxdim(d, GetStringBoundingBox(STR_BLACK_STRING));
292  }
293  d.width += padding.width;
294  d.height += padding.height;
295  *size = maxdim(*size, d);
296  break;
297  }
298 
299  case WID_AP_AIRPORT_LIST: {
300  for (int i = 0; i < NUM_AIRPORTS; i++) {
301  const AirportSpec *as = AirportSpec::Get(i);
302  if (!as->enabled) continue;
303 
304  size->width = max(size->width, GetStringBoundingBox(as->name).width);
305  }
306 
307  this->line_height = FONT_HEIGHT_NORMAL + WD_MATRIX_TOP + WD_MATRIX_BOTTOM;
308  size->height = 5 * this->line_height;
309  break;
310  }
311 
313  for (int i = 0; i < NUM_AIRPORTS; i++) {
314  const AirportSpec *as = AirportSpec::Get(i);
315  if (!as->enabled) continue;
316  for (byte layout = 0; layout < as->num_table; layout++) {
317  SpriteID sprite = GetCustomAirportSprite(as, layout);
318  if (sprite != 0) {
319  Dimension d = GetSpriteSize(sprite);
322  *size = maxdim(d, *size);
323  }
324  }
325  }
326  break;
327 
328  case WID_AP_EXTRA_TEXT:
329  for (int i = NEW_AIRPORT_OFFSET; i < NUM_AIRPORTS; i++) {
330  const AirportSpec *as = AirportSpec::Get(i);
331  if (!as->enabled) continue;
332  for (byte layout = 0; layout < as->num_table; layout++) {
334  if (string == STR_UNDEFINED) continue;
335 
336  /* STR_BLACK_STRING is used to start the string with {BLACK} */
337  SetDParam(0, string);
338  Dimension d = GetStringMultiLineBoundingBox(STR_BLACK_STRING, *size);
339  *size = maxdim(d, *size);
340  }
341  }
342  break;
343 
344  default: break;
345  }
346  }
347 
348  virtual void DrawWidget(const Rect &r, int widget) const
349  {
350  switch (widget) {
351  case WID_AP_AIRPORT_LIST: {
352  int y = r.top;
354  for (uint i = this->vscroll->GetPosition(); this->vscroll->IsVisible(i) && i < apclass->GetSpecCount(); i++) {
355  const AirportSpec *as = apclass->GetSpec(i);
356  if (!as->IsAvailable()) {
357  GfxFillRect(r.left + 1, y + 1, r.right - 1, y + this->line_height - 2, PC_BLACK, FILLRECT_CHECKER);
358  }
359  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);
360  y += this->line_height;
361  }
362  break;
363  }
364 
366  if (this->preview_sprite != 0) {
368  DrawSprite(this->preview_sprite, COMPANY_SPRITE_COLOUR(_local_company), (r.left + r.right - d.width) / 2, (r.top + r.bottom - d.height) / 2);
369  }
370  break;
371 
372  case WID_AP_EXTRA_TEXT:
373  if (_selected_airport_index != -1) {
376  if (string != STR_UNDEFINED) {
377  SetDParam(0, string);
378  DrawStringMultiLine(r.left, r.right, r.top, r.bottom, STR_BLACK_STRING);
379  }
380  }
381  break;
382  }
383  }
384 
385  virtual void OnPaint()
386  {
387  this->DrawWidgets();
388 
389  uint16 top = this->GetWidget<NWidgetBase>(WID_AP_BTN_DOHILIGHT)->pos_y + this->GetWidget<NWidgetBase>(WID_AP_BTN_DOHILIGHT)->current_y + WD_PAR_VSEP_NORMAL;
390  NWidgetBase *panel_nwi = this->GetWidget<NWidgetBase>(WID_AP_BOTTOMPANEL);
391 
392  int right = panel_nwi->pos_x + panel_nwi->current_x;
393  int bottom = panel_nwi->pos_y + panel_nwi->current_y;
394 
395  if (_selected_airport_index != -1) {
398 
399  /* only show the station (airport) noise, if the noise option is activated */
401  /* show the noise of the selected airport */
402  SetDParam(0, as->noise_level);
403  DrawString(panel_nwi->pos_x + WD_FRAMERECT_LEFT, right - WD_FRAMERECT_RIGHT, top, STR_STATION_BUILD_NOISE);
405  }
406 
407  /* strings such as 'Size' and 'Coverage Area' */
408  top = DrawStationCoverageAreaText(panel_nwi->pos_x + WD_FRAMERECT_LEFT, right - WD_FRAMERECT_RIGHT, top, SCT_ALL, rad, false) + WD_PAR_VSEP_NORMAL;
410  }
411 
412  /* Resize background if the window is too small.
413  * Never make the window smaller to avoid oscillating if the size change affects the acceptance.
414  * (This is the case, if making the window bigger moves the mouse into the window.) */
415  if (top > bottom) {
416  ResizeWindow(this, 0, top - bottom, false);
417  }
418  }
419 
420  void SelectOtherAirport(int airport_index)
421  {
422  _selected_airport_index = airport_index;
424 
425  this->UpdateSelectSize();
426  this->SetDirty();
427  }
428 
429  void UpdateSelectSize()
430  {
431  if (_selected_airport_index == -1) {
432  SetTileSelectSize(1, 1);
435  } else {
437  int w = as->size_x;
438  int h = as->size_y;
440  if (rotation == DIR_E || rotation == DIR_W) Swap(w, h);
441  SetTileSelectSize(w, h);
442 
443  this->preview_sprite = GetCustomAirportSprite(as, _selected_airport_layout);
444 
447 
449  if (_settings_client.gui.station_show_coverage) SetTileSelectBigSize(-rad, -rad, 2 * rad, 2 * rad);
450  }
451  }
452 
453  virtual void OnClick(Point pt, int widget, int click_count)
454  {
455  switch (widget) {
458  break;
459 
460  case WID_AP_AIRPORT_LIST: {
461  int num_clicked = this->vscroll->GetPosition() + (pt.y - this->nested_array[widget]->pos_y) / this->line_height;
462  if (num_clicked >= this->vscroll->GetCount()) break;
463  const AirportSpec *as = AirportClass::Get(_selected_airport_class)->GetSpec(num_clicked);
464  if (as->IsAvailable()) this->SelectOtherAirport(num_clicked);
465  break;
466  }
467 
472  this->SetDirty();
473  if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
474  this->UpdateSelectSize();
475  break;
476 
479  this->UpdateSelectSize();
480  this->SetDirty();
481  break;
482 
485  this->UpdateSelectSize();
486  this->SetDirty();
487  break;
488  }
489  }
490 
496  void SelectFirstAvailableAirport(bool change_class)
497  {
498  /* First try to select an airport in the selected class. */
500  for (uint i = 0; i < sel_apclass->GetSpecCount(); i++) {
501  const AirportSpec *as = sel_apclass->GetSpec(i);
502  if (as->IsAvailable()) {
503  this->SelectOtherAirport(i);
504  return;
505  }
506  }
507  if (change_class) {
508  /* If that fails, select the first available airport
509  * from a random class. */
510  for (AirportClassID j = APC_BEGIN; j < APC_MAX; j++) {
511  AirportClass *apclass = AirportClass::Get(j);
512  for (uint i = 0; i < apclass->GetSpecCount(); i++) {
513  const AirportSpec *as = apclass->GetSpec(i);
514  if (as->IsAvailable()) {
516  this->SelectOtherAirport(i);
517  return;
518  }
519  }
520  }
521  }
522  /* If all airports are unavailable, select nothing. */
523  this->SelectOtherAirport(-1);
524  }
525 
526  virtual void OnDropdownSelect(int widget, int index)
527  {
528  assert(widget == WID_AP_CLASS_DROPDOWN);
530  this->vscroll->SetCount(AirportClass::Get(_selected_airport_class)->GetSpecCount());
531  this->SelectFirstAvailableAirport(false);
532  }
533 
534  virtual void OnTick()
535  {
537  }
538 };
539 
540 static const NWidgetPart _nested_build_airport_widgets[] = {
542  NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN),
543  NWidget(WWT_CAPTION, COLOUR_DARK_GREEN), SetDataTip(STR_STATION_BUILD_AIRPORT_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
544  EndContainer(),
545  NWidget(WWT_PANEL, COLOUR_DARK_GREEN), SetFill(1, 0), SetPIP(2, 0, 2),
546  NWidget(WWT_LABEL, COLOUR_DARK_GREEN), SetDataTip(STR_STATION_BUILD_AIRPORT_CLASS_LABEL, STR_NULL), SetFill(1, 0),
547  NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_AP_CLASS_DROPDOWN), SetFill(1, 0), SetDataTip(STR_BLACK_STRING, STR_STATION_BUILD_AIRPORT_TOOLTIP),
548  NWidget(WWT_EMPTY, COLOUR_DARK_GREEN, WID_AP_AIRPORT_SPRITE), SetFill(1, 0),
550  NWidget(WWT_MATRIX, COLOUR_GREY, WID_AP_AIRPORT_LIST), SetFill(1, 0), SetMatrixDataTip(1, 5, STR_STATION_BUILD_AIRPORT_TOOLTIP), SetScrollbar(WID_AP_SCROLLBAR),
552  EndContainer(),
555  NWidget(WWT_LABEL, COLOUR_GREY, WID_AP_LAYOUT_NUM), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_BLACK_STRING, STR_NULL),
557  EndContainer(),
558  NWidget(WWT_EMPTY, COLOUR_DARK_GREEN, WID_AP_EXTRA_TEXT), SetFill(1, 0), SetMinimalSize(150, 0),
559  EndContainer(),
560  /* Bottom panel. */
561  NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_AP_BOTTOMPANEL), SetPIP(2, 2, 2),
562  NWidget(WWT_LABEL, COLOUR_DARK_GREEN), SetDataTip(STR_STATION_BUILD_COVERAGE_AREA_TITLE, STR_NULL), SetFill(1, 0),
564  NWidget(NWID_SPACER), SetMinimalSize(14, 0), SetFill(1, 0),
566  NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_AP_BTN_DONTHILIGHT), SetMinimalSize(60, 12), SetFill(1, 0),
567  SetDataTip(STR_STATION_BUILD_COVERAGE_OFF, STR_STATION_BUILD_COVERAGE_AREA_OFF_TOOLTIP),
568  NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_AP_BTN_DOHILIGHT), SetMinimalSize(60, 12), SetFill(1, 0),
569  SetDataTip(STR_STATION_BUILD_COVERAGE_ON, STR_STATION_BUILD_COVERAGE_AREA_ON_TOOLTIP),
570  EndContainer(),
571  NWidget(NWID_SPACER), SetMinimalSize(14, 0), SetFill(1, 0),
572  EndContainer(),
573  NWidget(NWID_SPACER), SetMinimalSize(0, 10), SetResize(0, 1), SetFill(1, 0),
574  EndContainer(),
575 };
576 
577 static WindowDesc _build_airport_desc(
578  WDP_AUTO, "build_station_air", 0, 0,
581  _nested_build_airport_widgets, lengthof(_nested_build_airport_widgets)
582 );
583 
584 static void ShowBuildAirportPicker(Window *parent)
585 {
586  new BuildAirportWindow(&_build_airport_desc, parent);
587 }
588 
589 void InitializeAirportGui()
590 {
593 }