The Battle for Wesnoth  1.13.4+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
hotkey_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 #include "log.hpp"
17 #include "hotkey_item.hpp"
18 #include "hotkey_command.hpp"
19 #include "config.hpp"
20 
21 #define GETTEXT_DOMAIN "wesnoth-lib"
22 
23 #include "gettext.hpp"
25 #include "sdl/utils.hpp"
26 
27 #include <boost/algorithm/string/join.hpp>
28 #include <boost/algorithm/string.hpp>
29 #include <boost/algorithm/string/predicate.hpp>
30 #include "utils/functional.hpp"
31 
32 #include "key.hpp"
33 
34 #include <SDL.h>
35 
36 
37 static lg::log_domain log_config("config");
38 #define ERR_G LOG_STREAM(err, lg::general())
39 #define LOG_G LOG_STREAM(info, lg::general())
40 #define DBG_G LOG_STREAM(debug, lg::general())
41 #define ERR_CF LOG_STREAM(err, log_config)
42 
43 namespace hotkey {
44 
47 
48 static unsigned int sdl_get_mods()
49 {
50  unsigned int mods;
51  mods = SDL_GetModState();
52 
53  mods &= ~KMOD_NUM;
54  mods &= ~KMOD_CAPS;
55  mods &= ~KMOD_MODE;
56 
57  // save the matching for checking right vs left keys
58  if (mods & KMOD_SHIFT)
59  mods |= KMOD_SHIFT;
60 
61  if (mods & KMOD_CTRL)
62  mods |= KMOD_CTRL;
63 
64  if (mods & KMOD_ALT)
65  mods |= KMOD_ALT;
66 
67  if (mods & KMOD_GUI)
68  mods |= KMOD_GUI;
69 
70  return mods;
71 }
72 
74 {
75  std::string ret = "";
76 
77  if (mod_ & KMOD_CTRL)
78  ret += "ctrl";
79 
80  ret +=
81  (!ret.empty() && !boost::algorithm::ends_with(ret, "+") ?
82  "+" : "");
83  if (mod_ & KMOD_ALT)
84  ret += "alt";
85 
86  ret +=
87  (!ret.empty() && !boost::algorithm::ends_with(ret, "+") ?
88  "+" : "");
89  if (mod_ & KMOD_SHIFT)
90  ret += "shift";
91 
92  ret +=
93  (!ret.empty() && !boost::algorithm::ends_with(ret, "+") ?
94  "+" : "");
95  if (mod_ & KMOD_GUI)
96 #ifdef __APPLE__
97  ret += "cmd";
98 #else
99  ret += "win";
100 #endif
101 
102  ret +=
103  (!ret.empty() && !boost::algorithm::ends_with(ret, "+") ?
104  "+" : "");
105  return ret += get_name_helper();
106 }
107 
109 {
110 
111  bool ret;
112 
113  if (other == hotkey_ptr()) {
114  return false;
115  }
116 
118  & hotkey::get_hotkey_command(other->get_command()).scope;
119 
120  if (scopematch.none()) {
121  return false;
122  }
123 
124  ret = mod_ == other->mod_ && bindings_equal_helper(other);
125 
126  return ret;
127 }
128 
129 bool hotkey_base::matches(const SDL_Event &event) const
130 {
131  unsigned int mods = sdl_get_mods();
132 
134  !active() || is_disabled()) {
135  return false;
136  }
137 
138  if ((mods != mod_)) {
139  return false;
140  }
141 
142  return matches_helper(event);
143 }
144 
145 void hotkey_base::save(config& item) const
146 {
147  item["command"] = get_command();
148  item["disabled"] = is_disabled();
149 
150  item["shift"] = !!(mod_ & KMOD_SHIFT);
151  item["ctrl"] = !!(mod_ & KMOD_CTRL);
152  item["cmd"] = !!(mod_ & KMOD_GUI);
153  item["alt"] = !!(mod_ & KMOD_ALT);
154 
155  save_helper(item);
156 }
157 
159 {
160  hotkey_ptr base = hotkey_ptr(new hotkey_void);
161 
162  switch (event.type) {
163  case SDL_KEYDOWN:
164  case SDL_KEYUP: {
165  hotkey_keyboard_ptr keyboard(new hotkey_keyboard());
166  base = boost::dynamic_pointer_cast<hotkey_base>(keyboard);
167  SDL_Scancode code;
168  code = event.key.keysym.scancode;
169  keyboard->set_scancode(code);
170  break;
171  }
172  case SDL_MOUSEBUTTONDOWN:
173  case SDL_MOUSEBUTTONUP: {
174  hotkey_mouse_ptr mouse(new hotkey_mouse());
175  base = boost::dynamic_pointer_cast<hotkey_base>(mouse);
176  mouse->set_button(event.button.button);
177  break;
178  }
179  default:
180  ERR_G<< "Trying to bind an unknown event type:" << event.type << "\n";
181  break;
182  }
183 
184  base->set_mods(sdl_get_mods());
185  base->set_command(id);
186  base->unset_default();
187 
188  return base;
189 }
190 
192 {
193  hotkey_ptr base = hotkey_ptr(new hotkey_void());
194 
195  const std::string& mouse_cfg = cfg["mouse"];
196  if (!mouse_cfg.empty()) {
197  hotkey_mouse_ptr mouse(new hotkey_mouse());
198  base = boost::dynamic_pointer_cast<hotkey_base>(mouse);
199  mouse->set_button(cfg["button"].to_int());
200  }
201  // TODO: add joystick support back
202 #if 0
203  const std::string& joystick_cfg = cfg["joystick"];
204  if (!joystick_cfg.empty()) {
205  joystick_ = cfg["joystick"].to_int();
206  }
207  const std::string& hat = cfg["hat"];
208  if (!hat.empty()) {
209  hat_ = cfg["hat"].to_int();
210  value_ = cfg["value"].to_int();
211  }
212 
213  const std::string& button = cfg["button"];
214  if (!button.empty()) {
215  button_ = cfg["button"].to_int();
216  }
217 #endif
218 
219  const std::string& key_cfg = cfg["key"];
220  if (!key_cfg.empty()) {
221  hotkey_keyboard_ptr keyboard(new hotkey_keyboard());
222  base = boost::dynamic_pointer_cast<hotkey_base>(keyboard);
223 
224  SDL_Scancode scancode = SDL_GetScancodeFromName(key_cfg.c_str());
225  if (scancode == SDL_SCANCODE_UNKNOWN) {
226  ERR_G<< "Unknown key: " << key_cfg << "\n";
227  }
228  keyboard->set_scancode(scancode);
229  }
230 
231  if (base == hotkey_ptr()) {
232  return base;
233  }
234 
235  unsigned int mods = 0;
236 
237  if (cfg["shift"].to_bool())
238  mods |= KMOD_SHIFT;
239  if (cfg["ctrl"].to_bool())
240  mods |= KMOD_CTRL;
241  if (cfg["cmd"].to_bool())
242  mods |= KMOD_GUI;
243  if (cfg["alt"].to_bool())
244  mods |= KMOD_ALT;
245 
246  base->set_mods(mods);
247  base->set_command(cfg["command"].str());
248 
249  cfg["disabled"].to_bool() ? base->disable() : base->enable();
250 
251  return base;
252 }
253 
254 bool hotkey_mouse::matches_helper(const SDL_Event &event) const
255 {
256  if (event.type != SDL_MOUSEBUTTONUP && event.type != SDL_MOUSEBUTTONDOWN) {
257  return false;
258  }
259 
260  if (event.button.button != button_) {
261  return false;
262  }
263 
264  return true;
265 }
266 
268 {
269  return "mouse " + std::to_string(button_);
270 }
271 
273 {
274  item["mouse"] = 0;
275  if (button_ != 0) {
276  item["button"] = button_;
277  }
278 }
279 
281 {
282  std::string ret = std::string(SDL_GetKeyName(SDL_GetKeyFromScancode(scancode_)));
283 
284  if (ret.size() == 1) {
285  boost::algorithm::to_lower(ret);
286  }
287 
288  return ret;
289 }
290 
291 bool hotkey_keyboard::matches_helper(const SDL_Event &event) const
292 {
293  if (event.type != SDL_KEYDOWN && event.type != SDL_KEYUP) {
294  return false;
295  }
296 
297  SDL_Scancode code;
298  code = event.key.keysym.scancode;
299 
300  if (code != scancode_) {
301  return false;
302  }
303 
304  return true;
305 }
306 
308 {
309  hotkey_mouse_ptr other_m = boost::dynamic_pointer_cast<hotkey_mouse>(other);
310 
311  if (other_m == hotkey_mouse_ptr()) {
312  return false;
313  }
314 
315  return button_ == other_m->button_;
316 }
317 
319 {
320  if (scancode_ != SDL_SCANCODE_UNKNOWN) {
321  item["key"] = SDL_GetScancodeName(scancode_);
322  }
323 }
324 
325 bool has_hotkey_item(const std::string& command)
326 {
327  for (hotkey_ptr item : hotkeys_) {
328  if (item->get_command() == command) {
329  return true;
330  }
331 
332  }
333  return false;
334 }
335 
337 {
338  hotkey_keyboard_ptr other_k = boost::dynamic_pointer_cast<hotkey_keyboard>(
339  other);
340  if (other_k == hotkey_keyboard_ptr()) {
341  return false;
342  }
343 
344  return scancode_ == other_k->scancode_;
345 }
346 
348 {
349  if (!hotkeys_.empty()) {
350  hotkeys_.erase(std::remove(hotkeys_.begin(), hotkeys_.end(), item));
351  }
352 }
353 
354 void add_hotkey(const hotkey_ptr item)
355 {
356 
357  if (item == hotkey_ptr()) {
358  return;
359  }
360 
361  scope_changer scope_ch;
362  set_active_scopes(hotkey::get_hotkey_command(item->get_command()).scope);
363 
364  if (!hotkeys_.empty()) {
365  hotkeys_.erase(
366  std::remove_if(hotkeys_.begin(), hotkeys_.end(),
367  std::bind(&hotkey_base::bindings_equal, _1, (item))),
368  hotkeys_.end());
369  }
370 
371  hotkeys_.push_back(item);
372 
373 }
374 
375 void clear_hotkeys(const std::string& command)
376 {
377  for (hotkey::hotkey_ptr item : hotkeys_) {
378  if (item->get_command() == command)
379  {
380  if (item->is_default())
381  item->disable();
382  else
383  item->clear();
384  }
385  }
386 }
387 
389 {
390  hotkeys_.clear();
391 }
392 
393 const hotkey_ptr get_hotkey(const SDL_Event &event)
394 {
395  for (hotkey_ptr item : hotkeys_) {
396  if (item->matches(event)) {
397  return item;
398  }
399  }
400  return hotkey_ptr(new hotkey_void());
401 }
402 
403 void load_hotkeys(const config& cfg, bool set_as_default)
404 {
405  for (const config &hk : cfg.child_range("hotkey")) {
406 
407  hotkey_ptr item = load_from_config(hk);
408  if (!set_as_default) {
409  item->unset_default();
410  }
411 
412  if (!item->null()) {
413  add_hotkey(item);
414  }
415  }
416 
417  if (set_as_default) {
418  default_hotkey_cfg_ = cfg;
419  }
420 }
421 
423 {
424  hotkeys_.clear();
425 
426  if (!default_hotkey_cfg_.empty()) {
427  load_hotkeys(default_hotkey_cfg_, true);
428  } else {
429  ERR_G<< "no default hotkeys set yet; all hotkeys are now unassigned!" << std::endl;
430  }
431 }
432 
434 {
435  return hotkeys_;
436 }
437 
439 {
440  cfg.clear_children("hotkey");
441 
442  for (hotkey_ptr item : hotkeys_) {
443  if ((!item->is_default() && item->active()) ||
444  (item->is_default() && item->is_disabled())) {
445  item->save(cfg.add_child("hotkey"));
446  }
447  }
448 }
449 
451 {
452  // Names are used in places like the hot-key preferences menu
453  std::vector<std::string> names;
454  for (const hotkey::hotkey_ptr item : hotkeys_) {
455  if (item->get_command() == id && !item->null() && !item->is_disabled()) {
456  names.push_back(item->get_name());
457  }
458  }
459 
460  // These are hard-coded, non-rebindable hotkeys
461  if (id == "quit") {
462  names.push_back("escape");
463  }
464  else if (id == "quit-to-desktop") {
465 #ifdef __APPLE__
466  names.push_back("cmd+q");
467 #else
468  names.push_back("alt+F4");
469 #endif
470  }
471 
472  return boost::algorithm::join(names, ", ");
473 }
474 
475 }
void save(config &cfg) const
Save the hotkey into the configuration object.
child_itors child_range(const std::string &key)
Definition: config.cpp:613
static unsigned int sdl_get_mods()
Definition: hotkey_item.cpp:48
hotkey_ptr load_from_config(const config &cfg)
Create and instantiate a hotkey from a config element.
boost::shared_ptr< hotkey_keyboard > hotkey_keyboard_ptr
Definition: hotkey_item.hpp:35
void save_hotkeys(config &cfg)
Save the non-default hotkeys to the config.
scope
Available hotkey scopes.
bool ends_with(const std::string &str, const std::string &suffix)
bool matches(const SDL_Event &event) const
Used to evaluate whether:
virtual bool bindings_equal_helper(hotkey_ptr other) const =0
This is invoked by hotkey_base::bindings_equal as a helper for the concrete classes.
virtual const std::string get_name_helper() const
This is invoked by hotkey_base::get_name and must be implemented by subclasses.
static lg::log_domain log_config("config")
void del_hotkey(hotkey_ptr item)
Remove a hotkey from the list of hotkeys.
void clear_hotkeys(const std::string &command)
Unset the command bindings for all hotkeys matching the command.
bool is_scope_active(hk_scopes s)
bool has_hotkey_item(const std::string &command)
virtual bool matches_helper(const SDL_Event &event) const
This is invoked by hotkey_base::matches as a helper for the concrete classes.
virtual void save_helper(config &cfg) const
void clear_children(const std::string &key)
Definition: config.cpp:820
bool empty() const
Definition: config.cpp:1105
Definitions for the interface to Wesnoth Markup Language (WML).
virtual bool matches_helper(const SDL_Event &event) const =0
This is invoked by hotkey_base::matches as a helper for the concrete classes.
void set_active_scopes(hk_scopes s)
Keyboard shortcuts for game actions.
#define ERR_G
Definition: hotkey_item.cpp:38
const hotkey_list & get_hotkeys()
Returns the list of hotkeys.
std::bitset< SCOPE_COUNT > hk_scopes
virtual void save_helper(config &cfg) const =0
const hotkey_ptr get_hotkey(const SDL_Event &event)
Iterate through the list of hotkeys and return a hotkey that matches the SDL_Event and the current ke...
config & add_child(const std::string &key)
Definition: config.cpp:743
hotkey_ptr create_hotkey(const std::string &id, SDL_Event &event)
Create a new hotkey item for a command from an SDL_Event.
void add_hotkey(const hotkey_ptr item)
Add a hotkey to the list of hotkeys.
This class is used to return non-valid results in order to save other people from null checks...
const hotkey::hk_scopes scope
The visibility scope of the command.
std::vector< hotkey::hotkey_ptr > hotkey_list
Definition: hotkey_item.hpp:37
GLuint const GLuint * names
Definition: glew.h:2552
std::string join(T const &v, const std::string &s=",")
Generates a new string joining container items in a list.
bool active() const
virtual const std::string get_name_helper() const =0
This is invoked by hotkey_base::get_name and must be implemented by subclasses.
virtual bool matches_helper(const SDL_Event &event) const
This is invoked by hotkey_base::matches as a helper for the concrete classes.
boost::shared_ptr< hotkey_mouse > hotkey_mouse_ptr
Definition: hotkey_item.hpp:34
hotkey_list hotkeys_
Definition: hotkey_item.cpp:45
std::string get_names(std::string id)
Returns a comma-separated string of hotkey names.
boost::shared_ptr< hotkey_base > hotkey_ptr
Definition: hotkey_item.hpp:32
void reset_default_hotkeys()
Reset all hotkeys to the defaults.
virtual const std::string get_name_helper() const
This is invoked by hotkey_base::get_name and must be implemented by subclasses.
config default_hotkey_cfg_
Definition: hotkey_item.cpp:46
This class is responsible for handling mouse button presses.
const std::string & get_command() const
Returns the string name of the HOTKEY_COMMAND.
Definition: hotkey_item.hpp:70
This is the base class for hotkey event matching.
Definition: hotkey_item.hpp:43
virtual bool bindings_equal_helper(hotkey_ptr other) const
This is invoked by hotkey_base::bindings_equal as a helper for the concrete classes.
cl_event event
Definition: glew.h:3070
void load_hotkeys(const config &cfg, bool set_as_default)
Iterates through all hotkeys present in the config struct and creates and adds them to the hotkey lis...
This class is responsible for handling keys, not modifiers.
virtual bool bindings_equal_helper(hotkey_ptr other) const
This is invoked by hotkey_base::bindings_equal as a helper for the concrete classes.
Standard logging facilities (interface).
virtual bool bindings_equal(hotkey_ptr other)
Checks whether the hotkey bindings and scope are equal.
const std::string remove
remove directive
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:83
virtual void save_helper(config &cfg) const
const hotkey_command & get_hotkey_command(const std::string &command)
returns the hotkey_command with the given name
GLsizei const GLcharARB ** string
Definition: glew.h:4503
bool is_disabled() const
const std::string get_name() const
Return "name" of hotkey.
Definition: hotkey_item.cpp:73