The Battle for Wesnoth  1.15.0-dev
manager_impl.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 
16 
17 #include "game_events/handlers.hpp"
18 #include "formula/string_utils.hpp"
19 #include "log.hpp"
21 
22 #include <boost/algorithm/string.hpp>
23 
24 #include <iostream>
25 
26 static lg::log_domain log_engine("engine");
27 #define DBG_NG LOG_STREAM(debug, log_engine)
28 #define LOG_NG LOG_STREAM(info, log_engine)
29 #define WRN_NG LOG_STREAM(warn, log_engine)
30 
31 static lg::log_domain log_event_handler("event_handler");
32 #define DBG_EH LOG_STREAM(debug, log_event_handler)
33 
34 namespace game_events
35 {
37 {
38  if(lg::debug().dont_log("event_handler")) {
39  return;
40  }
41 
42  std::stringstream ss;
43 
44  for(const handler_ptr& h : active_) {
45  if(!h) {
46  continue;
47  }
48 
49  const config& cfg = h->get_config();
50  ss << "name=" << cfg["name"] << ", with id=" << cfg["id"] << "; ";
51  }
52 
53  DBG_EH << "active handlers are now " << ss.str() << "\n";
54 }
55 
56 /**
57  * Utility to standardize the event names used in by_name_.
58  * This means stripping leading and trailing spaces, and converting internal
59  * spaces to underscores.
60  */
61 std::string event_handlers::standardize_name(const std::string& name)
62 {
63  std::string retval = name;
64 
65  // Trim leading and trailing spaces.
66  boost::trim(retval);
67 
68  // Replace internal spaces with underscores.
69  boost::replace_all(retval, " ", "_");
70 
71  return retval;
72 }
73 
74 /**
75  * Read-only access to the handlers with fixed event names, by event name.
76  */
78 {
79  // Empty list for the "not found" case.
80  static handler_list empty_list;
81 
82  // Look for the name in the name map.
83  auto find_it = by_name_.find(standardize_name(name));
84  return find_it == by_name_.end() ? empty_list : find_it->second;
85 }
86 
87 /**
88  * Adds an event handler.
89  * An event with a nonempty ID will not be added if an event with that
90  * ID already exists.
91  */
92 void event_handlers::add_event_handler(const config& cfg, bool is_menu_item)
93 {
94  // Someone decided to register an empty event... bail.
95  if(cfg.empty()) {
96  return;
97  }
98 
99  std::string name = cfg["name"];
100  std::string id = cfg["id"];
101 
102  if(!id.empty()) {
103  // Ignore this handler if there is already one with this ID.
104  auto find_it = id_map_.find(id);
105 
106  if(find_it != id_map_.end() && !find_it->second.expired()) {
107  DBG_EH << "ignoring event handler for name='" << name << "' with id '" << id << "'\n";
108  return;
109  }
110  }
111 
112  if(name.empty() && id.empty()) {
113  lg::wml_error() << "[event] is missing name or id field\n";
114  return;
115  }
116 
117  // Make a copy of the event cfg here in order to do some standardization on the
118  // name field. Will be moved into the handler.
119  config event_cfg = cfg;
120 
121  // Split the name field...
122  std::vector<std::string> standardized_names = utils::split(name);
123 
124  if(!name.empty()) {
125  // ...and standardize each one individually. This ensures they're all valid for by-name lookup.
126  for(std::string& single_name : standardized_names) {
127  if(!utils::might_contain_variables(single_name)) {
128  single_name = standardize_name(single_name);
129  }
130  }
131 
132  assert(!standardized_names.empty());
133 
134  // Write the new name back to the config.
135  name = utils::join(standardized_names);
136  event_cfg["name"] = name;
137  }
138 
139  // Create a new handler.
140  // Do note active_ holds the main shared_ptr, and the other three containers
141  // construct weak_ptrs from the shared one.
142  DBG_EH << "inserting event handler for name=" << name << " with id=" << id << "\n";
143  active_.emplace_back(new event_handler(std::move(event_cfg), is_menu_item, standardized_names));
144 
145  //
146  // !! event_cfg is invalid past this point! DO NOT USE!
147  //
148 
149  // File by name.
151  dynamic_.emplace_back(active_.back());
152  } else {
153  for(const std::string& single_name : standardized_names) {
154  by_name_[single_name].emplace_back(active_.back());
155  }
156  }
157 
158  // File by ID.
159  if(!id.empty()) {
160  id_map_[id] = active_.back();
161  }
162 
163  log_handlers();
164 }
165 
166 /**
167  * Removes an event handler, identified by its ID.
168  * Events with empty IDs cannot be removed.
169  */
170 void event_handlers::remove_event_handler(const std::string& id)
171 {
172  if(id.empty()) {
173  return;
174  }
175 
176  DBG_EH << "removing event handler with id " << id << "\n";
177 
178  // Find the existing handler with this ID.
179  auto find_it = id_map_.find(id);
180  if(find_it != id_map_.end()) {
181  handler_ptr handler = find_it->second.lock();
182 
183  if(handler && !handler->disabled()) {
184  handler->disable();
185  }
186 
187  // Do this even if the lock failed.
188  id_map_.erase(find_it);
189 
190  // We don't delete the handler from the other lists just yet. This is to ensure
191  // the main handler list's size doesn't change when iterating over the handlers.
192  // Any disabled handlers don't get executed, and will be removed during the next
193  // cleanup pass.
194  }
195 
196  log_handlers();
197 }
198 
199 void event_handlers::clean_up_expired_handlers(const std::string& event_name)
200 {
201  // First, remove all disabled handlers from the main list.
202  auto to_remove = std::remove_if(active_.begin(), active_.end(),
203  [](handler_ptr p) { return p->disabled(); }
204  );
205 
206  active_.erase(to_remove, active_.end());
207 
208  // Then remove any now-unlockable weak_ptrs from the by-name list.
209  // Might be more than one so we split.
210  for(const std::string& name : utils::split(event_name)) {
211  get(name).remove_if(
212  [](weak_handler_ptr ptr) { return ptr.expired(); }
213  );
214  }
215 
216  // And finally remove any now-unlockable weak_ptrs from the with-variables name list.
217  dynamic_.remove_if(
218  [](weak_handler_ptr ptr) { return ptr.expired(); }
219  );
220 }
221 
223 {
224  auto find_it = id_map_.find(id);
225  if(find_it != id_map_.end() && !find_it->second.expired()) {
226  return find_it->second.lock();
227  }
228 
229  return nullptr;
230 }
231 
232 } // end namespace game_events
std::string join(const T &v, const std::string &s=",")
Generates a new string joining container items in a list.
static std::string standardize_name(const std::string &name)
Utility to standardize the event names used in by_name_.
handler_list & get(const std::string &name)
Access to the handlers with fixed event names, by event name.
void clean_up_expired_handlers(const std::string &event_name)
Removes all expired event handlers and any weak_ptrs to them.
#define h
id_map_t id_map_
Allows quick locating of handlers by id.
std::vector< std::string > split(const std::string &val, const char c, const int flags)
Splits a (comma-)separated string into a vector of pieces.
static const char * name(const std::vector< SDL_Joystick *> &joysticks, const std::size_t index)
Definition: joystick.cpp:48
void remove_event_handler(const std::string &id)
Removes an event handler, identified by its ID.
handler_list dynamic_
Active event handlers with variables in their event names.
static lg::log_domain log_engine("engine")
std::weak_ptr< event_handler > weak_handler_ptr
Definition: fwd.hpp:25
handler_queue_t active_
Active event handlers.
logger & debug()
Definition: log.cpp:96
Domain specific events.
Definition: action_wml.cpp:88
void add_event_handler(const config &cfg, bool is_menu_item=false)
Adds an event handler.
std::stringstream & wml_error()
Use this logger to send errors due to deprecated WML.
Definition: log.cpp:269
mock_party p
map_t by_name_
Active event handlers with fixed event names, organized by event name.
bool might_contain_variables(const std::string &str)
Determines if a string might contain variables to interpolate.
std::list< weak_handler_ptr > handler_list
Definition: fwd.hpp:26
Define the handlers for the game&#39;s events mechanism.
Standard logging facilities (interface).
static lg::log_domain log_event_handler("event_handler")
retval
Default window/dialog return values.
Definition: retval.hpp:28
std::shared_ptr< event_handler > handler_ptr
Definition: fwd.hpp:24
const handler_ptr get_event_handler_by_id(const std::string &id)
Gets an event handler, identified by its ID.
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:92
bool empty() const
Definition: config.cpp:886
#define DBG_EH