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