OpenTTD
toolbar_gui.cpp
Go to the documentation of this file.
1 /* $Id: toolbar_gui.cpp 27178 2015-03-07 18:27:01Z 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 
86  uint checkmark_width;
87 public:
88  bool checked;
89 
90  DropDownListCheckedItem(StringID string, int result, bool masked, bool checked) : DropDownListStringItem(string, result, masked), checked(checked)
91  {
92  this->checkmark_width = GetStringBoundingBox(STR_JUST_CHECKMARK).width + 3;
93  }
94 
95  virtual ~DropDownListCheckedItem() {}
96 
97  uint Width() const
98  {
99  return DropDownListStringItem::Width() + this->checkmark_width;
100  }
101 
102  void Draw(int left, int right, int top, int bottom, bool sel, int bg_colour) const
103  {
104  bool rtl = _current_text_dir == TD_RTL;
105  if (this->checked) {
106  DrawString(left + WD_FRAMERECT_LEFT, right - WD_FRAMERECT_RIGHT, top, STR_JUST_CHECKMARK, sel ? TC_WHITE : TC_BLACK);
107  }
108  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);
109  }
110 };
111 
116  Dimension icon_size;
117 public:
118  bool greyed;
119 
120  DropDownListCompanyItem(int result, bool masked, bool greyed) : DropDownListItem(result, masked), greyed(greyed)
121  {
122  this->icon_size = GetSpriteSize(SPR_COMPANY_ICON);
123  }
124 
125  virtual ~DropDownListCompanyItem() {}
126 
127  bool Selectable() const
128  {
129  return true;
130  }
131 
132  uint Width() const
133  {
134  CompanyID company = (CompanyID)this->result;
135  SetDParam(0, company);
136  SetDParam(1, company);
137  return GetStringBoundingBox(STR_COMPANY_NAME_COMPANY_NUM).width + this->icon_size.width + 3;
138  }
139 
140  uint Height(uint width) const
141  {
142  return max(this->icon_size.height + 2U, (uint)FONT_HEIGHT_NORMAL);
143  }
144 
145  void Draw(int left, int right, int top, int bottom, bool sel, int bg_colour) const
146  {
147  CompanyID company = (CompanyID)this->result;
148  bool rtl = _current_text_dir == TD_RTL;
149 
150  /* It's possible the company is deleted while the dropdown is open */
151  if (!Company::IsValidID(company)) return;
152 
153  int icon_offset = (bottom - top - icon_size.height) / 2;
154  int text_offset = (bottom - top - FONT_HEIGHT_NORMAL) / 2;
155 
156  DrawCompanyIcon(company, rtl ? right - this->icon_size.width - WD_FRAMERECT_RIGHT : left + WD_FRAMERECT_LEFT, top + icon_offset);
157 
158  SetDParam(0, company);
159  SetDParam(1, company);
160  TextColour col;
161  if (this->greyed) {
162  col = (sel ? TC_SILVER : TC_GREY) | TC_NO_SHADE;
163  } else {
164  col = sel ? TC_WHITE : TC_BLACK;
165  }
166  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);
167  }
168 };
169 
177 static void PopupMainToolbMenu(Window *w, int widget, DropDownList *list, int def)
178 {
179  ShowDropDownList(w, list, def, widget, 0, true, true);
180  if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
181 }
182 
190 static void PopupMainToolbMenu(Window *w, int widget, StringID string, int count)
191 {
192  DropDownList *list = new DropDownList();
193  for (int i = 0; i < count; i++) {
194  *list->Append() = new DropDownListStringItem(string + i, i, false);
195  }
196  PopupMainToolbMenu(w, widget, list, 0);
197 }
198 
200 static const int CTMN_CLIENT_LIST = -1;
201 static const int CTMN_NEW_COMPANY = -2;
202 static const int CTMN_SPECTATE = -3;
203 static const int CTMN_SPECTATOR = -4;
204 
212 static void PopupMainCompanyToolbMenu(Window *w, int widget, int grey = 0, bool include_spectator = false)
213 {
214  DropDownList *list = new DropDownList();
215 
216 #ifdef ENABLE_NETWORK
217  if (_networking) {
218  if (widget == WID_TN_COMPANIES) {
219  /* Add the client list button for the companies menu */
220  *list->Append() = new DropDownListStringItem(STR_NETWORK_COMPANY_LIST_CLIENT_LIST, CTMN_CLIENT_LIST, false);
221  }
222 
223  if (include_spectator) {
224  if (widget == WID_TN_COMPANIES) {
226  *list->Append() = new DropDownListStringItem(STR_NETWORK_COMPANY_LIST_NEW_COMPANY, CTMN_NEW_COMPANY, NetworkMaxCompaniesReached());
227  } else {
228  *list->Append() = new DropDownListStringItem(STR_NETWORK_COMPANY_LIST_SPECTATE, CTMN_SPECTATE, NetworkMaxSpectatorsReached());
229  }
230  } else {
231  *list->Append() = new DropDownListStringItem(STR_NETWORK_TOOLBAR_LIST_SPECTATOR, CTMN_SPECTATOR, false);
232  }
233  }
234  }
235 #endif /* ENABLE_NETWORK */
236 
237  for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) {
238  if (!Company::IsValidID(c)) continue;
239  *list->Append() = new DropDownListCompanyItem(c, false, HasBit(grey, c));
240  }
241 
243 }
244 
245 
246 static ToolbarMode _toolbar_mode;
247 
248 static CallBackFunction SelectSignTool()
249 {
250  if (_cursor.sprite == SPR_CURSOR_SIGN) {
252  return CBF_NONE;
253  } else {
254  SetObjectToPlace(SPR_CURSOR_SIGN, PAL_NONE, HT_RECT, WC_MAIN_TOOLBAR, 0);
255  return CBF_PLACE_SIGN;
256  }
257 }
258 
259 /* --- Pausing --- */
260 
261 static CallBackFunction ToolbarPauseClick(Window *w)
262 {
263  if (_networking && !_network_server) return CBF_NONE; // only server can pause the game
264 
266  if (_settings_client.sound.confirm) SndPlayFx(SND_15_BEEP);
267  }
268  return CBF_NONE;
269 }
270 
278 {
279  _fast_forward ^= true;
280  if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
281  return CBF_NONE;
282 }
283 
288  OME_GAMEOPTIONS,
289  OME_SETTINGS,
290  OME_SCRIPT_SETTINGS,
291  OME_NEWGRFSETTINGS,
292  OME_TRANSPARENCIES,
293  OME_SHOW_TOWNNAMES,
294  OME_SHOW_STATIONNAMES,
295  OME_SHOW_WAYPOINTNAMES,
296  OME_SHOW_SIGNS,
297  OME_SHOW_COMPETITOR_SIGNS,
298  OME_FULL_ANIMATION,
299  OME_FULL_DETAILS,
300  OME_TRANSPARENTBUILDINGS,
301  OME_SHOW_STATIONSIGNS,
302 };
303 
311 {
312  DropDownList *list = new DropDownList();
313  *list->Append() = new DropDownListStringItem(STR_SETTINGS_MENU_GAME_OPTIONS, OME_GAMEOPTIONS, false);
314  *list->Append() = new DropDownListStringItem(STR_SETTINGS_MENU_CONFIG_SETTINGS_TREE, OME_SETTINGS, false);
315  /* Changes to the per-AI settings don't get send from the server to the clients. Clients get
316  * the settings once they join but never update it. As such don't show the window at all
317  * to network clients. */
318  if (!_networking || _network_server) *list->Append() = new DropDownListStringItem(STR_SETTINGS_MENU_SCRIPT_SETTINGS, OME_SCRIPT_SETTINGS, false);
319  *list->Append() = new DropDownListStringItem(STR_SETTINGS_MENU_NEWGRF_SETTINGS, OME_NEWGRFSETTINGS, false);
320  *list->Append() = new DropDownListStringItem(STR_SETTINGS_MENU_TRANSPARENCY_OPTIONS, OME_TRANSPARENCIES, false);
321  *list->Append() = new DropDownListItem(-1, false);
322  *list->Append() = new DropDownListCheckedItem(STR_SETTINGS_MENU_TOWN_NAMES_DISPLAYED, OME_SHOW_TOWNNAMES, false, HasBit(_display_opt, DO_SHOW_TOWN_NAMES));
323  *list->Append() = new DropDownListCheckedItem(STR_SETTINGS_MENU_STATION_NAMES_DISPLAYED, OME_SHOW_STATIONNAMES, false, HasBit(_display_opt, DO_SHOW_STATION_NAMES));
324  *list->Append() = new DropDownListCheckedItem(STR_SETTINGS_MENU_WAYPOINTS_DISPLAYED, OME_SHOW_WAYPOINTNAMES, false, HasBit(_display_opt, DO_SHOW_WAYPOINT_NAMES));
325  *list->Append() = new DropDownListCheckedItem(STR_SETTINGS_MENU_SIGNS_DISPLAYED, OME_SHOW_SIGNS, false, HasBit(_display_opt, DO_SHOW_SIGNS));
326  *list->Append() = new DropDownListCheckedItem(STR_SETTINGS_MENU_SHOW_COMPETITOR_SIGNS, OME_SHOW_COMPETITOR_SIGNS, false, HasBit(_display_opt, DO_SHOW_COMPETITOR_SIGNS));
327  *list->Append() = new DropDownListCheckedItem(STR_SETTINGS_MENU_FULL_ANIMATION, OME_FULL_ANIMATION, false, HasBit(_display_opt, DO_FULL_ANIMATION));
328  *list->Append() = new DropDownListCheckedItem(STR_SETTINGS_MENU_FULL_DETAIL, OME_FULL_DETAILS, false, HasBit(_display_opt, DO_FULL_DETAIL));
329  *list->Append() = new DropDownListCheckedItem(STR_SETTINGS_MENU_TRANSPARENT_BUILDINGS, OME_TRANSPARENTBUILDINGS, false, IsTransparencySet(TO_HOUSES));
330  *list->Append() = new DropDownListCheckedItem(STR_SETTINGS_MENU_TRANSPARENT_SIGNS, OME_SHOW_STATIONSIGNS, false, IsTransparencySet(TO_SIGNS));
331 
332  ShowDropDownList(w, list, 0, WID_TN_SETTINGS, 140, true, true);
333  if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
334  return CBF_NONE;
335 }
336 
344 {
345  switch (index) {
346  case OME_GAMEOPTIONS: ShowGameOptions(); return CBF_NONE;
347  case OME_SETTINGS: ShowGameSettings(); return CBF_NONE;
348  case OME_SCRIPT_SETTINGS: ShowAIConfigWindow(); return CBF_NONE;
349  case OME_NEWGRFSETTINGS: ShowNewGRFSettings(!_networking && _settings_client.gui.UserIsAllowedToChangeNewGRFs(), true, true, &_grfconfig); return CBF_NONE;
350  case OME_TRANSPARENCIES: ShowTransparencyToolbar(); break;
351 
352  case OME_SHOW_TOWNNAMES: ToggleBit(_display_opt, DO_SHOW_TOWN_NAMES); break;
353  case OME_SHOW_STATIONNAMES: ToggleBit(_display_opt, DO_SHOW_STATION_NAMES); break;
354  case OME_SHOW_WAYPOINTNAMES: ToggleBit(_display_opt, DO_SHOW_WAYPOINT_NAMES); break;
355  case OME_SHOW_SIGNS: ToggleBit(_display_opt, DO_SHOW_SIGNS); break;
356  case OME_SHOW_COMPETITOR_SIGNS:
359  break;
360  case OME_FULL_ANIMATION: ToggleBit(_display_opt, DO_FULL_ANIMATION); CheckBlitter(); break;
361  case OME_FULL_DETAILS: ToggleBit(_display_opt, DO_FULL_DETAIL); break;
362  case OME_TRANSPARENTBUILDINGS: ToggleTransparency(TO_HOUSES); break;
363  case OME_SHOW_STATIONSIGNS: ToggleTransparency(TO_SIGNS); break;
364  }
366  return CBF_NONE;
367 }
368 
373  SLEME_SAVE_SCENARIO = 0,
374  SLEME_LOAD_SCENARIO,
375  SLEME_SAVE_HEIGHTMAP,
376  SLEME_LOAD_HEIGHTMAP,
377  SLEME_EXIT_TOINTRO,
378  SLEME_EXIT_GAME = 6,
379  SLEME_MENUCOUNT,
380 };
381 
386  SLNME_SAVE_GAME = 0,
387  SLNME_LOAD_GAME,
388  SLNME_EXIT_TOINTRO,
389  SLNME_EXIT_GAME = 4,
390  SLNME_MENUCOUNT,
391 };
392 
400 {
401  PopupMainToolbMenu(w, WID_TN_SAVE, STR_FILE_MENU_SAVE_GAME, SLNME_MENUCOUNT);
402  return CBF_NONE;
403 }
404 
412 {
413  PopupMainToolbMenu(w, WID_TE_SAVE, STR_SCENEDIT_FILE_MENU_SAVE_SCENARIO, SLEME_MENUCOUNT);
414  return CBF_NONE;
415 }
416 
423 static CallBackFunction MenuClickSaveLoad(int index = 0)
424 {
425  if (_game_mode == GM_EDITOR) {
426  switch (index) {
427  case SLEME_SAVE_SCENARIO: ShowSaveLoadDialog(SLD_SAVE_SCENARIO); break;
428  case SLEME_LOAD_SCENARIO: ShowSaveLoadDialog(SLD_LOAD_SCENARIO); break;
429  case SLEME_SAVE_HEIGHTMAP: ShowSaveLoadDialog(SLD_SAVE_HEIGHTMAP); break;
430  case SLEME_LOAD_HEIGHTMAP: ShowSaveLoadDialog(SLD_LOAD_HEIGHTMAP); break;
431  case SLEME_EXIT_TOINTRO: AskExitToGameMenu(); break;
432  case SLEME_EXIT_GAME: HandleExitGameRequest(); break;
433  }
434  } else {
435  switch (index) {
436  case SLNME_SAVE_GAME: ShowSaveLoadDialog(SLD_SAVE_GAME); break;
437  case SLNME_LOAD_GAME: ShowSaveLoadDialog(SLD_LOAD_GAME); break;
438  case SLNME_EXIT_TOINTRO: AskExitToGameMenu(); break;
439  case SLNME_EXIT_GAME: HandleExitGameRequest(); break;
440  }
441  }
442  return CBF_NONE;
443 }
444 
445 /* --- Map button menu --- */
446 
447 enum MapMenuEntries {
448  MME_SHOW_SMALLMAP = 0,
449  MME_SHOW_EXTRAVIEWPORTS,
450  MME_SHOW_LINKGRAPH,
451  MME_SHOW_SIGNLISTS,
452  MME_SHOW_TOWNDIRECTORY,
453  MME_SHOW_INDUSTRYDIRECTORY,
454 };
455 
456 static CallBackFunction ToolbarMapClick(Window *w)
457 {
458  DropDownList *list = new DropDownList();
459  *list->Append() = new DropDownListStringItem(STR_MAP_MENU_MAP_OF_WORLD, MME_SHOW_SMALLMAP, false);
460  *list->Append() = new DropDownListStringItem(STR_MAP_MENU_EXTRA_VIEW_PORT, MME_SHOW_EXTRAVIEWPORTS, false);
461  *list->Append() = new DropDownListStringItem(STR_MAP_MENU_LINGRAPH_LEGEND, MME_SHOW_LINKGRAPH, false);
462  *list->Append() = new DropDownListStringItem(STR_MAP_MENU_SIGN_LIST, MME_SHOW_SIGNLISTS, false);
464  return CBF_NONE;
465 }
466 
467 static CallBackFunction ToolbarScenMapTownDir(Window *w)
468 {
469  DropDownList *list = new DropDownList();
470  *list->Append() = new DropDownListStringItem(STR_MAP_MENU_MAP_OF_WORLD, MME_SHOW_SMALLMAP, false);
471  *list->Append() = new DropDownListStringItem(STR_MAP_MENU_EXTRA_VIEW_PORT, MME_SHOW_EXTRAVIEWPORTS, false);
472  *list->Append() = new DropDownListStringItem(STR_MAP_MENU_SIGN_LIST, MME_SHOW_SIGNLISTS, false);
473  *list->Append() = new DropDownListStringItem(STR_TOWN_MENU_TOWN_DIRECTORY, MME_SHOW_TOWNDIRECTORY, false);
474  *list->Append() = new DropDownListStringItem(STR_INDUSTRY_MENU_INDUSTRY_DIRECTORY, MME_SHOW_INDUSTRYDIRECTORY, false);
476  return CBF_NONE;
477 }
478 
486 {
487  switch (index) {
488  case MME_SHOW_SMALLMAP: ShowSmallMap(); break;
489  case MME_SHOW_EXTRAVIEWPORTS: ShowExtraViewPortWindow(); break;
490  case MME_SHOW_LINKGRAPH: ShowLinkGraphLegend(); break;
491  case MME_SHOW_SIGNLISTS: ShowSignList(); break;
492  case MME_SHOW_TOWNDIRECTORY: ShowTownDirectory(); break;
493  case MME_SHOW_INDUSTRYDIRECTORY: ShowIndustryDirectory(); break;
494  }
495  return CBF_NONE;
496 }
497 
498 /* --- Town button menu --- */
499 
500 static CallBackFunction ToolbarTownClick(Window *w)
501 {
502  PopupMainToolbMenu(w, WID_TN_TOWNS, STR_TOWN_MENU_TOWN_DIRECTORY, (_settings_game.economy.found_town == TF_FORBIDDEN) ? 1 : 2);
503  return CBF_NONE;
504 }
505 
513 {
514  switch (index) {
515  case 0: ShowTownDirectory(); break;
516  case 1: // setting could be changed when the dropdown was open
517  if (_settings_game.economy.found_town != TF_FORBIDDEN) ShowFoundTownWindow();
518  break;
519  }
520  return CBF_NONE;
521 }
522 
523 /* --- Subidies button menu --- */
524 
525 static CallBackFunction ToolbarSubsidiesClick(Window *w)
526 {
527  PopupMainToolbMenu(w, WID_TN_SUBSIDIES, STR_SUBSIDIES_MENU_SUBSIDIES, 1);
528  return CBF_NONE;
529 }
530 
538 {
539  switch (index) {
540  case 0: ShowSubsidiesList(); break;
541  }
542  return CBF_NONE;
543 }
544 
545 /* --- Stations button menu --- */
546 
547 static CallBackFunction ToolbarStationsClick(Window *w)
548 {
550  return CBF_NONE;
551 }
552 
560 {
562  return CBF_NONE;
563 }
564 
565 /* --- Finances button menu --- */
566 
567 static CallBackFunction ToolbarFinancesClick(Window *w)
568 {
570  return CBF_NONE;
571 }
572 
580 {
582  return CBF_NONE;
583 }
584 
585 /* --- Company's button menu --- */
586 
587 static CallBackFunction ToolbarCompaniesClick(Window *w)
588 {
590  return CBF_NONE;
591 }
592 
600 {
601 #ifdef ENABLE_NETWORK
602  if (_networking) {
603  switch (index) {
604  case CTMN_CLIENT_LIST:
605  ShowClientList();
606  return CBF_NONE;
607 
608  case CTMN_NEW_COMPANY:
609  if (_network_server) {
611  } else {
612  NetworkSendCommand(0, 0, 0, CMD_COMPANY_CTRL, NULL, NULL, _local_company);
613  }
614  return CBF_NONE;
615 
616  case CTMN_SPECTATE:
617  if (_network_server) {
620  } else {
622  }
623  return CBF_NONE;
624  }
625  }
626 #endif /* ENABLE_NETWORK */
627  ShowCompany((CompanyID)index);
628  return CBF_NONE;
629 }
630 
631 /* --- Story button menu --- */
632 
633 static CallBackFunction ToolbarStoryClick(Window *w)
634 {
636  return CBF_NONE;
637 }
638 
646 {
648  return CBF_NONE;
649 }
650 
651 /* --- Goal button menu --- */
652 
653 static CallBackFunction ToolbarGoalClick(Window *w)
654 {
656  return CBF_NONE;
657 }
658 
666 {
668  return CBF_NONE;
669 }
670 
671 /* --- Graphs button menu --- */
672 
673 static CallBackFunction ToolbarGraphsClick(Window *w)
674 {
675  PopupMainToolbMenu(w, WID_TN_GRAPHS, STR_GRAPH_MENU_OPERATING_PROFIT_GRAPH, (_toolbar_mode == TB_NORMAL) ? 6 : 8);
676  return CBF_NONE;
677 }
678 
686 {
687  switch (index) {
688  case 0: ShowOperatingProfitGraph(); break;
689  case 1: ShowIncomeGraph(); break;
690  case 2: ShowDeliveredCargoGraph(); break;
691  case 3: ShowPerformanceHistoryGraph(); break;
692  case 4: ShowCompanyValueGraph(); break;
693  case 5: ShowCargoPaymentRates(); break;
694  /* functions for combined graphs/league button */
695  case 6: ShowCompanyLeagueTable(); break;
696  case 7: ShowPerformanceRatingDetail(); break;
697  }
698  return CBF_NONE;
699 }
700 
701 /* --- League button menu --- */
702 
703 static CallBackFunction ToolbarLeagueClick(Window *w)
704 {
705  PopupMainToolbMenu(w, WID_TN_LEAGUE, STR_GRAPH_MENU_COMPANY_LEAGUE_TABLE, _networking ? 2 : 3);
706  return CBF_NONE;
707 }
708 
716 {
717  switch (index) {
718  case 0: ShowCompanyLeagueTable(); break;
719  case 1: ShowPerformanceRatingDetail(); break;
720  case 2: ShowHighscoreTable(); break;
721  }
722  return CBF_NONE;
723 }
724 
725 /* --- Industries button menu --- */
726 
727 static CallBackFunction ToolbarIndustryClick(Window *w)
728 {
729  /* Disable build-industry menu if we are a spectator */
730  PopupMainToolbMenu(w, WID_TN_INDUSTRIES, STR_INDUSTRY_MENU_INDUSTRY_DIRECTORY, (_local_company == COMPANY_SPECTATOR) ? 2 : 3);
731  return CBF_NONE;
732 }
733 
741 {
742  switch (index) {
743  case 0: ShowIndustryDirectory(); break;
744  case 1: ShowIndustryCargoesWindow(); break;
745  case 2: ShowBuildIndustryWindow(); break;
746  }
747  return CBF_NONE;
748 }
749 
750 /* --- Trains button menu + 1 helper function for all vehicles. --- */
751 
752 static void ToolbarVehicleClick(Window *w, VehicleType veh)
753 {
754  const Vehicle *v;
755  int dis = ~0;
756 
757  FOR_ALL_VEHICLES(v) {
758  if (v->type == veh && v->IsPrimaryVehicle()) ClrBit(dis, v->owner);
759  }
761 }
762 
763 
764 static CallBackFunction ToolbarTrainClick(Window *w)
765 {
766  ToolbarVehicleClick(w, VEH_TRAIN);
767  return CBF_NONE;
768 }
769 
777 {
778  ShowVehicleListWindow((CompanyID)index, VEH_TRAIN);
779  return CBF_NONE;
780 }
781 
782 /* --- Road vehicle button menu --- */
783 
784 static CallBackFunction ToolbarRoadClick(Window *w)
785 {
786  ToolbarVehicleClick(w, VEH_ROAD);
787  return CBF_NONE;
788 }
789 
797 {
798  ShowVehicleListWindow((CompanyID)index, VEH_ROAD);
799  return CBF_NONE;
800 }
801 
802 /* --- Ship button menu --- */
803 
804 static CallBackFunction ToolbarShipClick(Window *w)
805 {
806  ToolbarVehicleClick(w, VEH_SHIP);
807  return CBF_NONE;
808 }
809 
817 {
818  ShowVehicleListWindow((CompanyID)index, VEH_SHIP);
819  return CBF_NONE;
820 }
821 
822 /* --- Aircraft button menu --- */
823 
824 static CallBackFunction ToolbarAirClick(Window *w)
825 {
826  ToolbarVehicleClick(w, VEH_AIRCRAFT);
827  return CBF_NONE;
828 }
829 
837 {
838  ShowVehicleListWindow((CompanyID)index, VEH_AIRCRAFT);
839  return CBF_NONE;
840 }
841 
842 /* --- Zoom in button --- */
843 
844 static CallBackFunction ToolbarZoomInClick(Window *w)
845 {
847  w->HandleButtonClick((_game_mode == GM_EDITOR) ? (byte)WID_TE_ZOOM_IN : (byte)WID_TN_ZOOM_IN);
848  if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
849  }
850  return CBF_NONE;
851 }
852 
853 /* --- Zoom out button --- */
854 
855 static CallBackFunction ToolbarZoomOutClick(Window *w)
856 {
858  w->HandleButtonClick((_game_mode == GM_EDITOR) ? (byte)WID_TE_ZOOM_OUT : (byte)WID_TN_ZOOM_OUT);
859  if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
860  }
861  return CBF_NONE;
862 }
863 
864 /* --- Rail button menu --- */
865 
866 static CallBackFunction ToolbarBuildRailClick(Window *w)
867 {
868  ShowDropDownList(w, GetRailTypeDropDownList(), _last_built_railtype, WID_TN_RAILS, 140, true, true);
869  if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
870  return CBF_NONE;
871 }
872 
880 {
881  _last_built_railtype = (RailType)index;
882  ShowBuildRailToolbar(_last_built_railtype);
883  return CBF_NONE;
884 }
885 
886 /* --- Road button menu --- */
887 
888 static CallBackFunction ToolbarBuildRoadClick(Window *w)
889 {
890  const Company *c = Company::Get(_local_company);
891  DropDownList *list = new DropDownList();
892 
893  /* Road is always visible and available. */
894  *list->Append() = new DropDownListStringItem(STR_ROAD_MENU_ROAD_CONSTRUCTION, ROADTYPE_ROAD, false);
895 
896  /* Tram is only visible when there will be a tram, and available when that has been introduced. */
897  Engine *e;
898  FOR_ALL_ENGINES_OF_TYPE(e, VEH_ROAD) {
899  if (!HasBit(e->info.climates, _settings_game.game_creation.landscape)) continue;
900  if (!HasBit(e->info.misc_flags, EF_ROAD_TRAM)) continue;
901 
902  *list->Append() = new DropDownListStringItem(STR_ROAD_MENU_TRAM_CONSTRUCTION, ROADTYPE_TRAM, !HasBit(c->avail_roadtypes, ROADTYPE_TRAM));
903  break;
904  }
905  ShowDropDownList(w, list, _last_built_roadtype, WID_TN_ROADS, 140, true, true);
906  if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
907  return CBF_NONE;
908 }
909 
917 {
918  _last_built_roadtype = (RoadType)index;
919  ShowBuildRoadToolbar(_last_built_roadtype);
920  return CBF_NONE;
921 }
922 
923 /* --- Water button menu --- */
924 
925 static CallBackFunction ToolbarBuildWaterClick(Window *w)
926 {
927  PopupMainToolbMenu(w, WID_TN_WATER, STR_WATERWAYS_MENU_WATERWAYS_CONSTRUCTION, 1);
928  return CBF_NONE;
929 }
930 
938 {
940  return CBF_NONE;
941 }
942 
943 /* --- Airport button menu --- */
944 
945 static CallBackFunction ToolbarBuildAirClick(Window *w)
946 {
947  PopupMainToolbMenu(w, WID_TN_AIR, STR_AIRCRAFT_MENU_AIRPORT_CONSTRUCTION, 1);
948  return CBF_NONE;
949 }
950 
958 {
960  return CBF_NONE;
961 }
962 
963 /* --- Forest button menu --- */
964 
965 static CallBackFunction ToolbarForestClick(Window *w)
966 {
967  PopupMainToolbMenu(w, WID_TN_LANDSCAPE, STR_LANDSCAPING_MENU_LANDSCAPING, 3);
968  return CBF_NONE;
969 }
970 
978 {
979  switch (index) {
980  case 0: ShowTerraformToolbar(); break;
981  case 1: ShowBuildTreesToolbar(); break;
982  case 2: return SelectSignTool();
983  }
984  return CBF_NONE;
985 }
986 
987 /* --- Music button menu --- */
988 
989 static CallBackFunction ToolbarMusicClick(Window *w)
990 {
991  PopupMainToolbMenu(w, WID_TN_MUSIC_SOUND, STR_TOOLBAR_SOUND_MUSIC, 1);
992  return CBF_NONE;
993 }
994 
1002 {
1003  ShowMusicWindow();
1004  return CBF_NONE;
1005 }
1006 
1007 /* --- Newspaper button menu --- */
1008 
1009 static CallBackFunction ToolbarNewspaperClick(Window *w)
1010 {
1011  PopupMainToolbMenu(w, WID_TN_MESSAGES, STR_NEWS_MENU_LAST_MESSAGE_NEWS_REPORT, 2);
1012  return CBF_NONE;
1013 }
1014 
1022 {
1023  switch (index) {
1024  case 0: ShowLastNewsMessage(); break;
1025  case 1: ShowMessageHistory(); break;
1026  }
1027  return CBF_NONE;
1028 }
1029 
1030 /* --- Help button menu --- */
1031 
1032 static CallBackFunction PlaceLandBlockInfo()
1033 {
1034  if (_cursor.sprite == SPR_CURSOR_QUERY) {
1036  return CBF_NONE;
1037  } else {
1038  SetObjectToPlace(SPR_CURSOR_QUERY, PAL_NONE, HT_RECT, WC_MAIN_TOOLBAR, 0);
1039  return CBF_PLACE_LANDINFO;
1040  }
1041 }
1042 
1043 static CallBackFunction ToolbarHelpClick(Window *w)
1044 {
1045  PopupMainToolbMenu(w, WID_TN_HELP, STR_ABOUT_MENU_LAND_BLOCK_INFO, _settings_client.gui.newgrf_developer_tools ? 12 : 9);
1046  return CBF_NONE;
1047 }
1048 
1049 static void MenuClickSmallScreenshot()
1050 {
1051  MakeScreenshot(SC_VIEWPORT, NULL);
1052 }
1053 
1059 static void ScreenshotConfirmCallback(Window *w, bool confirmed)
1060 {
1061  if (confirmed) MakeScreenshot(_confirmed_screenshot_type, NULL);
1062 }
1063 
1070 {
1071  ViewPort vp;
1072  SetupScreenshotViewport(t, &vp);
1073  if ((uint64)vp.width * (uint64)vp.height > 8192 * 8192) {
1074  /* Ask for confirmation */
1075  SetDParam(0, vp.width);
1076  SetDParam(1, vp.height);
1078  ShowQuery(STR_WARNING_SCREENSHOT_SIZE_CAPTION, STR_WARNING_SCREENSHOT_SIZE_MESSAGE, NULL, ScreenshotConfirmCallback);
1079  } else {
1080  /* Less than 64M pixels, just do it */
1081  MakeScreenshot(t, NULL);
1082  }
1083 }
1084 
1093 {
1094  extern bool _draw_bounding_boxes;
1095  /* Always allow to toggle them off */
1096  if (_settings_client.gui.newgrf_developer_tools || _draw_bounding_boxes) {
1097  _draw_bounding_boxes = !_draw_bounding_boxes;
1099  }
1100 }
1101 
1110 {
1111  extern bool _draw_dirty_blocks;
1112  /* Always allow to toggle them off */
1113  if (_settings_client.gui.newgrf_developer_tools || _draw_dirty_blocks) {
1114  _draw_dirty_blocks = !_draw_dirty_blocks;
1116  }
1117 }
1118 
1124 {
1127  /* If you open a savegame as scenario there may already be link graphs.*/
1129  SetDate(new_date, 0);
1130 }
1131 
1138 {
1139  switch (index) {
1140  case 0: return PlaceLandBlockInfo();
1141  case 2: IConsoleSwitch(); break;
1142  case 3: ShowAIDebugWindow(); break;
1143  case 4: MenuClickSmallScreenshot(); break;
1146  case 7: MenuClickLargeWorldScreenshot(SC_WORLD); break;
1147  case 8: ShowAboutWindow(); break;
1148  case 9: ShowSpriteAlignerWindow(); break;
1149  case 10: ToggleBoundingBoxes(); break;
1150  case 11: ToggleDirtyBlocks(); break;
1151  }
1152  return CBF_NONE;
1153 }
1154 
1155 /* --- Switch toolbar button --- */
1156 
1157 static CallBackFunction ToolbarSwitchClick(Window *w)
1158 {
1159  if (_toolbar_mode != TB_LOWER) {
1160  _toolbar_mode = TB_LOWER;
1161  } else {
1162  _toolbar_mode = TB_UPPER;
1163  }
1164 
1165  w->ReInit();
1166  w->SetWidgetLoweredState(WID_TN_SWITCH_BAR, _toolbar_mode == TB_LOWER);
1167  if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
1168  return CBF_NONE;
1169 }
1170 
1171 /* --- Scenario editor specific handlers. */
1172 
1177 {
1179  ShowQueryString(STR_JUST_INT, STR_MAPGEN_START_DATE_QUERY_CAPT, 8, w, CS_NUMERAL, QSF_ENABLE_DEFAULT);
1180  _left_button_clicked = false;
1181  return CBF_NONE;
1182 }
1183 
1184 static CallBackFunction ToolbarScenDateBackward(Window *w)
1185 {
1186  /* don't allow too fast scrolling */
1187  if (!(w->flags & WF_TIMEOUT) || w->timeout_timer <= 1) {
1189  w->SetDirty();
1190 
1192  }
1193  _left_button_clicked = false;
1194  return CBF_NONE;
1195 }
1196 
1197 static CallBackFunction ToolbarScenDateForward(Window *w)
1198 {
1199  /* don't allow too fast scrolling */
1200  if (!(w->flags & WF_TIMEOUT) || w->timeout_timer <= 1) {
1202  w->SetDirty();
1203 
1205  }
1206  _left_button_clicked = false;
1207  return CBF_NONE;
1208 }
1209 
1210 static CallBackFunction ToolbarScenGenLand(Window *w)
1211 {
1213  if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
1214 
1216  return CBF_NONE;
1217 }
1218 
1219 
1220 static CallBackFunction ToolbarScenGenTown(Window *w)
1221 {
1223  if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
1224  ShowFoundTownWindow();
1225  return CBF_NONE;
1226 }
1227 
1228 static CallBackFunction ToolbarScenGenIndustry(Window *w)
1229 {
1231  if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
1232  ShowBuildIndustryWindow();
1233  return CBF_NONE;
1234 }
1235 
1236 static CallBackFunction ToolbarScenBuildRoad(Window *w)
1237 {
1239  if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
1241  return CBF_NONE;
1242 }
1243 
1244 static CallBackFunction ToolbarScenBuildDocks(Window *w)
1245 {
1247  if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
1249  return CBF_NONE;
1250 }
1251 
1252 static CallBackFunction ToolbarScenPlantTrees(Window *w)
1253 {
1255  if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
1256  ShowBuildTreesToolbar();
1257  return CBF_NONE;
1258 }
1259 
1260 static CallBackFunction ToolbarScenPlaceSign(Window *w)
1261 {
1263  if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
1264  return SelectSignTool();
1265 }
1266 
1267 static CallBackFunction ToolbarBtn_NULL(Window *w)
1268 {
1269  return CBF_NONE;
1270 }
1271 
1272 typedef CallBackFunction MenuClickedProc(int index);
1273 
1274 static MenuClickedProc * const _menu_clicked_procs[] = {
1275  NULL, // 0
1276  NULL, // 1
1277  MenuClickSettings, // 2
1278  MenuClickSaveLoad, // 3
1279  MenuClickMap, // 4
1280  MenuClickTown, // 5
1281  MenuClickSubsidies, // 6
1282  MenuClickStations, // 7
1283  MenuClickFinances, // 8
1284  MenuClickCompany, // 9
1285  MenuClickStory, // 10
1286  MenuClickGoal, // 11
1287  MenuClickGraphs, // 12
1288  MenuClickLeague, // 13
1289  MenuClickIndustry, // 14
1290  MenuClickShowTrains, // 15
1291  MenuClickShowRoad, // 16
1292  MenuClickShowShips, // 17
1293  MenuClickShowAir, // 18
1294  MenuClickMap, // 19
1295  NULL, // 20
1296  MenuClickBuildRail, // 21
1297  MenuClickBuildRoad, // 22
1298  MenuClickBuildWater, // 23
1299  MenuClickBuildAir, // 24
1300  MenuClickForest, // 25
1301  MenuClickMusicWindow, // 26
1302  MenuClickNewspaper, // 27
1303  MenuClickHelp, // 28
1304 };
1305 
1309 protected:
1310  uint spacers;
1311 
1312 public:
1314  {
1315  }
1316 
1323  {
1324  return type == WWT_IMGBTN || type == WWT_IMGBTN_2 || type == WWT_PUSHIMGBTN;
1325  }
1326 
1327  void SetupSmallestSize(Window *w, bool init_array)
1328  {
1329  this->smallest_x = 0; // Biggest child
1330  this->smallest_y = 0; // Biggest child
1331  this->fill_x = 1;
1332  this->fill_y = 0;
1333  this->resize_x = 1; // We only resize in this direction
1334  this->resize_y = 0; // We never resize in this direction
1335  this->spacers = 0;
1336 
1337  uint nbuttons = 0;
1338  /* First initialise some variables... */
1339  for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) {
1340  child_wid->SetupSmallestSize(w, init_array);
1341  this->smallest_y = max(this->smallest_y, child_wid->smallest_y + child_wid->padding_top + child_wid->padding_bottom);
1342  if (this->IsButton(child_wid->type)) {
1343  nbuttons++;
1344  this->smallest_x = max(this->smallest_x, child_wid->smallest_x + child_wid->padding_left + child_wid->padding_right);
1345  } else if (child_wid->type == NWID_SPACER) {
1346  this->spacers++;
1347  }
1348  }
1349 
1350  /* ... then in a second pass make sure the 'current' heights are set. Won't change ever. */
1351  for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) {
1352  child_wid->current_y = this->smallest_y;
1353  if (!this->IsButton(child_wid->type)) {
1354  child_wid->current_x = child_wid->smallest_x;
1355  }
1356  }
1357  _toolbar_width = nbuttons * this->smallest_x;
1358  }
1359 
1360  void AssignSizePosition(SizingType sizing, uint x, uint y, uint given_width, uint given_height, bool rtl)
1361  {
1362  assert(given_width >= this->smallest_x && given_height >= this->smallest_y);
1363 
1364  this->pos_x = x;
1365  this->pos_y = y;
1366  this->current_x = given_width;
1367  this->current_y = given_height;
1368 
1369  /* Figure out what are the visible buttons */
1370  memset(this->visible, 0, sizeof(this->visible));
1371  uint arrangable_count, button_count, spacer_count;
1372  const byte *arrangement = GetButtonArrangement(given_width, arrangable_count, button_count, spacer_count);
1373  for (uint i = 0; i < arrangable_count; i++) {
1374  this->visible[arrangement[i]] = true;
1375  }
1376 
1377  /* Create us ourselves a quick lookup table */
1378  NWidgetBase *widgets[WID_TN_END];
1379  for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) {
1380  if (child_wid->type == NWID_SPACER) continue;
1381  widgets[((NWidgetCore*)child_wid)->index] = child_wid;
1382  }
1383 
1384  /* Now assign the widgets to their rightful place */
1385  uint position = 0; // Place to put next child relative to origin of the container.
1386  uint spacer_space = max(0, (int)given_width - (int)(button_count * this->smallest_x)); // Remaining spacing for 'spacer' widgets
1387  uint button_space = given_width - spacer_space; // Remaining spacing for the buttons
1388  uint spacer_i = 0;
1389  uint button_i = 0;
1390 
1391  /* Index into the arrangement indices. The macro lastof cannot be used here! */
1392  const byte *cur_wid = rtl ? &arrangement[arrangable_count - 1] : arrangement;
1393  for (uint i = 0; i < arrangable_count; i++) {
1394  NWidgetBase *child_wid = widgets[*cur_wid];
1395  /* If we have to give space to the spacers, do that */
1396  if (spacer_space != 0) {
1397  NWidgetBase *possible_spacer = rtl ? child_wid->next : child_wid->prev;
1398  if (possible_spacer != NULL && possible_spacer->type == NWID_SPACER) {
1399  uint add = spacer_space / (spacer_count - spacer_i);
1400  position += add;
1401  spacer_space -= add;
1402  spacer_i++;
1403  }
1404  }
1405 
1406  /* Buttons can be scaled, the others not. */
1407  if (this->IsButton(child_wid->type)) {
1408  child_wid->current_x = button_space / (button_count - button_i);
1409  button_space -= child_wid->current_x;
1410  button_i++;
1411  }
1412  child_wid->AssignSizePosition(sizing, x + position, y, child_wid->current_x, this->current_y, rtl);
1413  position += child_wid->current_x;
1414 
1415  if (rtl) {
1416  cur_wid--;
1417  } else {
1418  cur_wid++;
1419  }
1420  }
1421  }
1422 
1423  /* virtual */ void Draw(const Window *w)
1424  {
1425  /* Draw brown-red toolbar bg. */
1426  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);
1427  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);
1428 
1429  bool rtl = _current_text_dir == TD_RTL;
1430  for (NWidgetBase *child_wid = rtl ? this->tail : this->head; child_wid != NULL; child_wid = rtl ? child_wid->prev : child_wid->next) {
1431  if (child_wid->type == NWID_SPACER) continue;
1432  if (!this->visible[((NWidgetCore*)child_wid)->index]) continue;
1433 
1434  child_wid->Draw(w);
1435  }
1436  }
1437 
1438  /* virtual */ NWidgetCore *GetWidgetFromPos(int x, int y)
1439  {
1440  if (!IsInsideBS(x, this->pos_x, this->current_x) || !IsInsideBS(y, this->pos_y, this->current_y)) return NULL;
1441 
1442  for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) {
1443  if (child_wid->type == NWID_SPACER) continue;
1444  if (!this->visible[((NWidgetCore*)child_wid)->index]) continue;
1445 
1446  NWidgetCore *nwid = child_wid->GetWidgetFromPos(x, y);
1447  if (nwid != NULL) return nwid;
1448  }
1449  return NULL;
1450  }
1451 
1460  virtual const byte *GetButtonArrangement(uint &width, uint &arrangable_count, uint &button_count, uint &spacer_count) const = 0;
1461 };
1462 
1465  /* virtual */ const byte *GetButtonArrangement(uint &width, uint &arrangable_count, uint &button_count, uint &spacer_count) const
1466  {
1467  static const uint SMALLEST_ARRANGEMENT = 14;
1468  static const uint BIGGEST_ARRANGEMENT = 20;
1469  static const byte arrange14[] = {
1470  0, 1, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 29,
1471  2, 3, 4, 5, 6, 7, 8, 9, 12, 14, 26, 27, 28, 29,
1472  };
1473  static const byte arrange15[] = {
1474  0, 1, 4, 15, 16, 17, 18, 21, 22, 23, 24, 25, 19, 20, 29,
1475  0, 2, 4, 3, 5, 6, 7, 8, 9, 12, 14, 26, 27, 28, 29,
1476  };
1477  static const byte arrange16[] = {
1478  0, 1, 2, 4, 15, 16, 17, 18, 21, 22, 23, 24, 25, 19, 20, 29,
1479  0, 1, 3, 5, 6, 7, 8, 9, 12, 14, 26, 27, 28, 19, 20, 29,
1480  };
1481  static const byte arrange17[] = {
1482  0, 1, 2, 4, 6, 15, 16, 17, 18, 21, 22, 23, 24, 25, 19, 20, 29,
1483  0, 1, 3, 4, 6, 5, 7, 8, 9, 12, 14, 26, 27, 28, 19, 20, 29,
1484  };
1485  static const byte arrange18[] = {
1486  0, 1, 2, 4, 5, 6, 7, 8, 9, 14, 21, 22, 23, 24, 25, 19, 20, 29,
1487  0, 1, 3, 4, 5, 6, 7, 12, 15, 16, 17, 18, 26, 27, 28, 19, 20, 29,
1488  };
1489  static const byte arrange19[] = {
1490  0, 1, 2, 4, 5, 6, 15, 16, 17, 18, 21, 22, 23, 24, 25, 26, 19, 20, 29,
1491  0, 1, 3, 4, 7, 8, 9, 12, 14, 27, 21, 22, 23, 24, 25, 28, 19, 20, 29,
1492  };
1493  static const byte arrange20[] = {
1494  0, 1, 2, 4, 5, 6, 15, 16, 17, 18, 21, 22, 23, 24, 25, 26, 11, 19, 20, 29,
1495  0, 1, 3, 4, 7, 8, 9, 12, 14, 27, 21, 22, 23, 24, 25, 10, 28, 19, 20, 29,
1496  };
1497  static const byte arrange_all[] = {
1498  0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28
1499  };
1500 
1501  /* If at least BIGGEST_ARRANGEMENT fit, just spread all the buttons nicely */
1502  uint full_buttons = max(CeilDiv(width, this->smallest_x), SMALLEST_ARRANGEMENT);
1503  if (full_buttons > BIGGEST_ARRANGEMENT) {
1504  button_count = arrangable_count = lengthof(arrange_all);
1505  spacer_count = this->spacers;
1506  return arrange_all;
1507  }
1508 
1509  /* Introduce the split toolbar */
1510  static const byte * const arrangements[] = { arrange14, arrange15, arrange16, arrange17, arrange18, arrange19, arrange20 };
1511 
1512  button_count = arrangable_count = full_buttons;
1513  spacer_count = this->spacers;
1514  return arrangements[full_buttons - SMALLEST_ARRANGEMENT] + ((_toolbar_mode == TB_LOWER) ? full_buttons : 0);
1515  }
1516 };
1517 
1520  uint panel_widths[2];
1521 
1522  void SetupSmallestSize(Window *w, bool init_array)
1523  {
1524  this->NWidgetToolbarContainer::SetupSmallestSize(w, init_array);
1525 
1526  /* Find the size of panel_widths */
1527  uint i = 0;
1528  for (NWidgetBase *child_wid = this->head; child_wid != NULL; child_wid = child_wid->next) {
1529  if (child_wid->type == NWID_SPACER || this->IsButton(child_wid->type)) continue;
1530 
1531  assert(i < lengthof(this->panel_widths));
1532  this->panel_widths[i++] = child_wid->current_x;
1533  _toolbar_width += child_wid->current_x;
1534  }
1535  }
1536 
1537  /* virtual */ const byte *GetButtonArrangement(uint &width, uint &arrangable_count, uint &button_count, uint &spacer_count) const
1538  {
1539  static const byte arrange_all[] = {
1540  0, 1, 2, 3, 4, 18, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 26, 28,
1541  };
1542  static const byte arrange_nopanel[] = {
1543  0, 1, 2, 3, 18, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 26, 28,
1544  };
1545  static const byte arrange_switch[] = {
1546  18, 8, 11, 12, 13, 14, 15, 16, 17, 29,
1547  0, 1, 2, 3, 18, 9, 10, 26, 28, 29,
1548  };
1549 
1550  /* If we can place all buttons *and* the panels, show them. */
1551  uint min_full_width = (lengthof(arrange_all) - lengthof(this->panel_widths)) * this->smallest_x + this->panel_widths[0] + this->panel_widths[1];
1552  if (width >= min_full_width) {
1553  width -= this->panel_widths[0] + this->panel_widths[1];
1554  arrangable_count = lengthof(arrange_all);
1555  button_count = arrangable_count - 2;
1556  spacer_count = this->spacers;
1557  return arrange_all;
1558  }
1559 
1560  /* 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 */
1561  uint min_small_width = (lengthof(arrange_switch) - lengthof(this->panel_widths)) * this->smallest_x / 2 + this->panel_widths[1];
1562  if (width > min_small_width) {
1563  width -= this->panel_widths[1];
1564  arrangable_count = lengthof(arrange_nopanel);
1565  button_count = arrangable_count - 1;
1566  spacer_count = this->spacers - 1;
1567  return arrange_nopanel;
1568  }
1569 
1570  /* Split toolbar */
1571  width -= this->panel_widths[1];
1572  arrangable_count = lengthof(arrange_switch) / 2;
1573  button_count = arrangable_count - 1;
1574  spacer_count = 0;
1575  return arrange_switch + ((_toolbar_mode == TB_LOWER) ? arrangable_count : 0);
1576  }
1577 };
1578 
1579 /* --- Toolbar handling for the 'normal' case */
1580 
1581 typedef CallBackFunction ToolbarButtonProc(Window *w);
1582 
1583 static ToolbarButtonProc * const _toolbar_button_procs[] = {
1584  ToolbarPauseClick,
1588  ToolbarMapClick,
1589  ToolbarTownClick,
1590  ToolbarSubsidiesClick,
1591  ToolbarStationsClick,
1592  ToolbarFinancesClick,
1593  ToolbarCompaniesClick,
1594  ToolbarStoryClick,
1595  ToolbarGoalClick,
1596  ToolbarGraphsClick,
1597  ToolbarLeagueClick,
1598  ToolbarIndustryClick,
1599  ToolbarTrainClick,
1600  ToolbarRoadClick,
1601  ToolbarShipClick,
1602  ToolbarAirClick,
1603  ToolbarZoomInClick,
1604  ToolbarZoomOutClick,
1605  ToolbarBuildRailClick,
1606  ToolbarBuildRoadClick,
1607  ToolbarBuildWaterClick,
1608  ToolbarBuildAirClick,
1609  ToolbarForestClick,
1610  ToolbarMusicClick,
1611  ToolbarNewspaperClick,
1612  ToolbarHelpClick,
1613  ToolbarSwitchClick,
1614 };
1615 
1616 enum MainToolbarHotkeys {
1617  MTHK_PAUSE,
1618  MTHK_FASTFORWARD,
1619  MTHK_SETTINGS,
1620  MTHK_SAVEGAME,
1621  MTHK_LOADGAME,
1622  MTHK_SMALLMAP,
1623  MTHK_TOWNDIRECTORY,
1624  MTHK_SUBSIDIES,
1625  MTHK_STATIONS,
1626  MTHK_FINANCES,
1627  MTHK_COMPANIES,
1628  MTHK_STORY,
1629  MTHK_GOAL,
1630  MTHK_GRAPHS,
1631  MTHK_LEAGUE,
1632  MTHK_INDUSTRIES,
1633  MTHK_TRAIN_LIST,
1634  MTHK_ROADVEH_LIST,
1635  MTHK_SHIP_LIST,
1636  MTHK_AIRCRAFT_LIST,
1637  MTHK_ZOOM_IN,
1638  MTHK_ZOOM_OUT,
1639  MTHK_BUILD_RAIL,
1640  MTHK_BUILD_ROAD,
1641  MTHK_BUILD_DOCKS,
1642  MTHK_BUILD_AIRPORT,
1643  MTHK_BUILD_TREES,
1644  MTHK_MUSIC,
1645  MTHK_AI_DEBUG,
1646  MTHK_SMALL_SCREENSHOT,
1647  MTHK_ZOOMEDIN_SCREENSHOT,
1648  MTHK_DEFAULTZOOM_SCREENSHOT,
1649  MTHK_GIANT_SCREENSHOT,
1650  MTHK_CHEATS,
1651  MTHK_TERRAFORM,
1652  MTHK_EXTRA_VIEWPORT,
1653  MTHK_CLIENT_LIST,
1654  MTHK_SIGN_LIST,
1655 };
1656 
1660 
1661  MainToolbarWindow(WindowDesc *desc) : Window(desc)
1662  {
1663  this->InitNested(0);
1664 
1665  this->last_started_action = CBF_NONE;
1666  CLRBITS(this->flags, WF_WHITE_BORDER);
1667  this->SetWidgetDisabledState(WID_TN_PAUSE, _networking && !_network_server); // if not server, disable pause button
1668  this->SetWidgetDisabledState(WID_TN_FAST_FORWARD, _networking); // if networking, disable fast-forward button
1669  PositionMainToolbar(this);
1671  }
1672 
1673  virtual void FindWindowPlacementAndResize(int def_width, int def_height)
1674  {
1676  }
1677 
1678  virtual void OnPaint()
1679  {
1680  /* If spectator, disable all construction buttons
1681  * ie : Build road, rail, ships, airports and landscaping
1682  * Since enabled state is the default, just disable when needed */
1684  /* disable company list drop downs, if there are no companies */
1686 
1689 
1692 
1693  this->DrawWidgets();
1694  }
1695 
1696  virtual void OnClick(Point pt, int widget, int click_count)
1697  {
1698  if (_game_mode != GM_MENU && !this->IsWidgetDisabled(widget)) _toolbar_button_procs[widget](this);
1699  }
1700 
1701  virtual void OnDropdownSelect(int widget, int index)
1702  {
1703  CallBackFunction cbf = _menu_clicked_procs[widget](index);
1704  if (cbf != CBF_NONE) this->last_started_action = cbf;
1705  }
1706 
1707  virtual EventState OnHotkey(int hotkey)
1708  {
1709  switch (hotkey) {
1710  case MTHK_PAUSE: ToolbarPauseClick(this); break;
1711  case MTHK_FASTFORWARD: ToolbarFastForwardClick(this); break;
1712  case MTHK_SETTINGS: ShowGameOptions(); break;
1713  case MTHK_SAVEGAME: MenuClickSaveLoad(); break;
1714  case MTHK_LOADGAME: ShowSaveLoadDialog(SLD_LOAD_GAME); break;
1715  case MTHK_SMALLMAP: ShowSmallMap(); break;
1716  case MTHK_TOWNDIRECTORY: ShowTownDirectory(); break;
1717  case MTHK_SUBSIDIES: ShowSubsidiesList(); break;
1718  case MTHK_STATIONS: ShowCompanyStations(_local_company); break;
1719  case MTHK_FINANCES: ShowCompanyFinances(_local_company); break;
1720  case MTHK_COMPANIES: ShowCompany(_local_company); break;
1721  case MTHK_STORY: ShowStoryBook(_local_company); break;
1722  case MTHK_GOAL: ShowGoalsList(_local_company); break;
1723  case MTHK_GRAPHS: ShowOperatingProfitGraph(); break;
1724  case MTHK_LEAGUE: ShowCompanyLeagueTable(); break;
1725  case MTHK_INDUSTRIES: ShowBuildIndustryWindow(); break;
1726  case MTHK_TRAIN_LIST: ShowVehicleListWindow(_local_company, VEH_TRAIN); break;
1727  case MTHK_ROADVEH_LIST: ShowVehicleListWindow(_local_company, VEH_ROAD); break;
1728  case MTHK_SHIP_LIST: ShowVehicleListWindow(_local_company, VEH_SHIP); break;
1729  case MTHK_AIRCRAFT_LIST: ShowVehicleListWindow(_local_company, VEH_AIRCRAFT); break;
1730  case MTHK_ZOOM_IN: ToolbarZoomInClick(this); break;
1731  case MTHK_ZOOM_OUT: ToolbarZoomOutClick(this); break;
1732  case MTHK_BUILD_RAIL: if (CanBuildVehicleInfrastructure(VEH_TRAIN)) ShowBuildRailToolbar(_last_built_railtype); break;
1733  case MTHK_BUILD_ROAD: ShowBuildRoadToolbar(_last_built_roadtype); break;
1734  case MTHK_BUILD_DOCKS: ShowBuildDocksToolbar(); break;
1735  case MTHK_BUILD_AIRPORT: if (CanBuildVehicleInfrastructure(VEH_AIRCRAFT)) ShowBuildAirToolbar(); break;
1736  case MTHK_BUILD_TREES: ShowBuildTreesToolbar(); break;
1737  case MTHK_MUSIC: ShowMusicWindow(); break;
1738  case MTHK_AI_DEBUG: ShowAIDebugWindow(); break;
1739  case MTHK_SMALL_SCREENSHOT: MenuClickSmallScreenshot(); break;
1740  case MTHK_ZOOMEDIN_SCREENSHOT: MenuClickLargeWorldScreenshot(SC_ZOOMEDIN); break;
1741  case MTHK_DEFAULTZOOM_SCREENSHOT: MenuClickLargeWorldScreenshot(SC_DEFAULTZOOM); break;
1742  case MTHK_GIANT_SCREENSHOT: MenuClickLargeWorldScreenshot(SC_WORLD); break;
1743  case MTHK_CHEATS: if (!_networking) ShowCheatWindow(); break;
1744  case MTHK_TERRAFORM: ShowTerraformToolbar(); break;
1745  case MTHK_EXTRA_VIEWPORT: ShowExtraViewPortWindowForTileUnderCursor(); break;
1746 #ifdef ENABLE_NETWORK
1747  case MTHK_CLIENT_LIST: if (_networking) ShowClientList(); break;
1748 #endif
1749  case MTHK_SIGN_LIST: ShowSignList(); break;
1750  default: return ES_NOT_HANDLED;
1751  }
1752  return ES_HANDLED;
1753  }
1754 
1755  virtual void OnPlaceObject(Point pt, TileIndex tile)
1756  {
1757  switch (this->last_started_action) {
1758  case CBF_PLACE_SIGN:
1759  PlaceProc_Sign(tile);
1760  break;
1761 
1762  case CBF_PLACE_LANDINFO:
1763  ShowLandInfo(tile);
1764  break;
1765 
1766  default: NOT_REACHED();
1767  }
1768  }
1769 
1770  virtual void OnTick()
1771  {
1772  if (this->IsWidgetLowered(WID_TN_PAUSE) != !!_pause_mode) {
1775  }
1776 
1777  if (this->IsWidgetLowered(WID_TN_FAST_FORWARD) != !!_fast_forward) {
1780  }
1781  }
1782 
1783  virtual void OnTimeout()
1784  {
1785  /* We do not want to automatically raise the pause, fast forward and
1786  * switchbar buttons; they have to stay down when pressed etc. */
1787  for (uint i = WID_TN_SETTINGS; i < WID_TN_SWITCH_BAR; i++) {
1788  if (this->IsWidgetLowered(i)) {
1789  this->RaiseWidget(i);
1790  this->SetWidgetDirty(i);
1791  }
1792  }
1793  }
1794 
1800  virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
1801  {
1802  if (!gui_scope) return;
1804  }
1805 
1806  static HotkeyList hotkeys;
1807 };
1808 
1809 const uint16 _maintoolbar_pause_keys[] = {WKC_F1, WKC_PAUSE, 0};
1810 const uint16 _maintoolbar_zoomin_keys[] = {WKC_NUM_PLUS, WKC_EQUALS, WKC_SHIFT | WKC_EQUALS, WKC_SHIFT | WKC_F5, 0};
1811 const uint16 _maintoolbar_zoomout_keys[] = {WKC_NUM_MINUS, WKC_MINUS, WKC_SHIFT | WKC_MINUS, WKC_SHIFT | WKC_F6, 0};
1812 const uint16 _maintoolbar_smallmap_keys[] = {WKC_F4, 'M', 0};
1813 
1814 static Hotkey maintoolbar_hotkeys[] = {
1815  Hotkey(_maintoolbar_pause_keys, "pause", MTHK_PAUSE),
1816  Hotkey((uint16)0, "fastforward", MTHK_FASTFORWARD),
1817  Hotkey(WKC_F2, "settings", MTHK_SETTINGS),
1818  Hotkey(WKC_F3, "saveload", MTHK_SAVEGAME),
1819  Hotkey((uint16)0, "load_game", MTHK_LOADGAME),
1820  Hotkey(_maintoolbar_smallmap_keys, "smallmap", MTHK_SMALLMAP),
1821  Hotkey(WKC_F5, "town_list", MTHK_TOWNDIRECTORY),
1822  Hotkey(WKC_F6, "subsidies", MTHK_SUBSIDIES),
1823  Hotkey(WKC_F7, "station_list", MTHK_STATIONS),
1824  Hotkey(WKC_F8, "finances", MTHK_FINANCES),
1825  Hotkey(WKC_F9, "companies", MTHK_COMPANIES),
1826  Hotkey((uint16)0, "story_book", MTHK_STORY),
1827  Hotkey((uint16)0, "goal_list", MTHK_GOAL),
1828  Hotkey(WKC_F10, "graphs", MTHK_GRAPHS),
1829  Hotkey(WKC_F11, "league", MTHK_LEAGUE),
1830  Hotkey(WKC_F12, "industry_list", MTHK_INDUSTRIES),
1831  Hotkey(WKC_SHIFT | WKC_F1, "train_list", MTHK_TRAIN_LIST),
1832  Hotkey(WKC_SHIFT | WKC_F2, "roadveh_list", MTHK_ROADVEH_LIST),
1833  Hotkey(WKC_SHIFT | WKC_F3, "ship_list", MTHK_SHIP_LIST),
1834  Hotkey(WKC_SHIFT | WKC_F4, "aircraft_list", MTHK_AIRCRAFT_LIST),
1835  Hotkey(_maintoolbar_zoomin_keys, "zoomin", MTHK_ZOOM_IN),
1836  Hotkey(_maintoolbar_zoomout_keys, "zoomout", MTHK_ZOOM_OUT),
1837  Hotkey(WKC_SHIFT | WKC_F7, "build_rail", MTHK_BUILD_RAIL),
1838  Hotkey(WKC_SHIFT | WKC_F8, "build_road", MTHK_BUILD_ROAD),
1839  Hotkey(WKC_SHIFT | WKC_F9, "build_docks", MTHK_BUILD_DOCKS),
1840  Hotkey(WKC_SHIFT | WKC_F10, "build_airport", MTHK_BUILD_AIRPORT),
1841  Hotkey(WKC_SHIFT | WKC_F11, "build_trees", MTHK_BUILD_TREES),
1842  Hotkey(WKC_SHIFT | WKC_F12, "music", MTHK_MUSIC),
1843  Hotkey((uint16)0, "ai_debug", MTHK_AI_DEBUG),
1844  Hotkey(WKC_CTRL | 'S', "small_screenshot", MTHK_SMALL_SCREENSHOT),
1845  Hotkey(WKC_CTRL | 'P', "zoomedin_screenshot", MTHK_ZOOMEDIN_SCREENSHOT),
1846  Hotkey(WKC_CTRL | 'D', "defaultzoom_screenshot", MTHK_DEFAULTZOOM_SCREENSHOT),
1847  Hotkey((uint16)0, "giant_screenshot", MTHK_GIANT_SCREENSHOT),
1848  Hotkey(WKC_CTRL | WKC_ALT | 'C', "cheats", MTHK_CHEATS),
1849  Hotkey('L', "terraform", MTHK_TERRAFORM),
1850  Hotkey('V', "extra_viewport", MTHK_EXTRA_VIEWPORT),
1851 #ifdef ENABLE_NETWORK
1852  Hotkey((uint16)0, "client_list", MTHK_CLIENT_LIST),
1853 #endif
1854  Hotkey((uint16)0, "sign_list", MTHK_SIGN_LIST),
1855  HOTKEY_LIST_END
1856 };
1857 HotkeyList MainToolbarWindow::hotkeys("maintoolbar", maintoolbar_hotkeys);
1858 
1859 static NWidgetBase *MakeMainToolbar(int *biggest_index)
1860 {
1862  static const SpriteID toolbar_button_sprites[] = {
1863  SPR_IMG_PAUSE, // WID_TN_PAUSE
1864  SPR_IMG_FASTFORWARD, // WID_TN_FAST_FORWARD
1865  SPR_IMG_SETTINGS, // WID_TN_SETTINGS
1866  SPR_IMG_SAVE, // WID_TN_SAVE
1867  SPR_IMG_SMALLMAP, // WID_TN_SMALL_MAP
1868  SPR_IMG_TOWN, // WID_TN_TOWNS
1869  SPR_IMG_SUBSIDIES, // WID_TN_SUBSIDIES
1870  SPR_IMG_COMPANY_LIST, // WID_TN_STATIONS
1871  SPR_IMG_COMPANY_FINANCE, // WID_TN_FINANCES
1872  SPR_IMG_COMPANY_GENERAL, // WID_TN_COMPANIES
1873  SPR_IMG_STORY_BOOK, // WID_TN_STORY
1874  SPR_IMG_GOAL, // WID_TN_GOAL
1875  SPR_IMG_GRAPHS, // WID_TN_GRAPHS
1876  SPR_IMG_COMPANY_LEAGUE, // WID_TN_LEAGUE
1877  SPR_IMG_INDUSTRY, // WID_TN_INDUSTRIES
1878  SPR_IMG_TRAINLIST, // WID_TN_TRAINS
1879  SPR_IMG_TRUCKLIST, // WID_TN_ROADVEHS
1880  SPR_IMG_SHIPLIST, // WID_TN_SHIPS
1881  SPR_IMG_AIRPLANESLIST, // WID_TN_AIRCRAFT
1882  SPR_IMG_ZOOMIN, // WID_TN_ZOOMIN
1883  SPR_IMG_ZOOMOUT, // WID_TN_ZOOMOUT
1884  SPR_IMG_BUILDRAIL, // WID_TN_RAILS
1885  SPR_IMG_BUILDROAD, // WID_TN_ROADS
1886  SPR_IMG_BUILDWATER, // WID_TN_WATER
1887  SPR_IMG_BUILDAIR, // WID_TN_AIR
1888  SPR_IMG_LANDSCAPING, // WID_TN_LANDSCAPE
1889  SPR_IMG_MUSIC, // WID_TN_MUSIC_SOUND
1890  SPR_IMG_MESSAGES, // WID_TN_MESSAGES
1891  SPR_IMG_QUERY, // WID_TN_HELP
1892  SPR_IMG_SWITCH_TOOLBAR, // WID_TN_SWITCH_BAR
1893  };
1894 
1896  for (uint i = 0; i < WID_TN_END; i++) {
1897  switch (i) {
1898  case 4: case 8: case 15: case 19: case 21: case 26: hor->Add(new NWidgetSpacer(0, 0)); break;
1899  }
1900  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));
1901  }
1902 
1903  *biggest_index = max<int>(*biggest_index, WID_TN_SWITCH_BAR);
1904  return hor;
1905 }
1906 
1907 static const NWidgetPart _nested_toolbar_normal_widgets[] = {
1909 };
1910 
1911 static WindowDesc _toolb_normal_desc(
1912  WDP_MANUAL, NULL, 0, 0,
1914  WDF_NO_FOCUS,
1915  _nested_toolbar_normal_widgets, lengthof(_nested_toolbar_normal_widgets),
1916  &MainToolbarWindow::hotkeys
1917 );
1918 
1919 
1920 /* --- Toolbar handling for the scenario editor */
1921 
1922 static ToolbarButtonProc * const _scen_toolbar_button_procs[] = {
1923  ToolbarPauseClick,
1927  ToolbarBtn_NULL,
1929  ToolbarScenDateBackward,
1930  ToolbarScenDateForward,
1931  ToolbarScenMapTownDir,
1932  ToolbarZoomInClick,
1933  ToolbarZoomOutClick,
1934  ToolbarScenGenLand,
1935  ToolbarScenGenTown,
1936  ToolbarScenGenIndustry,
1937  ToolbarScenBuildRoad,
1938  ToolbarScenBuildDocks,
1939  ToolbarScenPlantTrees,
1940  ToolbarScenPlaceSign,
1941  ToolbarBtn_NULL,
1942  NULL,
1943  NULL,
1944  NULL,
1945  NULL,
1946  NULL,
1947  NULL,
1948  NULL,
1949  ToolbarMusicClick,
1950  NULL,
1951  ToolbarHelpClick,
1952  ToolbarSwitchClick,
1953 };
1954 
1955 enum MainToolbarEditorHotkeys {
1956  MTEHK_PAUSE,
1957  MTEHK_FASTFORWARD,
1958  MTEHK_SETTINGS,
1959  MTEHK_SAVEGAME,
1960  MTEHK_GENLAND,
1961  MTEHK_GENTOWN,
1962  MTEHK_GENINDUSTRY,
1963  MTEHK_BUILD_ROAD,
1964  MTEHK_BUILD_DOCKS,
1965  MTEHK_BUILD_TREES,
1966  MTEHK_SIGN,
1967  MTEHK_MUSIC,
1968  MTEHK_LANDINFO,
1969  MTEHK_SMALL_SCREENSHOT,
1970  MTEHK_ZOOMEDIN_SCREENSHOT,
1971  MTEHK_DEFAULTZOOM_SCREENSHOT,
1972  MTEHK_GIANT_SCREENSHOT,
1973  MTEHK_ZOOM_IN,
1974  MTEHK_ZOOM_OUT,
1975  MTEHK_TERRAFORM,
1976  MTEHK_SMALLMAP,
1977  MTEHK_EXTRA_VIEWPORT,
1978 };
1979 
1982 
1984  {
1985  this->InitNested(0);
1986 
1987  this->last_started_action = CBF_NONE;
1988  CLRBITS(this->flags, WF_WHITE_BORDER);
1989  PositionMainToolbar(this);
1991  }
1992 
1993  virtual void FindWindowPlacementAndResize(int def_width, int def_height)
1994  {
1996  }
1997 
1998  virtual void OnPaint()
1999  {
2002 
2003  this->DrawWidgets();
2004  }
2005 
2006  virtual void DrawWidget(const Rect &r, int widget) const
2007  {
2008  switch (widget) {
2009  case WID_TE_DATE:
2011  DrawString(r.left, r.right, (this->height - FONT_HEIGHT_NORMAL) / 2, STR_WHITE_DATE_LONG, TC_FROMSTRING, SA_HOR_CENTER);
2012  break;
2013 
2014  case WID_TE_SPACER: {
2015  int height = r.bottom - r.top;
2016  if (height > 2 * FONT_HEIGHT_NORMAL) {
2017  DrawString(r.left, r.right, (height + 1) / 2 - FONT_HEIGHT_NORMAL, STR_SCENEDIT_TOOLBAR_OPENTTD, TC_FROMSTRING, SA_HOR_CENTER);
2018  DrawString(r.left, r.right, (height + 1) / 2, STR_SCENEDIT_TOOLBAR_SCENARIO_EDITOR, TC_FROMSTRING, SA_HOR_CENTER);
2019  } else {
2020  DrawString(r.left, r.right, (height - FONT_HEIGHT_NORMAL) / 2, STR_SCENEDIT_TOOLBAR_SCENARIO_EDITOR, TC_FROMSTRING, SA_HOR_CENTER);
2021  }
2022  break;
2023  }
2024  }
2025  }
2026 
2027  virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
2028  {
2029  switch (widget) {
2030  case WID_TE_SPACER:
2031  size->width = max(GetStringBoundingBox(STR_SCENEDIT_TOOLBAR_OPENTTD).width, GetStringBoundingBox(STR_SCENEDIT_TOOLBAR_SCENARIO_EDITOR).width) + WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT;
2032  break;
2033 
2034  case WID_TE_DATE:
2035  SetDParam(0, ConvertYMDToDate(MAX_YEAR, 0, 1));
2036  *size = GetStringBoundingBox(STR_WHITE_DATE_LONG);
2037  size->height = max(size->height, GetSpriteSize(SPR_IMG_SAVE).height + WD_IMGBTN_TOP + WD_IMGBTN_BOTTOM);
2038  break;
2039  }
2040  }
2041 
2042  virtual void OnClick(Point pt, int widget, int click_count)
2043  {
2044  if (_game_mode == GM_MENU) return;
2045  CallBackFunction cbf = _scen_toolbar_button_procs[widget](this);
2046  if (cbf != CBF_NONE) this->last_started_action = cbf;
2047  }
2048 
2049  virtual void OnDropdownSelect(int widget, int index)
2050  {
2051  /* The map button is in a different location on the scenario
2052  * editor toolbar, so we need to adjust for it. */
2053  if (widget == WID_TE_SMALL_MAP) widget = WID_TN_SMALL_MAP;
2054  CallBackFunction cbf = _menu_clicked_procs[widget](index);
2055  if (cbf != CBF_NONE) this->last_started_action = cbf;
2056  if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
2057  }
2058 
2059  virtual EventState OnHotkey(int hotkey)
2060  {
2061  CallBackFunction cbf = CBF_NONE;
2062  switch (hotkey) {
2063  case MTEHK_PAUSE: ToolbarPauseClick(this); break;
2064  case MTEHK_FASTFORWARD: ToolbarFastForwardClick(this); break;
2065  case MTEHK_SETTINGS: ShowGameOptions(); break;
2066  case MTEHK_SAVEGAME: MenuClickSaveLoad(); break;
2067  case MTEHK_GENLAND: ToolbarScenGenLand(this); break;
2068  case MTEHK_GENTOWN: ToolbarScenGenTown(this); break;
2069  case MTEHK_GENINDUSTRY: ToolbarScenGenIndustry(this); break;
2070  case MTEHK_BUILD_ROAD: ToolbarScenBuildRoad(this); break;
2071  case MTEHK_BUILD_DOCKS: ToolbarScenBuildDocks(this); break;
2072  case MTEHK_BUILD_TREES: ToolbarScenPlantTrees(this); break;
2073  case MTEHK_SIGN: cbf = ToolbarScenPlaceSign(this); break;
2074  case MTEHK_MUSIC: ShowMusicWindow(); break;
2075  case MTEHK_LANDINFO: cbf = PlaceLandBlockInfo(); break;
2076  case MTEHK_SMALL_SCREENSHOT: MenuClickSmallScreenshot(); break;
2077  case MTEHK_ZOOMEDIN_SCREENSHOT: MenuClickLargeWorldScreenshot(SC_ZOOMEDIN); break;
2078  case MTEHK_DEFAULTZOOM_SCREENSHOT: MenuClickLargeWorldScreenshot(SC_DEFAULTZOOM); break;
2079  case MTEHK_GIANT_SCREENSHOT: MenuClickLargeWorldScreenshot(SC_WORLD); break;
2080  case MTEHK_ZOOM_IN: ToolbarZoomInClick(this); break;
2081  case MTEHK_ZOOM_OUT: ToolbarZoomOutClick(this); break;
2082  case MTEHK_TERRAFORM: ShowEditorTerraformToolbar(); break;
2083  case MTEHK_SMALLMAP: ShowSmallMap(); break;
2084  case MTEHK_EXTRA_VIEWPORT: ShowExtraViewPortWindowForTileUnderCursor(); break;
2085  default: return ES_NOT_HANDLED;
2086  }
2087  if (cbf != CBF_NONE) this->last_started_action = cbf;
2088  return ES_HANDLED;
2089  }
2090 
2091  virtual void OnPlaceObject(Point pt, TileIndex tile)
2092  {
2093  switch (this->last_started_action) {
2094  case CBF_PLACE_SIGN:
2095  PlaceProc_Sign(tile);
2096  break;
2097 
2098  case CBF_PLACE_LANDINFO:
2099  ShowLandInfo(tile);
2100  break;
2101 
2102  default: NOT_REACHED();
2103  }
2104  }
2105 
2106  virtual void OnTimeout()
2107  {
2111  }
2112 
2113  virtual void OnTick()
2114  {
2115  if (this->IsWidgetLowered(WID_TE_PAUSE) != !!_pause_mode) {
2117  this->SetDirty();
2118  }
2119 
2120  if (this->IsWidgetLowered(WID_TE_FAST_FORWARD) != !!_fast_forward) {
2122  this->SetDirty();
2123  }
2124  }
2125 
2131  virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
2132  {
2133  if (!gui_scope) return;
2135  }
2136 
2137  virtual void OnQueryTextFinished(char *str)
2138  {
2139  /* Was 'cancel' pressed? */
2140  if (str == NULL) return;
2141 
2142  int32 value;
2143  if (!StrEmpty(str)) {
2144  value = atoi(str);
2145  } else {
2146  /* An empty string means revert to the default */
2147  value = DEF_START_YEAR;
2148  }
2149  SetStartingYear(value);
2150 
2151  this->SetDirty();
2152  }
2153 
2154  static HotkeyList hotkeys;
2155 };
2156 
2157 static Hotkey scenedit_maintoolbar_hotkeys[] = {
2158  Hotkey(_maintoolbar_pause_keys, "pause", MTEHK_PAUSE),
2159  Hotkey((uint16)0, "fastforward", MTEHK_FASTFORWARD),
2160  Hotkey(WKC_F2, "settings", MTEHK_SETTINGS),
2161  Hotkey(WKC_F3, "saveload", MTEHK_SAVEGAME),
2162  Hotkey(WKC_F4, "gen_land", MTEHK_GENLAND),
2163  Hotkey(WKC_F5, "gen_town", MTEHK_GENTOWN),
2164  Hotkey(WKC_F6, "gen_industry", MTEHK_GENINDUSTRY),
2165  Hotkey(WKC_F7, "build_road", MTEHK_BUILD_ROAD),
2166  Hotkey(WKC_F8, "build_docks", MTEHK_BUILD_DOCKS),
2167  Hotkey(WKC_F9, "build_trees", MTEHK_BUILD_TREES),
2168  Hotkey(WKC_F10, "build_sign", MTEHK_SIGN),
2169  Hotkey(WKC_F11, "music", MTEHK_MUSIC),
2170  Hotkey(WKC_F12, "land_info", MTEHK_LANDINFO),
2171  Hotkey(WKC_CTRL | 'S', "small_screenshot", MTEHK_SMALL_SCREENSHOT),
2172  Hotkey(WKC_CTRL | 'P', "zoomedin_screenshot", MTEHK_ZOOMEDIN_SCREENSHOT),
2173  Hotkey(WKC_CTRL | 'D', "defaultzoom_screenshot", MTEHK_DEFAULTZOOM_SCREENSHOT),
2174  Hotkey((uint16)0, "giant_screenshot", MTEHK_GIANT_SCREENSHOT),
2175  Hotkey(_maintoolbar_zoomin_keys, "zoomin", MTEHK_ZOOM_IN),
2176  Hotkey(_maintoolbar_zoomout_keys, "zoomout", MTEHK_ZOOM_OUT),
2177  Hotkey('L', "terraform", MTEHK_TERRAFORM),
2178  Hotkey('M', "smallmap", MTEHK_SMALLMAP),
2179  Hotkey('V', "extra_viewport", MTEHK_EXTRA_VIEWPORT),
2180  HOTKEY_LIST_END
2181 };
2182 HotkeyList ScenarioEditorToolbarWindow::hotkeys("scenedit_maintoolbar", scenedit_maintoolbar_hotkeys);
2183 
2184 static const NWidgetPart _nested_toolb_scen_inner_widgets[] = {
2185  NWidget(WWT_IMGBTN, COLOUR_GREY, WID_TE_PAUSE), SetDataTip(SPR_IMG_PAUSE, STR_TOOLBAR_TOOLTIP_PAUSE_GAME),
2186  NWidget(WWT_IMGBTN, COLOUR_GREY, WID_TE_FAST_FORWARD), SetDataTip(SPR_IMG_FASTFORWARD, STR_TOOLBAR_TOOLTIP_FORWARD),
2187  NWidget(WWT_IMGBTN, COLOUR_GREY, WID_TE_SETTINGS), SetDataTip(SPR_IMG_SETTINGS, STR_TOOLBAR_TOOLTIP_OPTIONS),
2188  NWidget(WWT_IMGBTN_2, COLOUR_GREY, WID_TE_SAVE), SetDataTip(SPR_IMG_SAVE, STR_SCENEDIT_TOOLBAR_TOOLTIP_SAVE_SCENARIO_LOAD_SCENARIO),
2190  NWidget(WWT_PANEL, COLOUR_GREY, WID_TE_SPACER), EndContainer(),
2192  NWidget(WWT_PANEL, COLOUR_GREY, WID_TE_DATE_PANEL),
2193  NWidget(NWID_HORIZONTAL), SetPIP(3, 2, 3),
2194  NWidget(WWT_IMGBTN, COLOUR_GREY, WID_TE_DATE_BACKWARD), SetDataTip(SPR_ARROW_DOWN, STR_SCENEDIT_TOOLBAR_TOOLTIP_MOVE_THE_STARTING_DATE_BACKWARD),
2195  NWidget(WWT_EMPTY, COLOUR_GREY, WID_TE_DATE), SetDataTip(STR_NULL, STR_SCENEDIT_TOOLBAR_TOOLTIP_SET_DATE),
2196  NWidget(WWT_IMGBTN, COLOUR_GREY, WID_TE_DATE_FORWARD), SetDataTip(SPR_ARROW_UP, STR_SCENEDIT_TOOLBAR_TOOLTIP_MOVE_THE_STARTING_DATE_FORWARD),
2197  EndContainer(),
2198  EndContainer(),
2200  NWidget(WWT_IMGBTN, COLOUR_GREY, WID_TE_SMALL_MAP), SetDataTip(SPR_IMG_SMALLMAP, STR_SCENEDIT_TOOLBAR_TOOLTIP_DISPLAY_MAP_TOWN_DIRECTORY),
2202  NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_TE_ZOOM_IN), SetDataTip(SPR_IMG_ZOOMIN, STR_TOOLBAR_TOOLTIP_ZOOM_THE_VIEW_IN),
2203  NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_TE_ZOOM_OUT), SetDataTip(SPR_IMG_ZOOMOUT, STR_TOOLBAR_TOOLTIP_ZOOM_THE_VIEW_OUT),
2205  NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_TE_LAND_GENERATE), SetDataTip(SPR_IMG_LANDSCAPING, STR_SCENEDIT_TOOLBAR_LANDSCAPE_GENERATION),
2206  NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_TE_TOWN_GENERATE), SetDataTip(SPR_IMG_TOWN, STR_SCENEDIT_TOOLBAR_TOWN_GENERATION),
2207  NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_TE_INDUSTRY), SetDataTip(SPR_IMG_INDUSTRY, STR_SCENEDIT_TOOLBAR_INDUSTRY_GENERATION),
2208  NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_TE_ROADS), SetDataTip(SPR_IMG_BUILDROAD, STR_SCENEDIT_TOOLBAR_ROAD_CONSTRUCTION),
2209  NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_TE_WATER), SetDataTip(SPR_IMG_BUILDWATER, STR_TOOLBAR_TOOLTIP_BUILD_SHIP_DOCKS),
2210  NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_TE_TREES), SetDataTip(SPR_IMG_PLANTTREES, STR_SCENEDIT_TOOLBAR_PLANT_TREES),
2211  NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_TE_SIGNS), SetDataTip(SPR_IMG_SIGN, STR_SCENEDIT_TOOLBAR_PLACE_SIGN),
2213  NWidget(WWT_IMGBTN, COLOUR_GREY, WID_TE_MUSIC_SOUND), SetDataTip(SPR_IMG_MUSIC, STR_TOOLBAR_TOOLTIP_SHOW_SOUND_MUSIC_WINDOW),
2214  NWidget(WWT_IMGBTN, COLOUR_GREY, WID_TE_HELP), SetDataTip(SPR_IMG_QUERY, STR_TOOLBAR_TOOLTIP_LAND_BLOCK_INFORMATION),
2215  NWidget(WWT_IMGBTN, COLOUR_GREY, WID_TE_SWITCH_BAR), SetDataTip(SPR_IMG_SWITCH_TOOLBAR, STR_TOOLBAR_TOOLTIP_SWITCH_TOOLBAR),
2216 };
2217 
2218 static NWidgetBase *MakeScenarioToolbar(int *biggest_index)
2219 {
2220  return MakeNWidgets(_nested_toolb_scen_inner_widgets, lengthof(_nested_toolb_scen_inner_widgets), biggest_index, new NWidgetScenarioToolbarContainer());
2221 }
2222 
2223 static const NWidgetPart _nested_toolb_scen_widgets[] = {
2224  NWidgetFunction(MakeScenarioToolbar),
2225 };
2226 
2227 static WindowDesc _toolb_scen_desc(
2228  WDP_MANUAL, NULL, 0, 0,
2230  WDF_NO_FOCUS,
2231  _nested_toolb_scen_widgets, lengthof(_nested_toolb_scen_widgets),
2232  &ScenarioEditorToolbarWindow::hotkeys
2233 );
2234 
2237 {
2238  /* Clean old GUI values; railtype is (re)set by rail_gui.cpp */
2239  _last_built_roadtype = ROADTYPE_ROAD;
2240 
2241  if (_game_mode == GM_EDITOR) {
2242  new ScenarioEditorToolbarWindow(&_toolb_scen_desc);
2243  } else {
2244  new MainToolbarWindow(&_toolb_normal_desc);
2245  }
2246 }