The Battle for Wesnoth  1.17.0-dev
wmi_manager.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 https://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 container for wml_menu_item.
18  */
19 
22 #include "play_controller.hpp"
23 #include "resources.hpp"
24 
25 #include "config.hpp"
26 #include "game_data.hpp"
27 #include "log.hpp"
28 #include "map/location.hpp"
29 
30 static lg::log_domain log_engine("engine");
31 #define WRN_NG LOG_STREAM(warn, log_engine)
32 #define LOG_NG LOG_STREAM(info, log_engine)
33 
34 // This file is in the game_events namespace.
35 namespace game_events
36 {
38  : wml_menu_items_()
39 {
40 }
41 
42 /**
43  * Destructor.
44  * Default implementation, but defined here because this function needs to be
45  * able to see wml_menu_item's destructor.
46  */
48 {
49 }
50 
51 /** Erases the item with id @a key. */
52 bool wmi_manager::erase(const std::string& id)
53 {
54  // Locate the item to erase.
55  const auto iter = wml_menu_items_.find(id);
56 
57  if(iter == wml_menu_items_.end()) {
58  WRN_NG << "Trying to remove non-existent menu item '" << id << "'; ignoring." << std::endl;
59  // No such item.
60  return false;
61  }
62 
63  // Clean up our bookkeeping.
64  iter->second->finish_handler();
65 
66  // Remove the item from the map.
67  wml_menu_items_.erase(iter);
68 
69  return true; // Erased one item.
70 }
71 
72 /**
73  * Fires the menu item with the given @a id.
74  * @returns true if a matching item was found (even if it could not be fired).
75  * NOTE: The return value could be altered if it is decided that
76  * play_controller::execute_command() needs something different.
77  */
79  const std::string& id, const map_location& hex, game_data& gamedata, filter_context& fc, unit_map& units, bool is_key_hold_repeat) const
80 {
81  // Does this item exist?
82  item_ptr wmi = get_item(id);
83  if(!wmi) {
84  return false;
85  } else if(is_key_hold_repeat && !wmi->hotkey_repeat()) {
86  return false;
87  }
88 
89  // Prepare for can show().
90  config::attribute_value x1 = gamedata.get_variable("x1");
91  config::attribute_value y1 = gamedata.get_variable("y1");
92  gamedata.get_variable("x1") = hex.wml_x();
93  gamedata.get_variable("y1") = hex.wml_y();
94  scoped_xy_unit highlighted_unit("unit", hex, units);
95 
96  // Can this item be shown?
97  if(wmi->can_show(hex, gamedata, fc)) {
98  wmi->fire_event(hex, gamedata);
99  }
100  gamedata.get_variable("x1") = x1;
101  gamedata.get_variable("y1") = y1;
102  return true;
103 }
104 
105 /**
106  * Returns the menu items that can be shown for the given location.
107  *
108  * @param hex The current hex.
109  * @param[out] items Pointers to applicable menu items will be pushed onto @a items.
110  * @param[out] descriptions Menu item text will be pushed onto @a descriptions (in the same order as @a items).
111  * @param fc Used to check whether the menu's filter matches.
112  * @param gamedata Used to check whether to show if selecting is required.
113  * @param units Used to highlight a unit if needed.
114  */
116  std::vector<std::shared_ptr<const wml_menu_item>>& items,
117  std::vector<config>& descriptions,
118  filter_context& fc,
120  unit_map& units) const
121 {
122  if(empty()) {
123  // Nothing to do (skip setting game variables).
124  return;
125  }
126 
127  // Prepare for can show().
128 
129 
130  config::attribute_value x1 = gamedata.get_variable("x1");
131  config::attribute_value y1 = gamedata.get_variable("y1");
132  gamedata.get_variable("x1") = hex.wml_x();
133  gamedata.get_variable("y1") = hex.wml_y();
134  scoped_xy_unit highlighted_unit("unit", hex, units);
135 
136  // Check each menu item.
137  for(const auto& item_pair : wml_menu_items_) {
138  item_ptr item = item_pair.second;
139 
140  // Can this item be shown?
141  if(item->use_wml_menu() && (!item->is_synced() || resources::controller->can_use_synced_wml_menu())
142  && item->can_show(hex, gamedata, fc)) {
143  // Include this item.
144  items.push_back(item);
145  descriptions.emplace_back("id", item->menu_text());
146  }
147  }
148  gamedata.get_variable("x1") = x1;
149  gamedata.get_variable("y1") = y1;
150 }
151 
152 wmi_manager::item_ptr wmi_manager::get_item(const std::string& id) const
153 {
154  auto iter = wml_menu_items_.find(id);
155  if(iter != wml_menu_items_.end()) {
156  return iter->second;
157  }
158 
159  return nullptr;
160 }
161 
162 /**
163  * Initializes the implicit event handlers for inlined [command]s.
164  */
166 {
167  // Applying default hotkeys here currently does not work because
168  // the hotkeys are reset by play_controler::init_managers() ->
169  // display_manager::display_manager, which is called after this.
170  // The result is that default wml hotkeys will be ignored if wml
171  // hotkeys are set to default in the preferences menu. (They are
172  // still reapplied if set_menu_item is called again, for example
173  // by starting a new campaign.) Since it isn't that important
174  // I'll just leave it for now.
175 
176  unsigned wmi_count = 0;
177 
178  // Loop through each menu item.
179  for(const auto& item : wml_menu_items_) {
180  // If this menu item has a [command], add a handler for it.
181  item.second->init_handler();
182 
183  // Count the menu items (for the diagnostic message).
184  ++wmi_count;
185  }
186 
187  // Diagnostic:
188  if(wmi_count > 0) {
189  LOG_NG << wmi_count << " WML menu items found, loaded." << std::endl;
190  }
191 }
192 
194 {
195  // Loop through our items.
196  for(const auto& item : wml_menu_items_) {
197  // Add this item as a child of cfg.
198  item.second->to_config(cfg.add_child("menu_item"));
199  }
200 }
201 
202 /**
203  * Updates or creates (as appropriate) the menu item with the given @a id.
204  */
205 void wmi_manager::set_item(const std::string& id, const vconfig& menu_item)
206 {
207  // First, try to insert a brand new menu item.
208  auto [iter, success] = wml_menu_items_.emplace(id, std::make_shared<wml_menu_item>(id, menu_item));
209 
210  // If an entry already exists, reset it.
211  if(!success) {
212  // Create a new menu item based on the old. This leaves the old item
213  // alone in case someone else is holding on to (and processing) it.
214  iter->second.reset(new wml_menu_item(id, menu_item, *iter->second));
215  }
216 }
217 
218 /**
219  * Sets the current menu items to the "menu_item" children of @a cfg.
220  */
222 {
223  wml_menu_items_.clear();
224  for(const config& item : cfg.child_range("menu_item")) {
225  if(!item.has_attribute("id")) {
226  continue;
227  }
228 
229  const std::string& id = item["id"];
230  bool success;
231 
232  std::tie(std::ignore, success) = wml_menu_items_.emplace(id, std::make_shared<wml_menu_item>(id, item));
233 
234  if(!success) {
235  WRN_NG << "duplicate menu item (" << id << ") while loading from config" << std::endl;
236  }
237  }
238 }
239 
240 } // end namespace game_events
play_controller * controller
Definition: resources.cpp:21
#define WRN_NG
Definition: wmi_manager.cpp:31
std::map< std::string, item_ptr > wml_menu_items_
Definition: wmi_manager.hpp:98
Variant for storing WML attributes.
void set_item(const std::string &id, const vconfig &menu_item)
Updates or creates (as appropriate) the menu item with the given id.
child_itors child_range(config_key_type key)
Definition: config.cpp:356
bool empty() const
Returns true if no menu items are being managed.
Definition: wmi_manager.hpp:48
const std::string & gamedata
int wml_x() const
Definition: location.hpp:152
const std::vector< std::string > items
Definitions for the interface to Wesnoth Markup Language (WML).
item_ptr get_item(const std::string &id) const
Gets the menu item with the specified ID.
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:78
config::attribute_value & get_variable(const std::string &varname)
throws invalid_variablename_exception if varname is no valid variable name.
Definition: game_data.cpp:62
void init_handlers() const
Initializes the implicit event handlers for inlined [command]s.
int wml_y() const
Definition: location.hpp:153
bool erase(const std::string &id)
Erases the item with the provided id.
Definition: wmi_manager.cpp:52
std::shared_ptr< wml_menu_item > item_ptr
wml_menu_item pointers
Definition: wmi_manager.hpp:42
#define LOG_NG
Definition: wmi_manager.cpp:32
Declarations for a container for wml_menu_item.
~wmi_manager()
Destructor.
Definition: wmi_manager.cpp:47
void set_menu_items(const config &cfg)
Sets the current menu items to the "menu_item" children of cfg.
Encapsulates the map of the game.
Definition: location.hpp:37
void to_config(config &cfg) const
Domain specific events.
Definition: action_wml.cpp:86
bool can_use_synced_wml_menu() const
config & add_child(config_key_type key)
Definition: config.cpp:500
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.
A variable-expanding proxy for the config class.
Definition: variable.hpp:44
Standard logging facilities (interface).
Container associating units to locations.
Definition: map.hpp:97
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:59
std::pair< std::string, unsigned > item
Definition: help_impl.hpp:409
static lg::log_domain log_engine("engine")