The Battle for Wesnoth  1.13.11+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
controller_base.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2006 - 2018 by Joerg Hinrichs <joerg.hinrichs@alice-dsl.de>
3  wesnoth playlevel Copyright (C) 2003 by David White <dave@whitevine.net>
4  Part of the Battle for Wesnoth Project http://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 #include "controller_base.hpp"
17 
18 #include "display.hpp"
19 #include "events.hpp"
22 #include "log.hpp"
23 #include "map/map.hpp"
24 #include "mouse_handler_base.hpp"
25 #include "preferences/game.hpp"
27 #include "show_dialog.hpp" //gui::in_dialog
28 #include "soundsource.hpp"
29 static lg::log_domain log_display("display");
30 #define ERR_DP LOG_STREAM(err, log_display)
31 
33  : game_config_(game_config)
34  , key_()
35  , scrolling_(false)
36  , scroll_up_(false)
37  , scroll_down_(false)
38  , scroll_left_(false)
39  , scroll_right_(false)
40  , joystick_manager_()
41 {
42 }
43 
45 {
46 }
47 
48 void controller_base::handle_event(const SDL_Event& event)
49 {
50  if(gui::in_dialog()) {
51  return;
52  }
53 
54  static const hotkey::hotkey_command& quit_hotkey
56 
58 
59  switch(event.type) {
60  case SDL_TEXTINPUT:
61  if(have_keyboard_focus()) {
63  }
64  break;
65 
66  case SDL_TEXTEDITING:
67  if(have_keyboard_focus()) {
68  SDL_Event evt = event;
69  evt.type = SDL_TEXTINPUT;
71  SDL_StopTextInput();
72  SDL_StartTextInput();
73  }
74  break;
75 
76  case SDL_KEYDOWN:
77  // Detect key press events, unless there something that has keyboard focus
78  // in which case the key press events should go only to it.
79  if(have_keyboard_focus()) {
80  if(event.key.keysym.sym == SDLK_ESCAPE) {
82  break;
83  }
84 
85  process_keydown_event(event);
87  process_keyup_event(event);
88  } else {
90  }
91  break;
92 
93  case SDL_KEYUP:
94  process_keyup_event(event);
96  break;
97 
98  case SDL_JOYBUTTONDOWN:
99  process_keydown_event(event);
101  break;
102 
103  case SDL_JOYHATMOTION:
104  process_keydown_event(event);
106  break;
107 
108  case SDL_MOUSEMOTION:
109  // Ignore old mouse motion events in the event queue
110  SDL_Event new_event;
111  if(SDL_PeepEvents(&new_event, 1, SDL_GETEVENT, SDL_MOUSEMOTION, SDL_MOUSEMOTION) > 0) {
112  while(SDL_PeepEvents(&new_event, 1, SDL_GETEVENT, SDL_MOUSEMOTION, SDL_MOUSEMOTION) > 0) {
113  };
114  mh_base.mouse_motion_event(new_event.motion, is_browsing());
115  } else {
116  mh_base.mouse_motion_event(event.motion, is_browsing());
117  }
118  break;
119 
120  case SDL_MOUSEBUTTONDOWN:
121  process_keydown_event(event);
122  mh_base.mouse_press(event.button, is_browsing());
124  break;
125 
126  case SDL_MOUSEBUTTONUP:
127  mh_base.mouse_press(event.button, is_browsing());
128  if(mh_base.get_show_menu()) {
129  show_menu(get_display().get_theme().context_menu()->items(), event.button.x, event.button.y, true,
130  get_display());
131  }
132  break;
133 
134  case SDL_MOUSEWHEEL:
135 #if defined(_WIN32) || defined(__APPLE__)
136  mh_base.mouse_wheel(-event.wheel.x, event.wheel.y, is_browsing());
137 #else
138  mh_base.mouse_wheel(event.wheel.x, event.wheel.y, is_browsing());
139 #endif
140  break;
141 
142  default:
143  break;
144  }
145 }
146 
148 {
149  return true;
150 }
151 
152 bool controller_base::handle_scroll(int mousex, int mousey, int mouse_flags, double x_axis, double y_axis)
153 {
154  const bool mouse_in_window =
155  CVideo::get_singleton().window_has_flags(SDL_WINDOW_MOUSE_FOCUS)
156  || preferences::get("scroll_when_mouse_outside", true);
157 
159  int dx = 0, dy = 0;
160 
161  int scroll_threshold = preferences::mouse_scroll_enabled()
163  : 0;
164 
165  for(const theme::menu& m : get_display().get_theme().menus()) {
166  if(sdl::point_in_rect(mousex, mousey, m.get_location())) {
167  scroll_threshold = 0;
168  }
169  }
170 
171  // Apply keyboard scrolling
172  dy -= scroll_up_ * scroll_speed;
173  dy += scroll_down_ * scroll_speed;
174  dx -= scroll_left_ * scroll_speed;
175  dx += scroll_right_ * scroll_speed;
176 
177  // Scroll if mouse is placed near the edge of the screen
178  if(mouse_in_window) {
179  if(mousey < scroll_threshold) {
180  dy -= scroll_speed;
181  }
182 
183  if(mousey > get_display().video().get_height() - scroll_threshold) {
184  dy += scroll_speed;
185  }
186 
187  if(mousex < scroll_threshold) {
188  dx -= scroll_speed;
189  }
190 
191  if(mousex > get_display().video().get_width() - scroll_threshold) {
192  dx += scroll_speed;
193  }
194  }
195 
197 
198  // Scroll with middle-mouse if enabled
199  if((mouse_flags & SDL_BUTTON_MMASK) != 0 && preferences::middle_click_scrolls()) {
200  const SDL_Point original_loc = mh_base.get_scroll_start();
201 
202  if(mh_base.scroll_started()) {
203  const SDL_Rect& rect = get_display().map_outside_area();
204 
205  if(sdl::point_in_rect(mousex, mousey, rect) && mh_base.scroll_started()) {
206  // Scroll speed is proportional from the distance from the first
207  // middle click and scrolling speed preference.
208  const double speed = 0.04 * sqrt(static_cast<double>(scroll_speed));
209  const double snap_dist = 16; // Snap to horizontal/vertical scrolling
210  const double x_diff = (mousex - original_loc.x);
211  const double y_diff = (mousey - original_loc.y);
212 
213  if(std::fabs(x_diff) > snap_dist || std::fabs(y_diff) <= snap_dist) {
214  dx += speed * x_diff;
215  }
216 
217  if(std::fabs(y_diff) > snap_dist || std::fabs(x_diff) <= snap_dist) {
218  dy += speed * y_diff;
219  }
220  }
221  } else { // Event may fire mouse down out of order with respect to initial click
222  mh_base.set_scroll_start(mousex, mousey);
223  }
224  }
225 
226  // scroll with joystick
227  dx += round_double(x_axis * scroll_speed);
228  dy += round_double(y_axis * scroll_speed);
229 
230  return get_display().scroll(dx, dy);
231 }
232 
233 void controller_base::play_slice(bool is_delay_enabled)
234 {
235  CKey key;
236 
238  l->play_slice();
239  }
240 
241  events::pump();
244 
245  // Update sound sources before scrolling
247  l->update();
248  }
249 
250  const theme::menu* const m = get_display().menu_pressed();
251  if(m != nullptr) {
252  const SDL_Rect& menu_loc = m->location(get_display().video().screen_area());
253  show_menu(m->items(), menu_loc.x + 1, menu_loc.y + menu_loc.h + 1, false, get_display());
254 
255  return;
256  }
257 
258  const theme::action* const a = get_display().action_pressed();
259  if(a != nullptr) {
260  const SDL_Rect& action_loc = a->location(get_display().video().screen_area());
261  execute_action(a->items(), action_loc.x + 1, action_loc.y + action_loc.h + 1, false);
262 
263  return;
264  }
265 
266  auto str_vec = additional_actions_pressed();
267  if(!str_vec.empty()) {
268  execute_action(str_vec, 0, 0, false);
269  return;
270  }
271 
272  bool was_scrolling = scrolling_;
273 
274  std::pair<double, double> values = joystick_manager_.get_scroll_axis_pair();
275  const double joystickx = values.first;
276  const double joysticky = values.second;
277 
278  int mousex, mousey;
279  uint8_t mouse_flags = SDL_GetMouseState(&mousex, &mousey);
280 
281  // TODO enable after an axis choosing mechanism is implemented
282 #if 0
283  std::pair<double, double> values = joystick_manager_.get_mouse_axis_pair();
284  mousex += values.first * 10;
285  mousey += values.second * 10;
286  SDL_WarpMouse(mousex, mousey);
287 #endif
288 
289  scrolling_ = handle_scroll(mousex, mousey, mouse_flags, joystickx, joysticky);
290 
291  map_location highlighted_hex = get_display().mouseover_hex();
292 
293  // TODO: enable when the relative cursor movement is implemented well enough
294 #if 0
295  const map_location& selected_hex = get_display().selected_hex();
296 
297  if (selected_hex != map_location::null_location()) {
298  if (joystick_manager_.next_highlighted_hex(highlighted_hex, selected_hex)) {
299  get_mouse_handler_base().mouse_motion(0,0, true, true, highlighted_hex);
300  get_display().scroll_to_tile(highlighted_hex, display::ONSCREEN_WARP, false, true);
301  scrolling_ = true;
302  }
303  } else
304 #endif
305 
306  if(joystick_manager_.update_highlighted_hex(highlighted_hex) && get_display().get_map().on_board(highlighted_hex)) {
307  get_mouse_handler_base().mouse_motion(0, 0, true, true, highlighted_hex);
308  get_display().scroll_to_tile(highlighted_hex, display::ONSCREEN_WARP, false, true);
309  scrolling_ = true;
310  }
311 
312  // be nice when window is not visible // NOTE should be handled by display instead, to only disable drawing
313  if(is_delay_enabled && !CVideo::get_singleton().window_has_flags(SDL_WINDOW_SHOWN)) {
314  CVideo::delay(200);
315  }
316 
317  // Scrolling ended, update the cursor and the brightened hex
318  if(!scrolling_ && was_scrolling) {
319  get_mouse_handler_base().mouse_update(is_browsing(), highlighted_hex);
320  }
321 }
322 
324  const std::vector<config>& items_arg, int xloc, int yloc, bool context_menu, display& disp)
325 {
327  if(!cmd_exec) {
328  return;
329  }
330 
331  std::vector<config> items;
332  for(const config& c : items_arg) {
333  const std::string& id = c["id"];
335 
336  if(cmd_exec->can_execute_command(command) && (!context_menu || in_context_menu(command.id))) {
337  items.emplace_back(config{"id", id});
338  }
339  }
340 
341  if(items.empty()) {
342  return;
343  }
344 
345  cmd_exec->show_menu(items, xloc, yloc, context_menu, disp);
346 }
347 
348 void controller_base::execute_action(const std::vector<std::string>& items_arg, int xloc, int yloc, bool context_menu)
349 {
351  if(!cmd_exec) {
352  return;
353  }
354 
355  std::vector<std::string> items;
356  for(const std::string& item : items_arg) {
357  const hotkey::hotkey_command& command = hotkey::get_hotkey_command(item);
358  if(cmd_exec->can_execute_command(command)) {
359  items.push_back(item);
360  }
361  }
362 
363  if(items.empty()) {
364  return;
365  }
366 
367  cmd_exec->execute_action(items, xloc, yloc, context_menu, get_display());
368 }
369 
371 {
372  return true;
373 }
374 
376 {
377  if(theme_name.empty()) {
378  theme_name = preferences::theme();
379  }
380 
381  if(const config& c = game_config.find_child("theme", "id", theme_name)) {
382  return c;
383  }
384 
385  ERR_DP << "Theme '" << theme_name << "' not found. Trying the default theme." << std::endl;
386 
387  if(const config& c = game_config.find_child("theme", "id", "Default")) {
388  return c;
389  }
390 
391  ERR_DP << "Default theme not found." << std::endl;
392 
393  static config empty;
394  return empty;
395 }
static const config & get_theme(const config &game_config, std::string theme_name)
joystick_manager joystick_manager_
virtual plugins_context * get_plugins_context()
Get (optionally) a plugins context a derived class uses.
virtual bool is_browsing() const
virtual void process_keyup_event(const SDL_Event &)
Process keyup (always).
std::vector< char_t > string
HOTKEY_COMMAND id
the names are strange: the "hotkey::HOTKEY_COMMAND" is named id, and the string to identify the objec...
void set_scroll_start(int x, int y)
Called when the middle click scrolling.
int mouse_scroll_threshold()
Gets the threshold for when to scroll.
Definition: general.cpp:890
static lg::log_domain log_display("display")
virtual void show_menu(const std::vector< config > &items_arg, int xloc, int yloc, bool context_menu, display &disp)
config & find_child(config_key_type key, const std::string &name, const std::string &value)
Returns the first child of tag key with a name attribute containing value.
Definition: config.cpp:780
#define a
const std::vector< std::string > & items() const
Definition: theme.hpp:177
bool in_dialog()
Definition: show_dialog.cpp:56
int scroll_speed()
Definition: general.cpp:861
bool update_highlighted_hex(map_location &highlighted_hex)
Used for absolute movement of the cursor.
Definition: joystick.cpp:300
Stores all information related to functions that can be bound to hotkeys.
void play_slice(bool is_delay_enabled=true)
static CVideo & get_singleton()
Definition: video.hpp:43
void mbutton_event(const SDL_Event &event, command_executor *executor)
virtual display & get_display()=0
Get a reference to a display member a derived class uses.
const std::vector< std::string > items
virtual void mouse_motion(int x, int y, const bool browse, bool update=false, map_location new_loc=map_location::null_location())=0
Called when a mouse motion event takes place.
void mouse_motion_event(const SDL_MouseMotionEvent &event, const bool browse)
controller_base framework: controller_base is roughly analogous to a "dialog" class in a GUI toolkit ...
virtual bool have_keyboard_focus()
Derived classes should override this to return false when arrow keys should not scroll the map...
controller_base(const config &game_config)
std::pair< double, double > get_scroll_axis_pair()
Definition: joystick.cpp:155
SDL_Rect & location(const SDL_Rect &screen) const
Definition: theme.cpp:313
virtual bool can_execute_command(const hotkey_command &command, int index=-1) const =0
virtual std::vector< std::string > additional_actions_pressed()
const theme::action * action_pressed()
Definition: display.cpp:1736
virtual void mouse_press(const SDL_MouseButtonEvent &event, const bool browse)
virtual soundsource::manager * get_soundsource_man()
Get (optionally) a soundsources manager a derived class uses.
std::string get(const std::string &key)
Definition: general.cpp:230
void execute_command(const hotkey_command &command, command_executor *executor, int index, bool press)
const map_location & mouseover_hex() const
Definition: display.hpp:281
void jbutton_event(const SDL_Event &event, command_executor *executor)
const theme::menu * menu_pressed()
Definition: display.cpp:1752
std::string theme()
Definition: game.cpp:820
bool window_has_flags(uint32_t flags) const
Tests whether the given flags are currently set on the SDL window.
Definition: video.cpp:384
int round_double(double d)
Definition: math.hpp:67
int get_width(bool as_pixels=true) const
Returns the window renderer width in pixels or screen coordinates.
Definition: video.cpp:316
virtual void show_menu(const std::vector< config > &items_arg, int xloc, int yloc, bool context_menu, display &gui)
bool handle_scroll(int mousex, int mousey, int mouse_flags, double joystickx, double joysticky)
Handle scrolling by keyboard, joystick and moving mouse near map edges.
map_display and display: classes which take care of displaying the map and game-data on the screen...
virtual ~controller_base()
static const map_location & null_location()
Definition: location.hpp:224
void raise_draw_event()
Definition: events.cpp:650
void jhat_event(const SDL_Event &event, command_executor *executor)
virtual events::mouse_handler_base & get_mouse_handler_base()=0
Get a reference to a mouse handler member a derived class uses.
void pump()
Definition: events.cpp:409
bool point_in_rect(int x, int y, const SDL_Rect &rect)
Tests whether a point is inside a rectangle.
Definition: rect.cpp:22
virtual void execute_action(const std::vector< std::string > &items_arg, int xloc, int yloc, bool context_menu)
Encapsulates the map of the game.
Definition: location.hpp:42
virtual void process_keydown_event(const SDL_Event &)
Process keydown (always).
void raise_process_event()
Definition: events.cpp:627
virtual void process_focus_keydown_event(const SDL_Event &)
Process keydown (only when the general map display does not have focus).
void scroll_to_tile(const map_location &loc, SCROLL_TYPE scroll_type=ONSCREEN, bool check_fogged=true, bool force=true)
Scroll such that location loc is on-screen.
Definition: display.cpp:2196
Game configuration data as global variables.
Definition: build_info.cpp:53
bool middle_click_scrolls()
Definition: general.cpp:875
void execute_action(const std::vector< std::string > &items_arg, int xloc, int yloc, bool context_menu, display &gui)
const std::vector< config > & items() const
Definition: theme.hpp:230
virtual void mouse_wheel(int xscroll, int yscroll, bool browse)
Called when scrolling with the mouse wheel.
void key_event(const SDL_Event &event, command_executor *executor)
bool on_board(const map_location &loc) const
Tell if a location is on the map.
Definition: map.cpp:369
virtual hotkey::command_executor * get_hotkey_command_executor()
Optionally get a command executor to handle context menu events.
bool mouse_scroll_enabled()
Definition: general.cpp:880
int get_height(bool as_pixels=true) const
Returns the window renderer height in pixels or in screen coordinates.
Definition: video.cpp:321
const gamemap & get_map() const
Definition: display.hpp:91
Standard logging facilities (interface).
const SDL_Rect & map_outside_area() const
Returns the available area for a map, this may differ from the above.
Definition: display.hpp:236
CVideo & video()
Gets the underlying screen object.
Definition: display.hpp:195
static void delay(unsigned int milliseconds)
Waits a given number of milliseconds before returning.
Definition: video.cpp:326
const SDL_Point get_scroll_start() const
void handle_event(const SDL_Event &event)
Process mouse- and keypress-events from SDL.
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:93
Class that keeps track of all the keys on the keyboard.
Definition: key.hpp:27
mock_char c
const hotkey_command & get_hotkey_command(const std::string &command)
returns the hotkey_command with the given name
void mouse_update(const bool browse, map_location loc)
Update the mouse with a fake mouse motion.
std::pair< double, double > get_mouse_axis_pair()
TODO fendrin.
Definition: joystick.cpp:121
bool scroll(int xmov, int ymov, bool force=false)
Scrolls the display by xmov,ymov pixels.
Definition: display.cpp:1916
#define ERR_DP
virtual bool in_context_menu(hotkey::HOTKEY_COMMAND command) const
const map_location & selected_hex() const
Definition: display.hpp:280
static const hotkey_command & get_command_by_command(HOTKEY_COMMAND command)
the execute_command argument was changed from HOTKEY_COMMAND to hotkey_command, to be able to call it...