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