The Battle for Wesnoth  1.13.10+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
menu_item.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2018 by David White <dave@whitevine.net>
3  Part of the Battle for Wesnoth Project http://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 /**
16  * @file
17  * Definitions for a class that implements WML-defined (right-click) menu items.
18  */
19 
21 
23 #include "game_events/handlers.hpp"
24 #include "game_events/pump.hpp"
25 
26 #include "game_config.hpp"
28 #include "log.hpp"
29 #include "replay_helper.hpp"
30 #include "resources.hpp"
31 #include "synced_context.hpp"
32 #include "terrain/filter.hpp"
33 
34 static lg::log_domain log_engine("engine");
35 #define ERR_NG LOG_STREAM(err, log_engine)
36 #define LOG_NG LOG_STREAM(info, log_engine)
37 
38 // This file is in the game_events namespace.
39 namespace game_events
40 {
41 namespace
42 { // Some helpers for construction.
43 
44 /**
45  * Build the event name associated with the given menu item id.
46  * This is a separate function so it can be easily shared by multiple
47  * constructors.
48  */
49 inline std::string make_item_name(const std::string& id)
50 {
51  return std::string("menu item") + (id.empty() ? "" : ' ' + id);
52 }
53 
54 /**
55  * Build the hotkey id associated with the given menu item id.
56  * This is a separate function so it can be easily shared by multiple
57  * constructors.
58  */
59 inline std::string make_item_hotkey(const std::string& id)
60 {
62 }
63 
64 } // anonymous namespace
65 
66 // Constructor for reading from a saved config.
68  : item_id_(id)
69  , event_name_(make_item_name(id))
70  , hotkey_id_(make_item_hotkey(id))
71  , image_(cfg["image"].str())
72  , description_(cfg["description"].t_str())
73  , needs_select_(cfg["needs_select"].to_bool(false))
74  , show_if_(cfg.child_or_empty("show_if"), true)
75  , filter_location_(cfg.child_or_empty("filter_location"), true)
76  , command_(cfg.child_or_empty("command"))
77  , default_hotkey_(cfg.child_or_empty("default_hotkey"))
78  , use_hotkey_(cfg["use_hotkey"].to_bool(true))
79  , use_wml_menu_(cfg["use_hotkey"].str() != "only")
80  , is_synced_(cfg["synced"].to_bool(true))
81 {
82  if(needs_select_) {
83  ERR_NG << "needs_select=yes is deprecated\n";
84  }
85 }
86 
87 // Constructor for items defined in an event.
88 wml_menu_item::wml_menu_item(const std::string& id, const vconfig& definition)
89  : item_id_(id)
90  , event_name_(make_item_name(id))
91  , hotkey_id_(make_item_hotkey(id))
92  , image_()
93  , description_()
94  , needs_select_(false)
95  , show_if_(vconfig::empty_vconfig())
96  , filter_location_(vconfig::empty_vconfig())
97  , command_()
98  , default_hotkey_()
99  , use_hotkey_(true)
100  , use_wml_menu_(true)
101  , is_synced_(true)
102 {
103  // On the off-chance that update() doesn't do it, add the hotkey here.
104  // (Update can always modify it.)
106 
107  // Apply WML.
108  update(definition);
109 }
110 
111 // Constructor for items modified by an event.
112 wml_menu_item::wml_menu_item(const std::string& id, const vconfig& definition, const wml_menu_item& original)
113  : item_id_(id)
114  , event_name_(make_item_name(id))
115  , hotkey_id_(make_item_hotkey(id))
116  , image_(original.image_)
117  , description_(original.description_)
118  , needs_select_(original.needs_select_)
119  , show_if_(original.show_if_)
120  , filter_location_(original.filter_location_)
121  , command_(original.command_)
122  , default_hotkey_(original.default_hotkey_)
123  , use_hotkey_(original.use_hotkey_)
124  , use_wml_menu_(original.use_wml_menu_)
125  , is_synced_(original.is_synced_)
126 {
127  // Apply WML.
128  update(definition);
129 }
130 
132 {
133  // Default the image?
134  return image_.empty() ? game_config::images::wml_menu : image_;
135 }
136 
137 
139 {
140  // Failing the [show_if] tag means no show.
142  return false;
143  }
144 
145  // Failing the [fiter_location] tag means no show.
146  if(!filter_location_.empty() && !terrain_filter(filter_location_, &filter_con)(hex)) {
147  return false;
148  }
149 
150  // Failing to have a required selection means no show.
151  if(needs_select_ && !data.last_selected.valid()) {
152  return false;
153  }
154 
155  // Passed all tests.
156  return true;
157 }
158 
159 void wml_menu_item::fire_event(const map_location& event_hex, const game_data& data) const
160 {
161  if(!this->is_synced()) {
162  // It is possible to for example show a help menu during a [delay] of a synced event.
164  assert(resources::game_events != nullptr);
166  return;
167  }
168 
169  const map_location& last_select = data.last_selected;
170 
171  // No new player-issued commands allowed while this is firing.
172  const events::command_disabler disable_commands;
173 
174  // instead of adding a second "select" event like it was done before, we just fire the select event again, and this
175  // time in a synced context.
176  // note that there couldn't be a user choice during the last "select" event because it didn't run in a synced
177  // context.
178  if(needs_select_ && last_select.valid()) {
180  "fire_event", replay_helper::get_event(event_name_, event_hex, &last_select));
181  } else {
183  "fire_event", replay_helper::get_event(event_name_, event_hex, nullptr));
184  }
185 }
186 
188 {
189  if(!command_.empty()) {
190  assert(resources::game_events);
192  }
193 
194  // Hotkey support
195  if(use_hotkey_) {
197  }
198 }
199 
201 {
202  // If this menu item has a [command], add a handler for it.
203  if(!command_.empty()) {
204  assert(resources::game_events);
206  }
207 
208  // Hotkey support
209  if(use_hotkey_) {
211  }
212 }
213 
215 {
216  cfg["id"] = item_id_;
217  cfg["image"] = image_;
218  cfg["description"] = description_;
219  cfg["needs_select"] = needs_select_;
220  cfg["synced"] = is_synced_;
221 
222  if(use_hotkey_ && use_wml_menu_) {
223  cfg["use_hotkey"] = true;
224  }
225 
226  if(use_hotkey_ && !use_wml_menu_) {
227  cfg["use_hotkey"] = "only";
228  }
229 
230  if(!use_hotkey_ && use_wml_menu_) {
231  cfg["use_hotkey"] = false;
232  }
233 
234  if(!use_hotkey_ && !use_wml_menu_) {
235  ERR_NG << "Bad data: wml_menu_item with both use_wml_menu and use_hotkey set to false is not supposed to be "
236  "possible.";
237  cfg["use_hotkey"] = false;
238  }
239 
240  if(!show_if_.empty()) {
241  cfg.add_child("show_if", show_if_.get_config());
242  }
243 
244  if(!filter_location_.empty()) {
245  cfg.add_child("filter_location", filter_location_.get_config());
246  }
247 
248  if(!command_.empty()) {
249  cfg.add_child("command", command_);
250  }
251 
252  if(!default_hotkey_.empty()) {
253  cfg.add_child("default_hotkey", default_hotkey_);
254  }
255 }
256 
258 {
259  const bool old_use_hotkey = use_hotkey_;
260  // Tracks whether or not the hotkey has been updated.
261  bool hotkey_updated = false;
262 
263  if(vcfg.has_attribute("image")) {
264  image_ = vcfg["image"].str();
265  }
266 
267  if(vcfg.has_attribute("description")) {
268  description_ = vcfg["description"].t_str();
269  hotkey_updated = true;
270  }
271 
272  if(vcfg.has_attribute("needs_select")) {
273  ERR_NG << "needs_select= is deprecated\n";
274  needs_select_ = vcfg["needs_select"].to_bool();
275  }
276 
277  if(vcfg.has_attribute("synced")) {
278  is_synced_ = vcfg["synced"].to_bool(true);
279  }
280 
281  if(const vconfig& child = vcfg.child("show_if")) {
282  show_if_ = child;
284  }
285 
286  if(const vconfig& child = vcfg.child("filter_location")) {
287  filter_location_ = child;
289  }
290 
291  if(const vconfig& child = vcfg.child("default_hotkey")) {
292  default_hotkey_ = child.get_parsed_config();
293  hotkey_updated = true;
294  }
295 
296  if(vcfg.has_attribute("use_hotkey")) {
297  const config::attribute_value& use_hotkey_av = vcfg["use_hotkey"];
298 
299  use_hotkey_ = use_hotkey_av.to_bool(true);
300  use_wml_menu_ = use_hotkey_av.str() != "only";
301  }
302 
303  if(const vconfig& cmd = vcfg.child("command")) {
304  const bool delayed = cmd["delayed_variable_substitution"].to_bool(true);
305  update_command(delayed ? cmd.get_config() : cmd.get_parsed_config());
306  }
307 
308  // Update the registered hotkey?
309 
310  if(use_hotkey_ && !old_use_hotkey) {
311  // The hotkey needs to be enabled.
313 
314  } else if(use_hotkey_ && hotkey_updated) {
315  // The hotkey needs to be updated.
317 
318  } else if(!use_hotkey_ && old_use_hotkey) {
319  // The hotkey needs to be disabled.
321  }
322 }
323 
324 void wml_menu_item::update_command(const config& new_command)
325 {
326  // If there is an old command, remove it from the event handlers.
327  if(!command_.empty()) {
328  assert(resources::game_events);
329 
331  if(ptr->is_menu_item()) {
332  LOG_NG << "Removing command for " << event_name_ << ".\n";
333  man.remove_event_handler(command_["id"].str());
334  }
335  });
336  }
337 
338  // Update our stored command.
339  if(new_command.empty()) {
340  command_.clear();
341  } else {
342  command_ = new_command;
343 
344  // Set some fields required by event processing.
345  config::attribute_value& event_id = command_["id"];
346  if(event_id.empty() && !item_id_.empty()) {
347  event_id = item_id_;
348  }
349 
350  command_["name"] = event_name_;
351  command_["first_time_only"] = false;
352 
353  // Register the event.
354  LOG_NG << "Setting command for " << event_name_ << " to:\n" << command_;
355  assert(resources::game_events);
356  resources::game_events->add_event_handler(command_, true);
357  }
358 }
359 
360 } // end namespace game_events
void remove_event_handler(const std::string &id)
Removes an event handler.
Definition: manager.cpp:47
bool needs_select_
Whether or not this event says it makes use of the last selected unit.
Definition: menu_item.hpp:151
std::vector< char_t > string
bool to_bool(bool def=false) const
map_location last_selected
the last location where a select event fired.
Definition: game_data.hpp:83
std::string str(const std::string &fallback="") const
vconfig filter_location_
A location filter to be applied to the hex where the menu is invoked.
Definition: menu_item.hpp:167
Variant for storing WML attributes.
vconfig child(const std::string &key) const
Returns a child of *this whose key is key.
Definition: variable.cpp:252
config command_
Actions to take when this item is chosen.
Definition: menu_item.hpp:170
bool empty() const
Tests for an attribute that either was never set or was set to "".
t_string description_
The text to display in the menu for this item.
Definition: menu_item.hpp:148
void add_wml_hotkey(const std::string &id, const t_string &description, const config &default_hotkey)
adds a new wml hotkey to the list, but only if there is no hotkey with that id yet on the list...
void init_handler() const
Initializes the implicit event handler for an inlined [command].
Definition: menu_item.cpp:200
void to_config(config &cfg) const
Writes *this to the provided config.
Definition: menu_item.cpp:214
std::string image_
The image to display in the menu next to this item's description.
Definition: menu_item.hpp:145
void clear()
Definition: config.cpp:809
an object to leave the synced context during draw or unsynced wml items when we don’t know whether w...
bool empty() const
Definition: config.cpp:830
This file implements all the hotkey handling and menu details for play controller.
vconfig show_if_
A condition that must hold in order for this menu item to be visible.
Definition: menu_item.hpp:159
bool valid() const
Definition: location.hpp:72
filter_context * filter_con
Definition: resources.cpp:23
const std::string item_id_
The id of this menu item.
Definition: menu_item.hpp:136
game_events::manager * game_events
Definition: resources.cpp:24
static const std::string wml_menu_hotkey_prefix
bool can_show(const map_location &hex, const game_data &data, filter_context &context) const
Returns whether or not *this is applicable given the context.
Definition: menu_item.cpp:138
Encapsulates the map of the game.
Definition: location.hpp:40
Domain specific events.
Definition: action_wml.cpp:88
Define conditionals for the game's events mechanism, a.k.a.
void add_event_handler(const config &handler, bool is_menu_item=false)
Create an event handler.
Definition: manager.cpp:41
bool is_synced_
If true, this item will be sent ot ther clients.
Definition: menu_item.hpp:185
const config & get_config() const
Definition: variable.hpp:75
bool empty() const
Definition: variable.hpp:100
Define the game's event mechanism.
const std::string & image() const
The image associated with this menu item.
Definition: menu_item.cpp:131
The game event manager loads the scenario configuration object, and ensures that events are handled a...
Definition: manager.hpp:43
void fire_event(const map_location &event_hex, const game_data &data) const
Causes the event associated with this item to fire.
Definition: menu_item.cpp:159
config & add_child(config_key_type key)
Definition: config.cpp:475
pump_result_t fire(const std::string &event, const entity_location &loc1=entity_location::null_entity, const entity_location &loc2=entity_location::null_entity, const config &data=config())
Function to fire an event.
Definition: pump.cpp:491
bool use_hotkey_
If true, allow using a hotkey to trigger this item.
Definition: menu_item.hpp:176
static bool run_and_throw(const std::string &commandname, const config &data, bool use_undo=true, bool show=true, synced_command::error_handler_function error_handler=default_error_function)
bool conditional_passed(const vconfig &cond)
Define the handlers for the game's events mechanism.
static bool run_in_synced_context_if_not_already(const std::string &commandname, const config &data, bool use_undo=true, bool show=true, synced_command::error_handler_function error_handler=default_error_function)
checks whether we are currently running in a synced context, and if not we enters it...
const vconfig & make_safe() const
instruct the vconfig to make a private copy of its underlying data.
Definition: variable.cpp:127
void finish_handler() const
Removes the implicit event handler for an inlined [command].
Definition: menu_item.cpp:187
A variable-expanding proxy for the config class.
Definition: variable.hpp:42
Standard logging facilities (interface).
bool use_wml_menu_
If true, allow using the menu to trigger this item.
Definition: menu_item.hpp:179
wml_menu_item(const std::string &id, const config &cfg)
Constructor for reading from a saved config.
Definition: menu_item.cpp:67
void update_command(const config &new_command)
Updates our command to new_command.
Definition: menu_item.cpp:324
void update(const vconfig &vcfg)
Updates *this based on vcfg.
Definition: menu_item.cpp:257
game_events::wml_event_pump & pump()
Definition: manager.cpp:185
std::shared_ptr< event_handler > handler_ptr
Definition: fwd.hpp:24
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:93
static config get_event(const std::string &name, const map_location &loc, const map_location *last_select_loc)
std::string wml_menu
void execute_on_events(const std::string &event_id, event_func_t func)
Definition: manager.cpp:122
bool remove_wml_hotkey(const std::string &id)
removes a wml hotkey with the given id, returns true if the deletion was successful ...
const std::string event_name_
The name of this item's event(s); based on the item's id.
Definition: menu_item.hpp:139
bool has_attribute(const std::string &key) const
< Synonym for operator[]
Definition: variable.hpp:99
const std::string hotkey_id_
The id for this item's hotkey; based on the item's id.
Definition: menu_item.hpp:142
config default_hotkey_
Config object containing the default hotkey.
Definition: menu_item.hpp:173