The Battle for Wesnoth  1.13.4+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 - 2016 by Joerg Hinrichs <[email protected]>
3  wesnoth playlevel Copyright (C) 2003 by David White <[email protected]>
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 "dialogs.hpp"
19 #include "display.hpp"
20 #include "events.hpp"
21 #include "game_preferences.hpp"
24 #include "log.hpp"
25 #include "mouse_handler_base.hpp"
27 #include "soundsource.hpp"
28 
29 static lg::log_domain log_display("display");
30 #define ERR_DP LOG_STREAM(err, log_display)
31 
33  const config& game_config, CVideo& /*video*/)
34  : game_config_(game_config)
35  , key_()
36  , scrolling_(false)
37  , scroll_up_(false)
38  , scroll_down_(false)
39  , scroll_left_(false)
40  , scroll_right_(false)
41  , joystick_manager_()
42 {
43 }
44 
46 {
47 }
48 
49 void controller_base::handle_event(const SDL_Event& event)
50 {
51  if(gui::in_dialog()) {
52  return;
53  }
55 
56  switch(event.type) {
57  case SDL_KEYDOWN:
58  // Detect key press events, unless there something that has keyboard focus
59  // in which case the key press events should go only to it.
60  if(have_keyboard_focus()) {
61  if(event.key.keysym.sym == SDLK_ESCAPE) {
63  break;
64  }
65 
66  process_keydown_event(event);
68  process_keyup_event(event);
69  } else {
71  }
72  break;
73  case SDL_KEYUP:
74  process_keyup_event(event);
76  break;
77  case SDL_JOYBUTTONDOWN:
78  process_keydown_event(event);
80  break;
81  case SDL_JOYHATMOTION:
82  process_keydown_event(event);
84  break;
85  case SDL_MOUSEMOTION:
86  // Ignore old mouse motion events in the event queue
87  SDL_Event new_event;
88  if(SDL_PeepEvents(&new_event,1,SDL_GETEVENT,
89  SDL_EVENTMASK(SDL_MOUSEMOTION)) > 0) {
90  while(SDL_PeepEvents(&new_event,1,SDL_GETEVENT,
91  SDL_EVENTMASK(SDL_MOUSEMOTION)) > 0) {};
93  } else {
95  }
96  break;
97  case SDL_MOUSEBUTTONDOWN:
98  process_keydown_event(event);
100  if (get_mouse_handler_base().get_show_menu()){
101  show_menu(get_display().get_theme().context_menu()->items(),event.button.x,event.button.y,true, get_display());
102  }
104  break;
105  case SDL_MOUSEBUTTONUP:
107  if (get_mouse_handler_base().get_show_menu()){
108  show_menu(get_display().get_theme().context_menu()->items(),event.button.x,event.button.y,true, get_display());
109  }
110  break;
111  case SDL_MOUSEWHEEL:
112  get_mouse_handler_base().mouse_wheel(event.wheel.x, event.wheel.y, is_browsing());
113  break;
114  default:
115  break;
116  }
117 }
118 
120 {
121  return true;
122 }
123 
124 void controller_base::process_focus_keydown_event(const SDL_Event& /*event*/) {
125  //no action by default
126 }
127 
128 void controller_base::process_keydown_event(const SDL_Event& /*event*/) {
129  //no action by default
130 }
131 
132 void controller_base::process_keyup_event(const SDL_Event& /*event*/) {
133  //no action by default
134 }
135 
136 bool controller_base::handle_scroll(int mousex, int mousey, int mouse_flags, double x_axis, double y_axis)
137 {
138  bool mouse_in_window = (SDL_GetAppState() & SDL_APPMOUSEFOCUS) != 0
139  || preferences::get("scroll_when_mouse_outside", true);
141  int dx = 0, dy = 0;
142  int scroll_threshold = (preferences::mouse_scroll_enabled())
144  for (const theme::menu& m : get_display().get_theme().menus()) {
145  if (sdl::point_in_rect(mousex, mousey, m.get_location())) {
146  scroll_threshold = 0;
147  }
148  }
149 
150  // apply keyboard scrolling
151  dy -= scroll_up_ * scroll_speed;
152  dy += scroll_down_ * scroll_speed;
153  dx -= scroll_left_ * scroll_speed;
154  dx += scroll_right_ * scroll_speed;
155 
156  // scroll if mouse is placed near the edge of the screen
157  if (mouse_in_window) {
158  if (mousey < scroll_threshold) {
159  dy -= scroll_speed;
160  }
161  if (mousey > get_display().h() - scroll_threshold) {
162  dy += scroll_speed;
163  }
164  if (mousex < scroll_threshold) {
165  dx -= scroll_speed;
166  }
167  if (mousex > get_display().w() - scroll_threshold) {
168  dx += scroll_speed;
169  }
170  }
171 
172  // scroll with middle-mouse if enabled
173  if ((mouse_flags & SDL_BUTTON_MMASK) != 0 && preferences::middle_click_scrolls()) {
174  const map_location original_loc = get_mouse_handler_base().get_scroll_start();
175 
176  if (get_mouse_handler_base().scroll_started()) {
177  const SDL_Rect& rect = get_display().map_outside_area();
178  if (sdl::point_in_rect(mousex, mousey,rect) &&
179  get_mouse_handler_base().scroll_started()) {
180  // Scroll speed is proportional from the distance from the first
181  // middle click and scrolling speed preference.
182  const double speed = 0.04 * sqrt(static_cast<double>(scroll_speed));
183  const double snap_dist = 16; // Snap to horizontal/vertical scrolling
184  const double x_diff = (mousex - original_loc.x);
185  const double y_diff = (mousey - original_loc.y);
186 
187  if (fabs(x_diff) > snap_dist || fabs(y_diff) <= snap_dist) dx += speed * x_diff;
188  if (fabs(y_diff) > snap_dist || fabs(x_diff) <= snap_dist) dy += speed * y_diff;
189  }
190  }
191  else { // Event may fire mouse down out of order with respect to initial click
192  get_mouse_handler_base().set_scroll_start(mousex, mousey);
193  }
194  }
195 
196  // scroll with joystick
197  dx += round_double( x_axis * scroll_speed);
198  dy += round_double( y_axis * scroll_speed);
199 
200  return get_display().scroll(dx, dy);
201 }
202 
203 void controller_base::play_slice(bool is_delay_enabled)
204 {
205  CKey key;
206 
208  l->play_slice();
209  }
210 
211  events::pump();
214 
215  // Update sound sources before scrolling
217  l->update();
218  }
219 
220  const theme::menu* const m = get_display().menu_pressed();
221  if(m != nullptr) {
222  const SDL_Rect& menu_loc = m->location(get_display().screen_area());
223  show_menu(m->items(),menu_loc.x+1,menu_loc.y + menu_loc.h + 1,false, get_display());
224 
225  return;
226  }
227  const theme::action* const a = get_display().action_pressed();
228  if(a != nullptr) {
229  const SDL_Rect& action_loc = a->location(get_display().screen_area());
230  execute_action(a->items(), action_loc.x+1, action_loc.y + action_loc.h + 1,false);
231 
232  return;
233  }
234  auto str_vec = additional_actions_pressed();
235  if (!str_vec.empty()) {
236  execute_action(str_vec, 0, 0, false);
237  return;
238  }
239 
240  bool was_scrolling = scrolling_;
241 
242  std::pair<double, double> values = joystick_manager_.get_scroll_axis_pair();
243  const double joystickx = values.first;
244  const double joysticky = values.second;
245 
246  int mousex, mousey;
247  Uint8 mouse_flags = SDL_GetMouseState(&mousex, &mousey);
248 
249  /* TODO fendrin enable after an axis choosing mechanism is implemented
250  std::pair<double, double> values = joystick_manager_.get_mouse_axis_pair();
251  mousex += values.first * 10;
252  mousey += values.second * 10;
253  SDL_WarpMouse(mousex, mousey);
254  */
255  scrolling_ = handle_scroll(mousex, mousey, mouse_flags, joystickx, joysticky);
256 
257  map_location highlighted_hex = get_display().mouseover_hex();
258 
259  /* TODO fendrin enable when the relative cursor movement is implemented well enough
260  const map_location& selected_hex = get_display().selected_hex();
261 
262  if (selected_hex != map_location::null_location()) {
263  if (joystick_manager_.next_highlighted_hex(highlighted_hex, selected_hex)) {
264  get_mouse_handler_base().mouse_motion(0,0, true, true, highlighted_hex);
265  get_display().scroll_to_tile(highlighted_hex, display::ONSCREEN_WARP, false, true);
266  scrolling_ = true;
267  }
268  } else */
269 
270  if (joystick_manager_.update_highlighted_hex(highlighted_hex)
271  && get_display().get_map().on_board(highlighted_hex)) {
272  get_mouse_handler_base().mouse_motion(0,0, true, true, highlighted_hex);
273  get_display().scroll_to_tile(highlighted_hex, display::ONSCREEN_WARP, false, true);
274  scrolling_ = true;
275  }
276 
277  get_display().draw();
278 
279  // be nice when window is not visible
280  // NOTE should be handled by display instead, to only disable drawing
281  if (is_delay_enabled && (SDL_GetAppState() & SDL_APPACTIVE) == 0) {
282  CVideo::delay(200);
283  }
284 
285  if (!scrolling_ && was_scrolling) {
286  // scrolling ended, update the cursor and the brightened hex
287  get_mouse_handler_base().mouse_update(is_browsing(), highlighted_hex);
288  }
289 }
290 
291 void controller_base::show_menu(const std::vector<std::string>& items_arg, int xloc, int yloc, bool context_menu, display& disp)
292 {
294  if (!cmd_exec) {
295  return;
296  }
297 
298  std::vector<std::string> items = items_arg;
299  std::vector<std::string>::iterator i = items.begin();
300  while(i != items.end()) {
302  if(!cmd_exec->can_execute_command(command)
303  || (context_menu && !in_context_menu(command.id))) {
304  i = items.erase(i);
305  continue;
306  }
307  ++i;
308  }
309  if(items.empty())
310  return;
311  cmd_exec->show_menu(items, xloc, yloc, context_menu, disp);
312 }
313 
314 void controller_base::execute_action(const std::vector<std::string>& items_arg, int xloc, int yloc, bool context_menu)
315 {
317  if (!cmd_exec) {
318  return;
319  }
320 
321  std::vector<std::string> items;
322  for (const std::string& item : items_arg) {
323 
324  const hotkey::hotkey_command& command = hotkey::get_hotkey_command(item);
325  if(cmd_exec->can_execute_command(command))
326  items.push_back(item);
327  }
328 
329  if(items.empty())
330  return;
331  cmd_exec->execute_action(items, xloc, yloc, context_menu, get_display());
332 }
333 
334 
335 
337 {
338  return true;
339 }
340 
342 {
343  if (theme_name.empty()) theme_name = preferences::theme();
344 
345  if (const config &c = game_config.find_child("theme", "id", theme_name))
346  return c;
347 
348  ERR_DP << "Theme '" << theme_name << "' not found. Trying the default theme." << std::endl;
349 
350  if (const config &c = game_config.find_child("theme", "id", "Default"))
351  return c;
352 
353  ERR_DP << "Default theme not found." << std::endl;
354 
355  static config empty;
356  return empty;
357 }
358 
360 {
361  scroll_up_ = on;
362 }
363 
365 {
366  scroll_down_ = on;
367 }
368 
370 {
371  scroll_left_ = on;
372 }
373 
375 {
376  scroll_right_ = on;
377 }
static const config & get_theme(const config &game_config, std::string theme_name)
const map_location get_scroll_start()
joystick_manager joystick_manager_
virtual plugins_context * get_plugins_context()
Get (optionally) a plugins context a derived class uses.
virtual bool is_browsing() const
void set_scroll_up(bool on)
void set_scroll_down(bool on)
virtual void show_menu(const std::vector< std::string > &items_arg, int xloc, int yloc, bool context_menu, display &disp)
theme & get_theme()
Definition: display.hpp:385
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.
static lg::log_domain log_display("display")
virtual void process_focus_keydown_event(const SDL_Event &event)
Process keydown (only when the general map display does not have focus).
const GLfloat * c
Definition: glew.h:12741
const std::vector< std::string > & items() const
Definition: theme.hpp:194
Definition: video.hpp:58
bool in_dialog()
Definition: show_dialog.cpp:57
int scroll_speed()
bool update_highlighted_hex(map_location &highlighted_hex)
Used for absolute movement of the cursor.
Definition: joystick.cpp:299
Stores all information related to functions that can be bound to hotkeys.
const std::vector< menu > & menus() const
Definition: theme.hpp:263
void play_slice(bool is_delay_enabled=true)
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.
virtual void draw()
Draws invalidated items.
Definition: display.cpp:2706
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 void process_keydown_event(const SDL_Event &event)
Process keydown (always).
virtual bool have_keyboard_focus()
Derived classes should override this to return false when arrow keys should not scroll the map...
int round_double(double d)
Definition: util.hpp:67
expression_ptr key_
Definition: formula.cpp:435
std::pair< double, double > get_scroll_axis_pair()
Definition: joystick.cpp:153
SDL_Rect & location(const SDL_Rect &screen) const
Definition: theme.cpp:342
virtual bool can_execute_command(const hotkey_command &command, int index=-1) const =0
GLboolean GLenum GLenum GLvoid * values
Definition: glew.h:3799
GLdouble l
Definition: glew.h:6966
virtual std::vector< std::string > additional_actions_pressed()
SDL_Rect screen_area()
Definition: video.cpp:135
const theme::action * action_pressed()
Definition: display.cpp:1899
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)
void execute_command(const hotkey_command &command, command_executor *executor, int index, bool press)
const map_location & mouseover_hex() const
Definition: display.hpp:293
void jbutton_event(const SDL_Event &event, command_executor *executor)
const theme::menu * menu_pressed()
Definition: display.cpp:1916
std::string theme()
GLboolean GLboolean GLboolean GLboolean a
Definition: glew.h:7319
int w() const
the dimensions of the display.
Definition: display.hpp:220
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...
int h() const
height
Definition: display.hpp:221
virtual ~controller_base()
void raise_draw_event()
Definition: events.cpp:565
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:336
bool point_in_rect(int x, int y, const SDL_Rect &rect)
Tests whether a point is inside a rectangle.
Definition: rect.cpp:47
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:38
#define SDL_APPMOUSEFOCUS
The app has mouse coverage.
Definition: video.hpp:38
void raise_process_event()
Definition: events.cpp:539
#define SDL_APPACTIVE
The application is active.
Definition: video.hpp:40
controller_base(const config &game_config, CVideo &video)
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:2434
Game configuration data as global variables.
Definition: build_info.cpp:38
bool middle_click_scrolls()
void execute_action(const std::vector< std::string > &items_arg, int xloc, int yloc, bool context_menu, display &gui)
void set_scroll_right(bool on)
size_t i
Definition: function.cpp:1057
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:467
#define SDL_EVENTMASK(EVENT)
Definition: compat.hpp:39
virtual hotkey::command_executor * get_hotkey_command_executor()
Get (optionally) a command executor to handle context menu events.
bool mouse_scroll_enabled()
const std::vector< std::string > & items() const
Definition: theme.hpp:247
void set_scroll_left(bool on)
virtual void process_keyup_event(const SDL_Event &event)
Process keyup (always).
const GLdouble * m
Definition: glew.h:6968
const gamemap & get_map() const
Definition: display.hpp:92
cl_event event
Definition: glew.h:3070
const hotkey::HOTKEY_COMMAND id
the names are strange: the "hotkey::HOTKEY_COMMAND" is named id, and the string to identify the objec...
virtual void show_menu(const std::vector< std::string > &items_arg, int xloc, int yloc, bool context_menu, display &gui)
config & find_child(const std::string &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:1010
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:248
#define SDL_GetAppState
Definition: compat.hpp:40
static void delay(unsigned int milliseconds)
Definition: video.cpp:490
void handle_event(const SDL_Event &event)
Process mouse- and keypress-events from SDL.
std::string::const_iterator iterator
Definition: tokenizer.hpp:21
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:83
Class that keeps track of all the keys on the keyboard.
Definition: key.hpp:27
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
bool scroll(int xmov, int ymov, bool force=false)
Scrolls the display by xmov,ymov pixels.
Definition: display.cpp:2206
#define ERR_DP
GLsizei const GLcharARB ** string
Definition: glew.h:4503
virtual bool in_context_menu(hotkey::HOTKEY_COMMAND command) const
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...