OpenTTD
toolbar_gui.cpp
Go to the documentation of this file.
1 /* $Id: toolbar_gui.cpp 27671 2016-10-30 17:36:57Z 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 "gui.h"
14 #include "window_gui.h"
15 #include "window_func.h"
16 #include "viewport_func.h"
17 #include "command_func.h"
18 #include "vehicle_gui.h"
19 #include "rail_gui.h"
20 #include "road_gui.h"
21 #include "date_func.h"
22 #include "vehicle_func.h"
23 #include "sound_func.h"
24 #include "terraform_gui.h"
25 #include "strings_func.h"
26 #include "company_func.h"
27 #include "company_gui.h"
28 #include "vehicle_base.h"
29 #include "cheat_func.h"
30 #include "transparency_gui.h"
31 #include "screenshot.h"
32 #include "signs_func.h"
33 #include "fios.h"
34 #include "console_gui.h"
35 #include "news_gui.h"
36 #include "ai/ai_gui.hpp"
37 #include "tilehighlight_func.h"
38 #include "smallmap_gui.h"
39 #include "graph_gui.h"
40 #include "textbuf_gui.h"
42 #include "newgrf_debug.h"
43 #include "hotkeys.h"
44 #include "engine_base.h"
45 #include "highscore.h"
46 #include "game/game.hpp"
47 #include "goal_base.h"
48 #include "story_base.h"
49 #include "toolbar_gui.h"
50 
51 #include "widgets/toolbar_widget.h"
52 
53 #include "network/network.h"
54 #include "network/network_gui.h"
55 #include "network/network_func.h"
56 
57 #include "safeguards.h"
58 
59 
61 uint _toolbar_width = 0;
62 
63 RailType _last_built_railtype;
64 RoadType _last_built_roadtype;
65 
67 
70  TB_NORMAL,
71  TB_UPPER,
72  TB_LOWER
73 };
74 
77  CBF_NONE,
78  CBF_PLACE_SIGN,
79  CBF_PLACE_LANDINFO,
80 };
81 
83 
84 
89  uint checkmark_width;
90 public:
91  bool checked;
92 
93  DropDownListCheckedItem(StringID string, int result, bool masked, bool checked) : DropDownListStringItem(string, result, masked), checked(checked)
94  {
95  this->checkmark_width = GetStringBoundingBox(STR_JUST_CHECKMARK).width + 3;
96  }
97 
98  virtual ~DropDownListCheckedItem() {}
99 
100  uint Width() const
101  {
102  return DropDownListStringItem::Width() + this->checkmark_width;
103  }
104 
105  void Draw(int left, int right, int top, int bottom, bool sel, int bg_colour) const
106  {
107  bool rtl = _current_text_dir == TD_RTL;
108  if (this->checked) {
109  DrawString(left + WD_FRAMERECT_LEFT, right - WD_FRAMERECT_RIGHT, top, STR_JUST_CHECKMARK, sel ? TC_WHITE : TC_BLACK);
110  }
111  DrawString(left + WD_FRAMERECT_LEFT + (rtl ? 0 : this->checkmark_width), right - WD_FRAMERECT_RIGHT - (rtl ? this->checkmark_width : 0), top, this->String(), sel ? TC_WHITE : TC_BLACK);
112  }
113 };
114 
119  Dimension icon_size;
120 public:
121  bool greyed;
122 
123  DropDownListCompanyItem(int result, bool masked, bool greyed) : DropDownListItem(result, masked), greyed(greyed)
124  {
125  this->icon_size = GetSpriteSize(SPR_COMPANY_ICON);
126  }
127 
128  virtual ~DropDownListCompanyItem() {}
129 
130  bool Selectable() const
131  {
132  return true;
133  }
134 
135  uint Width() const
136  {
137  CompanyID company = (CompanyID)this->result;
138  SetDParam(0, company);
139  SetDParam(1, company);
140  return GetStringBoundingBox(STR_COMPANY_NAME_COMPANY_NUM).width + this->icon_size.width + 3;
141  }
142 
143  uint Height(uint width) const
144  {
145  return max(this->icon_size.height + 2U, (uint)FONT_HEIGHT_NORMAL);
146  }
147 
148  void Draw(int left, int right, int top, int bottom, bool sel, int bg_colour) const
149  {
150  CompanyID company = (CompanyID)this->result;
151  bool rtl = _current_text_dir == TD_RTL;
152 
153  /* It's possible the company is deleted while the dropdown is open */
154  if (!Company::IsValidID(company)) return;
155 
156  int icon_offset = (bottom - top - icon_size.height) / 2;
157  int text_offset = (bottom - top - FONT_HEIGHT_NORMAL) / 2;
158 
159  DrawCompanyIcon(company, rtl ? right - this->icon_size.width - WD_FRAMERECT_RIGHT : left + WD_FRAMERECT_LEFT, top + icon_offset);
160 
161  SetDParam(0, company);
162  SetDParam(1, company);
163  TextColour col;
164  if (this->greyed) {
165  col = (sel ? TC_SILVER : TC_GREY) | TC_NO_SHADE;
166  } else {
167  col = sel ? TC_WHITE : TC_BLACK;
168  }
169  DrawString(left + WD_FRAMERECT_LEFT + (rtl ? 0 : 3 + this->icon_size.width), right - WD_FRAMERECT_RIGHT - (rtl ? 3 + this->icon_size.width : 0), top + text_offset, STR_COMPANY_NAME_COMPANY_NUM, col);
170  }
171 };
172 
180 static void PopupMainToolbMenu(Window *w, int widget, DropDownList *list, int def)
181 {
182  ShowDropDownList(w, list, def, widget, 0, true, true);
183  if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
184 }
185 
193 static void PopupMainToolbMenu(Window *w, int widget, StringID string, int count)
194 {
195  DropDownList *list = new DropDownList();
196  for (int i = 0; i < count; i++) {
197  *list->Append() = new DropDownListStringItem(string + i, i, false);
198  }
199  PopupMainToolbMenu(w, widget, list, 0);
200 }
201 
203 static const int CTMN_CLIENT_LIST = -1;
204 static const int CTMN_NEW_COMPANY = -2;
205 static const int CTMN_SPECTATE = -3;
206 static const int CTMN_SPECTATOR = -4;
207 
215 static void PopupMainCompanyToolbMenu(Window *w, int widget, int grey = 0, bool include_spectator = false)
216 {
217  DropDownList *list = new DropDownList();
218 
219 #ifdef ENABLE_NETWORK
220  if (_networking) {
221  if (widget == WID_TN_COMPANIES) {
222  /* Add the client list button for the companies menu */
223  *list->Append() = new DropDownListStringItem(STR_NETWORK_COMPANY_LIST_CLIENT_LIST, CTMN_CLIENT_LIST, false);
224  }
225 
226  if (include_spectator) {
227  if (widget == WID_TN_COMPANIES) {
229  *list->Append() = new DropDownListStringItem(STR_NETWORK_COMPANY_LIST_NEW_COMPANY, CTMN_NEW_COMPANY, NetworkMaxCompaniesReached());
230  } else {
231  *list->Append() = new DropDownListStringItem(STR_NETWORK_COMPANY_LIST_SPECTATE, CTMN_SPECTATE, NetworkMaxSpectatorsReached());
232  }
233  } else {
234  *list->Append() = new DropDownListStringItem(STR_NETWORK_TOOLBAR_LIST_SPECTATOR, CTMN_SPECTATOR, false);
235  }
236  }
237  }
238 #endif /* ENABLE_NETWORK */
239 
240  for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) {
241  if (!Company::IsValidID(c)) continue;
242  *list->Append() = new DropDownListCompanyItem(c, false, HasBit(grey, c));
243  }
244 
246 }
247 
248 
249 static ToolbarMode _toolbar_mode;
250 
251 static CallBackFunction SelectSignTool()
252 {
253  if (_last_started_action == CBF_PLACE_SIGN) {
255  return CBF_NONE;
256  } else {
257  SetObjectToPlace(SPR_CURSOR_SIGN, PAL_NONE, HT_RECT, WC_MAIN_TOOLBAR, 0);
258  return CBF_PLACE_SIGN;
259  }
260 }
261 
262 /* --- Pausing --- */
263 
264 static CallBackFunction ToolbarPauseClick(Window *w)
265 {
266  if (_networking && !_network_server) return CBF_NONE; // only server can pause the game
267 
269  if (_settings_client.sound.confirm) SndPlayFx(SND_15_BEEP);
270  }
271  return CBF_NONE;
272 }
273 
281 {
282  _fast_forward ^= true;
283  if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
284  return CBF_NONE;
285 }
286 
291  OME_GAMEOPTIONS,
292  OME_SETTINGS,
293  OME_SCRIPT_SETTINGS,
294  OME_NEWGRFSETTINGS,
295  OME_TRANSPARENCIES,
296  OME_SHOW_TOWNNAMES,
297  OME_SHOW_STATIONNAMES,
298  OME_SHOW_WAYPOINTNAMES,
299  OME_SHOW_SIGNS,
300  OME_SHOW_COMPETITOR_SIGNS,
301  OME_FULL_ANIMATION,
302  OME_FULL_DETAILS,
303  OME_TRANSPARENTBUILDINGS,
304  OME_SHOW_STATIONSIGNS,
305 };
306 
314 {
315  DropDownList *list = new DropDownList();
316  *list->Append() = new DropDownListStringItem(STR_SETTINGS_MENU_GAME_OPTIONS, OME_GAMEOPTIONS, false);
317  *list->Append() = new DropDownListStringItem(STR_SETTINGS_MENU_CONFIG_SETTINGS_TREE, OME_SETTINGS, false);
318  /* Changes to the per-AI settings don't get send from the server to the clients. Clients get
319  * the settings once they join but never update it. As such don't show the window at all
320  * to network clients. */
321  if (!_networking || _network_server) *list->Append() = new DropDownListStringItem(STR_SETTINGS_MENU_SCRIPT_SETTINGS, OME_SCRIPT_SETTINGS, false);
322  *list->Append() = new DropDownListStringItem(STR_SETTINGS_MENU_NEWGRF_SETTINGS, OME_NEWGRFSETTINGS, false);
323  *list->Append() = new DropDownListStringItem(STR_SETTINGS_MENU_TRANSPARENCY_OPTIONS, OME_TRANSPARENCIES, false);
324  *list->Append() = new DropDownListItem(-1, false);
325  *list->Append() = new DropDownListCheckedItem(STR_SETTINGS_MENU_TOWN_NAMES_DISPLAYED, OME_SHOW_TOWNNAMES, false, HasBit(_display_opt, DO_SHOW_TOWN_NAMES));
326  *list->Append() = new DropDownListCheckedItem(STR_SETTINGS_MENU_STATION_NAMES_DISPLAYED, OME_SHOW_STATIONNAMES, false, HasBit(_display_opt, DO_SHOW_STATION_NAMES));
327  *list->Append() = new DropDownListCheckedItem(STR_SETTINGS_MENU_WAYPOINTS_DISPLAYED, OME_SHOW_WAYPOINTNAMES, false, HasBit(_display_opt, DO_SHOW_WAYPOINT_NAMES));
328  *list->Append() = new DropDownListCheckedItem(STR_SETTINGS_MENU_SIGNS_DISPLAYED, OME_SHOW_SIGNS, false, HasBit(_display_opt, DO_SHOW_SIGNS));
329  *list->Append() = new DropDownListCheckedItem(STR_SETTINGS_MENU_SHOW_COMPETITOR_SIGNS, OME_SHOW_COMPETITOR_SIGNS, false, HasBit(_display_opt, DO_SHOW_COMPETITOR_SIGNS));
330  *list->Append() = new DropDownListCheckedItem(STR_SETTINGS_MENU_FULL_ANIMATION, OME_FULL_ANIMATION, false, HasBit(_display_opt, DO_FULL_ANIMATION));
331  *list->Append() = new DropDownListCheckedItem(STR_SETTINGS_MENU_FULL_DETAIL, OME_FULL_DETAILS, false, HasBit(_display_opt, DO_FULL_DETAIL));
332  *list->Append() = new DropDownListCheckedItem(STR_SETTINGS_MENU_TRANSPARENT_BUILDINGS, OME_TRANSPARENTBUILDINGS, false, IsTransparencySet(TO_HOUSES));
333  *list->Append() = new DropDownListCheckedItem(STR_SETTINGS_MENU_TRANSPARENT_SIGNS, OME_SHOW_STATIONSIGNS, false, IsTransparencySet(TO_SIGNS));
334 
335  ShowDropDownList(w, list, 0, WID_TN_SETTINGS, 140, true, true);
336  if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
337  return CBF_NONE;
338 }
339 
347 {
348  switch (index) {
349  case OME_GAMEOPTIONS: ShowGameOptions(); return CBF_NONE;
350  case OME_SETTINGS: ShowGameSettings(); return CBF_NONE;
351  case OME_SCRIPT_SETTINGS: ShowAIConfigWindow(); return CBF_NONE;
352  case OME_NEWGRFSETTINGS: ShowNewGRFSettings(!_networking && _settings_client.gui.UserIsAllowedToChangeNewGRFs(), true, true, &_grfconfig); return CBF_NONE;
353  case OME_TRANSPARENCIES: ShowTransparencyToolbar(); break;
354 
355  case OME_SHOW_TOWNNAMES: ToggleBit(_display_opt, DO_SHOW_TOWN_NAMES); break;
356  case OME_SHOW_STATIONNAMES: ToggleBit(_display_opt, DO_SHOW_STATION_NAMES); break;
357  case OME_SHOW_WAYPOINTNAMES: ToggleBit(_display_opt, DO_SHOW_WAYPOINT_NAMES); break;
358  case OME_SHOW_SIGNS: ToggleBit(_display_opt, DO_SHOW_SIGNS); break;
359  case OME_SHOW_COMPETITOR_SIGNS:
362  break;
363  case OME_FULL_ANIMATION: ToggleBit(_display_opt, DO_FULL_ANIMATION); CheckBlitter(); break;
364  case OME_FULL_DETAILS: ToggleBit(_display_opt, DO_FULL_DETAIL); break;
365  case OME_TRANSPARENTBUILDINGS: ToggleTransparency(TO_HOUSES); break;
366  case OME_SHOW_STATIONSIGNS: ToggleTransparency(TO_SIGNS); break;
367  }
369  return CBF_NONE;
370 }
371 
376  SLEME_SAVE_SCENARIO = 0,
377  SLEME_LOAD_SCENARIO,
378  SLEME_SAVE_HEIGHTMAP,
379  SLEME_LOAD_HEIGHTMAP,
380  SLEME_EXIT_TOINTRO,
381  SLEME_EXIT_GAME = 6,
382  SLEME_MENUCOUNT,
383 };
384 
389  SLNME_SAVE_GAME = 0,
390  SLNME_LOAD_GAME,
391  SLNME_EXIT_TOINTRO,
392  SLNME_EXIT_GAME = 4,
393  SLNME_MENUCOUNT,
394 };
395 
403 {
404  PopupMainToolbMenu(w, WID_TN_SAVE, STR_FILE_MENU_SAVE_GAME, SLNME_MENUCOUNT);
405  return CBF_NONE;
406 }
407 
415 {
416  PopupMainToolbMenu(w, WID_TE_SAVE, STR_SCENEDIT_FILE_MENU_SAVE_SCENARIO, SLEME_MENUCOUNT);
417  return CBF_NONE;
418 }
419 
426 static CallBackFunction MenuClickSaveLoad(int index = 0)
427 {
428  if (_game_mode == GM_EDITOR) {
429  switch (index) {
430  case SLEME_SAVE_SCENARIO: ShowSaveLoadDialog(FT_SCENARIO, SLO_SAVE); break;
431  case SLEME_LOAD_SCENARIO: ShowSaveLoadDialog(FT_SCENARIO, SLO_LOAD); break;
432  case SLEME_SAVE_HEIGHTMAP: ShowSaveLoadDialog(FT_HEIGHTMAP,SLO_SAVE); break;
433  case SLEME_LOAD_HEIGHTMAP: ShowSaveLoadDialog(FT_HEIGHTMAP,SLO_LOAD); break;
434  case SLEME_EXIT_TOINTRO: AskExitToGameMenu(); break;
435  case SLEME_EXIT_GAME: HandleExitGameRequest(); break;
436  }
437  } else {
438  switch (index) {
439  case SLNME_SAVE_GAME: ShowSaveLoadDialog(FT_SAVEGAME, SLO_SAVE); break;
440  case SLNME_LOAD_GAME: ShowSaveLoadDialog(FT_SAVEGAME, SLO_LOAD); break;
441  case SLNME_EXIT_TOINTRO: AskExitToGameMenu(); break;
442  case SLNME_EXIT_GAME: HandleExitGameRequest(); break;
443  }
444  }
445  return CBF_NONE;
446 }
447 
448 /* --- Map button menu --- */
449 
450 enum MapMenuEntries {
451  MME_SHOW_SMALLMAP = 0,
452  MME_SHOW_EXTRAVIEWPORTS,
453  MME_SHOW_LINKGRAPH,
454  MME_SHOW_SIGNLISTS,
455  MME_SHOW_TOWNDIRECTORY,
456  MME_SHOW_INDUSTRYDIRECTORY,
457 };
458 
459 static CallBackFunction ToolbarMapClick(Window *w)
460 {
461  DropDownList *list = new DropDownList();
462  *list->Append() = new DropDownListStringItem(STR_MAP_MENU_MAP_OF_WORLD, MME_SHOW_SMALLMAP, false);
463  *list->Append() = new DropDownListStringItem(STR_MAP_MENU_EXTRA_VIEW_PORT, MME_SHOW_EXTRAVIEWPORTS, false);
464  *list->Append() = new DropDownListStringItem(STR_MAP_MENU_LINGRAPH_LEGEND, MME_SHOW_LINKGRAPH, false);
465  *list->Append() = new DropDownListStringItem(STR_MAP_MENU_SIGN_LIST, MME_SHOW_SIGNLISTS, false);
467  return CBF_NONE;
468 }
469 
470 static CallBackFunction ToolbarScenMapTownDir(Window *w)
471 {
472  DropDownList *list = new DropDownList();
473  *list->Append() = new DropDownListStringItem(STR_MAP_MENU_MAP_OF_WORLD, MME_SHOW_SMALLMAP, false);
474  *list->Append() = new DropDownListStringItem(STR_MAP_MENU_EXTRA_VIEW_PORT, MME_SHOW_EXTRAVIEWPORTS, false);
475  *list->Append() = new DropDownListStringItem(STR_MAP_MENU_SIGN_LIST, MME_SHOW_SIGNLISTS, false);
476  *list->Append() = new DropDownListStringItem(STR_TOWN_MENU_TOWN_DIRECTORY, MME_SHOW_TOWNDIRECTORY, false);
477  *list->Append() = new DropDownListStringItem(STR_INDUSTRY_MENU_INDUSTRY_DIRECTORY, MME_SHOW_INDUSTRYDIRECTORY, false);
479  return CBF_NONE;
480 }
481 
489 {
490  switch (index) {
491  case MME_SHOW_SMALLMAP: ShowSmallMap(); break;
492  case MME_SHOW_EXTRAVIEWPORTS: ShowExtraViewPortWindow(); break;
493  case MME_SHOW_LINKGRAPH: ShowLinkGraphLegend(); break;
494  case MME_SHOW_SIGNLISTS: ShowSignList(); break;
495  case MME_SHOW_TOWNDIRECTORY: ShowTownDirectory(); break;
496  case MME_SHOW_INDUSTRYDIRECTORY: ShowIndustryDirectory(); break;
497  }
498  return CBF_NONE;
499 }
500 
501 /* --- Town button menu --- */
502 
503 static CallBackFunction ToolbarTownClick(Window *w)
504 {
505  PopupMainToolbMenu(w, WID_TN_TOWNS, STR_TOWN_MENU_TOWN_DIRECTORY, (_settings_game.economy.found_town == TF_FORBIDDEN) ? 1 : 2);
506  return CBF_NONE;
507 }
508 
516 {
517  switch (index) {
518  case 0: ShowTownDirectory(); break;
519  case 1: // setting could be changed when the dropdown was open
520  if (_settings_game.economy.found_town != TF_FORBIDDEN) ShowFoundTownWindow();
521  break;
522  }
523  return CBF_NONE;
524 }
525 
526 /* --- Subidies button menu --- */
527 
528 static CallBackFunction ToolbarSubsidiesClick(Window *w)
529 {
530  PopupMainToolbMenu(w, WID_TN_SUBSIDIES, STR_SUBSIDIES_MENU_SUBSIDIES, 1);
531  return CBF_NONE;
532 }
533 
541 {
542  switch (index) {
543  case 0: ShowSubsidiesList(); break;
544  }
545  return CBF_NONE;
546 }
547 
548 /* --- Stations button menu --- */
549 
550 static CallBackFunction ToolbarStationsClick(Window *w)
551 {
553  return CBF_NONE;
554 }
555 
563 {
565  return CBF_NONE;
566 }
567 
568 /* --- Finances button menu --- */
569 
570 static CallBackFunction ToolbarFinancesClick(Window *w)
571 {
573  return CBF_NONE;
574 }
575 
583 {
585  return CBF_NONE;
586 }
587 
588 /* --- Company's button menu --- */
589 
590 static CallBackFunction ToolbarCompaniesClick(Window *w)
591 {
593  return CBF_NONE;
594 }
595 
603 {
604 #ifdef ENABLE_NETWORK
605  if (_networking) {
606  switch (index) {
607  case CTMN_CLIENT_LIST:
608  ShowClientList();
609  return CBF_NONE;
610 
611  case CTMN_NEW_COMPANY:
612  if (_network_server) {
614  } else {
615  NetworkSendCommand(0, 0, 0, CMD_COMPANY_CTRL, NULL, NULL, _local_company);
616  }
617  return CBF_NONE;
618 
619  case CTMN_SPECTATE:
620  if (_network_server) {
623  } else {
625  }
626  return CBF_NONE;
627  }
628  }
629 #endif /* ENABLE_NETWORK */
630  ShowCompany((CompanyID)index);
631  return CBF_NONE;
632 }
633 
634 /* --- Story button menu --- */
635 
636 static CallBackFunction ToolbarStoryClick(Window *w)
637 {
639  return CBF_NONE;
640 }
641 
649 {
651  return CBF_NONE;
652 }
653 
654 /* --- Goal button menu --- */
655 
656 static CallBackFunction ToolbarGoalClick(Window *w)
657 {
659  return CBF_NONE;
660 }
661 
669 {
671  return CBF_NONE;
672 }
673 
674 /* --- Graphs button menu --- */
675 
676 static CallBackFunction ToolbarGraphsClick(Window *w)
677 {
678  PopupMainToolbMenu(w, WID_TN_GRAPHS, STR_GRAPH_MENU_OPERATING_PROFIT_GRAPH, (_toolbar_mode == TB_NORMAL) ? 6 : 8);
679  return CBF_NONE;
680 }
681 
689 {
690  switch (index) {
691  case 0: ShowOperatingProfitGraph(); break;
692  case 1: ShowIncomeGraph(); break;
693  case 2: ShowDeliveredCargoGraph(); break;
694  case 3: ShowPerformanceHistoryGraph(); break;
695  case 4: ShowCompanyValueGraph(); break;
696  case 5: ShowCargoPaymentRates(); break;
697  /* functions for combined graphs/league button */
698  case 6: ShowCompanyLeagueTable(); break;
699  case 7: ShowPerformanceRatingDetail(); break;
700  }
701  return CBF_NONE;
702 }
703 
704 /* --- League button menu --- */
705 
706 static CallBackFunction ToolbarLeagueClick(Window *w)
707 {
708  PopupMainToolbMenu(w, WID_TN_LEAGUE, STR_GRAPH_MENU_COMPANY_LEAGUE_TABLE, _networking ? 2 : 3);
709  return CBF_NONE;
710 }
711 
719 {
720  switch (index) {
721  case 0: ShowCompanyLeagueTable(); break;
722  case 1: ShowPerformanceRatingDetail(); break;
723  case 2: ShowHighscoreTable(); break;
724  }
725  return CBF_NONE;
726 }
727 
728 /* --- Industries button menu --- */
729 
730 static CallBackFunction ToolbarIndustryClick(Window *w)
731 {
732  /* Disable build-industry menu if we are a spectator */
733  PopupMainToolbMenu(w, WID_TN_INDUSTRIES, STR_INDUSTRY_MENU_INDUSTRY_DIRECTORY, (_local_company == COMPANY_SPECTATOR) ? 2 : 3);
734  return CBF_NONE;
735 }
736 
744 {
745  switch (index) {
746  case 0: ShowIndustryDirectory(); break;
747  case 1: ShowIndustryCargoesWindow(); break;
748  case 2: ShowBuildIndustryWindow(); break;
749  }
750  return CBF_NONE;
751 }
752 
753 /* --- Trains button menu + 1 helper function for all vehicles. --- */
754 
755 static void ToolbarVehicleClick(Window *w, VehicleType veh)
756 {
757  const Vehicle *v;
758  int dis = ~0;
759 
760  FOR_ALL_VEHICLES(v) {
761  if (v->type == veh && v->IsPrimaryVehicle()) ClrBit(dis, v->owner);
762  }
764 }
765 
766 
767 static CallBackFunction ToolbarTrainClick(Window *w)
768 {
769  ToolbarVehicleClick(w, VEH_TRAIN);
770  return CBF_NONE;
771 }
772 
780 {
781  ShowVehicleListWindow((CompanyID)index, VEH_TRAIN);
782  return CBF_NONE;
783 }
784 
785 /* --- Road vehicle button menu --- */
786 
787 static CallBackFunction ToolbarRoadClick(Window *w)
788 {
789  ToolbarVehicleClick(w, VEH_ROAD);
790  return CBF_NONE;
791 }
792 
800 {
801  ShowVehicleListWindow((CompanyID)index, VEH_ROAD);
802  return CBF_NONE;
803 }
804 
805 /* --- Ship button menu --- */
806 
807 static CallBackFunction ToolbarShipClick(Window *w)
808 {
809  ToolbarVehicleClick(w, VEH_SHIP);
810  return CBF_NONE;
811 }
812 
820 {
821  ShowVehicleListWindow((CompanyID)index, VEH_SHIP);
822  return CBF_NONE;
823 }
824 
825 /* --- Aircraft button menu --- */
826 
827 static CallBackFunction ToolbarAirClick(Window *w)
828 {
829  ToolbarVehicleClick(w, VEH_AIRCRAFT);
830  return CBF_NONE;
831 }
832 
840 {
841  ShowVehicleListWindow((CompanyID)index, VEH_AIRCRAFT);
842  return CBF_NONE;
843 }
844 
845 /* --- Zoom in button --- */
846 
847 static CallBackFunction ToolbarZoomInClick(Window *w)
848 {
850  w->HandleButtonClick((_game_mode == GM_EDITOR) ? (byte)WID_TE_ZOOM_IN : (byte)WID_TN_ZOOM_IN);
851  if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
852  }
853  return CBF_NONE;
854 }
855 
856 /* --- Zoom out button --- */
857 
858 static CallBackFunction ToolbarZoomOutClick(Window *w)
859 {
861  w->HandleButtonClick((_game_mode == GM_EDITOR) ? (byte)WID_TE_ZOOM_OUT : (byte)WID_TN_ZOOM_OUT);
862  if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
863  }
864  return CBF_NONE;
865 }
866 
867 /* --- Rail button menu --- */
868 
869 static CallBackFunction ToolbarBuildRailClick(Window *w)
870 {
871  ShowDropDownList(w, GetRailTypeDropDownList(), _last_built_railtype, WID_TN_RAILS, 140, true, true);
872  if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
873  return CBF_NONE;
874 }
875 
883 {
884  _last_built_railtype = (RailType)index;
885  ShowBuildRailToolbar(_last_built_railtype);
886  return CBF_NONE;
887 }
888 
889 /* --- Road button menu --- */
890 
891 static CallBackFunction ToolbarBuildRoadClick(Window *w)
892 {
893  const Company *c = Company::Get(_local_company);
894  DropDownList *list = new DropDownList();
895 
896  /* Road is always visible and available. */
897  *list->Append() = new DropDownListStringItem(STR_ROAD_MENU_ROAD_CONSTRUCTION, ROADTYPE_ROAD, false);
898 
899  /* Tram is only visible when there will be a tram, and available when that has been introduced. */
900  Engine *e;
901  FOR_ALL_ENGINES_OF_TYPE(e, VEH_ROAD) {
902  if (!HasBit(e->info.climates, _settings_game.game_creation.landscape)) continue;
903  if (!HasBit(e->info.misc_flags, EF_ROAD_TRAM)) continue;
904 
905  *list->Append() = new DropDownListStringItem(STR_ROAD_MENU_TRAM_CONSTRUCTION, ROADTYPE_TRAM, !HasBit(c->avail_roadtypes, ROADTYPE_TRAM));
906  break;
907  }
908  ShowDropDownList(w, list, _last_built_roadtype, WID_TN_ROADS, 140, true, true);
909  if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
910  return CBF_NONE;
911 }
912 
920 {
921  _last_built_roadtype = (RoadType)index;
922  ShowBuildRoadToolbar(_last_built_roadtype);
923  return CBF_NONE;
924 }
925 
926 /* --- Water button menu --- */
927 
928 static CallBackFunction ToolbarBuildWaterClick(Window *w)
929 {
930  PopupMainToolbMenu(w, WID_TN_WATER, STR_WATERWAYS_MENU_WATERWAYS_CONSTRUCTION, 1);
931  return CBF_NONE;
932 }
933 
941 {
943  return CBF_NONE;
944 }
945 
946 /* --- Airport button menu --- */
947 
948 static CallBackFunction ToolbarBuildAirClick(Window *w)
949 {
950  PopupMainToolbMenu(w, WID_TN_AIR, STR_AIRCRAFT_MENU_AIRPORT_CONSTRUCTION, 1);
951  return CBF_NONE;
952 }
953 
961 {
963  return CBF_NONE;
964 }
965 
966 /* --- Forest button menu --- */
967 
968 static CallBackFunction ToolbarForestClick(Window *w)
969 {
970  PopupMainToolbMenu(w, WID_TN_LANDSCAPE, STR_LANDSCAPING_MENU_LANDSCAPING, 3);
971  return CBF_NONE;
972 }
973 
981 {
982  switch (index) {
983  case 0: ShowTerraformToolbar(); break;
984  case 1: ShowBuildTreesToolbar(); break;
985  case 2: return SelectSignTool();
986  }
987  return CBF_NONE;
988 }
989 
990 /* --- Music button menu --- */
991 
992 static CallBackFunction ToolbarMusicClick(Window *w)
993 {
994  PopupMainToolbMenu(w, WID_TN_MUSIC_SOUND, STR_TOOLBAR_SOUND_MUSIC, 1);
995  return CBF_NONE;
996 }
997 
1005 {
1006  ShowMusicWindow();
1007  return CBF_NONE;
1008 }
1009 
1010 /* --- Newspaper button menu --- */
1011 
1012 static CallBackFunction ToolbarNewspaperClick(Window *w)
1013 {
1014  PopupMainToolbMenu(w, WID_TN_MESSAGES, STR_NEWS_MENU_LAST_MESSAGE_NEWS_REPORT, 2);
1015  return CBF_NONE;
1016 }
1017 
1025 {
1026  switch (index) {
1027  case 0: ShowLastNewsMessage(); break;
1028  case 1: ShowMessageHistory(); break;
1029  }
1030  return CBF_NONE;
1031 }
1032 
1033 /* --- Help button menu --- */
1034 
1035 static CallBackFunction PlaceLandBlockInfo()
1036 {
1037  if (_last_started_action == CBF_PLACE_LANDINFO) {
1039  return CBF_NONE;
1040  } else {
1041  SetObjectToPlace(SPR_CURSOR_QUERY, PAL_NONE, HT_RECT, WC_MAIN_TOOLBAR, 0);
1042  return CBF_PLACE_LANDINFO;
1043  }
1044 }
1045 
1046 static CallBackFunction ToolbarHelpClick(Window *w)
1047 {
1048  PopupMainToolbMenu(w, WID_TN_HELP, STR_ABOUT_MENU_LAND_BLOCK_INFO, _settings_client.gui.newgrf_developer_tools ? 12 : 9);
1049  return CBF_NONE;
1050 }
1051 
1052 static void MenuClickSmallScreenshot()
1053 {
1054  MakeScreenshot(SC_VIEWPORT, NULL);
1055 }
1056 
1062 static void ScreenshotConfirmCallback(Window *w, bool confirmed)
1063 {
1064  if (confirmed) MakeScreenshot(_confirmed_screenshot_type, NULL);
1065 }
1066 
1073 {
1074  ViewPort vp;
1075  SetupScreenshotViewport(t, &vp);
1076  if ((uint64)vp.width * (uint64)vp.height > 8192 * 8192) {
1077  /* Ask for confirmation */
1078  SetDParam(0, vp.width);
1079  SetDParam(1, vp.height);
1081  ShowQuery(STR_WARNING_SCREENSHOT_SIZE_CAPTION, STR_WARNING_SCREENSHOT_SIZE_MESSAGE, NULL, ScreenshotConfirmCallback);
1082  } else {
1083  /* Less than 64M pixels, just do it */
1084  MakeScreenshot(t, NULL);
1085  }
1086 }
1087 
1096 {
1097  extern bool _draw_bounding_boxes;
1098  /* Always allow to toggle them off */
1099  if (_settings_client.gui.newgrf_developer_tools || _draw_bounding_boxes) {
1100  _draw_bounding_boxes = !_draw_bounding_boxes;
1102  }
1103 }
1104 
1113 {
1114  extern bool _draw_dirty_blocks;
1115  /* Always allow to toggle them off */
1116  if (_settings_client.gui.newgrf_developer_tools || _draw_dirty_blocks) {
1117  _draw_dirty_blocks = !_draw_dirty_blocks;
1119  }
1120 }
1121 
1127 {
1130  /* If you open a savegame as scenario there may already be link graphs.*/
1132  SetDate(new_date, 0);
1133 }
1134 
1141 {
1142  switch (index) {
1143  case 0: return PlaceLandBlockInfo();
1144  case 2: IConsoleSwitch(); break;
1145  case 3: ShowAIDebugWindow(); break;
1146  case 4: MenuClickSmallScreenshot(); break;
1149  case 7: MenuClickLargeWorldScreenshot(SC_WORLD); break;
1150  case 8: ShowAboutWindow(); break;
1151  case 9: ShowSpriteAlignerWindow(); break;
1152  case 10: ToggleBoundingBoxes(); break;
1153  case 11: ToggleDirtyBlocks(); break;
1154  }
1155  return CBF_NONE;
1156 }
1157 
1158 /* --- Switch toolbar button --- */
1159 
1160 static CallBackFunction ToolbarSwitchClick(Window *w)
1161 {
1162  if (_toolbar_mode != TB_LOWER) {
1163  _toolbar_mode = TB_LOWER;
1164  } else {
1165  _toolbar_mode = TB_UPPER;
1166  }
1167 
1168  w->ReInit();
1169  w->SetWidgetLoweredState(WID_TN_SWITCH_BAR, _toolbar_mode == TB_LOWER);
1170  if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
1171  return CBF_NONE;
1172 }
1173 
1174 /* --- Scenario editor specific handlers. */
1175 
1180 {
1182  ShowQueryString(STR_JUST_INT, STR_MAPGEN_START_DATE_QUERY_CAPT, 8, w, CS_NUMERAL, QSF_ENABLE_DEFAULT);
1183  _left_button_clicked = false;
1184  return CBF_NONE;
1185 }
1186 
1187 static CallBackFunction ToolbarScenDateBackward(Window *w)
1188 {
1189  /* don't allow too fast scrolling */
1190  if (!(w->flags & WF_TIMEOUT) || w->timeout_timer <= 1) {
1192  w->SetDirty();
1193 
1195  }
1196  _left_button_clicked = false;
1197  return CBF_NONE;
1198 }
1199 
1200 static CallBackFunction ToolbarScenDateForward(Window *w)
1201 {
1202  /* don't allow too fast scrolling */
1203  if (!(w->flags & WF_TIMEOUT) || w->timeout_timer <= 1) {
1205  w->SetDirty();
1206 
1208  }
1209  _left_button_clicked = false;
1210  return CBF_NONE;
1211 }
1212 
1213 static CallBackFunction ToolbarScenGenLand(Window *w)
1214 {
1216  if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
1217 
1219  return CBF_NONE;
1220 }
1221 
1222 
1223 static CallBackFunction ToolbarScenGenTown(Window *w)
1224 {
1226  if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
1227  ShowFoundTownWindow();
1228  return CBF_NONE;
1229 }
1230 
1231 static CallBackFunction ToolbarScenGenIndustry(Window *w)
1232 {
1234  if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
1235  ShowBuildIndustryWindow();
1236  return CBF_NONE;
1237 }
1238 
1239 static CallBackFunction ToolbarScenBuildRoad(Window *w)
1240 {
1242  if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
1244  return CBF_NONE;
1245 }
1246 
1247 static CallBackFunction ToolbarScenBuildDocks(Window *w)
1248 {
1250  if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
1252  return CBF_NONE;
1253 }
1254 
1255 static CallBackFunction ToolbarScenPlantTrees(Window *w)
1256 {
1258  if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
1259  ShowBuildTreesToolbar();
1260  return CBF_NONE;
1261 }
1262 
1263 static CallBackFunction ToolbarScenPlaceSign(Window *w)
1264 {
1266  if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
1267  return SelectSignTool();
1268 }
1269 
1270 static CallBackFunction ToolbarBtn_NULL(Window *w)
1271 {
1272  return CBF_NONE;
1273 }
1274 
1275 typedef CallBackFunction MenuClickedProc(int index);
1276 
1277 static MenuClickedProc * const _menu_clicked_procs[] = {
1278  NULL, // 0
1279  NULL, // 1
1280  MenuClickSettings, // 2
1281  MenuClickSaveLoad, // 3
1282  MenuClickMap, // 4
1283  MenuClickTown, // 5
1284  MenuClickSubsidies, // 6
1285  MenuClickStations, // 7
1286  MenuClickFinances, // 8
1287  MenuClickCompany, // 9
1288  MenuClickStory, // 10
1289  MenuClickGoal, // 11
1290  MenuClickGraphs, // 12
1291  MenuClickLeague, // 13
1292  MenuClickIndustry, // 14
1293  MenuClickShowTrains, // 15
1294  MenuClickShowRoad, // 16
1295  MenuClickShowShips, // 17
1296  MenuClickShowAir, // 18
1297  MenuClickMap, // 19
1298  NULL, // 20
1299  MenuClickBuildRail, // 21
1300  MenuClickBuildRoad, // 22
1301  MenuClickBuildWater, // 23
1302  MenuClickBuildAir, // 24
1303  MenuClickForest, // 25
1304  MenuClickMusicWindow, // 26
1305  MenuClickNewspaper, // 27
1306  MenuClickHelp, // 28
1307 };
1308 
1312 protected:
1313  uint spacers;
1314 
1315 public:
1317  {
1318  }
1319 
1326  {
1327  return type == WWT_IMGBTN || type == WWT_IMGBTN_2 || type == WWT_PUSHIMGBTN;
1328  }
1329 
1330  void SetupSmallestSize(Window *w, bool init_array)
1331  {
1332  this->smallest_x = 0; // Biggest child
1333  this->smallest_y = 0; // Biggest child
1334  this->fill_x = 1;
1335  this->fill_y = 0;
1336  this->resize_x = 1; // We only resize in this direction
1337  this->resize_y = 0; // We never resize in this direction
1338  this->spacers = 0;
1339 
1340  uint nbuttons = 0;
1341  /* First initialise some variables... */
1342  for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) {
1343  child_wid->SetupSmallestSize(w, init_array);
1344  this->smallest_y = max(this->smallest_y, child_wid->smallest_y + child_wid->padding_top + child_wid->padding_bottom);
1345  if (this->IsButton(child_wid->type)) {
1346  nbuttons++;
1347  this->smallest_x = max(this->smallest_x, child_wid->smallest_x + child_wid->padding_left + child_wid->padding_right);
1348  } else if (child_wid->type == NWID_SPACER) {
1349  this->spacers++;
1350  }
1351  }
1352 
1353  /* ... then in a second pass make sure the 'current' heights are set. Won't change ever. */
1354  for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) {
1355  child_wid->current_y = this->smallest_y;
1356  if (!this->IsButton(child_wid->type)) {
1357  child_wid->current_x = child_wid->smallest_x;
1358  }
1359  }
1360  _toolbar_width = nbuttons * this->smallest_x;
1361  }
1362 
1363  void AssignSizePosition(SizingType sizing, uint x, uint y, uint given_width, uint given_height, bool rtl)
1364  {
1365  assert(given_width >= this->smallest_x && given_height >= this->smallest_y);
1366 
1367  this->pos_x = x;
1368  this->pos_y = y;
1369  this->current_x = given_width;
1370  this->current_y = given_height;
1371 
1372  /* Figure out what are the visible buttons */
1373  memset(this->visible, 0, sizeof(this->visible));
1374  uint arrangable_count, button_count, spacer_count;
1375  const byte *arrangement = GetButtonArrangement(given_width, arrangable_count, button_count, spacer_count);
1376  for (uint i = 0; i < arrangable_count; i++) {
1377  this->visible[arrangement[i]] = true;
1378  }
1379 
1380  /* Create us ourselves a quick lookup table */
1381  NWidgetBase *widgets[WID_TN_END];
1382  for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) {
1383  if (child_wid->type == NWID_SPACER) continue;
1384  widgets[((NWidgetCore*)child_wid)->index] = child_wid;
1385  }
1386 
1387  /* Now assign the widgets to their rightful place */
1388  uint position = 0; // Place to put next child relative to origin of the container.
1389  uint spacer_space = max(0, (int)given_width - (int)(button_count * this->smallest_x)); // Remaining spacing for 'spacer' widgets
1390  uint button_space = given_width - spacer_space; // Remaining spacing for the buttons
1391  uint spacer_i = 0;
1392  uint button_i = 0;
1393 
1394  /* Index into the arrangement indices. The macro lastof cannot be used here! */
1395  const byte *cur_wid = rtl ? &arrangement[arrangable_count - 1] : arrangement;
1396  for (uint i = 0; i < arrangable_count; i++) {
1397  NWidgetBase *child_wid = widgets[*cur_wid];
1398  /* If we have to give space to the spacers, do that */
1399  if (spacer_space != 0) {
1400  NWidgetBase *possible_spacer = rtl ? child_wid->next : child_wid->prev;
1401  if (possible_spacer != NULL && possible_spacer->type == NWID_SPACER) {
1402  uint add = spacer_space / (spacer_count - spacer_i);
1403  position += add;
1404  spacer_space -= add;
1405  spacer_i++;
1406  }
1407  }
1408 
1409  /* Buttons can be scaled, the others not. */
1410  if (this->IsButton(child_wid->type)) {
1411  child_wid->current_x = button_space / (button_count - button_i);
1412  button_space -= child_wid->current_x;
1413  button_i++;
1414  }
1415  child_wid->AssignSizePosition(sizing, x + position, y, child_wid->current_x, this->current_y, rtl);
1416  position += child_wid->current_x;
1417 
1418  if (rtl) {
1419  cur_wid--;
1420  } else {
1421  cur_wid++;
1422  }
1423  }
1424  }
1425 
1426  /* virtual */ void Draw(const Window *w)
1427  {
1428  /* Draw brown-red toolbar bg. */
1429  GfxFillRect(this->pos_x, this->pos_y, this->pos_x + this->current_x - 1, this->pos_y + this->current_y - 1, PC_VERY_DARK_RED);
1430  GfxFillRect(this->pos_x, this->pos_y, this->pos_x + this->current_x - 1, this->pos_y + this->current_y - 1, PC_DARK_RED, FILLRECT_CHECKER);
1431 
1432  bool rtl = _current_text_dir == TD_RTL;
1433  for (NWidgetBase *child_wid = rtl ? this->tail : this->head; child_wid != NULL; child_wid = rtl ? child_wid->prev : child_wid->next) {
1434  if (child_wid->type == NWID_SPACER) continue;
1435  if (!this->visible[((NWidgetCore*)child_wid)->index]) continue;
1436 
1437  child_wid->Draw(w);
1438  }
1439  }
1440 
1441  /* virtual */ NWidgetCore *GetWidgetFromPos(int x, int y)
1442  {
1443  if (!IsInsideBS(x, this->pos_x, this->current_x) || !IsInsideBS(y, this->pos_y, this->current_y)) return NULL;
1444 
1445  for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) {
1446  if (child_wid->type == NWID_SPACER) continue;
1447  if (!this->visible[((NWidgetCore*)child_wid)->index]) continue;
1448 
1449  NWidgetCore *nwid = child_wid->GetWidgetFromPos(x, y);
1450  if (nwid != NULL) return nwid;
1451  }
1452  return NULL;
1453  }
1454 
1463  virtual const byte *GetButtonArrangement(uint &width, uint &arrangable_count, uint &button_count, uint &spacer_count) const = 0;
1464 };
1465 
1468  /* virtual */ const byte *GetButtonArrangement(uint &width, uint &arrangable_count, uint &button_count, uint &spacer_count) const
1469  {
1470  static const uint SMALLEST_ARRANGEMENT = 14;
1471  static const uint BIGGEST_ARRANGEMENT = 20;
1472 
1473  /* The number of buttons of each row of the toolbar should match the number of items which we want to be visible.
1474  * The total number of buttons should be equal to arrangable_count * 2.
1475  * No bad things happen, but we could see strange behaviours if we have buttons < (arrangable_count * 2) like a
1476  * pause button appearing on the right of the lower toolbar and weird resizing of the widgets even if there is
1477  * enough space.
1478  */
1479  static const byte arrange14[] = {
1480  WID_TN_PAUSE,
1482  WID_TN_TRAINS,
1484  WID_TN_SHIPS,
1488  WID_TN_RAILS,
1489  WID_TN_ROADS,
1490  WID_TN_WATER,
1491  WID_TN_AIR,
1494  // lower toolbar
1496  WID_TN_SAVE,
1498  WID_TN_TOWNS,
1503  WID_TN_GRAPHS,
1507  WID_TN_HELP,
1509  };
1510  static const byte arrange15[] = {
1511  WID_TN_PAUSE,
1514  WID_TN_TRAINS,
1516  WID_TN_SHIPS,
1518  WID_TN_RAILS,
1519  WID_TN_ROADS,
1520  WID_TN_WATER,
1521  WID_TN_AIR,
1526  // lower toolbar
1527  WID_TN_PAUSE,
1530  WID_TN_SAVE,
1531  WID_TN_TOWNS,
1536  WID_TN_GRAPHS,
1540  WID_TN_HELP,
1542  };
1543  static const byte arrange16[] = {
1544  WID_TN_PAUSE,
1548  WID_TN_TRAINS,
1550  WID_TN_SHIPS,
1552  WID_TN_RAILS,
1553  WID_TN_ROADS,
1554  WID_TN_WATER,
1555  WID_TN_AIR,
1560  // lower toolbar
1561  WID_TN_PAUSE,
1563  WID_TN_SAVE,
1564  WID_TN_TOWNS,
1569  WID_TN_GRAPHS,
1573  WID_TN_HELP,
1577  };
1578  static const byte arrange17[] = {
1579  WID_TN_PAUSE,
1584  WID_TN_TRAINS,
1586  WID_TN_SHIPS,
1588  WID_TN_RAILS,
1589  WID_TN_ROADS,
1590  WID_TN_WATER,
1591  WID_TN_AIR,
1596  // lower toolbar
1597  WID_TN_PAUSE,
1599  WID_TN_SAVE,
1602  WID_TN_TOWNS,
1606  WID_TN_GRAPHS,
1610  WID_TN_HELP,
1614  };
1615  static const byte arrange18[] = {
1616  WID_TN_PAUSE,
1620  WID_TN_TOWNS,
1626  WID_TN_RAILS,
1627  WID_TN_ROADS,
1628  WID_TN_WATER,
1629  WID_TN_AIR,
1634  // lower toolbar
1635  WID_TN_PAUSE,
1637  WID_TN_SAVE,
1639  WID_TN_TOWNS,
1642  WID_TN_GRAPHS,
1643  WID_TN_TRAINS,
1645  WID_TN_SHIPS,
1649  WID_TN_HELP,
1653  };
1654  static const byte arrange19[] = {
1655  WID_TN_PAUSE,
1659  WID_TN_TOWNS,
1661  WID_TN_TRAINS,
1663  WID_TN_SHIPS,
1665  WID_TN_RAILS,
1666  WID_TN_ROADS,
1667  WID_TN_WATER,
1668  WID_TN_AIR,
1674  // lower toolbar
1675  WID_TN_PAUSE,
1677  WID_TN_SAVE,
1682  WID_TN_GRAPHS,
1685  WID_TN_RAILS,
1686  WID_TN_ROADS,
1687  WID_TN_WATER,
1688  WID_TN_AIR,
1690  WID_TN_HELP,
1694  };
1695  static const byte arrange20[] = {
1696  WID_TN_PAUSE,
1700  WID_TN_TOWNS,
1702  WID_TN_TRAINS,
1704  WID_TN_SHIPS,
1706  WID_TN_RAILS,
1707  WID_TN_ROADS,
1708  WID_TN_WATER,
1709  WID_TN_AIR,
1712  WID_TN_GOAL,
1716  // lower toolbar
1717  WID_TN_PAUSE,
1719  WID_TN_SAVE,
1724  WID_TN_GRAPHS,
1727  WID_TN_RAILS,
1728  WID_TN_ROADS,
1729  WID_TN_WATER,
1730  WID_TN_AIR,
1732  WID_TN_STORY,
1733  WID_TN_HELP,
1737  };
1738  static const byte arrange_all[] = {
1739  WID_TN_PAUSE,
1742  WID_TN_SAVE,
1744  WID_TN_TOWNS,
1749  WID_TN_STORY,
1750  WID_TN_GOAL,
1751  WID_TN_GRAPHS,
1752  WID_TN_LEAGUE,
1754  WID_TN_TRAINS,
1756  WID_TN_SHIPS,
1760  WID_TN_RAILS,
1761  WID_TN_ROADS,
1762  WID_TN_WATER,
1763  WID_TN_AIR,
1767  WID_TN_HELP
1768  };
1769 
1770  /* If at least BIGGEST_ARRANGEMENT fit, just spread all the buttons nicely */
1771  uint full_buttons = max(CeilDiv(width, this->smallest_x), SMALLEST_ARRANGEMENT);
1772  if (full_buttons > BIGGEST_ARRANGEMENT) {
1773  button_count = arrangable_count = lengthof(arrange_all);
1774  spacer_count = this->spacers;
1775  return arrange_all;
1776  }
1777 
1778  /* Introduce the split toolbar */
1779  static const byte * const arrangements[] = { arrange14, arrange15, arrange16, arrange17, arrange18, arrange19, arrange20 };
1780 
1781  button_count = arrangable_count = full_buttons;
1782  spacer_count = this->spacers;
1783  return arrangements[full_buttons - SMALLEST_ARRANGEMENT] + ((_toolbar_mode == TB_LOWER) ? full_buttons : 0);
1784  }
1785 };
1786 
1789  uint panel_widths[2];
1790 
1791  void SetupSmallestSize(Window *w, bool init_array)
1792  {
1793  this->NWidgetToolbarContainer::SetupSmallestSize(w, init_array);
1794 
1795  /* Find the size of panel_widths */
1796  uint i = 0;
1797  for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) {
1798  if (child_wid->type == NWID_SPACER || this->IsButton(child_wid->type)) continue;
1799 
1800  assert(i < lengthof(this->panel_widths));
1801  this->panel_widths[i++] = child_wid->current_x;
1802  _toolbar_width += child_wid->current_x;
1803  }
1804  }
1805 
1806  /* virtual */ const byte *GetButtonArrangement(uint &width, uint &arrangable_count, uint &button_count, uint &spacer_count) const
1807  {
1808  static const byte arrange_all[] = {
1809  WID_TE_PAUSE,
1812  WID_TE_SAVE,
1813  WID_TE_SPACER,
1821  WID_TE_ROADS,
1822  WID_TE_WATER,
1823  WID_TE_TREES,
1824  WID_TE_SIGNS,
1826  WID_TE_HELP,
1827  };
1828  static const byte arrange_nopanel[] = {
1829  WID_TE_PAUSE,
1832  WID_TE_SAVE,
1840  WID_TE_ROADS,
1841  WID_TE_WATER,
1842  WID_TE_TREES,
1843  WID_TE_SIGNS,
1845  WID_TE_HELP,
1846  };
1847  static const byte arrange_switch[] = {
1853  WID_TE_ROADS,
1854  WID_TE_WATER,
1855  WID_TE_TREES,
1856  WID_TE_SIGNS,
1858  // lower toolbar
1859  WID_TE_PAUSE,
1862  WID_TE_SAVE,
1868  };
1869 
1870  /* If we can place all buttons *and* the panels, show them. */
1871  uint min_full_width = (lengthof(arrange_all) - lengthof(this->panel_widths)) * this->smallest_x + this->panel_widths[0] + this->panel_widths[1];
1872  if (width >= min_full_width) {
1873  width -= this->panel_widths[0] + this->panel_widths[1];
1874  arrangable_count = lengthof(arrange_all);
1875  button_count = arrangable_count - 2;
1876  spacer_count = this->spacers;
1877  return arrange_all;
1878  }
1879 
1880  /* Otherwise don't show the date panel and if we can't fit half the buttons and the panels anymore, split the toolbar in two */
1881  uint min_small_width = (lengthof(arrange_switch) - lengthof(this->panel_widths)) * this->smallest_x / 2 + this->panel_widths[1];
1882  if (width > min_small_width) {
1883  width -= this->panel_widths[1];
1884  arrangable_count = lengthof(arrange_nopanel);
1885  button_count = arrangable_count - 1;
1886  spacer_count = this->spacers - 1;
1887  return arrange_nopanel;
1888  }
1889 
1890  /* Split toolbar */
1891  width -= this->panel_widths[1];
1892  arrangable_count = lengthof(arrange_switch) / 2;
1893  button_count = arrangable_count - 1;
1894  spacer_count = 0;
1895  return arrange_switch + ((_toolbar_mode == TB_LOWER) ? arrangable_count : 0);
1896  }
1897 };
1898 
1899 /* --- Toolbar handling for the 'normal' case */
1900 
1901 typedef CallBackFunction ToolbarButtonProc(Window *w);
1902 
1903 static ToolbarButtonProc * const _toolbar_button_procs[] = {
1904  ToolbarPauseClick,
1908  ToolbarMapClick,
1909  ToolbarTownClick,
1910  ToolbarSubsidiesClick,
1911  ToolbarStationsClick,
1912  ToolbarFinancesClick,
1913  ToolbarCompaniesClick,
1914  ToolbarStoryClick,
1915  ToolbarGoalClick,
1916  ToolbarGraphsClick,
1917  ToolbarLeagueClick,
1918  ToolbarIndustryClick,
1919  ToolbarTrainClick,
1920  ToolbarRoadClick,
1921  ToolbarShipClick,
1922  ToolbarAirClick,
1923  ToolbarZoomInClick,
1924  ToolbarZoomOutClick,
1925  ToolbarBuildRailClick,
1926  ToolbarBuildRoadClick,
1927  ToolbarBuildWaterClick,
1928  ToolbarBuildAirClick,
1929  ToolbarForestClick,
1930  ToolbarMusicClick,
1931  ToolbarNewspaperClick,
1932  ToolbarHelpClick,
1933  ToolbarSwitchClick,
1934 };
1935 
1936 enum MainToolbarHotkeys {
1937  MTHK_PAUSE,
1938  MTHK_FASTFORWARD,
1939  MTHK_SETTINGS,
1940  MTHK_SAVEGAME,
1941  MTHK_LOADGAME,
1942  MTHK_SMALLMAP,
1943  MTHK_TOWNDIRECTORY,
1944  MTHK_SUBSIDIES,
1945  MTHK_STATIONS,
1946  MTHK_FINANCES,
1947  MTHK_COMPANIES,
1948  MTHK_STORY,
1949  MTHK_GOAL,
1950  MTHK_GRAPHS,
1951  MTHK_LEAGUE,
1952  MTHK_INDUSTRIES,
1953  MTHK_TRAIN_LIST,
1954  MTHK_ROADVEH_LIST,
1955  MTHK_SHIP_LIST,
1956  MTHK_AIRCRAFT_LIST,
1957  MTHK_ZOOM_IN,
1958  MTHK_ZOOM_OUT,
1959  MTHK_BUILD_RAIL,
1960  MTHK_BUILD_ROAD,
1961  MTHK_BUILD_DOCKS,
1962  MTHK_BUILD_AIRPORT,
1963  MTHK_BUILD_TREES,
1964  MTHK_MUSIC,
1965  MTHK_AI_DEBUG,
1966  MTHK_SMALL_SCREENSHOT,
1967  MTHK_ZOOMEDIN_SCREENSHOT,
1968  MTHK_DEFAULTZOOM_SCREENSHOT,
1969  MTHK_GIANT_SCREENSHOT,
1970  MTHK_CHEATS,
1971  MTHK_TERRAFORM,
1972  MTHK_EXTRA_VIEWPORT,
1973  MTHK_CLIENT_LIST,
1974  MTHK_SIGN_LIST,
1975 };
1976 
1979  MainToolbarWindow(WindowDesc *desc) : Window(desc)
1980  {
1981  this->InitNested(0);
1982 
1983  _last_started_action = CBF_NONE;
1984  CLRBITS(this->flags, WF_WHITE_BORDER);
1985  this->SetWidgetDisabledState(WID_TN_PAUSE, _networking && !_network_server); // if not server, disable pause button
1986  this->SetWidgetDisabledState(WID_TN_FAST_FORWARD, _networking); // if networking, disable fast-forward button
1987  PositionMainToolbar(this);
1989  }
1990 
1991  virtual void FindWindowPlacementAndResize(int def_width, int def_height)
1992  {
1994  }
1995 
1996  virtual void OnPaint()
1997  {
1998  /* If spectator, disable all construction buttons
1999  * ie : Build road, rail, ships, airports and landscaping
2000  * Since enabled state is the default, just disable when needed */
2002  /* disable company list drop downs, if there are no companies */
2004 
2007 
2010 
2011  this->DrawWidgets();
2012  }
2013 
2014  virtual void OnClick(Point pt, int widget, int click_count)
2015  {
2016  if (_game_mode != GM_MENU && !this->IsWidgetDisabled(widget)) _toolbar_button_procs[widget](this);
2017  }
2018 
2019  virtual void OnDropdownSelect(int widget, int index)
2020  {
2021  CallBackFunction cbf = _menu_clicked_procs[widget](index);
2022  if (cbf != CBF_NONE) _last_started_action = cbf;
2023  }
2024 
2025  virtual EventState OnHotkey(int hotkey)
2026  {
2027  switch (hotkey) {
2028  case MTHK_PAUSE: ToolbarPauseClick(this); break;
2029  case MTHK_FASTFORWARD: ToolbarFastForwardClick(this); break;
2030  case MTHK_SETTINGS: ShowGameOptions(); break;
2031  case MTHK_SAVEGAME: MenuClickSaveLoad(); break;
2032  case MTHK_LOADGAME: ShowSaveLoadDialog(FT_SAVEGAME, SLO_LOAD); break;
2033  case MTHK_SMALLMAP: ShowSmallMap(); break;
2034  case MTHK_TOWNDIRECTORY: ShowTownDirectory(); break;
2035  case MTHK_SUBSIDIES: ShowSubsidiesList(); break;
2036  case MTHK_STATIONS: ShowCompanyStations(_local_company); break;
2037  case MTHK_FINANCES: ShowCompanyFinances(_local_company); break;
2038  case MTHK_COMPANIES: ShowCompany(_local_company); break;
2039  case MTHK_STORY: ShowStoryBook(_local_company); break;
2040  case MTHK_GOAL: ShowGoalsList(_local_company); break;
2041  case MTHK_GRAPHS: ShowOperatingProfitGraph(); break;
2042  case MTHK_LEAGUE: ShowCompanyLeagueTable(); break;
2043  case MTHK_INDUSTRIES: ShowBuildIndustryWindow(); break;
2044  case MTHK_TRAIN_LIST: ShowVehicleListWindow(_local_company, VEH_TRAIN); break;
2045  case MTHK_ROADVEH_LIST: ShowVehicleListWindow(_local_company, VEH_ROAD); break;
2046  case MTHK_SHIP_LIST: ShowVehicleListWindow(_local_company, VEH_SHIP); break;
2047  case MTHK_AIRCRAFT_LIST: ShowVehicleListWindow(_local_company, VEH_AIRCRAFT); break;
2048  case MTHK_ZOOM_IN: ToolbarZoomInClick(this); break;
2049  case MTHK_ZOOM_OUT: ToolbarZoomOutClick(this); break;
2050  case MTHK_BUILD_RAIL: if (CanBuildVehicleInfrastructure(VEH_TRAIN)) ShowBuildRailToolbar(_last_built_railtype); break;
2051  case MTHK_BUILD_ROAD: ShowBuildRoadToolbar(_last_built_roadtype); break;
2052  case MTHK_BUILD_DOCKS: ShowBuildDocksToolbar(); break;
2053  case MTHK_BUILD_AIRPORT: if (CanBuildVehicleInfrastructure(VEH_AIRCRAFT)) ShowBuildAirToolbar(); break;
2054  case MTHK_BUILD_TREES: ShowBuildTreesToolbar(); break;
2055  case MTHK_MUSIC: ShowMusicWindow(); break;
2056  case MTHK_AI_DEBUG: ShowAIDebugWindow(); break;
2057  case MTHK_SMALL_SCREENSHOT: MenuClickSmallScreenshot(); break;
2058  case MTHK_ZOOMEDIN_SCREENSHOT: MenuClickLargeWorldScreenshot(SC_ZOOMEDIN); break;
2059  case MTHK_DEFAULTZOOM_SCREENSHOT: MenuClickLargeWorldScreenshot(SC_DEFAULTZOOM); break;
2060  case MTHK_GIANT_SCREENSHOT: MenuClickLargeWorldScreenshot(SC_WORLD); break;
2061  case MTHK_CHEATS: if (!_networking) ShowCheatWindow(); break;
2062  case MTHK_TERRAFORM: ShowTerraformToolbar(); break;
2063  case MTHK_EXTRA_VIEWPORT: ShowExtraViewPortWindowForTileUnderCursor(); break;
2064 #ifdef ENABLE_NETWORK
2065  case MTHK_CLIENT_LIST: if (_networking) ShowClientList(); break;
2066 #endif
2067  case MTHK_SIGN_LIST: ShowSignList(); break;
2068  default: return ES_NOT_HANDLED;
2069  }
2070  return ES_HANDLED;
2071  }
2072 
2073  virtual void OnPlaceObject(Point pt, TileIndex tile)
2074  {
2075  switch (_last_started_action) {
2076  case CBF_PLACE_SIGN:
2077  PlaceProc_Sign(tile);
2078  break;
2079 
2080  case CBF_PLACE_LANDINFO:
2081  ShowLandInfo(tile);
2082  break;
2083 
2084  default: NOT_REACHED();
2085  }
2086  }
2087 
2088  virtual void OnPlaceObjectAbort()
2089  {
2090  _last_started_action = CBF_NONE;
2091  }
2092 
2093  virtual void OnTick()
2094  {
2095  if (this->IsWidgetLowered(WID_TN_PAUSE) != !!_pause_mode) {
2098  }
2099 
2100  if (this->IsWidgetLowered(WID_TN_FAST_FORWARD) != !!_fast_forward) {
2103  }
2104  }
2105 
2106  virtual void OnTimeout()
2107  {
2108  /* We do not want to automatically raise the pause, fast forward and
2109  * switchbar buttons; they have to stay down when pressed etc. */
2110  for (uint i = WID_TN_SETTINGS; i < WID_TN_SWITCH_BAR; i++) {
2111  if (this->IsWidgetLowered(i)) {
2112  this->RaiseWidget(i);
2113  this->SetWidgetDirty(i);
2114  }
2115  }
2116  }
2117 
2123  virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
2124  {
2125  if (!gui_scope) return;
2127  }
2128 
2129  static HotkeyList hotkeys;
2130 };
2131 
2132 const uint16 _maintoolbar_pause_keys[] = {WKC_F1, WKC_PAUSE, 0};
2133 const uint16 _maintoolbar_zoomin_keys[] = {WKC_NUM_PLUS, WKC_EQUALS, WKC_SHIFT | WKC_EQUALS, WKC_SHIFT | WKC_F5, 0};
2134 const uint16 _maintoolbar_zoomout_keys[] = {WKC_NUM_MINUS, WKC_MINUS, WKC_SHIFT | WKC_MINUS, WKC_SHIFT | WKC_F6, 0};
2135 const uint16 _maintoolbar_smallmap_keys[] = {WKC_F4, 'M', 0};
2136 
2137 static Hotkey maintoolbar_hotkeys[] = {
2138  Hotkey(_maintoolbar_pause_keys, "pause", MTHK_PAUSE),
2139  Hotkey((uint16)0, "fastforward", MTHK_FASTFORWARD),
2140  Hotkey(WKC_F2, "settings", MTHK_SETTINGS),
2141  Hotkey(WKC_F3, "saveload", MTHK_SAVEGAME),
2142  Hotkey((uint16)0, "load_game", MTHK_LOADGAME),
2143  Hotkey(_maintoolbar_smallmap_keys, "smallmap", MTHK_SMALLMAP),
2144  Hotkey(WKC_F5, "town_list", MTHK_TOWNDIRECTORY),
2145  Hotkey(WKC_F6, "subsidies", MTHK_SUBSIDIES),
2146  Hotkey(WKC_F7, "station_list", MTHK_STATIONS),
2147  Hotkey(WKC_F8, "finances", MTHK_FINANCES),
2148  Hotkey(WKC_F9, "companies", MTHK_COMPANIES),
2149  Hotkey((uint16)0, "story_book", MTHK_STORY),
2150  Hotkey((uint16)0, "goal_list", MTHK_GOAL),
2151  Hotkey(WKC_F10, "graphs", MTHK_GRAPHS),
2152  Hotkey(WKC_F11, "league", MTHK_LEAGUE),
2153  Hotkey(WKC_F12, "industry_list", MTHK_INDUSTRIES),
2154  Hotkey(WKC_SHIFT | WKC_F1, "train_list", MTHK_TRAIN_LIST),
2155  Hotkey(WKC_SHIFT | WKC_F2, "roadveh_list", MTHK_ROADVEH_LIST),
2156  Hotkey(WKC_SHIFT | WKC_F3, "ship_list", MTHK_SHIP_LIST),
2157  Hotkey(WKC_SHIFT | WKC_F4, "aircraft_list", MTHK_AIRCRAFT_LIST),
2158  Hotkey(_maintoolbar_zoomin_keys, "zoomin", MTHK_ZOOM_IN),
2159  Hotkey(_maintoolbar_zoomout_keys, "zoomout", MTHK_ZOOM_OUT),
2160  Hotkey(WKC_SHIFT | WKC_F7, "build_rail", MTHK_BUILD_RAIL),
2161  Hotkey(WKC_SHIFT | WKC_F8, "build_road", MTHK_BUILD_ROAD),
2162  Hotkey(WKC_SHIFT | WKC_F9, "build_docks", MTHK_BUILD_DOCKS),
2163  Hotkey(WKC_SHIFT | WKC_F10, "build_airport", MTHK_BUILD_AIRPORT),
2164  Hotkey(WKC_SHIFT | WKC_F11, "build_trees", MTHK_BUILD_TREES),
2165  Hotkey(WKC_SHIFT | WKC_F12, "music", MTHK_MUSIC),
2166  Hotkey((uint16)0, "ai_debug", MTHK_AI_DEBUG),
2167  Hotkey(WKC_CTRL | 'S', "small_screenshot", MTHK_SMALL_SCREENSHOT),
2168  Hotkey(WKC_CTRL | 'P', "zoomedin_screenshot", MTHK_ZOOMEDIN_SCREENSHOT),
2169  Hotkey(WKC_CTRL | 'D', "defaultzoom_screenshot", MTHK_DEFAULTZOOM_SCREENSHOT),
2170  Hotkey((uint16)0, "giant_screenshot", MTHK_GIANT_SCREENSHOT),
2171  Hotkey(WKC_CTRL | WKC_ALT | 'C', "cheats", MTHK_CHEATS),
2172  Hotkey('L', "terraform", MTHK_TERRAFORM),
2173  Hotkey('V', "extra_viewport", MTHK_EXTRA_VIEWPORT),
2174 #ifdef ENABLE_NETWORK
2175  Hotkey((uint16)0, "client_list", MTHK_CLIENT_LIST),
2176 #endif
2177  Hotkey((uint16)0, "sign_list", MTHK_SIGN_LIST),
2178  HOTKEY_LIST_END
2179 };
2180 HotkeyList MainToolbarWindow::hotkeys("maintoolbar", maintoolbar_hotkeys);
2181 
2182 static NWidgetBase *MakeMainToolbar(int *biggest_index)
2183 {
2185  static const SpriteID toolbar_button_sprites[] = {
2186  SPR_IMG_PAUSE, // WID_TN_PAUSE
2187  SPR_IMG_FASTFORWARD, // WID_TN_FAST_FORWARD
2188  SPR_IMG_SETTINGS, // WID_TN_SETTINGS
2189  SPR_IMG_SAVE, // WID_TN_SAVE
2190  SPR_IMG_SMALLMAP, // WID_TN_SMALL_MAP
2191  SPR_IMG_TOWN, // WID_TN_TOWNS
2192  SPR_IMG_SUBSIDIES, // WID_TN_SUBSIDIES
2193  SPR_IMG_COMPANY_LIST, // WID_TN_STATIONS
2194  SPR_IMG_COMPANY_FINANCE, // WID_TN_FINANCES
2195  SPR_IMG_COMPANY_GENERAL, // WID_TN_COMPANIES
2196  SPR_IMG_STORY_BOOK, // WID_TN_STORY
2197  SPR_IMG_GOAL, // WID_TN_GOAL
2198  SPR_IMG_GRAPHS, // WID_TN_GRAPHS
2199  SPR_IMG_COMPANY_LEAGUE, // WID_TN_LEAGUE
2200  SPR_IMG_INDUSTRY, // WID_TN_INDUSTRIES
2201  SPR_IMG_TRAINLIST, // WID_TN_TRAINS
2202  SPR_IMG_TRUCKLIST, // WID_TN_ROADVEHS
2203  SPR_IMG_SHIPLIST, // WID_TN_SHIPS
2204  SPR_IMG_AIRPLANESLIST, // WID_TN_AIRCRAFT
2205  SPR_IMG_ZOOMIN, // WID_TN_ZOOMIN
2206  SPR_IMG_ZOOMOUT, // WID_TN_ZOOMOUT
2207  SPR_IMG_BUILDRAIL, // WID_TN_RAILS
2208  SPR_IMG_BUILDROAD, // WID_TN_ROADS
2209  SPR_IMG_BUILDWATER, // WID_TN_WATER
2210  SPR_IMG_BUILDAIR, // WID_TN_AIR
2211  SPR_IMG_LANDSCAPING, // WID_TN_LANDSCAPE
2212  SPR_IMG_MUSIC, // WID_TN_MUSIC_SOUND
2213  SPR_IMG_MESSAGES, // WID_TN_MESSAGES
2214  SPR_IMG_QUERY, // WID_TN_HELP
2215  SPR_IMG_SWITCH_TOOLBAR, // WID_TN_SWITCH_BAR
2216  };
2217 
2219  for (uint i = 0; i < WID_TN_END; i++) {
2220  switch (i) {
2221  case WID_TN_SMALL_MAP:
2222  case WID_TN_FINANCES:
2223  case WID_TN_VEHICLE_START:
2224  case WID_TN_ZOOM_IN:
2226  case WID_TN_MUSIC_SOUND:
2227  hor->Add(new NWidgetSpacer(0, 0));
2228  break;
2229  }
2230  hor->Add(new NWidgetLeaf(i == WID_TN_SAVE ? WWT_IMGBTN_2 : WWT_IMGBTN, COLOUR_GREY, i, toolbar_button_sprites[i], STR_TOOLBAR_TOOLTIP_PAUSE_GAME + i));
2231  }
2232 
2233  *biggest_index = max<int>(*biggest_index, WID_TN_SWITCH_BAR);
2234  return hor;
2235 }
2236 
2237 static const NWidgetPart _nested_toolbar_normal_widgets[] = {
2239 };
2240 
2241 static WindowDesc _toolb_normal_desc(
2242  WDP_MANUAL, NULL, 0, 0,
2244  WDF_NO_FOCUS,
2245  _nested_toolbar_normal_widgets, lengthof(_nested_toolbar_normal_widgets),
2246  &MainToolbarWindow::hotkeys
2247 );
2248 
2249 
2250 /* --- Toolbar handling for the scenario editor */
2251 
2252 static ToolbarButtonProc * const _scen_toolbar_button_procs[] = {
2253  ToolbarPauseClick,
2257  ToolbarBtn_NULL,
2259  ToolbarScenDateBackward,
2260  ToolbarScenDateForward,
2261  ToolbarScenMapTownDir,
2262  ToolbarZoomInClick,
2263  ToolbarZoomOutClick,
2264  ToolbarScenGenLand,
2265  ToolbarScenGenTown,
2266  ToolbarScenGenIndustry,
2267  ToolbarScenBuildRoad,
2268  ToolbarScenBuildDocks,
2269  ToolbarScenPlantTrees,
2270  ToolbarScenPlaceSign,
2271  ToolbarBtn_NULL,
2272  NULL,
2273  NULL,
2274  NULL,
2275  NULL,
2276  NULL,
2277  NULL,
2278  NULL,
2279  ToolbarMusicClick,
2280  NULL,
2281  ToolbarHelpClick,
2282  ToolbarSwitchClick,
2283 };
2284 
2285 enum MainToolbarEditorHotkeys {
2286  MTEHK_PAUSE,
2287  MTEHK_FASTFORWARD,
2288  MTEHK_SETTINGS,
2289  MTEHK_SAVEGAME,
2290  MTEHK_GENLAND,
2291  MTEHK_GENTOWN,
2292  MTEHK_GENINDUSTRY,
2293  MTEHK_BUILD_ROAD,
2294  MTEHK_BUILD_DOCKS,
2295  MTEHK_BUILD_TREES,
2296  MTEHK_SIGN,
2297  MTEHK_MUSIC,
2298  MTEHK_LANDINFO,
2299  MTEHK_SMALL_SCREENSHOT,
2300  MTEHK_ZOOMEDIN_SCREENSHOT,
2301  MTEHK_DEFAULTZOOM_SCREENSHOT,
2302  MTEHK_GIANT_SCREENSHOT,
2303  MTEHK_ZOOM_IN,
2304  MTEHK_ZOOM_OUT,
2305  MTEHK_TERRAFORM,
2306  MTEHK_SMALLMAP,
2307  MTEHK_EXTRA_VIEWPORT,
2308 };
2309 
2312  {
2313  this->InitNested(0);
2314 
2315  _last_started_action = CBF_NONE;
2316  CLRBITS(this->flags, WF_WHITE_BORDER);
2317  PositionMainToolbar(this);
2319  }
2320 
2321  virtual void FindWindowPlacementAndResize(int def_width, int def_height)
2322  {
2324  }
2325 
2326  virtual void OnPaint()
2327  {
2330 
2331  this->DrawWidgets();
2332  }
2333 
2334  virtual void DrawWidget(const Rect &r, int widget) const
2335  {
2336  switch (widget) {
2337  case WID_TE_DATE:
2339  DrawString(r.left, r.right, (this->height - FONT_HEIGHT_NORMAL) / 2, STR_WHITE_DATE_LONG, TC_FROMSTRING, SA_HOR_CENTER);
2340  break;
2341 
2342  case WID_TE_SPACER: {
2343  int height = r.bottom - r.top;
2344  if (height > 2 * FONT_HEIGHT_NORMAL) {
2345  DrawString(r.left, r.right, (height + 1) / 2 - FONT_HEIGHT_NORMAL, STR_SCENEDIT_TOOLBAR_OPENTTD, TC_FROMSTRING, SA_HOR_CENTER);
2346  DrawString(r.left, r.right, (height + 1) / 2, STR_SCENEDIT_TOOLBAR_SCENARIO_EDITOR, TC_FROMSTRING, SA_HOR_CENTER);
2347  } else {
2348  DrawString(r.left, r.right, (height - FONT_HEIGHT_NORMAL) / 2, STR_SCENEDIT_TOOLBAR_SCENARIO_EDITOR, TC_FROMSTRING, SA_HOR_CENTER);
2349  }
2350  break;
2351  }
2352  }
2353  }
2354 
2355  virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
2356  {
2357  switch (widget) {
2358  case WID_TE_SPACER:
2359  size->width = max(GetStringBoundingBox(STR_SCENEDIT_TOOLBAR_OPENTTD).width, GetStringBoundingBox(STR_SCENEDIT_TOOLBAR_SCENARIO_EDITOR).width) + WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT;
2360  break;
2361 
2362  case WID_TE_DATE:
2363  SetDParam(0, ConvertYMDToDate(MAX_YEAR, 0, 1));
2364  *size = GetStringBoundingBox(STR_WHITE_DATE_LONG);
2365  size->height = max(size->height, GetSpriteSize(SPR_IMG_SAVE).height + WD_IMGBTN_TOP + WD_IMGBTN_BOTTOM);
2366  break;
2367  }
2368  }
2369 
2370  virtual void OnClick(Point pt, int widget, int click_count)
2371  {
2372  if (_game_mode == GM_MENU) return;
2373  CallBackFunction cbf = _scen_toolbar_button_procs[widget](this);
2374  if (cbf != CBF_NONE) _last_started_action = cbf;
2375  }
2376 
2377  virtual void OnDropdownSelect(int widget, int index)
2378  {
2379  /* The map button is in a different location on the scenario
2380  * editor toolbar, so we need to adjust for it. */
2381  if (widget == WID_TE_SMALL_MAP) widget = WID_TN_SMALL_MAP;
2382  CallBackFunction cbf = _menu_clicked_procs[widget](index);
2383  if (cbf != CBF_NONE) _last_started_action = cbf;
2384  if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
2385  }
2386 
2387  virtual EventState OnHotkey(int hotkey)
2388  {
2389  CallBackFunction cbf = CBF_NONE;
2390  switch (hotkey) {
2391  case MTEHK_PAUSE: ToolbarPauseClick(this); break;
2392  case MTEHK_FASTFORWARD: ToolbarFastForwardClick(this); break;
2393  case MTEHK_SETTINGS: ShowGameOptions(); break;
2394  case MTEHK_SAVEGAME: MenuClickSaveLoad(); break;
2395  case MTEHK_GENLAND: ToolbarScenGenLand(this); break;
2396  case MTEHK_GENTOWN: ToolbarScenGenTown(this); break;
2397  case MTEHK_GENINDUSTRY: ToolbarScenGenIndustry(this); break;
2398  case MTEHK_BUILD_ROAD: ToolbarScenBuildRoad(this); break;
2399  case MTEHK_BUILD_DOCKS: ToolbarScenBuildDocks(this); break;
2400  case MTEHK_BUILD_TREES: ToolbarScenPlantTrees(this); break;
2401  case MTEHK_SIGN: cbf = ToolbarScenPlaceSign(this); break;
2402  case MTEHK_MUSIC: ShowMusicWindow(); break;
2403  case MTEHK_LANDINFO: cbf = PlaceLandBlockInfo(); break;
2404  case MTEHK_SMALL_SCREENSHOT: MenuClickSmallScreenshot(); break;
2405  case MTEHK_ZOOMEDIN_SCREENSHOT: MenuClickLargeWorldScreenshot(SC_ZOOMEDIN); break;
2406  case MTEHK_DEFAULTZOOM_SCREENSHOT: MenuClickLargeWorldScreenshot(SC_DEFAULTZOOM); break;
2407  case MTEHK_GIANT_SCREENSHOT: MenuClickLargeWorldScreenshot(SC_WORLD); break;
2408  case MTEHK_ZOOM_IN: ToolbarZoomInClick(this); break;
2409  case MTEHK_ZOOM_OUT: ToolbarZoomOutClick(this); break;
2410  case MTEHK_TERRAFORM: ShowEditorTerraformToolbar(); break;
2411  case MTEHK_SMALLMAP: ShowSmallMap(); break;
2412  case MTEHK_EXTRA_VIEWPORT: ShowExtraViewPortWindowForTileUnderCursor(); break;
2413  default: return ES_NOT_HANDLED;
2414  }
2415  if (cbf != CBF_NONE) _last_started_action = cbf;
2416  return ES_HANDLED;
2417  }
2418 
2419  virtual void OnPlaceObject(Point pt, TileIndex tile)
2420  {
2421  switch (_last_started_action) {
2422  case CBF_PLACE_SIGN:
2423  PlaceProc_Sign(tile);
2424  break;
2425 
2426  case CBF_PLACE_LANDINFO:
2427  ShowLandInfo(tile);
2428  break;
2429 
2430  default: NOT_REACHED();
2431  }
2432  }
2433 
2434  virtual void OnPlaceObjectAbort()
2435  {
2436  _last_started_action = CBF_NONE;
2437  }
2438 
2439  virtual void OnTimeout()
2440  {
2444  }
2445 
2446  virtual void OnTick()
2447  {
2448  if (this->IsWidgetLowered(WID_TE_PAUSE) != !!_pause_mode) {
2450  this->SetDirty();
2451  }
2452 
2453  if (this->IsWidgetLowered(WID_TE_FAST_FORWARD) != !!_fast_forward) {
2455  this->SetDirty();
2456  }
2457  }
2458 
2464  virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
2465  {
2466  if (!gui_scope) return;
2468  }
2469 
2470  virtual void OnQueryTextFinished(char *str)
2471  {
2472  /* Was 'cancel' pressed? */
2473  if (str == NULL) return;
2474 
2475  int32 value;
2476  if (!StrEmpty(str)) {
2477  value = atoi(str);
2478  } else {
2479  /* An empty string means revert to the default */
2480  value = DEF_START_YEAR;
2481  }
2482  SetStartingYear(value);
2483 
2484  this->SetDirty();
2485  }
2486 
2487  static HotkeyList hotkeys;
2488 };
2489 
2490 static Hotkey scenedit_maintoolbar_hotkeys[] = {
2491  Hotkey(_maintoolbar_pause_keys, "pause", MTEHK_PAUSE),
2492  Hotkey((uint16)0, "fastforward", MTEHK_FASTFORWARD),
2493  Hotkey(WKC_F2, "settings", MTEHK_SETTINGS),
2494  Hotkey(WKC_F3, "saveload", MTEHK_SAVEGAME),
2495  Hotkey(WKC_F4, "gen_land", MTEHK_GENLAND),
2496  Hotkey(WKC_F5, "gen_town", MTEHK_GENTOWN),
2497  Hotkey(WKC_F6, "gen_industry", MTEHK_GENINDUSTRY),
2498  Hotkey(WKC_F7, "build_road", MTEHK_BUILD_ROAD),
2499  Hotkey(WKC_F8, "build_docks", MTEHK_BUILD_DOCKS),
2500  Hotkey(WKC_F9, "build_trees", MTEHK_BUILD_TREES),
2501  Hotkey(WKC_F10, "build_sign", MTEHK_SIGN),
2502  Hotkey(WKC_F11, "music", MTEHK_MUSIC),
2503  Hotkey(WKC_F12, "land_info", MTEHK_LANDINFO),
2504  Hotkey(WKC_CTRL | 'S', "small_screenshot", MTEHK_SMALL_SCREENSHOT),
2505  Hotkey(WKC_CTRL | 'P', "zoomedin_screenshot", MTEHK_ZOOMEDIN_SCREENSHOT),
2506  Hotkey(WKC_CTRL | 'D', "defaultzoom_screenshot", MTEHK_DEFAULTZOOM_SCREENSHOT),
2507  Hotkey((uint16)0, "giant_screenshot", MTEHK_GIANT_SCREENSHOT),
2508  Hotkey(_maintoolbar_zoomin_keys, "zoomin", MTEHK_ZOOM_IN),
2509  Hotkey(_maintoolbar_zoomout_keys, "zoomout", MTEHK_ZOOM_OUT),
2510  Hotkey('L', "terraform", MTEHK_TERRAFORM),
2511  Hotkey('M', "smallmap", MTEHK_SMALLMAP),
2512  Hotkey('V', "extra_viewport", MTEHK_EXTRA_VIEWPORT),
2513  HOTKEY_LIST_END
2514 };
2515 HotkeyList ScenarioEditorToolbarWindow::hotkeys("scenedit_maintoolbar", scenedit_maintoolbar_hotkeys);
2516 
2517 static const NWidgetPart _nested_toolb_scen_inner_widgets[] = {
2518  NWidget(WWT_IMGBTN, COLOUR_GREY, WID_TE_PAUSE), SetDataTip(SPR_IMG_PAUSE, STR_TOOLBAR_TOOLTIP_PAUSE_GAME),
2519  NWidget(WWT_IMGBTN, COLOUR_GREY, WID_TE_FAST_FORWARD), SetDataTip(SPR_IMG_FASTFORWARD, STR_TOOLBAR_TOOLTIP_FORWARD),
2520  NWidget(WWT_IMGBTN, COLOUR_GREY, WID_TE_SETTINGS), SetDataTip(SPR_IMG_SETTINGS, STR_TOOLBAR_TOOLTIP_OPTIONS),
2521  NWidget(WWT_IMGBTN_2, COLOUR_GREY, WID_TE_SAVE), SetDataTip(SPR_IMG_SAVE, STR_SCENEDIT_TOOLBAR_TOOLTIP_SAVE_SCENARIO_LOAD_SCENARIO),
2523  NWidget(WWT_PANEL, COLOUR_GREY, WID_TE_SPACER), EndContainer(),
2525  NWidget(WWT_PANEL, COLOUR_GREY, WID_TE_DATE_PANEL),
2526  NWidget(NWID_HORIZONTAL), SetPIP(3, 2, 3),
2527  NWidget(WWT_IMGBTN, COLOUR_GREY, WID_TE_DATE_BACKWARD), SetDataTip(SPR_ARROW_DOWN, STR_SCENEDIT_TOOLBAR_TOOLTIP_MOVE_THE_STARTING_DATE_BACKWARD),
2528  NWidget(WWT_EMPTY, COLOUR_GREY, WID_TE_DATE), SetDataTip(STR_NULL, STR_SCENEDIT_TOOLBAR_TOOLTIP_SET_DATE),
2529  NWidget(WWT_IMGBTN, COLOUR_GREY, WID_TE_DATE_FORWARD), SetDataTip(SPR_ARROW_UP, STR_SCENEDIT_TOOLBAR_TOOLTIP_MOVE_THE_STARTING_DATE_FORWARD),
2530  EndContainer(),
2531  EndContainer(),
2533  NWidget(WWT_IMGBTN, COLOUR_GREY, WID_TE_SMALL_MAP), SetDataTip(SPR_IMG_SMALLMAP, STR_SCENEDIT_TOOLBAR_TOOLTIP_DISPLAY_MAP_TOWN_DIRECTORY),
2535  NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_TE_ZOOM_IN), SetDataTip(SPR_IMG_ZOOMIN, STR_TOOLBAR_TOOLTIP_ZOOM_THE_VIEW_IN),
2536  NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_TE_ZOOM_OUT), SetDataTip(SPR_IMG_ZOOMOUT, STR_TOOLBAR_TOOLTIP_ZOOM_THE_VIEW_OUT),
2538  NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_TE_LAND_GENERATE), SetDataTip(SPR_IMG_LANDSCAPING, STR_SCENEDIT_TOOLBAR_LANDSCAPE_GENERATION),
2539  NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_TE_TOWN_GENERATE), SetDataTip(SPR_IMG_TOWN, STR_SCENEDIT_TOOLBAR_TOWN_GENERATION),
2540  NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_TE_INDUSTRY), SetDataTip(SPR_IMG_INDUSTRY, STR_SCENEDIT_TOOLBAR_INDUSTRY_GENERATION),
2541  NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_TE_ROADS), SetDataTip(SPR_IMG_BUILDROAD, STR_SCENEDIT_TOOLBAR_ROAD_CONSTRUCTION),
2542  NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_TE_WATER), SetDataTip(SPR_IMG_BUILDWATER, STR_TOOLBAR_TOOLTIP_BUILD_SHIP_DOCKS),
2543  NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_TE_TREES), SetDataTip(SPR_IMG_PLANTTREES, STR_SCENEDIT_TOOLBAR_PLANT_TREES),
2544  NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_TE_SIGNS), SetDataTip(SPR_IMG_SIGN, STR_SCENEDIT_TOOLBAR_PLACE_SIGN),
2546  NWidget(WWT_IMGBTN, COLOUR_GREY, WID_TE_MUSIC_SOUND), SetDataTip(SPR_IMG_MUSIC, STR_TOOLBAR_TOOLTIP_SHOW_SOUND_MUSIC_WINDOW),
2547  NWidget(WWT_IMGBTN, COLOUR_GREY, WID_TE_HELP), SetDataTip(SPR_IMG_QUERY, STR_TOOLBAR_TOOLTIP_LAND_BLOCK_INFORMATION),
2548  NWidget(WWT_IMGBTN, COLOUR_GREY, WID_TE_SWITCH_BAR), SetDataTip(SPR_IMG_SWITCH_TOOLBAR, STR_TOOLBAR_TOOLTIP_SWITCH_TOOLBAR),
2549 };
2550 
2551 static NWidgetBase *MakeScenarioToolbar(int *biggest_index)
2552 {
2553  return MakeNWidgets(_nested_toolb_scen_inner_widgets, lengthof(_nested_toolb_scen_inner_widgets), biggest_index, new NWidgetScenarioToolbarContainer());
2554 }
2555 
2556 static const NWidgetPart _nested_toolb_scen_widgets[] = {
2557  NWidgetFunction(MakeScenarioToolbar),
2558 };
2559 
2560 static WindowDesc _toolb_scen_desc(
2561  WDP_MANUAL, NULL, 0, 0,
2563  WDF_NO_FOCUS,
2564  _nested_toolb_scen_widgets, lengthof(_nested_toolb_scen_widgets),
2565  &ScenarioEditorToolbarWindow::hotkeys
2566 );
2567 
2570 {
2571  /* Clean old GUI values; railtype is (re)set by rail_gui.cpp */
2572  _last_built_roadtype = ROADTYPE_ROAD;
2573 
2574  if (_game_mode == GM_EDITOR) {
2575  new ScenarioEditorToolbarWindow(&_toolb_scen_desc);
2576  } else {
2577  new MainToolbarWindow(&_toolb_normal_desc);
2578  }
2579 }