1 /*
2  * Copyright 2010-2014 OpenXcom Developers.
3  *
4  * This file is part of OpenXcom.
5  *
6  * OpenXcom 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 3 of the License, or
9  * (at your option) any later version.
10  *
11  * OpenXcom is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with OpenXcom.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 #include "InteractiveSurface.h"
20 #include "Action.h"
21 #include "Options.h"
22 
23 namespace OpenXcom
24 {
25 
26 const SDLKey InteractiveSurface::SDLK_ANY = (SDLKey)-1; // using an unused keycode to represent an "any key"
27 
28 /**
29  * Sets up a blank interactive surface with the specified size and position.
30  * @param width Width in pixels.
31  * @param height Height in pixels.
32  * @param x X position in pixels.
33  * @param y Y position in pixels.
34  */
InteractiveSurface(int width,int height,int x,int y)35 InteractiveSurface::InteractiveSurface(int width, int height, int x, int y) : Surface(width, height, x, y), _buttonsPressed(0), _in(0), _over(0), _out(0), _isHovered(false), _isFocused(true), _listButton(false)
36 {
37 }
38 
39 /**
40  *
41  */
~InteractiveSurface()42 InteractiveSurface::~InteractiveSurface()
43 {
44 }
45 
isButtonHandled(Uint8 button)46 bool InteractiveSurface::isButtonHandled(Uint8 button)
47 {
48 	bool handled = (_click.find(0) != _click.end() ||
49 					_press.find(0) != _press.end() ||
50 					_release.find(0) != _release.end());
51 	if (!handled && button != 0)
52 	{
53 		handled = (_click.find(button) != _click.end() ||
54 				   _press.find(button) != _press.end() ||
55 				   _release.find(button) != _release.end());
56 	}
57 	return handled;
58 }
59 
isButtonPressed(Uint8 button)60 bool InteractiveSurface::isButtonPressed(Uint8 button)
61 {
62 	if (button == 0)
63 	{
64 		return (_buttonsPressed != 0);
65 	}
66 	else
67 	{
68 		return (_buttonsPressed & SDL_BUTTON(button)) != 0;
69 	}
70 }
71 
setButtonPressed(Uint8 button,bool pressed)72 void InteractiveSurface::setButtonPressed(Uint8 button, bool pressed)
73 {
74 	if (pressed)
75 	{
76 		_buttonsPressed = _buttonsPressed | SDL_BUTTON(button);
77 	}
78 	else
79 	{
80 		_buttonsPressed = _buttonsPressed & (!SDL_BUTTON(button));
81 	}
82 }
83 
84 /**
85  * Changes the visibility of the surface. A hidden surface
86  * isn't blitted nor receives events.
87  * @param visible New visibility.
88  */
setVisible(bool visible)89 void InteractiveSurface::setVisible(bool visible)
90 {
91 	Surface::setVisible(visible);
92 	// Unpress button if it was hidden
93 	if (!_visible)
94 	{
95 		unpress(0);
96 	}
97 }
98 
99 /**
100  * Called whenever an action occurs, and processes it to
101  * check if it's relevant to the surface and convert it
102  * into a meaningful interaction like a "click", calling
103  * the respective handlers.
104  * @param action Pointer to an action.
105  * @param state State that the action handlers belong to.
106  */
handle(Action * action,State * state)107 void InteractiveSurface::handle(Action *action, State *state)
108 {
109 	if (!_visible || _hidden)
110 		return;
111 
112 	action->setSender(this);
113 
114 	if (action->getDetails()->type == SDL_MOUSEBUTTONUP || action->getDetails()->type == SDL_MOUSEBUTTONDOWN)
115 	{
116 		action->setMouseAction(action->getDetails()->button.x, action->getDetails()->button.y, getX(), getY());
117 	}
118 	else if (action->getDetails()->type == SDL_MOUSEMOTION)
119 	{
120 		action->setMouseAction(action->getDetails()->motion.x, action->getDetails()->motion.y, getX(), getY());
121 	}
122 
123 	if (action->isMouseAction())
124 	{
125 		if ((action->getAbsoluteXMouse() >= getX() && action->getAbsoluteXMouse() < getX() + getWidth()) &&
126 			(action->getAbsoluteYMouse() >= getY() && action->getAbsoluteYMouse() < getY() + getHeight()))
127 		{
128 			if (!_isHovered)
129 			{
130 				_isHovered = true;
131 				mouseIn(action, state);
132 			}
133 				if (_listButton && action->getDetails()->type == SDL_MOUSEMOTION)
134 				{
135 					_buttonsPressed = SDL_GetMouseState(0, 0);
136 					for (Uint8 i = 1; i <= NUM_BUTTONS; ++i)
137 					{
138 						if (isButtonPressed(i))
139 						{
140 							action->getDetails()->button.button = i;
141 							mousePress(action, state);
142 						}
143 					}
144 				}
145 			mouseOver(action, state);
146 		}
147 		else
148 		{
149 			if (_isHovered)
150 			{
151 				_isHovered = false;
152 				mouseOut(action, state);
153 				if (_listButton && action->getDetails()->type == SDL_MOUSEMOTION)
154 				{
155 					for (Uint8 i = 1; i <= NUM_BUTTONS; ++i)
156 					{
157 						if (isButtonPressed(i))
158 						{
159 							setButtonPressed(i, false);
160 						}
161 						action->getDetails()->button.button = i;
162 						mouseRelease(action, state);
163 					}
164 				}
165 			}
166 		}
167 	}
168 
169 	if (action->getDetails()->type == SDL_MOUSEBUTTONDOWN)
170 	{
171 		if (_isHovered && !isButtonPressed(action->getDetails()->button.button))
172 		{
173 			setButtonPressed(action->getDetails()->button.button, true);
174 			mousePress(action, state);
175 		}
176 	}
177 	else if (action->getDetails()->type == SDL_MOUSEBUTTONUP)
178 	{
179 		if (isButtonPressed(action->getDetails()->button.button))
180 		{
181 			setButtonPressed(action->getDetails()->button.button, false);
182 			mouseRelease(action, state);
183 			if (_isHovered)
184 			{
185 				mouseClick(action, state);
186 			}
187 		}
188 	}
189 
190 	if (_isFocused)
191 	{
192 		if (action->getDetails()->type == SDL_KEYDOWN)
193 		{
194 			keyboardPress(action, state);
195 		}
196 		else if (action->getDetails()->type == SDL_KEYUP)
197 		{
198 			keyboardRelease(action, state);
199 		}
200 	}
201 }
202 
203 /**
204  * Changes the surface's focus. Surfaces will only receive
205  * keyboard events if focused.
206  * @param focus Is it focused?
207  */
setFocus(bool focus)208 void InteractiveSurface::setFocus(bool focus)
209 {
210 	_isFocused = focus;
211 }
212 
213 /**
214  * Returns the surface's focus. Surfaces will only receive
215  * keyboard events if focused.
216  * @return Is it focused?
217  */
isFocused() const218 bool InteractiveSurface::isFocused() const
219 {
220 	return _isFocused;
221 }
222 
223 /**
224  * Simulates a "mouse button release". Used in circumstances
225  * where the surface is unpressed without user input.
226  * @param state Pointer to running state.
227  */
unpress(State * state)228 void InteractiveSurface::unpress(State *state)
229 {
230 	if (isButtonPressed())
231 	{
232 		_buttonsPressed = 0;
233 		SDL_Event ev;
234 		ev.type = SDL_MOUSEBUTTONUP;
235 		ev.button.button = SDL_BUTTON_LEFT;
236 		Action a = Action(&ev, 0.0, 0.0, 0, 0);
237 		mouseRelease(&a, state);
238 	}
239 }
240 
241 /**
242  * Called every time there's a mouse press over the surface.
243  * Allows the surface to have custom functionality for this action,
244  * and can be called externally to simulate the action.
245  * @param action Pointer to an action.
246  * @param state State that the action handlers belong to.
247  */
mousePress(Action * action,State * state)248 void InteractiveSurface::mousePress(Action *action, State *state)
249 {
250 	std::map<Uint8, ActionHandler>::iterator allHandler = _press.find(0);
251 	std::map<Uint8, ActionHandler>::iterator oneHandler = _press.find(action->getDetails()->button.button);
252 	if (allHandler != _press.end())
253 	{
254 		ActionHandler handler = allHandler->second;
255 		(state->*handler)(action);
256 	}
257 	if (oneHandler != _press.end())
258 	{
259 		ActionHandler handler = oneHandler->second;
260 		(state->*handler)(action);
261 	}
262 }
263 
264 /**
265  * Called every time there's a mouse release over the surface.
266  * Allows the surface to have custom functionality for this action,
267  * and can be called externally to simulate the action.
268  * @param action Pointer to an action.
269  * @param state State that the action handlers belong to.
270  */
mouseRelease(Action * action,State * state)271 void InteractiveSurface::mouseRelease(Action *action, State *state)
272 {
273 	std::map<Uint8, ActionHandler>::iterator allHandler = _release.find(0);
274 	std::map<Uint8, ActionHandler>::iterator oneHandler = _release.find(action->getDetails()->button.button);
275 	if (allHandler != _release.end())
276 	{
277 		ActionHandler handler = allHandler->second;
278 		(state->*handler)(action);
279 	}
280 	if (oneHandler != _release.end())
281 	{
282 		ActionHandler handler = oneHandler->second;
283 		(state->*handler)(action);
284 	}
285 }
286 
287 /**
288  * Called every time there's a mouse click on the surface.
289  * Allows the surface to have custom functionality for this action,
290  * and can be called externally to simulate the action.
291  * @param action Pointer to an action.
292  * @param state State that the action handlers belong to.
293  */
mouseClick(Action * action,State * state)294 void InteractiveSurface::mouseClick(Action *action, State *state)
295 {
296 	std::map<Uint8, ActionHandler>::iterator allHandler = _click.find(0);
297 	std::map<Uint8, ActionHandler>::iterator oneHandler = _click.find(action->getDetails()->button.button);
298 	if (allHandler != _click.end())
299 	{
300 		ActionHandler handler = allHandler->second;
301 		(state->*handler)(action);
302 	}
303 	if (oneHandler != _click.end())
304 	{
305 		ActionHandler handler = oneHandler->second;
306 		(state->*handler)(action);
307 	}
308 }
309 
310 /**
311  * Called every time the mouse moves into the surface.
312  * Allows the surface to have custom functionality for this action,
313  * and can be called externally to simulate the action.
314  * @param action Pointer to an action.
315  * @param state State that the action handlers belong to.
316  */
mouseIn(Action * action,State * state)317 void InteractiveSurface::mouseIn(Action *action, State *state)
318 {
319 	if (_in != 0)
320 	{
321 		(state->*_in)(action);
322 	}
323 }
324 
325 /**
326  * Called every time the mouse moves over the surface.
327  * Allows the surface to have custom functionality for this action,
328  * and can be called externally to simulate the action.
329  * @param action Pointer to an action.
330  * @param state State that the action handlers belong to.
331  */
mouseOver(Action * action,State * state)332 void InteractiveSurface::mouseOver(Action *action, State *state)
333 {
334 	if (_over != 0)
335 	{
336 		(state->*_over)(action);
337 	}
338 }
339 
340 /**
341  * Called every time the mouse moves out of the surface.
342  * Allows the surface to have custom functionality for this action,
343  * and can be called externally to simulate the action.
344  * @param action Pointer to an action.
345  * @param state State that the action handlers belong to.
346  */
mouseOut(Action * action,State * state)347 void InteractiveSurface::mouseOut(Action *action, State *state)
348 {
349 	if (_out != 0)
350 	{
351 		(state->*_out)(action);
352 	}
353 }
354 
355 /**
356  * Called every time there's a keyboard press when the surface is focused.
357  * Allows the surface to have custom functionality for this action,
358  * and can be called externally to simulate the action.
359  * @param action Pointer to an action.
360  * @param state State that the action handlers belong to.
361  */
keyboardPress(Action * action,State * state)362 void InteractiveSurface::keyboardPress(Action *action, State *state)
363 {
364 	std::map<SDLKey, ActionHandler>::iterator allHandler = _keyPress.find(SDLK_ANY);
365 	std::map<SDLKey, ActionHandler>::iterator oneHandler = _keyPress.find(action->getDetails()->key.keysym.sym);
366 	if (allHandler != _keyPress.end())
367 	{
368 		ActionHandler handler = allHandler->second;
369 		(state->*handler)(action);
370 	}
371 	// Check if Ctrl, Alt and Shift aren't pressed
372 	bool mod = ((action->getDetails()->key.keysym.mod & (KMOD_CTRL|KMOD_ALT|KMOD_SHIFT)) != 0);
373 	if (oneHandler != _keyPress.end() && !mod)
374 	{
375 		ActionHandler handler = oneHandler->second;
376 		(state->*handler)(action);
377 	}
378 }
379 
380 /**
381  * Called every time there's a keyboard release over the surface.
382  * Allows the surface to have custom functionality for this action,
383  * and can be called externally to simulate the action.
384  * @param action Pointer to an action.
385  * @param state State that the action handlers belong to.
386  */
keyboardRelease(Action * action,State * state)387 void InteractiveSurface::keyboardRelease(Action *action, State *state)
388 {
389 	std::map<SDLKey, ActionHandler>::iterator allHandler = _keyRelease.find(SDLK_ANY);
390 	std::map<SDLKey, ActionHandler>::iterator oneHandler = _keyRelease.find(action->getDetails()->key.keysym.sym);
391 	if (allHandler != _keyRelease.end())
392 	{
393 		ActionHandler handler = allHandler->second;
394 		(state->*handler)(action);
395 	}
396 	// Check if Ctrl, Alt and Shift aren't pressed
397 	bool mod = ((action->getDetails()->key.keysym.mod & (KMOD_CTRL|KMOD_ALT|KMOD_SHIFT)) != 0);
398 	if (oneHandler != _keyRelease.end() && !mod)
399 	{
400 		ActionHandler handler = oneHandler->second;
401 		(state->*handler)(action);
402 	}
403 }
404 
405 /**
406  * Sets a function to be called every time the surface is mouse clicked.
407  * @param handler Action handler.
408  * @param button Mouse button to check for. Set to 0 for any button.
409  */
onMouseClick(ActionHandler handler,Uint8 button)410 void InteractiveSurface::onMouseClick(ActionHandler handler, Uint8 button)
411 {
412 	if (handler != 0)
413 	{
414 		_click[button] = handler;
415 	}
416 	else
417 	{
418 		_click.erase(button);
419 	}
420 }
421 
422 /**
423  * Sets a function to be called every time the surface is mouse pressed.
424  * @param handler Action handler.
425  * @param button Mouse button to check for. Set to 0 for any button.
426  */
onMousePress(ActionHandler handler,Uint8 button)427 void InteractiveSurface::onMousePress(ActionHandler handler, Uint8 button)
428 {
429 	if (handler != 0)
430 	{
431 		_press[button] = handler;
432 	}
433 	else
434 	{
435 		_press.erase(button);
436 	}
437 }
438 
439 /**
440  * Sets a function to be called every time the surface is mouse released.
441  * @param handler Action handler.
442  * @param button Mouse button to check for. Set to 0 for any button.
443  */
onMouseRelease(ActionHandler handler,Uint8 button)444 void InteractiveSurface::onMouseRelease(ActionHandler handler, Uint8 button)
445 {
446 	if (handler != 0)
447 	{
448 		_release[button] = handler;
449 	}
450 	else
451 	{
452 		_release.erase(button);
453 	}
454 }
455 
456 /**
457  * Sets a function to be called every time the mouse moves into the surface.
458  * @param handler Action handler.
459  */
onMouseIn(ActionHandler handler)460 void InteractiveSurface::onMouseIn(ActionHandler handler)
461 {
462 	_in = handler;
463 }
464 
465 /**
466  * Sets a function to be called every time the mouse moves over the surface.
467  * @param handler Action handler.
468  */
onMouseOver(ActionHandler handler)469 void InteractiveSurface::onMouseOver(ActionHandler handler)
470 {
471 	_over = handler;
472 }
473 
474 /**
475  * Sets a function to be called every time the mouse moves out of the surface.
476  * @param handler Action handler.
477  */
onMouseOut(ActionHandler handler)478 void InteractiveSurface::onMouseOut(ActionHandler handler)
479 {
480 	_out = handler;
481 }
482 
483 /**
484  * Sets a function to be called every time a key is pressed when the surface is focused.
485  * @param handler Action handler.
486  * @param key Keyboard button to check for (note: ignores key modifiers). Set to 0 for any key.
487  */
onKeyboardPress(ActionHandler handler,SDLKey key)488 void InteractiveSurface::onKeyboardPress(ActionHandler handler, SDLKey key)
489 {
490 	if (handler != 0)
491 	{
492 		_keyPress[key] = handler;
493 	}
494 	else
495 	{
496 		_keyPress.erase(key);
497 	}
498 }
499 
500 /**
501  * Sets a function to be called every time a key is released when the surface is focused.
502  * @param handler Action handler.
503  * @param key Keyboard button to check for (note: ignores key modifiers). Set to 0 for any key.
504  */
onKeyboardRelease(ActionHandler handler,SDLKey key)505 void InteractiveSurface::onKeyboardRelease(ActionHandler handler, SDLKey key)
506 {
507 	if (handler != 0)
508 	{
509 		_keyRelease[key] = handler;
510 	}
511 	else
512 	{
513 		_keyRelease.erase(key);
514 	}
515 }
516 
517 /**
518  * Sets a flag for this button to say "i'm a member of a textList" to true.
519  */
setListButton()520 void InteractiveSurface::setListButton()
521 {
522 	_listButton = true;
523 }
524 
525 }
526