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 - 2017 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"
27 #include "game_data.hpp"
29 #include "log.hpp"
30 #include "play_controller.hpp"
31 #include "replay_helper.hpp"
32 #include "resources.hpp"
33 #include "synced_context.hpp"
34 #include "terrain/filter.hpp"
35 
36 static lg::log_domain log_engine("engine");
37 #define ERR_NG LOG_STREAM(err, log_engine)
38 #define LOG_NG LOG_STREAM(info, log_engine)
39 
40 // This file is in the game_events namespace.
41 namespace game_events
42 {
43 namespace
44 { // Some helpers for construction.
45 
46 /**
47  * Build the event name associated with the given menu item id.
48  * This is a separate function so it can be easily shared by multiple
49  * constructors.
50  */
51 inline std::string make_item_name(const std::string& id)
52 {
53  return std::string("menu item") + (id.empty() ? "" : ' ' + id);
54 }
55 
56 /**
57  * Build the hotkey id associated with the given menu item id.
58  * This is a separate function so it can be easily shared by multiple
59  * constructors.
60  */
61 inline std::string make_item_hotkey(const std::string& id)
62 {
64 }
65 
66 } // anonymous namespace
67 
68 // Constructor for reading from a saved config.
70  : item_id_(id)
71  , event_name_(make_item_name(id))
72  , hotkey_id_(make_item_hotkey(id))
73  , image_(cfg["image"].str())
74  , description_(cfg["description"].t_str())
75  , needs_select_(cfg["needs_select"].to_bool(false))
76  , show_if_(cfg.child_or_empty("show_if"), true)
77  , filter_location_(cfg.child_or_empty("filter_location"), true)
78  , command_(cfg.child_or_empty("command"))
79  , default_hotkey_(cfg.child_or_empty("default_hotkey"))
80  , use_hotkey_(cfg["use_hotkey"].to_bool(true))
81  , use_wml_menu_(cfg["use_hotkey"].str() != "only")
82  , is_synced_(cfg["synced"].to_bool(true))
83 {
84  if(needs_select_) {
85  ERR_NG << "needs_select=yes is deprecated\n";
86  }
87 }
88 
89 // Constructor for items defined in an event.
90 wml_menu_item::wml_menu_item(const std::string& id, const vconfig& definition)
91  : item_id_(id)
92  , event_name_(make_item_name(id))
93  , hotkey_id_(make_item_hotkey(id))
94  , image_()
95  , description_()
96  , needs_select_(false)
97  , show_if_(vconfig::empty_vconfig())
98  , filter_location_(vconfig::empty_vconfig())
99  , command_()
100  , default_hotkey_()
101  , use_hotkey_(true)
102  , use_wml_menu_(true)
103  , is_synced_(true)
104 {
105  // On the off-chance that update() doesn't do it, add the hotkey here.
106  // (Update can always modify it.)
108 
109  // Apply WML.
110  update(definition);
111 }
112 
113 // Constructor for items modified by an event.
114 wml_menu_item::wml_menu_item(const std::string& id, const vconfig& definition, const wml_menu_item& original)
115  : item_id_(id)
116  , event_name_(make_item_name(id))
117  , hotkey_id_(make_item_hotkey(id))
118  , image_(original.image_)
119  , description_(original.description_)
120  , needs_select_(original.needs_select_)
121  , show_if_(original.show_if_)
122  , filter_location_(original.filter_location_)
123  , command_(original.command_)
124  , default_hotkey_(original.default_hotkey_)
125  , use_hotkey_(original.use_hotkey_)
126  , use_wml_menu_(original.use_wml_menu_)
127  , is_synced_(original.is_synced_)
128 {
129  // Apply WML.
130  update(definition);
131 }
132 
134 {
135  // Default the image?
136  return image_.empty() ? game_config::images::wml_menu : image_;
137 }
138 
139 
141 {
142  // Failing the [show_if] tag means no show.
144  return false;
145  }
146 
147  // Failing the [fiter_location] tag means no show.
148  if(!filter_location_.empty() && !terrain_filter(filter_location_, &filter_con)(hex)) {
149  return false;
150  }
151 
152  // Failing to have a required selection means no show.
153  if(needs_select_ && !data.last_selected.valid()) {
154  return false;
155  }
156 
157  // Passed all tests.
158  return true;
159 }
160 
161 void wml_menu_item::fire_event(const map_location& event_hex, const game_data& data) const
162 {
163  if(!this->is_synced()) {
164  // It is possible to for example show a help menu during a [delay] of a synced event.
166  assert(resources::game_events != nullptr);
168  return;
169  }
170 
171  const map_location& last_select = data.last_selected;
172 
173  // No new player-issued commands allowed while this is firing.
174  const events::command_disabler disable_commands;
175 
176  // instead of adding a second "select" event like it was done before, we just fire the select event again, and this
177  // time in a synced context.
178  // note that there couldn't be a user choice during the last "select" event because it didn't run in a synced
179  // context.
180  if(needs_select_ && last_select.valid()) {
182  "fire_event", replay_helper::get_event(event_name_, event_hex, &last_select));
183  } else {
185  "fire_event", replay_helper::get_event(event_name_, event_hex, nullptr));
186  }
187 }
188 
190 {
191  if(!command_.empty()) {
192  assert(resources::game_events);
194  }
195 
196  // Hotkey support
197  if(use_hotkey_) {
199  }
200 }
201 
203 {
204  // If this menu item has a [command], add a handler for it.
205  if(!command_.empty()) {
206  assert(resources::game_events);
208  }
209 
210  // Hotkey support
211  if(use_hotkey_) {
213  }
214 }
215 
217 {
218  cfg["id"] = item_id_;
219  cfg["image"] = image_;
220  cfg["description"] = description_;
221  cfg["needs_select"] = needs_select_;
222  cfg["synced"] = is_synced_;
223 
224  if(use_hotkey_ && use_wml_menu_) {
225  cfg["use_hotkey"] = true;
226  }
227 
228  if(use_hotkey_ && !use_wml_menu_) {
229  cfg["use_hotkey"] = "only";
230  }
231 
232  if(!use_hotkey_ && use_wml_menu_) {
233  cfg["use_hotkey"] = false;
234  }
235 
236  if(!use_hotkey_ && !use_wml_menu_) {
237  ERR_NG << "Bad data: wml_menu_item with both use_wml_menu and use_hotkey set to false is not supposed to be "
238  "possible.";
239  cfg["use_hotkey"] = false;
240  }
241 
242  if(!show_if_.empty()) {
243  cfg.add_child("show_if", show_if_.get_config());
244  }
245 
246  if(!filter_location_.empty()) {
247  cfg.add_child("filter_location", filter_location_.get_config());
248  }
249 
250  if(!command_.empty()) {
251  cfg.add_child("command", command_);
252  }
253 
254  if(!default_hotkey_.empty()) {
255  cfg.add_child("default_hotkey", default_hotkey_);
256  }
257 }
258 
260 {
261  const bool old_use_hotkey = use_hotkey_;
262  // Tracks whether or not the hotkey has been updated.
263  bool hotkey_updated = false;
264 
265  if(vcfg.has_attribute("image")) {
266  image_ = vcfg["image"].str();
267  }
268 
269  if(vcfg.has_attribute("description")) {
270  description_ = vcfg["description"].t_str();
271  hotkey_updated = true;
272  }
273 
274  if(vcfg.has_attribute("needs_select")) {
275  ERR_NG << "needs_select= is deprecated\n";
276  needs_select_ = vcfg["needs_select"].to_bool();
277  }
278 
279  if(vcfg.has_attribute("synced")) {
280  is_synced_ = vcfg["synced"].to_bool(true);
281  }
282 
283  if(const vconfig& child = vcfg.child("show_if")) {
284  show_if_ = child;
286  }
287 
288  if(const vconfig& child = vcfg.child("filter_location")) {
289  filter_location_ = child;
291  }
292 
293  if(const vconfig& child = vcfg.child("default_hotkey")) {
294  default_hotkey_ = child.get_parsed_config();
295  hotkey_updated = true;
296  }
297 
298  if(vcfg.has_attribute("use_hotkey")) {
299  const config::attribute_value& use_hotkey_av = vcfg["use_hotkey"];
300 
301  use_hotkey_ = use_hotkey_av.to_bool(true);
302  use_wml_menu_ = use_hotkey_av.str() != "only";
303  }
304 
305  if(const vconfig& cmd = vcfg.child("command")) {
306  const bool delayed = cmd["delayed_variable_substitution"].to_bool(true);
307  update_command(delayed ? cmd.get_config() : cmd.get_parsed_config());
308  }
309 
310  // Update the registered hotkey?
311 
312  if(use_hotkey_ && !old_use_hotkey) {
313  // The hotkey needs to be enabled.
315 
316  } else if(use_hotkey_ && hotkey_updated) {
317  // The hotkey needs to be updated.
319 
320  } else if(!use_hotkey_ && old_use_hotkey) {
321  // The hotkey needs to be disabled.
323  }
324 }
325 
326 void wml_menu_item::update_command(const config& new_command)
327 {
328  // If there is an old command, remove it from the event handlers.
329  if(!command_.empty()) {
330  assert(resources::game_events);
331 
333  if(ptr->is_menu_item()) {
334  LOG_NG << "Removing command for " << event_name_ << ".\n";
335  man.remove_event_handler(command_["id"].str());
336  }
337  });
338  }
339 
340  // Update our stored command.
341  if(new_command.empty()) {
342  command_.clear();
343  } else {
344  command_ = new_command;
345 
346  // Set some fields required by event processing.
347  config::attribute_value& event_id = command_["id"];
348  if(event_id.empty() && !item_id_.empty()) {
349  event_id = item_id_;
350  }
351 
352  command_["name"] = event_name_;
353  command_["first_time_only"] = false;
354 
355  // Register the event.
356  LOG_NG << "Setting command for " << event_name_ << " to:\n" << command_;
357  assert(resources::game_events);
358  resources::game_events->add_event_handler(command_, true);
359  }
360 }
361 
362 } // end namespace game_events
void remove_event_handler(const std::string &id)
Removes an event handler.
Definition: manager.cpp:51
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:202
void to_config(config &cfg) const
Writes *this to the provided config.
Definition: menu_item.cpp:216
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:753
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:761
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:140
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:45
bool is_synced_
If true, this item will be sended 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:133
std::shared_ptr< event_handler > handler_ptr
Shared pointer to handler objects.
Definition: handlers.hpp:40
The game event manager loads the scenario configuration object, and ensures that events are handled a...
Definition: manager.hpp:45
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:161
config & add_child(config_key_type key)
Definition: config.cpp:419
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:493
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:189
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:69
void update_command(const config &new_command)
Updates our command to new_command.
Definition: menu_item.cpp:326
void update(const vconfig &vcfg)
Updates *this based on vcfg.
Definition: menu_item.cpp:259
game_events::wml_event_pump & pump()
Definition: manager.cpp:217
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:207
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