The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
wmi_manager.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2003 - 2025
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
24#include "config.hpp"
26#include "game_data.hpp"
27#include "log.hpp"
28#include "map/location.hpp"
29#include "play_controller.hpp"
30#include "resources.hpp"
31
32static lg::log_domain log_engine("engine");
33#define WRN_NG LOG_STREAM(warn, log_engine)
34#define LOG_NG LOG_STREAM(info, log_engine)
35
36namespace 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 */
51
52/** Erases the item with id @a key. */
53bool 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 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 Menu items. consisting of menu text, menu icons, and action ids.
110 * @param fc Used to check whether the menu's filter matches.
111 * @param gamedata Used to check whether to show if selecting is required.
112 * @param units Used to highlight a unit if needed.
113 */
115 std::vector<config>& items,
116 filter_context& fc,
118 unit_map& units) const
119{
120 if(empty()) {
121 // Nothing to do (skip setting game variables).
122 return;
123 }
124
125 // Prepare for can_show().
126 config::attribute_value x1 = gamedata.get_variable("x1");
127 config::attribute_value y1 = gamedata.get_variable("y1");
128 gamedata.get_variable("x1") = hex.wml_x();
129 gamedata.get_variable("y1") = hex.wml_y();
130 scoped_xy_unit highlighted_unit("unit", hex, units);
131
132 for(const auto& [id, item] : wml_menu_items_) {
133 // Not synched, or synched in an allowed context
134 bool synched_allowed = !item->is_synced() || resources::controller->can_use_synced_wml_menu();
135
136 // Can this item be shown?
137 if(!item->use_wml_menu() || !synched_allowed || !item->can_show(hex, gamedata, fc)) {
138 continue;
139 }
140
141 // Allows variables to be substituted at invocation time
142 auto description = utils::interpolate_variables_into_string(item->menu_text(), gamedata);
143 items.emplace_back("id", item->hotkey_id(), "label", description, "icon", item->image());
144 }
145
146 // Restore old values
147 gamedata.get_variable("x1") = x1;
148 gamedata.get_variable("y1") = y1;
149}
150
152{
153 auto iter = wml_menu_items_.find(id);
154 if(iter != wml_menu_items_.end()) {
155 return iter->second;
156 }
157
158 return nullptr;
159}
160
161/**
162 * Initializes the implicit event handlers for inlined [command]s.
163 */
165{
166 // Applying default hotkeys here currently does not work because
167 // the hotkeys are reset by play_controller::init_managers() ->
168 // display_manager::display_manager, which is called after this.
169 // The result is that default wml hotkeys will be ignored if wml
170 // hotkeys are set to default in the preferences menu. (They are
171 // still reapplied if set_menu_item is called again, for example
172 // by starting a new campaign.) Since it isn't that important
173 // I'll just leave it for now.
174
175 for(const auto& [id, item] : wml_menu_items_) {
176 // If this menu item has a [command], add a handler for it.
177 item->init_handler(lk);
178 }
179
180 // Diagnostic:
181 LOG_NG << wml_menu_items_.size() << " WML menu items found, loaded.";
182}
183
185{
186 for(const auto& [id, item] : wml_menu_items_) {
187 // Add this item as a child of cfg.
188 item->to_config(cfg.add_child("menu_item"));
189 }
190}
191
192/**
193 * Updates or creates (as appropriate) the menu item with the given @a id.
194 */
195void wmi_manager::set_item(const std::string& id, const vconfig& menu_item)
196{
197 // First, try to insert a brand new menu item.
198 auto [iter, success] = wml_menu_items_.emplace(id, std::make_shared<wml_menu_item>(id, menu_item));
199
200 // If an entry already exists, reset it.
201 if(!success) {
202 // Create a new menu item based on the old. This leaves the old item
203 // alone in case someone else is holding on to (and processing) it.
204 iter->second.reset(new wml_menu_item(id, menu_item, *iter->second));
205 }
206}
207
208/**
209 * Sets the current menu items to the "menu_item" children of @a cfg.
210 */
212{
213 wml_menu_items_.clear();
214 for(const config& item : cfg.child_range("menu_item")) {
215 if(!item.has_attribute("id")) {
216 continue;
217 }
218
219 const std::string& id = item["id"];
220 auto [iter, success] = wml_menu_items_.emplace(id, std::make_shared<wml_menu_item>(id, item));
221
222 if(!success) {
223 WRN_NG << "duplicate menu item (" << id << ") while loading from config";
224 }
225 }
226}
227
228} // end namespace game_events
#define WRN_NG
#define LOG_NG
Variant for storing WML attributes.
A config object defines a single node in a WML file, with access to child nodes.
Definition config.hpp:157
config & add_child(std::string_view key)
Definition config.cpp:436
child_itors child_range(std::string_view key)
Definition config.cpp:268
void get_items(const map_location &hex, std::vector< config > &items, 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.
std::shared_ptr< wml_menu_item > item_ptr
wml_menu_item pointers
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.
std::map< std::string, item_ptr > wml_menu_items_
bool erase(const std::string &id)
Erases the item with the provided id.
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:98
A variable-expanding proxy for the config class.
Definition variable.hpp:45
Definitions for the interface to Wesnoth Markup Language (WML).
const config * cfg
Standard logging facilities (interface).
Declarations for a class that implements WML-defined (right-click) menu items.
Domain specific events.
play_controller * controller
Definition resources.cpp:21
std::string interpolate_variables_into_string(const std::string &str, const string_map *const symbols)
Function which will interpolate variables, starting with '$' in the string 'str' with the equivalent ...
Encapsulates the map of the game.
Definition location.hpp:46
int wml_y() const
Definition location.hpp:187
int wml_x() const
Definition location.hpp:186
const std::string & gamedata
static lg::log_domain log_engine("engine")
Declarations for a container for wml_menu_item.