The Battle for Wesnoth  1.17.0-dev
hotkey_handler.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2014 - 2021
3  by Chris Beck <render787@gmail.com>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
17 
18 #include "actions/create.hpp"
19 #include "font/standard_colors.hpp"
20 #include "formula/string_utils.hpp"
21 #include "game_display.hpp"
22 #include "game_errors.hpp"
25 #include "preferences/game.hpp"
26 #include "game_state.hpp"
28 #include "hotkey/hotkey_item.hpp"
29 #include "map/map.hpp"
30 #include "play_controller.hpp"
31 #include "preferences/display.hpp"
32 #include "savegame.hpp"
33 #include "saved_game.hpp"
34 #include "whiteboard/manager.hpp"
35 
36 #include "units/unit.hpp"
37 
38 const std::string play_controller::hotkey_handler::wml_menu_hotkey_prefix = "wml_menu:";
39 
41  : play_controller_(pc)
42  , menu_handler_(pc.get_menu_handler())
43  , mouse_handler_(pc.get_mouse_handler_base())
44  , saved_game_(sg)
45  , savenames_()
46  , wml_commands_()
47  , last_context_menu_x_(0)
48  , last_context_menu_y_(0)
49 {}
50 
52 
54  return &play_controller_.get_display();
55 }
56 
58  return play_controller_.gamestate();
59 }
60 
62  return play_controller_.gamestate();
63 }
64 
67 
70 
73 }
74 
77 }
78 
81 }
82 
85 }
86 
89 }
90 
93 }
94 
97 }
98 
101 }
102 
105 }
106 
108  int x = gui()->get_location_x(gui()->mouseover_hex());
109  int y = gui()->get_location_y(gui()->mouseover_hex());
110 
111  SDL_MouseButtonEvent event;
112 
113  event.button = 1;
114  event.x = x + 30;
115  event.y = y + 30;
116  event.which = 0;
117  event.state = SDL_PRESSED;
118 
119  mouse_handler_.mouse_press(event, false);
120 }
121 
124 }
125 
127  auto touched_hex = gui()->mouseover_hex();
128  mouse_handler_.touch_action(touched_hex, false);
129 }
130 
133 }
134 
137 }
139  mouse_handler_.select_hex(gui()->mouseover_hex(), false);
140 }
141 
143  int x = gui()->get_location_x(gui()->mouseover_hex());
144  int y = gui()->get_location_y(gui()->mouseover_hex());
145 
146  SDL_MouseButtonEvent event;
147 
148  event.button = 3;
149  event.x = x + 30;
150  event.y = y + 30;
151  event.which = 0;
152  event.state = SDL_PRESSED;
153 
154  mouse_handler_.mouse_press(event, true);
155 }
156 
157 
160 }
161 
164 }
165 
168 }
169 
172 }
173 
176 }
177 
180 }
181 
184 }
185 
188 }
189 
192 }
193 
196 }
197 
200 }
201 
204 }
205 
208 }
209 
212 }
213 
215 {
217 
219  ao.discard_previous = true;
220 
221  if (preferences::turbo())
222  {
223  utils::string_map symbols;
225  gui()->announce(_("Accelerated speed enabled!") + "\n" + VGETTEXT("(press $hk to disable)", symbols), font::NORMAL_COLOR, ao);
226  }
227  else
228  {
229  gui()->announce(_("Accelerated speed disabled!"), font::NORMAL_COLOR, ao);
230  }
231 }
232 
234 {
236 }
237 
239 {
241 }
242 
244 {
246 }
247 
249 {
251 }
252 
254 {
255  hotkey::HOTKEY_COMMAND command = cmd.id;
256  if(index >= 0) {
257  unsigned i = static_cast<unsigned>(index);
258  if(i < savenames_.size() && !savenames_[i].empty()) {
259  // Load the game by throwing load_game_exception
261 
262  } else if ( i < wml_commands_.size() && wml_commands_[i] ) {
264  return true;
265  }
266  }
267  int prefixlen = wml_menu_hotkey_prefix.length();
268  if(command == hotkey::HOTKEY_WML && cmd.command.compare(0, prefixlen, wml_menu_hotkey_prefix) == 0)
269  {
270  std::string name = cmd.command.substr(prefixlen);
272 
274  name, hex, gamestate().gamedata_, gamestate(), play_controller_.get_units(), !press);
275  }
276  return command_executor::do_execute_command(cmd, index, press, release);
277 }
278 
280 {
281  if(index >= 0) {
282  unsigned i = static_cast<unsigned>(index);
283  if((i < savenames_.size() && !savenames_[i].empty())
284  || (i < wml_commands_.size() && wml_commands_[i])) {
285  return true;
286  }
287  }
288  switch(cmd.id) {
289 
290  // Commands we can always do:
307  case hotkey::HOTKEY_MUTE:
315  case hotkey::HOTKEY_HELP:
331  case hotkey::HOTKEY_NULL:
334  case hotkey::LUA_CONSOLE:
339  return true;
340 
342  std::size_t humans_notme_cnt = 0;
343  for(const auto& t : play_controller_.get_teams()) {
344  if(t.is_network_human()) {
345  ++humans_notme_cnt;
346  }
347  }
348 
349  return !(humans_notme_cnt < 1 || play_controller_.is_linger_mode() || play_controller_.is_observer());
350  }
351  // Commands that have some preconditions:
354 
357  return !linger() && play_controller_.enemies_visible();
358 
360  return !play_controller_.is_networked_mp(); // Can only load games if not in a network game
361 
366  return true;
367 
368  case hotkey::HOTKEY_REDO:
369  return play_controller_.can_redo();
370  case hotkey::HOTKEY_UNDO:
371  return play_controller_.can_undo();
372 
374  return menu_handler_.current_unit().valid();
375 
377  return mouse_handler_.get_last_hex().valid();
378 
380  return !events::commands_disabled &&
382  !(menu_handler_.current_unit()->unrenamable()) &&
383  menu_handler_.current_unit()->side() == gui()->viewing_side() &&
384  play_controller_.get_teams()[menu_handler_.current_unit()->side() - 1].is_local_human();
385 
386  default:
387  return false;
388  }
389 }
390 
391 template<typename T>
392 static void trim_items(std::vector<T>& newitems)
393 {
394  if(newitems.size() > 5) {
395  std::vector<T> subitems;
396  subitems.push_back(std::move(newitems[0]));
397  subitems.push_back(std::move(newitems[1]));
398  subitems.push_back(std::move(newitems[newitems.size() / 3]));
399  subitems.push_back(std::move(newitems[newitems.size() * 2 / 3]));
400  subitems.push_back(std::move(newitems.back()));
401  newitems = subitems;
402  }
403 }
404 
406 {
408 
409  savenames_.resize(i);
410 
411  auto pos = items.erase(items.begin() + i);
412  std::vector<config> newitems;
413  std::vector<std::string> newsaves;
414 
416  savegame::autosave_savegame autosave(saved_game_, compression_format);
417  savegame::scenariostart_savegame scenariostart_save(saved_game_, compression_format);
418 
419  const std::string start_name = scenariostart_save.create_filename();
420 
421  for(unsigned int turn = play_controller_.turn(); turn != 0; turn--) {
422  const std::string name = autosave.create_filename(turn);
423 
424  if(savegame::save_game_exists(name, comp_format)) {
425  newsaves.emplace_back(name + compression::format_extension(comp_format));
426  newitems.emplace_back("label", _("Back to Turn ") + std::to_string(turn));
427  }
428  }
429 
430  if(savegame::save_game_exists(start_name, comp_format)) {
431  newsaves.emplace_back(start_name + compression::format_extension(comp_format));
432  newitems.emplace_back("label", _("Back to Start"));
433  }
434 
435  // Make sure list doesn't get too long: keep top two, midpoint and bottom.
436  trim_items(newitems);
437  trim_items(newsaves);
438 
439  items.insert(pos, newitems.begin(), newitems.end());
440  savenames_.insert(savenames_.end(), newsaves.begin(), newsaves.end());
441 }
442 
444 {
445  // Pad the commands with null pointers (keeps the indices of items and wml_commands_ synced).
446  wml_commands_.resize(i);
447 
448  auto pos = items.erase(items.begin() + i);
449  std::vector<config> newitems;
450 
453 
454  // Replace this placeholder entry with available menu items.
455  items.insert(pos, newitems.begin(), newitems.end());
456 }
457 
458 void play_controller::hotkey_handler::show_menu(const std::vector<config>& items_arg, int xloc, int yloc, bool context_menu, display& disp)
459 {
460  if(context_menu) {
461  last_context_menu_x_ = xloc;
462  last_context_menu_y_ = yloc;
463  }
464 
465  std::vector<config> items;
466  for(const auto& item : items_arg) {
467  const std::string& id = item["id"];
469 
470  if(id == "wml" || (can_execute_command(command) && (!context_menu || in_context_menu(command.id)))) {
471  items.emplace_back("id", id);
472  }
473  }
474 
475  // Add special non-hotkey items to the menu and remember their indices
476  // Iterate in reverse to avoid also iterating over the new inserted items
477  savenames_.clear();
478  wml_commands_.clear();
479 
480  for(int i = items.size() - 1; i >= 0; i--) {
481  if(items[i]["id"] == "AUTOSAVES") {
482  expand_autosaves(items, i);
483  } else if(items[i]["id"] == "wml") {
484  expand_wml_commands(items, i);
485  }
486  }
487 
488  if(items.empty()) {
489  return;
490  }
491 
492  command_executor::show_menu(items, xloc, yloc, context_menu, disp);
493 }
494 
496 {
497  switch(command) {
498  // Only display these if the mouse is over a castle or keep tile
501  case hotkey::HOTKEY_RECALL: {
502  // last_hex_ is set by mouse_events::mouse_motion
503  const map_location & last_hex = mouse_handler_.get_last_hex();
504  const int viewing_side = gui()->viewing_side();
505 
506  // A quick check to save us having to create the future map and
507  // possibly loop through all units.
508  if ( !play_controller_.get_map().is_keep(last_hex) &&
509  !play_controller_.get_map().is_castle(last_hex) )
510  return false;
511 
512  wb::future_map future; /* lasts until method returns. */
513 
514  return gamestate().side_can_recruit_on(viewing_side, last_hex);
515  }
516  default:
517  return true;
518  }
519 }
520 
522 {
523  if(index >= 0 && index < static_cast<int>(wml_commands_.size())) {
524  const const_item_ptr wmi = wml_commands_[index];
525  if ( wmi ) {
526  return wmi->image();
527  }
528  }
529  return command_executor::get_action_image(command, index);
530 }
531 
533 {
534  switch(command) {
535 
547  return (gui()->get_zoom_factor() == 1.0) ? hotkey::ACTION_ON : hotkey::ACTION_OFF;
550  default:
552  }
553 }
554 
555 void play_controller::hotkey_handler::load_autosave(const std::string& filename)
556 {
558 }
virtual void search() override
virtual void toggle_accelerated_speed() override
const map_location & mouseover_hex() const
Definition: display.hpp:290
void set_turbo(bool ison)
Definition: display.cpp:53
const team & viewing_team() const
bool is_keep(const map_location &loc) const
Definition: map.cpp:72
bool discard_previous
An announcement according these options should replace the previous announce (typical of fast announc...
Definition: display.hpp:601
void set_scroll_up(bool on)
virtual void toggle_grid() override
void goto_leader(int side_num)
void set_scroll_down(bool on)
bool minimap_draw_units()
Definition: general.cpp:825
Class for start-of-scenario saves.
Definition: savegame.hpp:305
hotkey_handler(play_controller &, saved_game &)
virtual void unit_list() override
HOTKEY_COMMAND id
the names are strange: the "hotkey::HOTKEY_COMMAND" is named id, and the string to identify the objec...
std::map< std::string, t_string > string_map
void expand_autosaves(std::vector< config > &items, int i)
void show_menu(const std::vector< config > &items_arg, int xloc, int yloc, bool context_menu, display &disp) override
bool is_castle(const map_location &loc) const
Definition: map.cpp:70
const unit_map & get_units() const
game_events::wmi_manager & get_wml_menu_items()
Definition: game_state.cpp:415
virtual void show_chat_log() override
virtual void select_hex() override
game_display & get_display() override
Get a reference to a display member a derived class uses.
static std::shared_ptr< save_index_class > default_saves_dir()
Returns an instance for managing saves in filesystem::get_saves_dir()
Definition: save_index.cpp:210
bool minimap_movement_coding()
Definition: general.cpp:805
Class for autosaves.
Definition: savegame.hpp:279
virtual void load_game() override
virtual void select_and_action() override
bool enemies_visible() const
Stores all information related to functions that can be bound to hotkeys.
int viewing_side() const
Definition: display.hpp:107
const gamemap & get_map() const
const std::vector< std::string > items
static std::string _(const char *str)
Definition: gettext.hpp:93
This file implements all the hotkey handling and menu details for play controller.
void select_hex(const map_location &hex, const bool browse, const bool highlight=true, const bool fire_event=true)
virtual bool can_execute_command(const hotkey::hotkey_command &command, int index=-1) const override
Check if a command can be executed.
bool fire_item(const std::string &id, const map_location &hex, game_data &gamedata, filter_context &fc, unit_map &units, bool is_key_hold_repeat=false) const
Fires the menu item with the given id.
Definition: wmi_manager.cpp:79
play_controller & play_controller_
References to parent object / constituents.
void show_statistics(int side_num)
virtual void move_action() override
virtual void objectives() override
bool can_undo() const
void touch_action(const map_location hex, bool browse)
std::vector< std::string > savenames_
virtual void mouse_press(const SDL_MouseButtonEvent &event, const bool browse)
void expand_wml_commands(std::vector< config > &items, int i)
Replaces "wml" in items with all active WML menu items for the current field.
virtual void deselect_hex() override
bool is_lingering() const
virtual void show_enemy_moves(bool ignore_units) override
bool minimap_draw_villages()
Definition: general.cpp:835
std::string get_names(const std::string &id)
Returns a comma-separated string of hotkey names.
const map_location & get_last_hex() const
This class stores all the data for a single &#39;side&#39; (in game nomenclature).
Definition: team.hpp:72
virtual void show_statistics() override
virtual void terrain_description() override
virtual void save_replay() override
virtual void undo() override
virtual void preferences() override
virtual void speak() override
bool is_linger_mode() const
virtual void save_game() override
Applies the planned unit map for the duration of the struct&#39;s life.
Definition: manager.hpp:252
bool in_context_menu(hotkey::HOTKEY_COMMAND command) const
Determines whether the command should be in the context menu or not.
bool valid() const
Definition: location.hpp:89
events::menu_handler & menu_handler_
std::string create_filename() const
Build the filename according to the specific savegame&#39;s needs.
Definition: savegame.hpp:180
std::vector< team > & get_teams()
std::shared_ptr< const game_events::wml_menu_item > const_item_ptr
A smart pointer used when retrieving menu items.
virtual void right_mouse_click() override
int show_menu(lua_State *L)
Displays a popup menu at the current mouse position Best used from a [set_menu_item], to show a submenu.
Definition: lua_gui2.cpp:185
virtual void unit_description() override
virtual void scroll_up(bool on) override
Declarations for a container for wml_menu_item.
static const std::string wml_menu_hotkey_prefix
bool is_browsing() const override
const color_t NORMAL_COLOR
bool is_observer() const
Encapsulates the map of the game.
Definition: location.hpp:38
bool auto_shroud_updates() const
Definition: team.hpp:351
void cycle_back_units(const bool browse)
Various functions related to the creation of units (recruits, recalls, and placed units)...
std::string format_extension(format compression_format)
Definition: compression.hpp:27
virtual void left_mouse_click() override
virtual void scroll_left(bool on) override
Exception used to signal that the user has decided to abortt a game, and to load another game instead...
Definition: savegame.hpp:83
virtual bool do_execute_command(const hotkey::hotkey_command &command, int index=-1, bool press=true, bool release=false) override
virtual bool is_networked_mp() const
virtual void load_autosave(const std::string &filename)
std::size_t i
Definition: function.cpp:967
virtual void cycle_units() override
void announce(const std::string &msg, const color_t &color=font::GOOD_COLOR, const announce_options &options=announce_options())
Announce a message prominently.
Definition: display.cpp:1780
std::string command
The command is unique.
Holds options for calls to function &#39;announce&#39; (announce).
Definition: display.hpp:591
virtual void redo() override
std::size_t playing_team() const
The playing team is the team whose turn it is.
Definition: display.hpp:101
void terrain_description(mouse_handler &mousehandler)
int get_location_y(const map_location &loc) const
Definition: display.cpp:755
void set_scroll_right(bool on)
game_data gamedata_
Definition: game_state.hpp:46
events::mouse_handler & mouse_handler_
virtual void scroll_down(bool on) override
std::size_t index(const std::string &str, const std::size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
Definition: unicode.cpp:72
#define VGETTEXT(msgid,...)
Handy wrappers around interpolate_variables_into_string and gettext.
virtual void status_table() override
void cycle_units(const bool browse, const bool reverse=false)
bool can_redo() const
void get_items(const map_location &hex, std::vector< std::shared_ptr< const wml_menu_item >> &items, std::vector< config > &descriptions, filter_context &fc, game_data &gamedata, unit_map &units) const
Returns the menu items that can be shown for the given location.
compression::format save_compression_format()
Definition: game.cpp:857
void set_scroll_left(bool on)
bool side_can_recruit_on(int side, map_location loc) const
Checks if any of the sides leaders can recruit at a location.
Definition: game_state.cpp:398
int get_location_x(const map_location &loc) const
Functions to get the on-screen positions of hexes.
Definition: display.cpp:750
virtual void goto_leader() override
unit_map::iterator current_unit()
std::vector< const_item_ptr > wml_commands_
game_state & gamestate()
double t
Definition: astarsearch.cpp:65
bool minimap_draw_terrain()
Definition: general.cpp:845
void show_enemy_moves(bool ignore_units, int side_num)
virtual hotkey::ACTION_STATE get_action_state(hotkey::HOTKEY_COMMAND command, int index) const override
std::size_t viewing_team() const
The viewing team is the team currently viewing the game.
Definition: display.hpp:106
virtual void cycle_back_units() override
virtual std::string get_action_image(hotkey::HOTKEY_COMMAND, int index) const override
int current_side() const
Returns the number of the side whose turn it is.
bool turbo()
Definition: general.cpp:445
game_display * gui() const
virtual void scroll_right(bool on) override
virtual void touch_hex() override
std::size_t turn() const
bool save_game_exists(std::string name, compression::format compressed)
Returns true if there is already a savegame with this name, looking only in the default save director...
Definition: savegame.cpp:62
virtual void toggle_ellipses() override
bool valid() const
Definition: map.hpp:274
const hotkey_command & get_hotkey_command(const std::string &command)
returns the hotkey_command with the given name
virtual void save_map() override
static void trim_items(std::vector< T > &newitems)
virtual void show_help() override
std::pair< std::string, unsigned > item
Definition: help_impl.hpp:410
static const hotkey_command & get_command_by_command(HOTKEY_COMMAND command)
the execute_command argument was changed from HOTKEY_COMMAND to hotkey_command, to be able to call it...
void move_action(bool browse)
Overridden in derived class.
void select_or_action(bool browse)
bool minimap_terrain_coding()
Definition: general.cpp:815