# Fix hotkey bugs.
Simplify hotkey system to use strings consistently. Restrict scope of GUI hotkey bindings to the associated page. Avoid hard-coding list of hotkeys. Clean up the code a little bit and fix some bugs. Remove unused "!" prefix for key negation. Fixes #567. This was SVN commit r8444.
This commit is contained in:
parent
f6154b0d89
commit
9e499cdec5
@ -601,19 +601,19 @@ void CGameView::Update(float DeltaTime)
|
||||
mouse_last_x = g_mouse_x;
|
||||
mouse_last_y = g_mouse_y;
|
||||
|
||||
if (hotkeys[HOTKEY_CAMERA_ROTATE_CW])
|
||||
if (HotkeyIsPressed("camera.rotate.cw"))
|
||||
m->RotateY.AddSmoothly(m->ViewRotateYSpeed * DeltaTime);
|
||||
if (hotkeys[HOTKEY_CAMERA_ROTATE_CCW])
|
||||
if (HotkeyIsPressed("camera.rotate.ccw"))
|
||||
m->RotateY.AddSmoothly(-m->ViewRotateYSpeed * DeltaTime);
|
||||
if (hotkeys[HOTKEY_CAMERA_ROTATE_UP])
|
||||
if (HotkeyIsPressed("camera.rotate.up"))
|
||||
m->RotateX.AddSmoothly(-m->ViewRotateXSpeed * DeltaTime);
|
||||
if (hotkeys[HOTKEY_CAMERA_ROTATE_DOWN])
|
||||
if (HotkeyIsPressed("camera.rotate.down"))
|
||||
m->RotateX.AddSmoothly(m->ViewRotateXSpeed * DeltaTime);
|
||||
|
||||
float moveRightward = 0.f;
|
||||
float moveForward = 0.f;
|
||||
|
||||
if (hotkeys[HOTKEY_CAMERA_PAN])
|
||||
if (HotkeyIsPressed("camera.pan"))
|
||||
{
|
||||
moveRightward += m->ViewDragSpeed * mouse_dx;
|
||||
moveForward += m->ViewDragSpeed * -mouse_dy;
|
||||
@ -632,15 +632,15 @@ void CGameView::Update(float DeltaTime)
|
||||
moveForward += m->ViewScrollSpeed * DeltaTime;
|
||||
}
|
||||
|
||||
if (hotkeys[HOTKEY_CAMERA_PAN_KEYBOARD])
|
||||
if (HotkeyIsPressed("camera.pan.keyboard"))
|
||||
{
|
||||
if (hotkeys[HOTKEY_CAMERA_RIGHT])
|
||||
if (HotkeyIsPressed("camera.right"))
|
||||
moveRightward += m->ViewScrollSpeed * DeltaTime;
|
||||
if (hotkeys[HOTKEY_CAMERA_LEFT])
|
||||
if (HotkeyIsPressed("camera.left"))
|
||||
moveRightward -= m->ViewScrollSpeed * DeltaTime;
|
||||
if (hotkeys[HOTKEY_CAMERA_UP])
|
||||
if (HotkeyIsPressed("camera.up"))
|
||||
moveForward += m->ViewScrollSpeed * DeltaTime;
|
||||
if (hotkeys[HOTKEY_CAMERA_DOWN])
|
||||
if (HotkeyIsPressed("camera.down"))
|
||||
moveForward -= m->ViewScrollSpeed * DeltaTime;
|
||||
}
|
||||
|
||||
@ -688,9 +688,9 @@ void CGameView::Update(float DeltaTime)
|
||||
}
|
||||
}
|
||||
|
||||
if (hotkeys[HOTKEY_CAMERA_ZOOM_IN])
|
||||
if (HotkeyIsPressed("camera.zoom.in"))
|
||||
m->Zoom.AddSmoothly(m->ViewZoomSpeed * DeltaTime);
|
||||
if (hotkeys[HOTKEY_CAMERA_ZOOM_OUT])
|
||||
if (HotkeyIsPressed("camera.zoom.out"))
|
||||
m->Zoom.AddSmoothly(-m->ViewZoomSpeed * DeltaTime);
|
||||
|
||||
float zoomDelta = m->Zoom.Update(DeltaTime);
|
||||
@ -894,9 +894,10 @@ InReaction CGameView::HandleEvent(const SDL_Event_* ev)
|
||||
{
|
||||
|
||||
case SDL_HOTKEYDOWN:
|
||||
switch(ev->ev.user.code)
|
||||
std::string hotkey = static_cast<const char*>(ev->ev.user.data1);
|
||||
|
||||
if (hotkey == "wireframe")
|
||||
{
|
||||
case HOTKEY_WIREFRAME:
|
||||
if (g_Renderer.GetModelRenderMode() == SOLID)
|
||||
{
|
||||
g_Renderer.SetTerrainRenderMode(EDGED_FACES);
|
||||
@ -913,27 +914,32 @@ InReaction CGameView::HandleEvent(const SDL_Event_* ev)
|
||||
g_Renderer.SetModelRenderMode(SOLID);
|
||||
}
|
||||
return IN_HANDLED;
|
||||
|
||||
}
|
||||
// Mouse wheel must be treated using events instead of polling,
|
||||
// because SDL auto-generates a sequence of mousedown/mouseup events
|
||||
// and we never get to see the "down" state inside Update().
|
||||
case HOTKEY_CAMERA_ZOOM_WHEEL_IN:
|
||||
else if (hotkey == "camera.zoom.wheel.in")
|
||||
{
|
||||
m->Zoom.AddSmoothly(m->ViewZoomSpeedWheel);
|
||||
return IN_HANDLED;
|
||||
|
||||
case HOTKEY_CAMERA_ZOOM_WHEEL_OUT:
|
||||
}
|
||||
else if (hotkey == "camera.zoom.wheel.out")
|
||||
{
|
||||
m->Zoom.AddSmoothly(-m->ViewZoomSpeedWheel);
|
||||
return IN_HANDLED;
|
||||
|
||||
case HOTKEY_CAMERA_ROTATE_WHEEL_CW:
|
||||
}
|
||||
else if (hotkey == "camera.rotate.wheel.cw")
|
||||
{
|
||||
m->RotateY.AddSmoothly(m->ViewRotateYSpeedWheel);
|
||||
return IN_HANDLED;
|
||||
|
||||
case HOTKEY_CAMERA_ROTATE_WHEEL_CCW:
|
||||
}
|
||||
else if (hotkey == "camera.rotate.wheel.ccw")
|
||||
{
|
||||
m->RotateY.AddSmoothly(-m->ViewRotateYSpeedWheel);
|
||||
return IN_HANDLED;
|
||||
|
||||
case HOTKEY_CAMERA_RESET:
|
||||
}
|
||||
else if (hotkey == "camera.reset")
|
||||
{
|
||||
ResetCameraAngleZoom();
|
||||
return IN_HANDLED;
|
||||
}
|
||||
|
@ -75,18 +75,17 @@ InReaction CGUI::HandleEvent(const SDL_Event_* ev)
|
||||
{
|
||||
InReaction ret = IN_PASS;
|
||||
|
||||
if (ev->ev.type == SDL_GUIHOTKEYPRESS)
|
||||
if (ev->ev.type == SDL_HOTKEYDOWN)
|
||||
{
|
||||
const CStr& objectName = *(CStr*) ev->ev.user.data1;
|
||||
IGUIObject* object = FindObjectByName(objectName);
|
||||
if (! object)
|
||||
const char* hotkey = static_cast<const char*>(ev->ev.user.data1);
|
||||
std::map<CStr, std::vector<IGUIObject*> >::iterator it = m_HotkeyObjects.find(hotkey);
|
||||
if (it != m_HotkeyObjects.end())
|
||||
{
|
||||
LOG(CLogger::Error, LOG_CATEGORY, L"Cannot find hotkeyed object '%hs'", objectName.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
object->HandleMessage( SGUIMessage( GUIM_PRESSED ) );
|
||||
object->ScriptEvent("press");
|
||||
for (size_t i = 0; i < it->second.size(); ++i)
|
||||
{
|
||||
it->second[i]->HandleMessage(SGUIMessage(GUIM_PRESSED));
|
||||
it->second[i]->ScriptEvent("press");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1226,7 +1225,7 @@ void CGUI::Xeromyces_ReadObject(XMBElement Element, CXeromyces* pFile, IGUIObjec
|
||||
|
||||
// Attempt to register the hotkey tag, if one was provided
|
||||
if (! hotkeyTag.empty())
|
||||
HotkeyRegisterGuiObject(object->GetName(), hotkeyTag);
|
||||
m_HotkeyObjects[hotkeyTag].push_back(object);
|
||||
|
||||
CStrW caption (Element.GetText());
|
||||
if (! caption.empty())
|
||||
|
@ -651,6 +651,15 @@ private:
|
||||
*/
|
||||
std::map<CStr, ConstructObjectFunction> m_ObjectTypes;
|
||||
|
||||
/**
|
||||
* Map from hotkey names to objects that listen to the hotkey.
|
||||
* (This is an optimisation to avoid recursing over the whole GUI
|
||||
* tree every time a hotkey is pressed).
|
||||
* Currently this is only set at load time - dynamic changes to an
|
||||
* object's hotkey property will be ignored.
|
||||
*/
|
||||
std::map<CStr, std::vector<IGUIObject*> > m_HotkeyObjects;
|
||||
|
||||
//--------------------------------------------------------
|
||||
// Databases
|
||||
//--------------------------------------------------------
|
||||
|
@ -79,7 +79,8 @@ InReaction CInput::ManuallyHandleEvent(const SDL_Event_* ev)
|
||||
|
||||
if (ev->ev.type == SDL_HOTKEYDOWN)
|
||||
{
|
||||
if (ev->ev.user.code == HOTKEY_CONSOLE_PASTE)
|
||||
std::string hotkey = static_cast<const char*>(ev->ev.user.data1);
|
||||
if (hotkey == "console.paste")
|
||||
{
|
||||
wchar_t* text = sys_clipboard_get();
|
||||
if (text)
|
||||
|
@ -37,8 +37,6 @@ CGUIManager* g_GUI = NULL;
|
||||
// A lot of the CGUI data could (and should) be shared between
|
||||
// multiple pages, instead of treating them as completely independent, to save
|
||||
// memory and loading time.
|
||||
//
|
||||
// Hotkeys are not unregistered when a page is unloaded.
|
||||
|
||||
|
||||
// called from main loop when (input) events are received.
|
||||
|
@ -43,7 +43,6 @@ template<> jsval ScriptInterface::ToJSVal<SDL_Event_>(JSContext* cx, SDL_Event_
|
||||
case SDL_MOUSEBUTTONUP: typeName = "mousebuttonup"; break;
|
||||
case SDL_HOTKEYDOWN: typeName = "hotkeydown"; break;
|
||||
case SDL_HOTKEYUP: typeName = "hotkeyup"; break;
|
||||
case SDL_GUIHOTKEYPRESS: typeName = "guihotkeypress"; break;
|
||||
default: typeName = "(unknown)"; break;
|
||||
}
|
||||
|
||||
@ -117,14 +116,7 @@ template<> jsval ScriptInterface::ToJSVal<SDL_Event_>(JSContext* cx, SDL_Event_
|
||||
case SDL_HOTKEYDOWN:
|
||||
case SDL_HOTKEYUP:
|
||||
{
|
||||
CStr name = HotkeyGetName(val.ev.user.code);
|
||||
SET(obj, "hotkey", name.c_str());
|
||||
break;
|
||||
}
|
||||
case SDL_GUIHOTKEYPRESS:
|
||||
{
|
||||
CStr* name = static_cast<CStr*>(val.ev.user.data1);
|
||||
SET(obj, "object", name->c_str());
|
||||
SET(obj, "hotkey", static_cast<const char*>(val.ev.user.data1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -100,26 +100,26 @@ static InReaction MainInputHandler(const SDL_Event_* ev)
|
||||
break;
|
||||
|
||||
case SDL_HOTKEYDOWN:
|
||||
switch(ev->ev.user.code)
|
||||
std::string hotkey = static_cast<const char*>(ev->ev.user.data1);
|
||||
if (hotkey == "exit")
|
||||
{
|
||||
case HOTKEY_EXIT:
|
||||
kill_mainloop();
|
||||
return IN_HANDLED;
|
||||
|
||||
case HOTKEY_SCREENSHOT:
|
||||
}
|
||||
else if (hotkey == "screenshot")
|
||||
{
|
||||
WriteScreenshot(L".png");
|
||||
return IN_HANDLED;
|
||||
|
||||
case HOTKEY_BIGSCREENSHOT:
|
||||
}
|
||||
else if (hotkey == "bigscreenshot")
|
||||
{
|
||||
WriteBigScreenshot(L".bmp", 10);
|
||||
return IN_HANDLED;
|
||||
|
||||
case HOTKEY_TOGGLEFULLSCREEN:
|
||||
}
|
||||
else if (hotkey == "togglefullscreen")
|
||||
{
|
||||
g_VideoMode.ToggleFullscreen();
|
||||
return IN_HANDLED;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -759,17 +759,19 @@ InReaction conInputHandler(const SDL_Event_* ev)
|
||||
{
|
||||
if( ev->ev.type == SDL_HOTKEYDOWN )
|
||||
{
|
||||
if( ev->ev.user.code == HOTKEY_CONSOLE_TOGGLE )
|
||||
std::string hotkey = static_cast<const char*>(ev->ev.user.data1);
|
||||
|
||||
if( hotkey == "console.toggle" )
|
||||
{
|
||||
g_Console->ToggleVisible();
|
||||
return IN_HANDLED;
|
||||
}
|
||||
else if( ev->ev.user.code == HOTKEY_CONSOLE_COPY )
|
||||
else if( hotkey == "console.copy" )
|
||||
{
|
||||
sys_clipboard_set( g_Console->GetBuffer() );
|
||||
return IN_HANDLED;
|
||||
}
|
||||
else if( ev->ev.user.code == HOTKEY_CONSOLE_PASTE )
|
||||
else if( hotkey == "console.paste" )
|
||||
{
|
||||
wchar_t* text = sys_clipboard_get();
|
||||
if(text)
|
||||
@ -794,7 +796,7 @@ InReaction conInputHandler(const SDL_Event_* ev)
|
||||
// Stop unprintable characters (ctrl+, alt+ and escape),
|
||||
// also prevent ` and/or ~ appearing in console every time it's toggled.
|
||||
if( !isUnprintableChar(ev->ev.key.keysym) &&
|
||||
!hotkeys[HOTKEY_CONSOLE_TOGGLE] )
|
||||
!HotkeyIsPressed("console.toggle") )
|
||||
g_Console->InsertChar(sym, (wchar_t)ev->ev.key.keysym.unicode );
|
||||
|
||||
return IN_PASS;
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2009 Wildfire Games.
|
||||
/* Copyright (C) 2010 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
@ -24,6 +24,8 @@
|
||||
#include "Filesystem.h"
|
||||
#include "scripting/ScriptingHost.h"
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
typedef std::map <CStr, CConfigValueSet> TConfigMap;
|
||||
TConfigMap CConfigDB::m_Map[CFG_LAST];
|
||||
CStrW CConfigDB::m_ConfigFile[CFG_LAST];
|
||||
@ -213,7 +215,7 @@ CConfigValue *CConfigDB::GetValue(EConfigNamespace ns, const CStr& name)
|
||||
return &( (*values)[0] );
|
||||
}
|
||||
|
||||
CConfigValueSet *CConfigDB::GetValues(EConfigNamespace ns, const CStr& name )
|
||||
CConfigValueSet *CConfigDB::GetValues(EConfigNamespace ns, const CStr& name)
|
||||
{
|
||||
if (ns < 0 || ns >= CFG_LAST)
|
||||
{
|
||||
@ -235,6 +237,34 @@ CConfigValueSet *CConfigDB::GetValues(EConfigNamespace ns, const CStr& name )
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
std::vector<std::pair<CStr, CConfigValueSet> > CConfigDB::GetValuesWithPrefix(EConfigNamespace ns, const CStr& prefix)
|
||||
{
|
||||
std::vector<std::pair<CStr, CConfigValueSet> > ret;
|
||||
|
||||
if (ns < 0 || ns >= CFG_LAST)
|
||||
{
|
||||
debug_warn(L"CConfigDB: Invalid ns value");
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (TConfigMap::iterator it = m_Map[CFG_COMMAND].begin(); it != m_Map[CFG_COMMAND].end(); ++it)
|
||||
{
|
||||
if (boost::algorithm::starts_with(it->first, prefix))
|
||||
ret.push_back(std::make_pair(it->first, it->second));
|
||||
}
|
||||
|
||||
for (int search_ns = ns; search_ns >= 0; search_ns--)
|
||||
{
|
||||
for (TConfigMap::iterator it = m_Map[search_ns].begin(); it != m_Map[search_ns].end(); ++it)
|
||||
{
|
||||
if (boost::algorithm::starts_with(it->first, prefix))
|
||||
ret.push_back(std::make_pair(it->first, it->second));
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
CConfigValue *CConfigDB::CreateValue(EConfigNamespace ns, const CStr& name)
|
||||
{
|
||||
if (ns < 0 || ns >= CFG_LAST)
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2009 Wildfire Games.
|
||||
/* Copyright (C) 2010 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
@ -83,57 +83,70 @@ public:
|
||||
// the JS interface can be registered.
|
||||
CConfigDB();
|
||||
|
||||
// GetValue()
|
||||
// Attempt to find a config variable with the given name; will search all
|
||||
// namespaces from system up to the specified namespace.
|
||||
//
|
||||
// Returns a pointer to the config value structure for the variable, or
|
||||
// NULL if such a variable could not be found
|
||||
/**
|
||||
* Attempt to find a config variable with the given name; will search all
|
||||
* namespaces from system up to the specified namespace.
|
||||
*
|
||||
* Returns a pointer to the config value structure for the variable, or
|
||||
* NULL if such a variable could not be found
|
||||
*/
|
||||
CConfigValue *GetValue(EConfigNamespace ns, const CStr& name);
|
||||
|
||||
// GetValues()
|
||||
// Attempt to retrieve a vector of values corresponding to the given setting;
|
||||
// will search all namespaces from system up to the specified namespace.
|
||||
//
|
||||
// Returns a pointer to the vector, or NULL if the setting could not be found.
|
||||
/**
|
||||
* Attempt to retrieve a vector of values corresponding to the given setting;
|
||||
* will search all namespaces from system up to the specified namespace.
|
||||
*
|
||||
* Returns a pointer to the vector, or NULL if the setting could not be found.
|
||||
*/
|
||||
CConfigValueSet *GetValues(EConfigNamespace ns, const CStr& name);
|
||||
|
||||
// CreateValue()
|
||||
// Create a new config value in the specified namespace. If such a
|
||||
// variable already exists, the old value is returned and the effect is
|
||||
// exactly the same as that of GetValue()
|
||||
//
|
||||
// Returns a pointer to the value of the newly created config variable, or
|
||||
// that of the already existing config variable.
|
||||
/**
|
||||
* Retrieve a vector of values corresponding to settings whose names begin
|
||||
* with the given prefix;
|
||||
* will search all namespaces from system up to the specified namespace.
|
||||
*/
|
||||
std::vector<std::pair<CStr, CConfigValueSet> > GetValuesWithPrefix(EConfigNamespace ns, const CStr& prefix);
|
||||
|
||||
/**
|
||||
* Create a new config value in the specified namespace. If such a
|
||||
* variable already exists, the old value is returned and the effect is
|
||||
* exactly the same as that of GetValue()
|
||||
*
|
||||
* Returns a pointer to the value of the newly created config variable, or
|
||||
* that of the already existing config variable.
|
||||
*/
|
||||
CConfigValue *CreateValue(EConfigNamespace ns, const CStr& name);
|
||||
|
||||
// SetConfigFile()
|
||||
// Set the path to the config file used to populate the specified namespace
|
||||
// Note that this function does not actually load the config file. Use
|
||||
// the Reload() method if you want to read the config file at the same time.
|
||||
//
|
||||
// 'path': The path to the config file.
|
||||
// VFS: relative to VFS root
|
||||
// non-VFS: relative to current working directory (binaries/data/)
|
||||
// 'useVFS': true if the path is a VFS path, false if it is a real path
|
||||
/**
|
||||
* Set the path to the config file used to populate the specified namespace
|
||||
* Note that this function does not actually load the config file. Use
|
||||
* the Reload() method if you want to read the config file at the same time.
|
||||
*
|
||||
* 'path': The path to the config file.
|
||||
* VFS: relative to VFS root
|
||||
* non-VFS: relative to current working directory (binaries/data/)
|
||||
* 'useVFS': true if the path is a VFS path, false if it is a real path
|
||||
*/
|
||||
void SetConfigFile(EConfigNamespace ns, bool useVFS, const CStrW& path);
|
||||
|
||||
// Reload()
|
||||
// Reload the config file associated with the specified config namespace
|
||||
// (the last config file path set with SetConfigFile)
|
||||
//
|
||||
// Returns:
|
||||
// true: if the reload succeeded,
|
||||
// false: if the reload failed
|
||||
/**
|
||||
* Reload the config file associated with the specified config namespace
|
||||
* (the last config file path set with SetConfigFile)
|
||||
*
|
||||
* Returns:
|
||||
* true: if the reload succeeded,
|
||||
* false: if the reload failed
|
||||
*/
|
||||
bool Reload(EConfigNamespace);
|
||||
|
||||
// WriteFile()
|
||||
// Write the current state of the specified config namespace to the file
|
||||
// specified by 'path'
|
||||
//
|
||||
// Returns:
|
||||
// true: if the config namespace was successfully written to the file
|
||||
// false: if an error occured
|
||||
/**
|
||||
* Write the current state of the specified config namespace to the file
|
||||
* specified by 'path'
|
||||
*
|
||||
* Returns:
|
||||
* true: if the config namespace was successfully written to the file
|
||||
* false: if an error occurred
|
||||
*/
|
||||
bool WriteFile(EConfigNamespace ns, bool useVFS, const CStrW& path);
|
||||
};
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2009 Wildfire Games.
|
||||
/* Copyright (C) 2010 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
@ -26,161 +26,51 @@
|
||||
#include "ps/Globals.h"
|
||||
#include "KeyName.h"
|
||||
|
||||
extern CConsole* g_Console;
|
||||
static bool unified[UNIFIED_LAST - UNIFIED_SHIFT];
|
||||
|
||||
static bool unified[5];
|
||||
|
||||
/* SDL-type */
|
||||
struct SKey
|
||||
{
|
||||
SDLKey code; // keycode or MOUSE_ or UNIFIED_ value
|
||||
bool negated; // whether the key must be pressed (false) or unpressed (true)
|
||||
};
|
||||
|
||||
// Hotkey data associated with an externally-specified 'primary' keycode
|
||||
struct SHotkeyMapping
|
||||
{
|
||||
int mapsTo;
|
||||
bool negation;
|
||||
std::vector<int> requires;
|
||||
SHotkeyMapping() : mapsTo(-1) {}
|
||||
CStr name; // name of the hotkey
|
||||
bool negated; // whether the primary key must be pressed (false) or unpressed (true)
|
||||
std::vector<SKey> requires; // list of non-primary keys that must also be active
|
||||
};
|
||||
|
||||
typedef std::vector<SHotkeyMapping> KeyMapping;
|
||||
|
||||
/**
|
||||
* HK_MAX_KEYCODES: Global maximum number of keycodes, including our "fake" keycodes for
|
||||
* mouse buttons and unified modifiers.
|
||||
*/
|
||||
const int HK_MAX_KEYCODES = UNIFIED_SUPER + 1;
|
||||
// A mapping of keycodes onto the hotkeys that are associated with that key.
|
||||
// (A hotkey triggered by a combination of multiple keys will be in this map
|
||||
// multiple times.)
|
||||
static std::map<int, KeyMapping> g_HotkeyMap;
|
||||
|
||||
// A mapping of keycodes onto sets of SDL event codes
|
||||
static KeyMapping hotkeyMap[HK_MAX_KEYCODES];
|
||||
// The current pressed status of hotkeys
|
||||
std::map<std::string, bool> g_HotkeyStatus;
|
||||
|
||||
// An array of the status of virtual keys
|
||||
bool hotkeys[HOTKEY_LAST];
|
||||
|
||||
|
||||
struct SHotkeyInfo
|
||||
{
|
||||
int code;
|
||||
const char* name;
|
||||
int defaultmapping1, defaultmapping2;
|
||||
};
|
||||
|
||||
// Will phase out the default shortcuts at sometime in the near future
|
||||
// (or, failing that, will update them so they can do the tricky stuff
|
||||
// the config file can.)
|
||||
|
||||
static SHotkeyInfo hotkeyInfo[] =
|
||||
{
|
||||
{ HOTKEY_EXIT, "exit", SDLK_ESCAPE, 0 },
|
||||
{ HOTKEY_SCREENSHOT, "screenshot", SDLK_PRINT, 0 },
|
||||
{ HOTKEY_BIGSCREENSHOT, "bigscreenshot", 0, 0 },
|
||||
{ HOTKEY_WIREFRAME, "wireframe", SDLK_w, 0 },
|
||||
{ HOTKEY_TOGGLEFULLSCREEN, "togglefullscreen", 0, 0 },
|
||||
{ HOTKEY_CAMERA_RESET, "camera.reset", 0, 0 },
|
||||
{ HOTKEY_CAMERA_FOLLOW, "camera.follow", 0, 0 },
|
||||
{ HOTKEY_CAMERA_ZOOM_IN, "camera.zoom.in", SDLK_PLUS, SDLK_KP_PLUS },
|
||||
{ HOTKEY_CAMERA_ZOOM_OUT, "camera.zoom.out", SDLK_MINUS, SDLK_KP_MINUS },
|
||||
{ HOTKEY_CAMERA_ZOOM_WHEEL_IN, "camera.zoom.wheel.in", MOUSE_WHEELUP, 0 },
|
||||
{ HOTKEY_CAMERA_ZOOM_WHEEL_OUT, "camera.zoom.wheel.out", MOUSE_WHEELDOWN, 0 },
|
||||
{ HOTKEY_CAMERA_ROTATE_CW, "camera.rotate.cw", 0, 0 },
|
||||
{ HOTKEY_CAMERA_ROTATE_CCW, "camera.rotate.ccw", 0, 0 },
|
||||
{ HOTKEY_CAMERA_ROTATE_UP, "camera.rotate.up", 0, 0 },
|
||||
{ HOTKEY_CAMERA_ROTATE_DOWN, "camera.rotate.down", 0, 0 },
|
||||
{ HOTKEY_CAMERA_ROTATE_WHEEL_CW, "camera.rotate.wheel.cw", 0, 0 },
|
||||
{ HOTKEY_CAMERA_ROTATE_WHEEL_CCW, "camera.rotate.wheel.ccw", 0, 0 },
|
||||
{ HOTKEY_CAMERA_PAN, "camera.pan", MOUSE_MIDDLE, 0 },
|
||||
{ HOTKEY_CAMERA_PAN_KEYBOARD, "camera.pan.keyboard", 0, 0 },
|
||||
{ HOTKEY_CAMERA_LEFT, "camera.left", SDLK_LEFT, 0 },
|
||||
{ HOTKEY_CAMERA_RIGHT, "camera.right", SDLK_RIGHT, 0 },
|
||||
{ HOTKEY_CAMERA_UP, "camera.up", SDLK_UP, 0 },
|
||||
{ HOTKEY_CAMERA_DOWN, "camera.down", SDLK_DOWN, 0 },
|
||||
{ HOTKEY_CAMERA_CINEMA_ADD, "camera.cinema.add", SDLK_l, 0 },
|
||||
{ HOTKEY_CAMERA_CINEMA_DELETE, "camera.cinema.delete", SDLK_u, 0 },
|
||||
{ HOTKEY_CAMERA_CINEMA_DELETE_ALL, "camera.cinema.delete.all", SDLK_r, 0 },
|
||||
{ HOTKEY_CAMERA_CINEMA_QUEUE, "camera.cinema.write", SDLK_i, 0},
|
||||
{ HOTKEY_CONSOLE_TOGGLE, "console.toggle", SDLK_F1, 0 },
|
||||
{ HOTKEY_CONSOLE_COPY, "console.copy", 0, 0 },
|
||||
{ HOTKEY_CONSOLE_PASTE, "console.paste", 0, 0 },
|
||||
{ HOTKEY_SELECTION_ADD, "selection.add", SDLK_LSHIFT, SDLK_RSHIFT },
|
||||
{ HOTKEY_SELECTION_REMOVE, "selection.remove", SDLK_LCTRL, SDLK_RCTRL },
|
||||
{ HOTKEY_SELECTION_GROUP_0, "selection.group.0", SDLK_0, 0, },
|
||||
{ HOTKEY_SELECTION_GROUP_1, "selection.group.1", SDLK_1, 0, },
|
||||
{ HOTKEY_SELECTION_GROUP_2, "selection.group.2", SDLK_2, 0, },
|
||||
{ HOTKEY_SELECTION_GROUP_3, "selection.group.3", SDLK_3, 0, },
|
||||
{ HOTKEY_SELECTION_GROUP_4, "selection.group.4", SDLK_4, 0, },
|
||||
{ HOTKEY_SELECTION_GROUP_5, "selection.group.5", SDLK_5, 0, },
|
||||
{ HOTKEY_SELECTION_GROUP_6, "selection.group.6", SDLK_6, 0, },
|
||||
{ HOTKEY_SELECTION_GROUP_7, "selection.group.7", SDLK_7, 0, },
|
||||
{ HOTKEY_SELECTION_GROUP_8, "selection.group.8", SDLK_8, 0, },
|
||||
{ HOTKEY_SELECTION_GROUP_9, "selection.group.9", SDLK_9, 0, },
|
||||
{ HOTKEY_SELECTION_GROUP_10, "selection.group.10", 0, 0, },
|
||||
{ HOTKEY_SELECTION_GROUP_11, "selection.group.11", 0, 0, },
|
||||
{ HOTKEY_SELECTION_GROUP_12, "selection.group.12", 0, 0, },
|
||||
{ HOTKEY_SELECTION_GROUP_13, "selection.group.13", 0, 0, },
|
||||
{ HOTKEY_SELECTION_GROUP_14, "selection.group.14", 0, 0, },
|
||||
{ HOTKEY_SELECTION_GROUP_15, "selection.group.15", 0, 0, },
|
||||
{ HOTKEY_SELECTION_GROUP_16, "selection.group.16", 0, 0, },
|
||||
{ HOTKEY_SELECTION_GROUP_17, "selection.group.17", 0, 0, },
|
||||
{ HOTKEY_SELECTION_GROUP_18, "selection.group.18", 0, 0, },
|
||||
{ HOTKEY_SELECTION_GROUP_19, "selection.group.19", 0, 0, },
|
||||
{ HOTKEY_SELECTION_GROUP_ADD, "selection.group.add", SDLK_LSHIFT, SDLK_RSHIFT },
|
||||
{ HOTKEY_SELECTION_GROUP_SAVE, "selection.group.save", SDLK_LCTRL, SDLK_RCTRL },
|
||||
{ HOTKEY_SELECTION_GROUP_SNAP, "selection.group.snap", SDLK_LALT, SDLK_RALT },
|
||||
{ HOTKEY_SELECTION_SNAP, "selection.snap", SDLK_HOME, 0 },
|
||||
{ HOTKEY_ORDER_QUEUE, "order.queue", SDLK_LSHIFT, SDLK_RSHIFT },
|
||||
{ HOTKEY_CONTEXTORDER_NEXT, "contextorder.next", SDLK_RIGHTBRACKET, 0 },
|
||||
{ HOTKEY_CONTEXTORDER_PREVIOUS, "contextorder.previous", SDLK_LEFTBRACKET, 0 },
|
||||
{ HOTKEY_HIGHLIGHTALL, "highlightall", SDLK_o, 0 },
|
||||
{ HOTKEY_PROFILE_TOGGLE, "profile.toggle", SDLK_F11, 0 },
|
||||
{ HOTKEY_PROFILE_SAVE, "profile.save", 0, 0 },
|
||||
{ HOTKEY_PLAYMUSIC, "playmusic", SDLK_p, 0 },
|
||||
{ HOTKEY_PAUSE, "pause", SDLK_PAUSE, 0 },
|
||||
{ HOTKEY_SPEED_INCREASE, "speed.increase", 0, 0 },
|
||||
{ HOTKEY_SPEED_DECREASE, "speed.decrease", 0, 0 },
|
||||
{ HOTKEY_KILL, "killUnit", 0, 0 },
|
||||
{ HOTKEY_CHAT, "chat", 0, 0 }
|
||||
};
|
||||
|
||||
/* SDL-type ends */
|
||||
|
||||
/* GUI-type */
|
||||
|
||||
struct SHotkeyMappingGui
|
||||
{
|
||||
CStr mapsTo;
|
||||
bool negation;
|
||||
std::vector<int> requires;
|
||||
SHotkeyMappingGui() : mapsTo(-1) {}
|
||||
};
|
||||
|
||||
typedef std::vector<SHotkeyMappingGui> GuiMapping;
|
||||
|
||||
// A mapping of keycodes onto sets of hotkey name strings (e.g. '[hotkey.]camera.reset')
|
||||
static GuiMapping hotkeyMapGui[HK_MAX_KEYCODES];
|
||||
|
||||
typedef std::vector<CStr> GuiObjectList; // A list of GUI objects
|
||||
typedef std::map<CStr,GuiObjectList> GuiHotkeyMap; // A mapping of name strings to lists of GUI objects that they trigger
|
||||
|
||||
static GuiHotkeyMap guiHotkeyMap;
|
||||
|
||||
// Look up a key binding in the config file and set the mappings for
|
||||
// Look up each key binding in the config file and set the mappings for
|
||||
// all key combinations that trigger it.
|
||||
static void setBindings( const CStr& hotkeyName, int integerMapping = -1 )
|
||||
static void LoadConfigBindings()
|
||||
{
|
||||
CConfigValueSet* binding = g_ConfigDB.GetValues( CFG_USER, CStr( "hotkey." ) + hotkeyName );
|
||||
if( binding )
|
||||
std::vector<std::pair<CStr, CConfigValueSet> > bindings = g_ConfigDB.GetValuesWithPrefix( CFG_USER, CStr( "hotkey." ));
|
||||
|
||||
CParser multikeyParser;
|
||||
multikeyParser.InputTaskType( "multikey", "<[~$arg(_negate)]$value_+_>_[~$arg(_negate)]$value" );
|
||||
|
||||
for( std::vector<std::pair<CStr, CConfigValueSet> >::iterator bindingsIt = bindings.begin(); bindingsIt != bindings.end(); ++bindingsIt )
|
||||
{
|
||||
int mapping;
|
||||
|
||||
CConfigValueSet::iterator it;
|
||||
CParser multikeyParser;
|
||||
multikeyParser.InputTaskType( "multikey", "<[!$arg(_negate)][~$arg(_negate)]$value_+_>_[!$arg(_negate)][~$arg(_negate)]$value" );
|
||||
std::string hotkeyName = bindingsIt->first.substr(7); // strip the "hotkey." prefix
|
||||
|
||||
// Iterate through the bindings for this event...
|
||||
|
||||
for( it = binding->begin(); it != binding->end(); it++ )
|
||||
for( CConfigValueSet::iterator it = bindingsIt->second.begin(); it != bindingsIt->second.end(); ++it )
|
||||
{
|
||||
std::string hotkey;
|
||||
if( it->GetString( hotkey ) )
|
||||
{
|
||||
std::vector<int> keyCombination;
|
||||
std::vector<SKey> keyCombination;
|
||||
|
||||
CParserLine multikeyIdentifier;
|
||||
multikeyIdentifier.ParseString( multikeyParser, hotkey );
|
||||
@ -191,7 +81,7 @@ static void setBindings( const CStr& hotkeyName, int integerMapping = -1 )
|
||||
|
||||
for( size_t t = 0; t < multikeyIdentifier.GetArgCount(); t++ )
|
||||
{
|
||||
|
||||
|
||||
if( multikeyIdentifier.GetArgString( (int)t, hotkey ) )
|
||||
{
|
||||
if( hotkey == "_negate" )
|
||||
@ -201,123 +91,81 @@ static void setBindings( const CStr& hotkeyName, int integerMapping = -1 )
|
||||
}
|
||||
|
||||
// Attempt decode as key name
|
||||
mapping = FindKeyCode( hotkey );
|
||||
int mapping = FindKeyCode( hotkey );
|
||||
|
||||
// Attempt to decode as a negation of a keyname
|
||||
// Yes, it's going a bit far, perhaps.
|
||||
// Too powerful for most uses, probably.
|
||||
// However, it got some hardcoding out of the engine.
|
||||
// Thus it makes me happy.
|
||||
|
||||
if( !mapping )
|
||||
if( !it->GetInt( mapping ) ) // Attempt decode as key code
|
||||
{
|
||||
LOG(CLogger::Warning, L"hotkey", L"Couldn't map '%hs'", hotkey.c_str() );
|
||||
continue;
|
||||
}
|
||||
|
||||
if( negateNext ) mapping |= HOTKEY_NEGATION_FLAG;
|
||||
if( !mapping )
|
||||
{
|
||||
LOGWARNING(L"Hotkey mapping used invalid key '%hs'", hotkey.c_str() );
|
||||
continue;
|
||||
}
|
||||
|
||||
SKey key = { (SDLKey)mapping, negateNext };
|
||||
keyCombination.push_back(key);
|
||||
|
||||
negateNext = false;
|
||||
|
||||
keyCombination.push_back( mapping );
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<int>::iterator itKey, itKey2;
|
||||
|
||||
SHotkeyMapping bindCode;
|
||||
SHotkeyMappingGui bindName;
|
||||
std::vector<SKey>::iterator itKey, itKey2;
|
||||
|
||||
for( itKey = keyCombination.begin(); itKey != keyCombination.end(); itKey++ )
|
||||
{
|
||||
bindName.mapsTo = hotkeyName;
|
||||
bindName.negation = ( ( *itKey & HOTKEY_NEGATION_FLAG ) ? true : false );
|
||||
bindName.requires.clear();
|
||||
if( integerMapping != -1 )
|
||||
{
|
||||
bindCode.mapsTo = integerMapping;
|
||||
bindCode.negation = ( ( *itKey & HOTKEY_NEGATION_FLAG ) ? true : false );
|
||||
bindCode.requires.clear();
|
||||
}
|
||||
SHotkeyMapping bindCode;
|
||||
|
||||
bindCode.name = hotkeyName;
|
||||
bindCode.negated = itKey->negated;
|
||||
|
||||
for( itKey2 = keyCombination.begin(); itKey2 != keyCombination.end(); itKey2++ )
|
||||
{
|
||||
// Push any auxiliary keys.
|
||||
if( itKey != itKey2 )
|
||||
{
|
||||
bindName.requires.push_back( *itKey2 );
|
||||
if( integerMapping != -1 )
|
||||
bindCode.requires.push_back( *itKey2 );
|
||||
}
|
||||
bindCode.requires.push_back( *itKey2 );
|
||||
}
|
||||
|
||||
hotkeyMapGui[*itKey & ~HOTKEY_NEGATION_FLAG].push_back( bindName );
|
||||
if( integerMapping != -1 )
|
||||
hotkeyMap[*itKey & ~HOTKEY_NEGATION_FLAG].push_back( bindCode );
|
||||
g_HotkeyMap[itKey->code].push_back( bindCode );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if( integerMapping != -1 )
|
||||
{
|
||||
SHotkeyMapping bind[2];
|
||||
bind[0].mapsTo = integerMapping;
|
||||
bind[1].mapsTo = integerMapping;
|
||||
bind[0].requires.clear();
|
||||
bind[1].requires.clear();
|
||||
bind[0].negation = false;
|
||||
bind[1].negation = false;
|
||||
hotkeyMap[ hotkeyInfo[integerMapping].defaultmapping1 ].push_back( bind[0] );
|
||||
if( hotkeyInfo[integerMapping].defaultmapping2 )
|
||||
hotkeyMap[ hotkeyInfo[integerMapping].defaultmapping2 ].push_back( bind[1] );
|
||||
}
|
||||
}
|
||||
|
||||
void LoadHotkeys()
|
||||
{
|
||||
InitKeyNameMap();
|
||||
|
||||
for(int i = 0; i < HOTKEY_LAST; i++ )
|
||||
setBindings( hotkeyInfo[i].name, i );
|
||||
|
||||
LoadConfigBindings();
|
||||
|
||||
// Set up the state of the hotkeys given no key is down.
|
||||
// i.e. find those hotkeys triggered by all negations.
|
||||
|
||||
std::vector<SHotkeyMapping>::iterator it;
|
||||
std::vector<int>::iterator j;
|
||||
bool allNegated;
|
||||
|
||||
for(int i = 1; i < HK_MAX_KEYCODES; i++ )
|
||||
for( std::map<int, KeyMapping>::iterator mapIt = g_HotkeyMap.begin(); mapIt != g_HotkeyMap.end(); ++mapIt )
|
||||
{
|
||||
for( it = hotkeyMap[i].begin(); it != hotkeyMap[i].end(); it++ )
|
||||
KeyMapping& hotkeyMap = mapIt->second;
|
||||
|
||||
for( std::vector<SHotkeyMapping>::iterator it = hotkeyMap.begin(); it != hotkeyMap.end(); it++ )
|
||||
{
|
||||
if( !it->negation )
|
||||
if( !it->negated )
|
||||
continue;
|
||||
|
||||
allNegated = true;
|
||||
bool allNegated = true;
|
||||
|
||||
for( j = it->requires.begin(); j != it->requires.end(); j++ )
|
||||
if( !( *j & HOTKEY_NEGATION_FLAG ) )
|
||||
for( std::vector<SKey>::iterator j = it->requires.begin(); j != it->requires.end(); j++ )
|
||||
if( !j->negated )
|
||||
allNegated = false;
|
||||
|
||||
debug_assert((size_t)it->mapsTo < ARRAY_SIZE(hotkeys));
|
||||
|
||||
if( allNegated )
|
||||
hotkeys[it->mapsTo] = true;
|
||||
g_HotkeyStatus[it->name] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HotkeyRegisterGuiObject( const CStr& objName, const CStr& hotkeyName )
|
||||
{
|
||||
GuiObjectList& boundTo = guiHotkeyMap[hotkeyName];
|
||||
if( boundTo.empty() )
|
||||
{
|
||||
// Load keybindings from the config file
|
||||
setBindings( hotkeyName );
|
||||
}
|
||||
boundTo.push_back( objName );
|
||||
}
|
||||
|
||||
InReaction HotkeyInputHandler( const SDL_Event_* ev )
|
||||
{
|
||||
int keycode = 0;
|
||||
@ -377,11 +225,15 @@ InReaction HotkeyInputHandler( const SDL_Event_* ev )
|
||||
HotkeyInputHandler( &phantom );
|
||||
}
|
||||
|
||||
// Check whether we have any hotkeys registered for this particular keycode
|
||||
if( g_HotkeyMap.find(keycode) == g_HotkeyMap.end() )
|
||||
return( IN_PASS );
|
||||
|
||||
// Inhibit the dispatch of hotkey events caused by printable or control keys
|
||||
// while the console is up. (But allow multiple-key - 'Ctrl+F' events, and whatever
|
||||
// key toggles the console.)
|
||||
|
||||
bool consoleCapture = false, isCapturable;
|
||||
bool consoleCapture = false;
|
||||
|
||||
if( g_Console->IsActive() && (
|
||||
( keycode == 8 ) || ( keycode == 9 ) || ( keycode == 13 ) || /* Editing */
|
||||
@ -390,11 +242,6 @@ InReaction HotkeyInputHandler( const SDL_Event_* ev )
|
||||
( keycode != SDLK_INSERT ) ) ) ) /* keys (<282) except insert */
|
||||
consoleCapture = true;
|
||||
|
||||
std::vector<SHotkeyMapping>::iterator it;
|
||||
std::vector<SHotkeyMappingGui>::iterator itGUI;
|
||||
|
||||
SDL_Event hotkeyNotification;
|
||||
|
||||
// Here's an interesting bit:
|
||||
// If you have an event bound to, say, 'F', and another to, say, 'Ctrl+F', pressing
|
||||
// 'F' while control is down would normally fire off both.
|
||||
@ -409,66 +256,53 @@ InReaction HotkeyInputHandler( const SDL_Event_* ev )
|
||||
|
||||
// -- KEYDOWN SECTION --
|
||||
|
||||
// SDL-events bit
|
||||
|
||||
size_t closestMap = 0; // avoid "uninitialized" warning
|
||||
const char* closestMapName = NULL;
|
||||
size_t closestMapMatch = 0;
|
||||
|
||||
for( it = hotkeyMap[keycode].begin(); it < hotkeyMap[keycode].end(); it++ )
|
||||
for( std::vector<SHotkeyMapping>::iterator it = g_HotkeyMap[keycode].begin(); it < g_HotkeyMap[keycode].end(); it++ )
|
||||
{
|
||||
// If a key has been pressed, and this event triggers on it's release, skip it.
|
||||
// Similarly, if the key's been released and the event triggers on a keypress, skip it.
|
||||
if( it->negation == typeKeyDown )
|
||||
if( it->negated == typeKeyDown )
|
||||
continue;
|
||||
|
||||
// Check to see if all auxiliary keys are down
|
||||
|
||||
std::vector<int>::iterator itKey;
|
||||
bool accept = true;
|
||||
isCapturable = true;
|
||||
bool isCapturable = true;
|
||||
|
||||
for( itKey = it->requires.begin(); itKey != it->requires.end(); itKey++ )
|
||||
for( std::vector<SKey>::iterator itKey = it->requires.begin(); itKey != it->requires.end(); itKey++ )
|
||||
{
|
||||
int keyCode = *itKey & ~HOTKEY_NEGATION_FLAG; // Clear the negation-modifier bit
|
||||
bool rqdState = ( *itKey & HOTKEY_NEGATION_FLAG ) == 0;
|
||||
bool rqdState = !itKey->negated;
|
||||
|
||||
// debug_assert( !rqdState );
|
||||
|
||||
if( keyCode < SDLK_LAST )
|
||||
if( (int)itKey->code < SDLK_LAST )
|
||||
{
|
||||
if( g_keys[keyCode] != rqdState ) accept = false;
|
||||
if( g_keys[itKey->code] != rqdState ) accept = false;
|
||||
}
|
||||
else if( keyCode < UNIFIED_SHIFT )
|
||||
else if( (int)itKey->code < UNIFIED_SHIFT )
|
||||
{
|
||||
if( g_mouse_buttons[keyCode-SDLK_LAST] != rqdState ) accept = false;
|
||||
if( g_mouse_buttons[itKey->code - SDLK_LAST] != rqdState ) accept = false;
|
||||
}
|
||||
else if( (size_t)(keyCode-UNIFIED_SHIFT) < ARRAY_SIZE(unified) )
|
||||
else if( (int)itKey->code < UNIFIED_LAST )
|
||||
{
|
||||
if( unified[keyCode-UNIFIED_SHIFT] != rqdState ) accept = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
debug_printf(L"keyCode = %i\n", keyCode);
|
||||
debug_warn(L"keyCode out of range in GUI hotkey requirements");
|
||||
if( unified[itKey->code - UNIFIED_SHIFT] != rqdState ) accept = false;
|
||||
}
|
||||
|
||||
// If this event requires a multiple keypress (with the exception
|
||||
// of shift+key combinations) the console won't inhibit it.
|
||||
if( rqdState && ( *itKey != SDLK_RSHIFT ) && ( *itKey != SDLK_LSHIFT ) )
|
||||
if( rqdState && ( itKey->code != SDLK_RSHIFT ) && ( itKey->code != SDLK_LSHIFT ) )
|
||||
isCapturable = false;
|
||||
}
|
||||
|
||||
if( it->mapsTo == HOTKEY_CONSOLE_TOGGLE ) isCapturable = false; // Because that would be silly.
|
||||
if( it->name == "console.toggle" ) isCapturable = false; // Because that would be silly.
|
||||
|
||||
debug_assert((size_t)it->mapsTo < ARRAY_SIZE(hotkeys));
|
||||
|
||||
if( accept && !( isCapturable && consoleCapture ) )
|
||||
{
|
||||
hotkeys[it->mapsTo] = true;
|
||||
g_HotkeyStatus[it->name] = true;
|
||||
if( it->requires.size() >= closestMapMatch )
|
||||
{
|
||||
// Only if it's a more precise match, and it either isn't capturable or the console won't capture it.
|
||||
closestMap = it->mapsTo;
|
||||
closestMapName = it->name.c_str();
|
||||
closestMapMatch = it->requires.size() + 1;
|
||||
}
|
||||
}
|
||||
@ -476,125 +310,51 @@ InReaction HotkeyInputHandler( const SDL_Event_* ev )
|
||||
|
||||
if( closestMapMatch )
|
||||
{
|
||||
SDL_Event hotkeyNotification;
|
||||
hotkeyNotification.type = SDL_HOTKEYDOWN;
|
||||
hotkeyNotification.user.code = (int)closestMap;
|
||||
hotkeyNotification.user.data1 = const_cast<char*>(closestMapName);
|
||||
SDL_PushEvent( &hotkeyNotification );
|
||||
}
|
||||
// GUI bit... could do with some optimization later.
|
||||
|
||||
CStr closestMapName = -1;
|
||||
closestMapMatch = 0;
|
||||
|
||||
for( itGUI = hotkeyMapGui[keycode].begin(); itGUI != hotkeyMapGui[keycode].end(); itGUI++ )
|
||||
{
|
||||
// If a key has been pressed, and this event triggers on it's release, skip it.
|
||||
// Similarly, if the key's been released and the event triggers on a keypress, skip it.
|
||||
if( itGUI->negation == typeKeyDown )
|
||||
continue;
|
||||
|
||||
// Check to see if all auxiliary keys are down
|
||||
|
||||
std::vector<int>::iterator itKey;
|
||||
bool accept = true;
|
||||
isCapturable = true;
|
||||
|
||||
for( itKey = itGUI->requires.begin(); itKey != itGUI->requires.end(); itKey++ )
|
||||
{
|
||||
int keyCode = *itKey & ~HOTKEY_NEGATION_FLAG; // Clear the negation-modifier bit
|
||||
bool rqdState = ( *itKey & HOTKEY_NEGATION_FLAG ) == 0;
|
||||
|
||||
if( keyCode < SDLK_LAST )
|
||||
{
|
||||
if( g_keys[keyCode] != rqdState ) accept = false;
|
||||
}
|
||||
else if( keyCode < UNIFIED_SHIFT )
|
||||
{
|
||||
if( g_mouse_buttons[keyCode-SDLK_LAST] != rqdState ) accept = false;
|
||||
}
|
||||
else if( (size_t)(keyCode-UNIFIED_SHIFT) < ARRAY_SIZE(unified) )
|
||||
{
|
||||
if( unified[keyCode-UNIFIED_SHIFT] != rqdState ) accept = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
debug_printf(L"keyCode = %i\n", keyCode);
|
||||
debug_warn(L"keyCode out of range in GUI hotkey requirements");
|
||||
}
|
||||
|
||||
// If this event requires a multiple keypress (with the exception
|
||||
// of shift+key combinations) the console won't inhibit it.
|
||||
if( rqdState && ( *itKey != SDLK_RSHIFT ) && ( *itKey != SDLK_LSHIFT ) )
|
||||
isCapturable = false;
|
||||
}
|
||||
|
||||
if( accept && !( isCapturable && consoleCapture ) )
|
||||
{
|
||||
if( itGUI->requires.size() >= closestMapMatch )
|
||||
{
|
||||
closestMapName = itGUI->mapsTo;
|
||||
closestMapMatch = itGUI->requires.size() + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
// GUI-objects bit
|
||||
// This fragment is an obvious candidate for rewriting when speed becomes an issue.
|
||||
|
||||
if( closestMapMatch )
|
||||
{
|
||||
GuiHotkeyMap::iterator map_it;
|
||||
GuiObjectList::iterator obj_it;
|
||||
map_it = guiHotkeyMap.find( closestMapName );
|
||||
if( map_it != guiHotkeyMap.end() )
|
||||
{
|
||||
GuiObjectList& targets = map_it->second;
|
||||
for( obj_it = targets.begin(); obj_it != targets.end(); obj_it++ )
|
||||
{
|
||||
hotkeyNotification.type = SDL_GUIHOTKEYPRESS;
|
||||
hotkeyNotification.user.data1 = &(*obj_it);
|
||||
SDL_PushEvent( &hotkeyNotification );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -- KEYUP SECTION --
|
||||
|
||||
for( it = hotkeyMap[keycode].begin(); it < hotkeyMap[keycode].end(); it++ )
|
||||
for( std::vector<SHotkeyMapping>::iterator it = g_HotkeyMap[keycode].begin(); it < g_HotkeyMap[keycode].end(); it++ )
|
||||
{
|
||||
// If it's a keydown event, won't cause HotKeyUps in anything that doesn't
|
||||
// use this key negated => skip them
|
||||
// If it's a keyup event, won't cause HotKeyUps in anything that does use
|
||||
// this key negated => skip them too.
|
||||
if( it->negation != typeKeyDown )
|
||||
if( it->negated != typeKeyDown )
|
||||
continue;
|
||||
|
||||
// Check to see if all auxiliary keys are down
|
||||
|
||||
std::vector<int>::iterator itKey;
|
||||
bool accept = true;
|
||||
|
||||
for( itKey = it->requires.begin(); itKey != it->requires.end(); itKey++ )
|
||||
for( std::vector<SKey>::iterator itKey = it->requires.begin(); itKey != it->requires.end(); itKey++ )
|
||||
{
|
||||
if( *itKey < SDLK_LAST )
|
||||
bool rqdState = !itKey->negated;
|
||||
|
||||
if( (int)itKey->code < SDLK_LAST )
|
||||
{
|
||||
if( !g_keys[*itKey] ) accept = false;
|
||||
if( g_keys[itKey->code] != rqdState ) accept = false;
|
||||
}
|
||||
else if( *itKey < UNIFIED_SHIFT )
|
||||
else if( (int)itKey->code < UNIFIED_SHIFT )
|
||||
{
|
||||
if( !g_mouse_buttons[(*itKey)-SDLK_LAST] ) accept = false;
|
||||
if( g_mouse_buttons[itKey->code - SDLK_LAST] != rqdState ) accept = false;
|
||||
}
|
||||
else if( *itKey < HOTKEY_NEGATION_FLAG )
|
||||
else if( (int)itKey->code < UNIFIED_LAST )
|
||||
{
|
||||
if( !unified[(*itKey)-UNIFIED_SHIFT] ) accept = false;
|
||||
if( unified[itKey->code - UNIFIED_SHIFT] != rqdState ) accept = false;
|
||||
}
|
||||
}
|
||||
|
||||
debug_assert((size_t)it->mapsTo < ARRAY_SIZE(hotkeys));
|
||||
|
||||
if( accept )
|
||||
{
|
||||
hotkeys[it->mapsTo] = false;
|
||||
g_HotkeyStatus[it->name] = false;
|
||||
SDL_Event hotkeyNotification;
|
||||
hotkeyNotification.type = SDL_HOTKEYUP;
|
||||
hotkeyNotification.user.code = it->mapsTo;
|
||||
hotkeyNotification.user.data1 = const_cast<char*>(it->name.c_str());
|
||||
SDL_PushEvent( &hotkeyNotification );
|
||||
}
|
||||
}
|
||||
@ -602,23 +362,7 @@ InReaction HotkeyInputHandler( const SDL_Event_* ev )
|
||||
return( IN_PASS );
|
||||
}
|
||||
|
||||
CStr HotkeyGetName(int hotkey)
|
||||
{
|
||||
if (hotkey < 0 || hotkey >= HOTKEY_LAST)
|
||||
return "";
|
||||
return hotkeyInfo[hotkey].name;
|
||||
}
|
||||
|
||||
bool HotkeyRespondsTo(int hotkey, int sdlkey)
|
||||
{
|
||||
for (KeyMapping::iterator it = hotkeyMap[sdlkey].begin(); it != hotkeyMap[sdlkey].end(); ++it)
|
||||
if (it->mapsTo == hotkey)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool HotkeyIsPressed(const CStr& keyname)
|
||||
{
|
||||
return hotkeys[FindKeyCode(keyname)];
|
||||
return g_HotkeyStatus[keyname];
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2009 Wildfire Games.
|
||||
/* Copyright (C) 2010 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
@ -15,25 +15,20 @@
|
||||
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PS_HOTKEY_H
|
||||
#define PS_HOTKEY_H
|
||||
#ifndef INCLUDED_HOTKEY
|
||||
#define INCLUDED_HOTKEY
|
||||
|
||||
// Hotkey.h
|
||||
//
|
||||
// Constant definitions and a couple of exports for the hotkey processor
|
||||
//
|
||||
// Hotkeys can be mapped onto SDL events (for use internal to the engine),
|
||||
// or used to trigger activation of GUI buttons.
|
||||
//
|
||||
// Adding a hotkey (SDL event type):
|
||||
//
|
||||
// - Define your constant in the enum, just below;
|
||||
// - Add an entry to hotkeyInfo[], in Hotkey.cpp
|
||||
// first column is this constant, second is the config string (minus 'hotkey.') it maps to
|
||||
// third and fourth are the default keys, used if the config file doesn't contain that string.
|
||||
// - Create an input handler for SDL_HOTKEYDOWN, SDL_HOTKEYUP, or poll the hotkeys[] array.
|
||||
// For SDL_HOTKEYDOWN, SDL_HOTKEYUP, the constant is passed in as ev->ev.user.code.
|
||||
// - Add some bindings to the config file.
|
||||
/**
|
||||
* @file
|
||||
* Hotkey system.
|
||||
*
|
||||
* Hotkeys consist of a name (an arbitrary string), and a key mapping.
|
||||
* The names and mappings are loaded from the config system (any
|
||||
* config setting with the name prefix "hotkey.").
|
||||
* When a hotkey is pressed or released, SDL_HOTKEYDOWN and SDL_HOTKEYUP
|
||||
* events are triggered, with the hotkey name stored in ev.user.data1
|
||||
* as a const char*.
|
||||
*/
|
||||
|
||||
#include "CStr.h"
|
||||
#include "lib/input.h"
|
||||
@ -45,105 +40,10 @@
|
||||
|
||||
const int SDL_HOTKEYDOWN = SDL_USEREVENT;
|
||||
const int SDL_HOTKEYUP = SDL_USEREVENT + 1;
|
||||
const int SDL_GUIHOTKEYPRESS = SDL_USEREVENT + 2;
|
||||
|
||||
enum
|
||||
{
|
||||
HOTKEY_EXIT,
|
||||
HOTKEY_SCREENSHOT,
|
||||
HOTKEY_BIGSCREENSHOT,
|
||||
HOTKEY_WIREFRAME,
|
||||
HOTKEY_TOGGLEFULLSCREEN,
|
||||
HOTKEY_CAMERA_RESET,
|
||||
HOTKEY_CAMERA_FOLLOW,
|
||||
HOTKEY_CAMERA_ZOOM_IN,
|
||||
HOTKEY_CAMERA_ZOOM_OUT,
|
||||
HOTKEY_CAMERA_ZOOM_WHEEL_IN,
|
||||
HOTKEY_CAMERA_ZOOM_WHEEL_OUT,
|
||||
HOTKEY_CAMERA_ROTATE_CW,
|
||||
HOTKEY_CAMERA_ROTATE_CCW,
|
||||
HOTKEY_CAMERA_ROTATE_UP,
|
||||
HOTKEY_CAMERA_ROTATE_DOWN,
|
||||
HOTKEY_CAMERA_ROTATE_WHEEL_CW,
|
||||
HOTKEY_CAMERA_ROTATE_WHEEL_CCW,
|
||||
HOTKEY_CAMERA_PAN,
|
||||
HOTKEY_CAMERA_PAN_KEYBOARD,
|
||||
HOTKEY_CAMERA_LEFT,
|
||||
HOTKEY_CAMERA_RIGHT,
|
||||
HOTKEY_CAMERA_UP,
|
||||
HOTKEY_CAMERA_DOWN,
|
||||
HOTKEY_CAMERA_CINEMA_ADD,
|
||||
HOTKEY_CAMERA_CINEMA_DELETE,
|
||||
HOTKEY_CAMERA_CINEMA_DELETE_ALL,
|
||||
HOTKEY_CAMERA_CINEMA_QUEUE,
|
||||
HOTKEY_CONSOLE_TOGGLE,
|
||||
HOTKEY_CONSOLE_COPY,
|
||||
HOTKEY_CONSOLE_PASTE,
|
||||
HOTKEY_SELECTION_ADD,
|
||||
HOTKEY_SELECTION_REMOVE,
|
||||
HOTKEY_SELECTION_GROUP_0,
|
||||
HOTKEY_SELECTION_GROUP_1,
|
||||
HOTKEY_SELECTION_GROUP_2,
|
||||
HOTKEY_SELECTION_GROUP_3,
|
||||
HOTKEY_SELECTION_GROUP_4,
|
||||
HOTKEY_SELECTION_GROUP_5,
|
||||
HOTKEY_SELECTION_GROUP_6,
|
||||
HOTKEY_SELECTION_GROUP_7,
|
||||
HOTKEY_SELECTION_GROUP_8,
|
||||
HOTKEY_SELECTION_GROUP_9,
|
||||
HOTKEY_SELECTION_GROUP_10,
|
||||
HOTKEY_SELECTION_GROUP_11,
|
||||
HOTKEY_SELECTION_GROUP_12,
|
||||
HOTKEY_SELECTION_GROUP_13,
|
||||
HOTKEY_SELECTION_GROUP_14,
|
||||
HOTKEY_SELECTION_GROUP_15,
|
||||
HOTKEY_SELECTION_GROUP_16,
|
||||
HOTKEY_SELECTION_GROUP_17,
|
||||
HOTKEY_SELECTION_GROUP_18,
|
||||
HOTKEY_SELECTION_GROUP_19,
|
||||
HOTKEY_SELECTION_GROUP_ADD,
|
||||
HOTKEY_SELECTION_GROUP_SAVE,
|
||||
HOTKEY_SELECTION_GROUP_SNAP,
|
||||
HOTKEY_SELECTION_SNAP,
|
||||
HOTKEY_ORDER_QUEUE,
|
||||
HOTKEY_CONTEXTORDER_NEXT,
|
||||
HOTKEY_CONTEXTORDER_PREVIOUS,
|
||||
HOTKEY_HIGHLIGHTALL,
|
||||
HOTKEY_PROFILE_TOGGLE,
|
||||
HOTKEY_PROFILE_SAVE,
|
||||
HOTKEY_PLAYMUSIC,
|
||||
HOTKEY_PAUSE,
|
||||
HOTKEY_SPEED_INCREASE,
|
||||
HOTKEY_SPEED_DECREASE,
|
||||
HOTKEY_KILL,
|
||||
HOTKEY_CHAT,
|
||||
|
||||
HOTKEY_LAST,
|
||||
|
||||
HOTKEY_NEGATION_FLAG = 65536
|
||||
};
|
||||
|
||||
extern void LoadHotkeys();
|
||||
extern InReaction HotkeyInputHandler(const SDL_Event_* ev);
|
||||
extern void HotkeyRegisterGuiObject(const CStr& objName, const CStr& hotkeyName);
|
||||
|
||||
/**
|
||||
* @return the name of the specified HOTKEY_*, or empty string if not defined
|
||||
**/
|
||||
extern CStr HotkeyGetName(int hotkey);
|
||||
|
||||
/**
|
||||
* @return whether the specified HOTKEY_* responds to the specified SDLK_*
|
||||
* (mainly for the screenshot system to know whether it needs to override
|
||||
* the printscreen screen). Ignores modifier keys.
|
||||
**/
|
||||
extern bool HotkeyRespondsTo(int hotkey, int sdlkey);
|
||||
|
||||
/**
|
||||
* @return whether one of the key combinations for the given hotkey is pressed
|
||||
**/
|
||||
extern bool HotkeyIsPressed(const CStr& keyname);
|
||||
|
||||
extern bool hotkeys[HOTKEY_LAST];
|
||||
|
||||
#endif // PS_HOTKEY_H
|
||||
#endif // INCLUDED_HOTKEY
|
||||
|
@ -40,7 +40,8 @@ enum {
|
||||
UNIFIED_CTRL,
|
||||
UNIFIED_ALT,
|
||||
UNIFIED_META,
|
||||
UNIFIED_SUPER
|
||||
UNIFIED_SUPER,
|
||||
UNIFIED_LAST
|
||||
};
|
||||
|
||||
#endif // #ifndef INCLUDED_KEYNAME
|
||||
|
@ -286,7 +286,9 @@ InReaction CProfileViewer::Input(const SDL_Event_* ev)
|
||||
break;
|
||||
}
|
||||
case SDL_HOTKEYDOWN:
|
||||
if( ev->ev.user.code == HOTKEY_PROFILE_TOGGLE )
|
||||
std::string hotkey = static_cast<const char*>(ev->ev.user.data1);
|
||||
|
||||
if( hotkey == "profile.toggle" )
|
||||
{
|
||||
if (!m->profileVisible)
|
||||
{
|
||||
@ -319,7 +321,7 @@ InReaction CProfileViewer::Input(const SDL_Event_* ev)
|
||||
}
|
||||
return( IN_HANDLED );
|
||||
}
|
||||
else if( ev->ev.user.code == HOTKEY_PROFILE_SAVE )
|
||||
else if( hotkey == "profile.save" )
|
||||
{
|
||||
SaveToFile();
|
||||
return( IN_HANDLED );
|
||||
|
Loading…
Reference in New Issue
Block a user