/*
* Copyright 2010-2016 OpenXcom Developers.
*
* This file is part of OpenXcom.
*
* OpenXcom is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* OpenXcom is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenXcom. If not, see <http://www.gnu.org/licenses/>.
*/
#include "InteractiveSurface.h"
#include "Action.h"
namespace OpenXcom
{
const SDLKey InteractiveSurface::SDLK_ANY = (SDLKey)-1; // using an unused keycode to represent an "any key"
/**
* Sets up a blank interactive surface with the specified size and position.
* @param width Width in pixels.
* @param height Height in pixels.
* @param x X position in pixels.
* @param y Y position in pixels.
*/
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)
{
}
/**
*
*/
InteractiveSurface::~InteractiveSurface()
{
}
bool InteractiveSurface::isButtonHandled(Uint8 button)
{
bool handled = (_click.find(0) != _click.end() ||
_press.find(0) != _press.end() ||
_release.find(0) != _release.end());
if (!handled && button != 0)
{
handled = (_click.find(button) != _click.end() ||
_press.find(button) != _press.end() ||
_release.find(button) != _release.end());
}
return handled;
}
bool InteractiveSurface::isButtonPressed(Uint8 button) const
{
if (button == 0)
{
return (_buttonsPressed != 0);
}
else
{
return (_buttonsPressed & SDL_BUTTON(button)) != 0;
}
}
void InteractiveSurface::setButtonPressed(Uint8 button, bool pressed)
{
if (pressed)
{
_buttonsPressed |= SDL_BUTTON(button);
}
else
{
_buttonsPressed &= (~SDL_BUTTON(button));
}
}
/**
* Changes the visibility of the surface. A hidden surface
* isn't blitted nor receives events.
* @param visible New visibility.
*/
void InteractiveSurface::setVisible(bool visible)
{
Surface::setVisible(visible);
// Unpress button if it was hidden
if (!_visible)
{
unpress(0);
}
}
/**
* Called whenever an action occurs, and processes it to
* check if it's relevant to the surface and convert it
* into a meaningful interaction like a "click", calling
* the respective handlers.
* @param action Pointer to an action.
* @param state State that the action handlers belong to.
*/
void InteractiveSurface::handle(Action *action, State *state)
{
if (!_visible || _hidden)
return;
action->setSender(this);
if (action->getDetails()->type == SDL_MOUSEBUTTONUP || action->getDetails()->type == SDL_MOUSEBUTTONDOWN)
{
action->setMouseAction(action->getDetails()->button.x, action->getDetails()->button.y, getX(), getY());
}
else if (action->getDetails()->type == SDL_MOUSEMOTION)
{
action->setMouseAction(action->getDetails()->motion.x, action->getDetails()->motion.y, getX(), getY());
}
if (action->isMouseAction())
{
if ((action->getAbsoluteXMouse() >= getX() && action->getAbsoluteXMouse() < getX() + getWidth()) &&
(action->getAbsoluteYMouse() >= getY() && action->getAbsoluteYMouse() < getY() + getHeight()))
{
if (!_isHovered)
{
_isHovered = true;
mouseIn(action, state);
}
if (_listButton && action->getDetails()->type == SDL_MOUSEMOTION)
{
_buttonsPressed = SDL_GetMouseState(0, 0);
for (Uint8 i = 1; i <= NUM_BUTTONS; ++i)
{
if (isButtonPressed(i))
{
action->getDetails()->button.button = i;
mousePress(action, state);
}
}
}
mouseOver(action, state);
}
else
{
if (_isHovered)
{
_isHovered = false;
mouseOut(action, state);
if (_listButton && action->getDetails()->type == SDL_MOUSEMOTION)
{
for (Uint8 i = 1; i <= NUM_BUTTONS; ++i)
{
if (isButtonPressed(i))
{
setButtonPressed(i, false);
}
action->getDetails()->button.button = i;
mouseRelease(action, state);
}
}
}
}
}
if (action->getDetails()->type == SDL_MOUSEBUTTONDOWN)
{
if (_isHovered && !isButtonPressed(action->getDetails()->button.button))
{
setButtonPressed(action->getDetails()->button.button, true);
mousePress(action, state);
}
}
else if (action->getDetails()->type == SDL_MOUSEBUTTONUP)
{
if (isButtonPressed(action->getDetails()->button.button))
{
setButtonPressed(action->getDetails()->button.button, false);
mouseRelease(action, state);
if (_isHovered)
{
mouseClick(action, state);
}
}
}
if (_isFocused)
{
if (action->getDetails()->type == SDL_KEYDOWN)
{
keyboardPress(action, state);
}
else if (action->getDetails()->type == SDL_KEYUP)
{
keyboardRelease(action, state);
}
}
}
/**
* Changes the surface's focus. Surfaces will only receive
* keyboard events if focused.
* @param focus Is it focused?
*/
void InteractiveSurface::setFocus(bool focus)
{
_isFocused = focus;
}
/**
* Returns the surface's focus. Surfaces will only receive
* keyboard events if focused.
* @return Is it focused?
*/
bool InteractiveSurface::isFocused() const
{
return _isFocused;
}
/**
* Simulates a "mouse button release". Used in circumstances
* where the surface is unpressed without user input.
* @param state Pointer to running state.
*/
void InteractiveSurface::unpress(State *state)
{
if (isButtonPressed())
{
_buttonsPressed = 0;
SDL_Event ev;
ev.type = SDL_MOUSEBUTTONUP;
ev.button.button = SDL_BUTTON_LEFT;
Action a = Action(&ev, 0.0, 0.0, 0, 0);
mouseRelease(&a, state);
}
}
/**
* Called every time there's a mouse press over the surface.
* Allows the surface to have custom functionality for this action,
* and can be called externally to simulate the action.
* @param action Pointer to an action.
* @param state State that the action handlers belong to.
*/
void InteractiveSurface::mousePress(Action *action, State *state)
{
std::map<Uint8, ActionHandler>::iterator allHandler = _press.find(0);
std::map<Uint8, ActionHandler>::iterator oneHandler = _press.find(action->getDetails()->button.button);
if (allHandler != _press.end())
{
ActionHandler handler = allHandler->second;
(state->*handler)(action);
}
if (oneHandler != _press.end())
{
ActionHandler handler = oneHandler->second;
(state->*handler)(action);
}
}
/**
* Called every time there's a mouse release over the surface.
* Allows the surface to have custom functionality for this action,
* and can be called externally to simulate the action.
* @param action Pointer to an action.
* @param state State that the action handlers belong to.
*/
void InteractiveSurface::mouseRelease(Action *action, State *state)
{
std::map<Uint8, ActionHandler>::iterator allHandler = _release.find(0);
std::map<Uint8, ActionHandler>::iterator oneHandler = _release.find(action->getDetails()->button.button);
if (allHandler != _release.end())
{
ActionHandler handler = allHandler->second;
(state->*handler)(action);
}
if (oneHandler != _release.end())
{
ActionHandler handler = oneHandler->second;
(state->*handler)(action);
}
}
/**
* Called every time there's a mouse click on the surface.
* Allows the surface to have custom functionality for this action,
* and can be called externally to simulate the action.
* @param action Pointer to an action.
* @param state State that the action handlers belong to.
*/
void InteractiveSurface::mouseClick(Action *action, State *state)
{
std::map<Uint8, ActionHandler>::iterator allHandler = _click.find(0);
std::map<Uint8, ActionHandler>::iterator oneHandler = _click.find(action->getDetails()->button.button);
if (allHandler != _click.end())
{
ActionHandler handler = allHandler->second;
(state->*handler)(action);
}
if (oneHandler != _click.end())
{
ActionHandler handler = oneHandler->second;
(state->*handler)(action);
}
}
/**
* Called every time the mouse moves into the surface.
* Allows the surface to have custom functionality for this action,
* and can be called externally to simulate the action.
* @param action Pointer to an action.
* @param state State that the action handlers belong to.
*/
void InteractiveSurface::mouseIn(Action *action, State *state)
{
if (_in != 0)
{
(state->*_in)(action);
}
}
/**
* Called every time the mouse moves over the surface.
* Allows the surface to have custom functionality for this action,
* and can be called externally to simulate the action.
* @param action Pointer to an action.
* @param state State that the action handlers belong to.
*/
void InteractiveSurface::mouseOver(Action *action, State *state)
{
if (_over != 0)
{
(state->*_over)(action);
}
}
/**
* Called every time the mouse moves out of the surface.
* Allows the surface to have custom functionality for this action,
* and can be called externally to simulate the action.
* @param action Pointer to an action.
* @param state State that the action handlers belong to.
*/
void InteractiveSurface::mouseOut(Action *action, State *state)
{
if (_out != 0)
{
(state->*_out)(action);
}
}
/**
* Called every time there's a keyboard press when the surface is focused.
* Allows the surface to have custom functionality for this action,
* and can be called externally to simulate the action.
* @param action Pointer to an action.
* @param state State that the action handlers belong to.
*/
void InteractiveSurface::keyboardPress(Action *action, State *state)
{
std::map<SDLKey, ActionHandler>::iterator allHandler = _keyPress.find(SDLK_ANY);
std::map<SDLKey, ActionHandler>::iterator oneHandler = _keyPress.find(action->getDetails()->key.keysym.sym);
if (allHandler != _keyPress.end())
{
ActionHandler handler = allHandler->second;
(state->*handler)(action);
}
// Check if Ctrl, Alt and Shift aren't pressed
bool mod = ((action->getDetails()->key.keysym.mod & (KMOD_CTRL|KMOD_ALT|KMOD_SHIFT)) != 0);
if (oneHandler != _keyPress.end() && !mod)
{
ActionHandler handler = oneHandler->second;
(state->*handler)(action);
}
}
/**
* Called every time there's a keyboard release over the surface.
* Allows the surface to have custom functionality for this action,
* and can be called externally to simulate the action.
* @param action Pointer to an action.
* @param state State that the action handlers belong to.
*/
void InteractiveSurface::keyboardRelease(Action *action, State *state)
{
std::map<SDLKey, ActionHandler>::iterator allHandler = _keyRelease.find(SDLK_ANY);
std::map<SDLKey, ActionHandler>::iterator oneHandler = _keyRelease.find(action->getDetails()->key.keysym.sym);
if (allHandler != _keyRelease.end())
{
ActionHandler handler = allHandler->second;
(state->*handler)(action);
}
// Check if Ctrl, Alt and Shift aren't pressed
bool mod = ((action->getDetails()->key.keysym.mod & (KMOD_CTRL|KMOD_ALT|KMOD_SHIFT)) != 0);
if (oneHandler != _keyRelease.end() && !mod)
{
ActionHandler handler = oneHandler->second;
(state->*handler)(action);
}
}
/**
* Sets a function to be called every time the surface is mouse clicked.
* @param handler Action handler.
* @param button Mouse button to check for. Set to 0 for any button.
*/
void InteractiveSurface::onMouseClick(ActionHandler handler, Uint8 button)
{
if (handler != 0)
{
_click[button] = handler;
}
else
{
_click.erase(button);
}
}
/**
* Sets a function to be called every time the surface is mouse pressed.
* @param handler Action handler.
* @param button Mouse button to check for. Set to 0 for any button.
*/
void InteractiveSurface::onMousePress(ActionHandler handler, Uint8 button)
{
if (handler != 0)
{
_press[button] = handler;
}
else
{
_press.erase(button);
}
}
/**
* Sets a function to be called every time the surface is mouse released.
* @param handler Action handler.
* @param button Mouse button to check for. Set to 0 for any button.
*/
void InteractiveSurface::onMouseRelease(ActionHandler handler, Uint8 button)
{
if (handler != 0)
{
_release[button] = handler;
}
else
{
_release.erase(button);
}
}
/**
* Sets a function to be called every time the mouse moves into the surface.
* @param handler Action handler.
*/
void InteractiveSurface::onMouseIn(ActionHandler handler)
{
_in = handler;
}
/**
* Sets a function to be called every time the mouse moves over the surface.
* @param handler Action handler.
*/
void InteractiveSurface::onMouseOver(ActionHandler handler)
{
_over = handler;
}
/**
* Sets a function to be called every time the mouse moves out of the surface.
* @param handler Action handler.
*/
void InteractiveSurface::onMouseOut(ActionHandler handler)
{
_out = handler;
}
/**
* Sets a function to be called every time a key is pressed when the surface is focused.
* @param handler Action handler.
* @param key Keyboard button to check for (note: ignores key modifiers). Set to SDLK_ANY for any key.
*/
void InteractiveSurface::onKeyboardPress(ActionHandler handler, SDLKey key)
{
if (key == SDLK_UNKNOWN)
{
// Ignore unknown keys
return;
}
if (handler != 0)
{
_keyPress[key] = handler;
}
else
{
_keyPress.erase(key);
}
}
/**
* Sets a function to be called every time a key is released when the surface is focused.
* @param handler Action handler.
* @param key Keyboard button to check for (note: ignores key modifiers). Set to SDLK_ANY for any key.
*/
void InteractiveSurface::onKeyboardRelease(ActionHandler handler, SDLKey key)
{
if (key == SDLK_UNKNOWN)
{
// Ignore unknown keys
return;
}
if (handler != 0)
{
_keyRelease[key] = handler;
}
else
{
_keyRelease.erase(key);
}
}
/**
* Sets a flag for this button to say "i'm a member of a textList" to true.
*/
void InteractiveSurface::setListButton()
{
_listButton = true;
}
}
↑ V1002 The 'Action' class, containing pointers, constructor and destructor, is copied by the automatically generated copy constructor.