The Battle for Wesnoth  1.13.4+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 - 2016 by David White <[email protected]>
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 
20 #include "menu_item.hpp"
21 
22 #include "global.hpp"
23 
24 #include "conditional_wml.hpp"
25 #include "handlers.hpp"
26 #include "manager.hpp"
27 #include "pump.hpp"
28 
29 #include "actions/undo.hpp"
30 #include "game_config.hpp"
31 #include "game_data.hpp"
32 #include "log.hpp"
33 #include "mouse_handler_base.hpp"
36 #include "play_controller.hpp"
37 #include "preferences.hpp"
38 #include "replay.hpp"
39 #include "replay_helper.hpp"
40 #include "resources.hpp"
41 #include "synced_context.hpp"
42 #include "terrain/filter.hpp"
43 
44 static lg::log_domain log_engine("engine");
45 #define ERR_NG LOG_STREAM(err, log_engine)
46 #define LOG_NG LOG_STREAM(info, log_engine)
47 
48 
49 // This file is in the game_events namespace.
50 namespace game_events
51 {
52 
53 namespace { // Some helpers for construction.
54 
55  /**
56  * Build the event name associated with the given menu item id.
57  * This is a separate function so it can be easily shared by multiple
58  * constructors.
59  */
60  inline std::string make_item_name(const std::string & id)
61  {
62  return std::string("menu item") + (id.empty() ? "" : ' ' + id);
63  }
64 
65  /**
66  * Build the hotkey id associated with the given menu item id.
67  * This is a separate function so it can be easily shared by multiple
68  * constructors.
69  */
70  inline std::string make_item_hotkey(const std::string & id)
71  {
73  }
74 
75 }// anonymous namespace
76 
77 
78 /**
79  * Constructor for when read from a saved config.
80  * This is the reverse of to_config() and corresponds to reading [menu_item].
81  * Handlers are not initialized.
82  */
84  item_id_(id),
85  event_name_(make_item_name(id)),
86  hotkey_id_(make_item_hotkey(id)),
87  image_(cfg["image"].str()),
88  description_(cfg["description"].t_str()),
89  needs_select_(cfg["needs_select"].to_bool(false)),
90  show_if_(cfg.child_or_empty("show_if"), true),
91  filter_location_(cfg.child_or_empty("filter_location"), true),
92  command_(cfg.child_or_empty("command")),
93  default_hotkey_(cfg.child_or_empty("default_hotkey")),
94  use_hotkey_(cfg["use_hotkey"].to_bool(true)),
95  use_wml_menu_(cfg["use_hotkey"].str() != "only"),
96  is_synced_(cfg["synced"].to_bool(true))
97 {
98 }
99 
100 /**
101  * Constructor for when defined in an event.
102  * This is where default values are defined (the other constructors should have
103  * all values to work with).
104  * @param[in] id The id of the menu item.
105  * @param[in] definition The WML defining this menu item.
106  */
107 wml_menu_item::wml_menu_item(const std::string& id, const vconfig & definition) :
108  item_id_(id),
109  event_name_(make_item_name(id)),
110  hotkey_id_(make_item_hotkey(id)),
111  image_(),
112  description_(),
113  needs_select_(false),
114  show_if_(vconfig::empty_vconfig()),
115  filter_location_(vconfig::empty_vconfig()),
116  command_(),
117  default_hotkey_(),
118  use_hotkey_(true),
119  use_wml_menu_(true),
120  is_synced_(true)
121 {
122  // On the off-chance that update() doesn't do it, add the hotkey here.
123  // (Update can always modify it.)
125 
126  // Apply WML.
127  update(definition);
128 }
129 
130 /**
131  * Constructor for when modified by an event.
132  * (To avoid problems with a menu item's command changing itself, we make a
133  * new menu item instead of modifying the existing one.)
134  * @param[in] id The id of the menu item.
135  * @param[in] definition The WML defining this menu item.
136  * @param[in] original The previous version of the menu item with this id.
137  */
138 wml_menu_item::wml_menu_item(const std::string& id, const vconfig & definition,
139  const wml_menu_item & original) :
140  item_id_(id),
141  event_name_(make_item_name(id)),
142  hotkey_id_(make_item_hotkey(id)),
143  image_(original.image_),
144  description_(original.description_),
145  needs_select_(original.needs_select_),
146  show_if_(original.show_if_),
147  filter_location_(original.filter_location_),
148  command_(original.command_),
149  default_hotkey_(original.default_hotkey_),
150  use_hotkey_(original.use_hotkey_),
151  use_wml_menu_(original.use_wml_menu_),
152  is_synced_(original.is_synced_)
153 {
154  // Apply WML.
155  update(definition);
156 }
157 
158 
159 /**
160  * The image associated with this menu item.
161  * The returned string will not be empty; a default will be supplied if needed.
162  */
164 {
165  // Default the image?
166  return image_.empty() ? game_config::images::wml_menu : image_;
167 }
168 
169 /**
170  * Returns whether or not *this is applicable given the context.
171  * Assumes game variables x1, y1, and unit have been set.
172  * @param[in] hex The hex where the menu will appear.
173  */
175 {
176  // Failing the [show_if] tag means no show.
178  return false;
179 
180  // Failing the [fiter_location] tag means no show.
181  if ( !filter_location_.empty() &&
182  !terrain_filter(filter_location_, &filter_con)(hex) )
183  return false;
184 
185  // Failing to have a required selection means no show.
186  if ( needs_select_ && !data.last_selected.valid() )
187  return false;
188 
189  // Passed all tests.
190  return true;
191 }
192 
193 /**
194  * Causes the event associated with this item to fire.
195  * Also records the event.
196  * This includes recording the previous select event, if applicable.
197  * The undo stack will be cleared if the event mutated the gamestate.
198  *
199  * @param[in] event_hex The location of the event (where the menu was opened).
200  * @param[in] last_select The location of the most recent "select" event.
201  */
202 #define STR(X) #X
203 void wml_menu_item::fire_event(const map_location & event_hex, const game_data & data) const
204 {
205  if(!this->is_synced())
206  {
207  //It is possible to for example show a help menu during a [delay] of a synced event.
209  assert(resources::game_events != nullptr);
211  return;
212  }
213  const map_location & last_select = data.last_selected;
214 
215  // No new player-issued commands allowed while this is firing.
216  const events::command_disabler disable_commands;
217 
218  // instead of adding a second "select" event like it was done before, we just fire the select event again, and this time in a synced context.
219  // note that there coudn't be a user choice during the last "select" event because it didn't run in a synced context.
220  if ( needs_select_ && last_select.valid() )
221  {
222  synced_context::run_and_throw("fire_event", replay_helper::get_event(event_name_, event_hex, &last_select));
223  }
224  else
225  {
227  }
228 }
229 
230 /**
231  * Removes the implicit event handler for an inlined [command].
232  */
234 {
235  if ( !command_.empty() ) {
236  assert(resources::game_events);
238  }
239 
240  // Hotkey support
241  if ( use_hotkey_ )
243 }
244 
245 /**
246  * Initializes the implicit event handler for an inlined [command].
247  */
249 {
250  // If this menu item has a [command], add a handler for it.
251  if ( !command_.empty() ) {
252  assert(resources::game_events);
254  }
255 
256  // Hotkey support
257  if ( use_hotkey_ ) {
259  }
260 }
261 
262 /**
263  * Writes *this to the provided config.
264  * This is the reverse of the constructor from a config and corresponds to
265  * what will appear in [menu_item].
266  */
268 {
269  cfg["id"] = item_id_;
270  cfg["image"] = image_;
271  cfg["description"] = description_;
272  cfg["needs_select"] = needs_select_;
273  cfg["synced"] = is_synced_;
274 
275  if ( use_hotkey_ && use_wml_menu_ )
276  cfg["use_hotkey"] = true;
277  if ( use_hotkey_ && !use_wml_menu_ )
278  cfg["use_hotkey"] = "only";
279  if ( !use_hotkey_ && use_wml_menu_ )
280  cfg["use_hotkey"] = false;
281  if ( !use_hotkey_ && !use_wml_menu_ )
282  {
283  ERR_NG << "Bad data: wml_menu_item with both use_wml_menu and use_hotkey set to false is not supposed to be possible.";
284  cfg["use_hotkey"] = false;
285  }
286  if ( !show_if_.empty() )
287  cfg.add_child("show_if", show_if_.get_config());
288  if ( !filter_location_.empty() )
289  cfg.add_child("filter_location", filter_location_.get_config());
290  if ( !command_.empty() )
291  cfg.add_child("command", command_);
292  if ( !default_hotkey_.empty() )
293  cfg.add_child("default_hotkey", default_hotkey_);
294 }
295 
296 /**
297  * Updates *this based on @a vcfg.
298  * This corresponds to what can appear in [set_menu_item].
299  */
300 void wml_menu_item::update(const vconfig & vcfg)
301 {
302  const bool old_use_hotkey = use_hotkey_;
303  // Tracks whether or not the hotkey has been updated.
304  bool hotkey_updated = false;
305 
306  if ( vcfg.has_attribute("image") )
307  image_ = vcfg["image"].str();
308 
309  if ( vcfg.has_attribute("description") ) {
310  description_ = vcfg["description"].t_str();
311  hotkey_updated = true;
312  }
313 
314  if ( vcfg.has_attribute("needs_select") )
315  needs_select_ = vcfg["needs_select"].to_bool();
316  if ( vcfg.has_attribute("synced") )
317  is_synced_ = vcfg["synced"].to_bool(true);
318 
319  if ( const vconfig & child = vcfg.child("show_if") ) {
320  show_if_ = child;
322  }
323 
324  if ( const vconfig & child = vcfg.child("filter_location") ) {
325  filter_location_ = child;
327  }
328 
329  if ( const vconfig & child = vcfg.child("default_hotkey") ) {
330  default_hotkey_ = child.get_parsed_config();
331  hotkey_updated = true;
332  }
333 
334  if ( vcfg.has_attribute("use_hotkey") ) {
335  const config::attribute_value & use_hotkey_av = vcfg["use_hotkey"];
336 
337  use_hotkey_ = use_hotkey_av.to_bool(true);
338  use_wml_menu_ = use_hotkey_av.str() != "only";
339  }
340 
341  if ( const vconfig & cmd = vcfg.child("command") ) {
342  const bool delayed = cmd["delayed_variable_substitution"].to_bool(true);
343  update_command(delayed ? cmd.get_config() : cmd.get_parsed_config());
344  }
345 
346 
347  // Update the registered hotkey?
348 
349  if ( use_hotkey_ && !old_use_hotkey )
350  // The hotkey needs to be enabled.
352 
353  else if ( use_hotkey_ && hotkey_updated )
354  // The hotkey needs to be updated.
356 
357  else if ( !use_hotkey_ && old_use_hotkey )
358  // The hotkey needs to be disabled.
360 }
361 
362 /**
363  * Updates our command to @a new_command.
364  */
365 void wml_menu_item::update_command(const config & new_command)
366 {
367  // If there is an old command, remove it from the event handlers.
368  if ( !command_.empty() ) {
369  assert(resources::game_events);
371  while ( handler_ptr hand = *iter ) {
372  if ( hand->is_menu_item() ) {
373  LOG_NG << "Removing command for " << event_name_ << ".\n";
375  }
376  ++iter;
377  }
378  }
379 
380  // Update our stored command.
381  if ( new_command.empty() )
382  command_.clear();
383  else {
384  command_ = new_command;
385 
386  // Set some fields required by event processing.
387  config::attribute_value & event_id = command_["id"];
388  if ( event_id.empty() && !item_id_.empty() ) {
389  event_id = item_id_;
390  }
391  command_["name"] = event_name_;
392  command_["first_time_only"] = false;
393 
394  // Register the event.
395  LOG_NG << "Setting command for " << event_name_ << " to:\n" << command_;
396  assert(resources::game_events);
397  resources::game_events->add_event_handler(command_, true);
398  }
399 }
400 
401 } // end namespace game_events
402 
void remove_event_handler(const std::string &id)
Removes an event handler.
Definition: manager.cpp:56
bool needs_select_
Whether or not this event says it makes use of the last selected unit.
Definition: menu_item.hpp:86
std::string str() const
Definition: config.cpp:353
map_location last_selected
the last location where a select event fired.
Definition: game_data.hpp:85
vconfig filter_location_
A location filter to be applied to the hex where the menu is invoked.
Definition: menu_item.hpp:92
vconfig child(const std::string &key) const
Returns a child of *this whose key is key.
Definition: variable.cpp:243
config command_
Actions to take when this item is chosen.
Definition: menu_item.hpp:94
bool to_bool(bool def=false) const
Definition: config.cpp:275
t_string description_
The text to display in the menu for this item.
Definition: menu_item.hpp:84
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...
surface image_
The image is cached in this surface.
Definition: canvas.cpp:961
void init_handler() const
Initializes the implicit event handler for an inlined [command].
Definition: menu_item.cpp:248
bool 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:471
void to_config(config &cfg) const
Writes *this to the provided config.
Definition: menu_item.cpp:267
Replay control code.
std::string image_
The image to display in the menu next to this item's description.
Definition: menu_item.hpp:82
void clear()
Definition: config.cpp:1055
an object to leave the synced context during draw or unsynced wml items when we don’t know whether w...
GLint GLenum GLsizei GLint GLsizei const GLvoid * data
Definition: glew.h:1347
bool empty() const
Definition: config.cpp:1105
bool empty() const
Tests for an attribute that either was never set or was set to "".
Definition: config.cpp:375
This file implements all the hotkey handling and menu details for play controller.
Variant for storing WML attributes.
Definition: config.hpp:223
vconfig show_if_
A condition that must hold in order for this menu item to be visible.
Definition: menu_item.hpp:89
GLuint id
Definition: glew.h:1647
bool valid() const
Definition: location.hpp:69
filter_context * filter_con
Definition: resources.cpp:23
config & add_child(const std::string &key)
Definition: config.cpp:743
const std::string item_id_
The id of this menu item.
Definition: menu_item.hpp:76
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:174
Encapsulates the map of the game.
Definition: location.hpp:38
Domain specific events.
Definition: action_wml.cpp:93
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:50
bool is_synced_
If true, this item will be sended ot ther clients.
Definition: menu_item.hpp:103
const config & get_config() const
Definition: variable.hpp:68
bool empty() const
Definition: variable.hpp:93
Define the game's event mechanism.
const std::string & image() const
The image associated with this menu item.
Definition: menu_item.cpp:163
This class is similar to an input iterator through event handlers, except each instance knows its own...
Definition: manager.hpp:60
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:203
game_events::t_pump & pump()
Definition: manager.cpp:194
bool use_hotkey_
If true, allow using a hotkey to trigger this item.
Definition: menu_item.hpp:98
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...
void finish_handler() const
Removes the implicit event handler for an inlined [command].
Definition: menu_item.cpp:233
A variable-expanding proxy for the config class.
Definition: variable.hpp:36
Various functions that implement the undoing (and redoing) of in-game commands.
Standard logging facilities (interface).
bool use_wml_menu_
If true, allow using the menu to trigger this item.
Definition: menu_item.hpp:100
wml_menu_item(const std::string &id, const config &cfg)
Constructor for when read from a saved config.
Definition: menu_item.cpp:83
void update_command(const config &new_command)
Updates our command to new_command.
Definition: menu_item.cpp:365
void update(const vconfig &vcfg)
Updates *this based on vcfg.
Definition: menu_item.cpp:300
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:83
static config get_event(const std::string &name, const map_location &loc, const map_location *last_select_loc)
void make_safe() const
instruct the vconfig to make a private copy of its underlying data.
Definition: variable.cpp:119
std::string wml_menu
bool remove_wml_hotkey(const std::string &id)
removes a wml hotkey with the given id, returns true if the deletion was successful ...
GLsizei const GLcharARB ** string
Definition: glew.h:4503
const std::string event_name_
The name of this item's event(s); based on the item's id.
Definition: menu_item.hpp:78
bool has_attribute(const std::string &key) const
< Synonym for operator[]
Definition: variable.hpp:92
const std::string hotkey_id_
The id for this item's hotkey; based on the item's id.
Definition: menu_item.hpp:80
config default_hotkey_
Config object containing the default hotkey.
Definition: menu_item.hpp:96