Files
barrier/cmd/launcher/CHotkeyOptions.cpp
crs23 b728885e25 Applied patch 1547642, key broadcasting. Modified the patch to conform
to style guidelines.  Also enhanced it to allow binding hot keys to not
only toggling broadcasting but also turning it on or off, resized the
hot key dialog to accommodate the keyboard broadcasting option, changed
the configuration string for the keyboard broadcasting option, and
added documentation.

This change does not include the VS8 fix included in the patch.
2007-09-10 00:59:51 +00:00

1939 lines
47 KiB
C++

/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2006 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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.
*/
#include "CArchMiscWindows.h"
#include "CMSWindowsKeyState.h"
#include "CConfig.h"
#include "CHotkeyOptions.h"
#include "CStringUtil.h"
#include "LaunchUtil.h"
#include "resource.h"
#if !defined(WM_XBUTTONDOWN)
#define WM_XBUTTONDOWN 0x020B
#define WM_XBUTTONUP 0x020C
#define WM_XBUTTONDBLCLK 0x020D
#define XBUTTON1 0x0001
#define XBUTTON2 0x0002
#endif
//
// CAdvancedOptions
//
CHotkeyOptions* CHotkeyOptions::s_singleton = NULL;
CHotkeyOptions::CHotkeyOptions(HWND parent, CConfig* config) :
m_parent(parent),
m_config(config)
{
assert(s_singleton == NULL);
s_singleton = this;
}
CHotkeyOptions::~CHotkeyOptions()
{
s_singleton = NULL;
}
void
CHotkeyOptions::doModal()
{
// do dialog
m_inputFilter = m_config->getInputFilter();
DialogBoxParam(s_instance, MAKEINTRESOURCE(IDD_HOTKEY_OPTIONS),
m_parent, (DLGPROC)dlgProc, (LPARAM)this);
}
void
CHotkeyOptions::doInit(HWND hwnd)
{
m_activeRuleIndex = (UInt32)-1;
fillHotkeys(hwnd);
openRule(hwnd);
}
void
CHotkeyOptions::fillHotkeys(HWND hwnd, UInt32 select)
{
HWND rules = getItem(hwnd, IDC_HOTKEY_HOTKEYS);
SendMessage(rules, LB_RESETCONTENT, 0, 0);
for (UInt32 i = 0, n = m_inputFilter->getNumRules(); i < n; ++i) {
CInputFilter::CRule& rule = m_inputFilter->getRule(i);
SendMessage(rules, LB_INSERTSTRING, (WPARAM)-1,
(LPARAM)rule.getCondition()->format().c_str());
}
if (select < m_inputFilter->getNumRules()) {
SendMessage(rules, LB_SETCURSEL, select, 0);
}
updateHotkeysControls(hwnd);
}
void
CHotkeyOptions::updateHotkeysControls(HWND hwnd)
{
HWND child = getItem(hwnd, IDC_HOTKEY_HOTKEYS);
bool selected = (SendMessage(child, LB_GETCURSEL, 0, 0) != LB_ERR);
enableItem(hwnd, IDC_HOTKEY_ADD_HOTKEY, TRUE);
enableItem(hwnd, IDC_HOTKEY_EDIT_HOTKEY, selected);
enableItem(hwnd, IDC_HOTKEY_REMOVE_HOTKEY, selected);
}
void
CHotkeyOptions::addHotkey(HWND hwnd)
{
closeRule(hwnd);
CInputFilter::CCondition* condition = NULL;
if (editCondition(hwnd, condition)) {
m_inputFilter->addFilterRule(CInputFilter::CRule(condition));
fillHotkeys(hwnd, m_inputFilter->getNumRules() - 1);
}
else {
delete condition;
}
openRule(hwnd);
}
void
CHotkeyOptions::removeHotkey(HWND hwnd)
{
UInt32 ruleIndex = m_activeRuleIndex;
closeRule(hwnd);
m_inputFilter->removeFilterRule(ruleIndex);
UInt32 n = m_inputFilter->getNumRules();
if (n > 0 && ruleIndex >= n) {
ruleIndex = n - 1;
}
fillHotkeys(hwnd, ruleIndex);
openRule(hwnd);
}
void
CHotkeyOptions::editHotkey(HWND hwnd)
{
// save selected item in action list
HWND actions = getItem(hwnd, IDC_HOTKEY_ACTIONS);
LRESULT aIndex = SendMessage(actions, LB_GETCURSEL, 0, 0);
UInt32 index = m_activeRuleIndex;
closeRule(hwnd);
CInputFilter::CRule& rule = m_inputFilter->getRule(index);
CInputFilter::CCondition* condition = rule.getCondition()->clone();
if (editCondition(hwnd, condition)) {
rule.setCondition(condition);
fillHotkeys(hwnd, index);
}
else {
delete condition;
}
openRule(hwnd);
// restore selected item in action list
if (aIndex != LB_ERR) {
SendMessage(actions, LB_SETCURSEL, aIndex, 0);
}
}
void
CHotkeyOptions::fillActions(HWND hwnd, UInt32 select)
{
HWND actions = getItem(hwnd, IDC_HOTKEY_ACTIONS);
SendMessage(actions, LB_RESETCONTENT, 0, 0);
if (m_activeRuleIndex != (UInt32)-1) {
UInt32 n = m_activeRule.getNumActions(true);
UInt32 n2 = m_activeRule.getNumActions(false);
for (UInt32 i = 0; i < n; ++i) {
const CInputFilter::CAction& action =
m_activeRule.getAction(true, i);
CString line("A ");
line += action.format();
SendMessage(actions, LB_INSERTSTRING, (WPARAM)-1,
(LPARAM)line.c_str());
SendMessage(actions, LB_SETITEMDATA, (WPARAM)i, (LPARAM)i);
}
for (UInt32 i = 0; i < n2; ++i) {
const CInputFilter::CAction& action =
m_activeRule.getAction(false, i);
CString line("D ");
line += action.format();
SendMessage(actions, LB_INSERTSTRING, (WPARAM)-1,
(LPARAM)line.c_str());
SendMessage(actions, LB_SETITEMDATA, (WPARAM)i + n,
(LPARAM)(i | 0x80000000u));
}
if (select < n + n2) {
SendMessage(actions, LB_SETCURSEL, select, 0);
}
}
updateActionsControls(hwnd);
}
void
CHotkeyOptions::updateActionsControls(HWND hwnd)
{
HWND child = getItem(hwnd, IDC_HOTKEY_HOTKEYS);
bool active = (m_activeRuleIndex != (UInt32)-1);
child = getItem(hwnd, IDC_HOTKEY_ACTIONS);
bool selected =
(active && (SendMessage(child, LB_GETCURSEL, 0, 0) != LB_ERR));
enableItem(hwnd, IDC_HOTKEY_ADD_ACTION, active);
enableItem(hwnd, IDC_HOTKEY_EDIT_ACTION, selected);
enableItem(hwnd, IDC_HOTKEY_REMOVE_ACTION, selected);
}
void
CHotkeyOptions::addAction(HWND hwnd)
{
CInputFilter::CAction* action = NULL;
bool onActivate = true;
if (editAction(hwnd, action, onActivate)) {
m_activeRule.adoptAction(action, onActivate);
UInt32 actionIndex = m_activeRule.getNumActions(true) - 1;
if (!onActivate) {
actionIndex += m_activeRule.getNumActions(false);
}
fillActions(hwnd, actionIndex);
}
else {
delete action;
}
}
void
CHotkeyOptions::removeAction(HWND hwnd)
{
HWND child = getItem(hwnd, IDC_HOTKEY_ACTIONS);
LRESULT index = SendMessage(child, LB_GETCURSEL, 0, 0);
if (index != LB_ERR) {
UInt32 actionIndex =
(UInt32)SendMessage(child, LB_GETITEMDATA, index, 0);
bool onActivate = ((actionIndex & 0x80000000u) == 0);
actionIndex &= ~0x80000000u;
m_activeRule.removeAction(onActivate, actionIndex);
actionIndex = static_cast<UInt32>(index);
UInt32 n = m_activeRule.getNumActions(true) +
m_activeRule.getNumActions(false);
if (n > 0 && actionIndex >= n) {
actionIndex = n - 1;
}
fillActions(hwnd, actionIndex);
}
}
void
CHotkeyOptions::editAction(HWND hwnd)
{
HWND child = getItem(hwnd, IDC_HOTKEY_ACTIONS);
LRESULT index = SendMessage(child, LB_GETCURSEL, 0, 0);
if (index != LB_ERR) {
UInt32 actionIndex =
(UInt32)SendMessage(child, LB_GETITEMDATA, index, 0);
bool onActivate = ((actionIndex & 0x80000000u) == 0);
actionIndex &= ~0x80000000u;
CInputFilter::CAction* action =
m_activeRule.getAction(onActivate, actionIndex).clone();
bool newOnActivate = onActivate;
if (editAction(hwnd, action, newOnActivate)) {
if (onActivate == newOnActivate) {
m_activeRule.replaceAction(action, onActivate, actionIndex);
actionIndex = static_cast<UInt32>(index);
}
else {
m_activeRule.removeAction(onActivate, actionIndex);
m_activeRule.adoptAction(action, newOnActivate);
actionIndex = m_activeRule.getNumActions(true) - 1;
if (!newOnActivate) {
actionIndex += m_activeRule.getNumActions(false);
}
}
fillActions(hwnd, actionIndex);
}
else {
delete action;
}
}
}
bool
CHotkeyOptions::editCondition(HWND hwnd, CInputFilter::CCondition*& condition)
{
return CConditionDialog::doModal(hwnd, condition);
}
bool
CHotkeyOptions::editAction(HWND hwnd, CInputFilter::CAction*& action,
bool& onActivate)
{
return CActionDialog::doModal(hwnd, m_config, action, onActivate);
}
void
CHotkeyOptions::openRule(HWND hwnd)
{
// get the active rule and copy it, merging down/up pairs of keystroke
// and mouse button actions into single actions for the convenience of
// of the user.
HWND rules = getItem(hwnd, IDC_HOTKEY_HOTKEYS);
LRESULT index = SendMessage(rules, LB_GETCURSEL, 0, 0);
if (index != LB_ERR) {
// copy the rule as is
m_activeRuleIndex = (SInt32)index;
m_activeRule = m_inputFilter->getRule(m_activeRuleIndex);
// look for actions to combine
for (UInt32 i = 0, n = m_activeRule.getNumActions(true); i < n; ++i) {
// get next activate action
const CInputFilter::CAction* action =
&m_activeRule.getAction(true, i);
// check if it's a key or mouse action
const CInputFilter::CKeystrokeAction* keyAction =
dynamic_cast<const CInputFilter::CKeystrokeAction*>(action);
const CInputFilter::CMouseButtonAction* mouseAction =
dynamic_cast<const CInputFilter::CMouseButtonAction*>(action);
if (keyAction == NULL && mouseAction == NULL) {
continue;
}
// check for matching deactivate action
UInt32 j = (UInt32)-1;
CInputFilter::CAction* newAction = NULL;
if (keyAction != NULL) {
j = findMatchingAction(keyAction);
if (j != (UInt32)-1) {
// found a match
const IPlatformScreen::CKeyInfo* oldInfo =
keyAction->getInfo();
IPlatformScreen::CKeyInfo* newInfo =
IKeyState::CKeyInfo::alloc(*oldInfo);
newAction = new CKeystrokeDownUpAction(newInfo);
}
}
else if (mouseAction != NULL) {
j = findMatchingAction(mouseAction);
if (j != (UInt32)-1) {
// found a match
const IPlatformScreen::CButtonInfo* oldInfo =
mouseAction->getInfo();
IPlatformScreen::CButtonInfo* newInfo =
IPrimaryScreen::CButtonInfo::alloc(*oldInfo);
newAction = new CMouseButtonDownUpAction(newInfo);
}
}
// perform merge
if (newAction != NULL) {
m_activeRule.replaceAction(newAction, true, i);
m_activeRule.removeAction(false, j);
}
}
}
else {
m_activeRuleIndex = (UInt32)-1;
}
fillActions(hwnd);
}
void
CHotkeyOptions::closeRule(HWND)
{
// copy rule back to input filter, expanding merged actions into the
// two component actions.
if (m_activeRuleIndex != (UInt32)-1) {
// expand merged rules
for (UInt32 i = 0, n = m_activeRule.getNumActions(true); i < n; ++i) {
// get action
const CInputFilter::CAction* action =
&m_activeRule.getAction(true, i);
// check if it's a merged key or mouse action
const CKeystrokeDownUpAction* keyAction =
dynamic_cast<const CKeystrokeDownUpAction*>(action);
const CMouseButtonDownUpAction* mouseAction =
dynamic_cast<const CMouseButtonDownUpAction*>(action);
if (keyAction == NULL && mouseAction == NULL) {
continue;
}
// expand
if (keyAction != NULL) {
const IPlatformScreen::CKeyInfo* oldInfo =
keyAction->getInfo();
IPlatformScreen::CKeyInfo* newInfo =
IKeyState::CKeyInfo::alloc(*oldInfo);
CInputFilter::CKeystrokeAction* downAction =
new CInputFilter::CKeystrokeAction(newInfo, true);
newInfo = IKeyState::CKeyInfo::alloc(*oldInfo);
CInputFilter::CKeystrokeAction* upAction =
new CInputFilter::CKeystrokeAction(newInfo, false);
m_activeRule.replaceAction(downAction, true, i);
m_activeRule.adoptAction(upAction, false);
}
else if (mouseAction != NULL) {
const IPlatformScreen::CButtonInfo* oldInfo =
mouseAction->getInfo();
IPlatformScreen::CButtonInfo* newInfo =
IPrimaryScreen::CButtonInfo::alloc(*oldInfo);
CInputFilter::CMouseButtonAction* downAction =
new CInputFilter::CMouseButtonAction(newInfo, true);
newInfo = IPrimaryScreen::CButtonInfo::alloc(*oldInfo);
CInputFilter::CMouseButtonAction* upAction =
new CInputFilter::CMouseButtonAction(newInfo, false);
m_activeRule.replaceAction(downAction, true, i);
m_activeRule.adoptAction(upAction, false);
}
}
// copy it back
m_inputFilter->getRule(m_activeRuleIndex) = m_activeRule;
}
m_activeRuleIndex = (UInt32)-1;
}
UInt32
CHotkeyOptions::findMatchingAction(
const CInputFilter::CKeystrokeAction* src) const
{
for (UInt32 i = 0, n = m_activeRule.getNumActions(false); i < n; ++i) {
const CInputFilter::CKeystrokeAction* dst =
dynamic_cast<const CInputFilter::CKeystrokeAction*>(
&m_activeRule.getAction(false, i));
if (dst != NULL &&
IKeyState::CKeyInfo::equal(src->getInfo(), dst->getInfo())) {
return i;
}
}
return (UInt32)-1;
}
UInt32
CHotkeyOptions::findMatchingAction(
const CInputFilter::CMouseButtonAction* src) const
{
for (UInt32 i = 0, n = m_activeRule.getNumActions(false); i < n; ++i) {
const CInputFilter::CMouseButtonAction* dst =
dynamic_cast<const CInputFilter::CMouseButtonAction*>(
&m_activeRule.getAction(false, i));
if (dst != NULL &&
IPrimaryScreen::CButtonInfo::equal(
src->getInfo(), dst->getInfo())) {
return i;
}
}
return (UInt32)-1;
}
BOOL
CHotkeyOptions::doDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM)
{
switch (message) {
case WM_INITDIALOG:
doInit(hwnd);
return TRUE;
case WM_COMMAND:
switch (LOWORD(wParam)) {
case IDOK:
case IDCANCEL:
closeRule(hwnd);
EndDialog(hwnd, 0);
return TRUE;
case IDC_HOTKEY_HOTKEYS:
switch (HIWORD(wParam)) {
case LBN_DBLCLK:
editHotkey(hwnd);
return TRUE;
case LBN_SELCHANGE: {
HWND rules = getItem(hwnd, IDC_HOTKEY_HOTKEYS);
LRESULT index = SendMessage(rules, LB_GETCURSEL, 0, 0);
if (m_activeRuleIndex != (UInt32)index) {
closeRule(hwnd);
updateHotkeysControls(hwnd);
openRule(hwnd);
}
return TRUE;
}
}
break;
case IDC_HOTKEY_ADD_HOTKEY:
addHotkey(hwnd);
return TRUE;
case IDC_HOTKEY_REMOVE_HOTKEY:
removeHotkey(hwnd);
return TRUE;
case IDC_HOTKEY_EDIT_HOTKEY:
editHotkey(hwnd);
return TRUE;
case IDC_HOTKEY_ACTIONS:
switch (HIWORD(wParam)) {
case LBN_DBLCLK:
editAction(hwnd);
return TRUE;
case LBN_SELCHANGE:
updateActionsControls(hwnd);
return TRUE;
}
break;
case IDC_HOTKEY_ADD_ACTION:
addAction(hwnd);
return TRUE;
case IDC_HOTKEY_REMOVE_ACTION:
removeAction(hwnd);
return TRUE;
case IDC_HOTKEY_EDIT_ACTION:
editAction(hwnd);
return TRUE;
}
break;
default:
break;
}
return FALSE;
}
BOOL CALLBACK
CHotkeyOptions::dlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
return s_singleton->doDlgProc(hwnd, message, wParam, lParam);
}
//
// CHotkeyOptions::CConditionDialog
//
CInputFilter::CCondition*
CHotkeyOptions::CConditionDialog::s_condition = NULL;
CInputFilter::CCondition*
CHotkeyOptions::CConditionDialog::s_lastGoodCondition = NULL;
WNDPROC CHotkeyOptions::CConditionDialog::s_editWndProc = NULL;
bool
CHotkeyOptions::CConditionDialog::doModal(HWND parent,
CInputFilter::CCondition*& condition)
{
s_condition = condition;
if (s_condition != NULL) {
s_lastGoodCondition = s_condition->clone();
}
else {
s_lastGoodCondition = NULL;
}
int n = DialogBox(s_instance, MAKEINTRESOURCE(IDD_HOTKEY_CONDITION),
parent, dlgProc);
condition = s_condition;
delete s_lastGoodCondition;
s_condition = NULL;
s_lastGoodCondition = NULL;
// user effectively cancelled if the condition is NULL
if (condition == NULL) {
n = 0;
}
return (n == 1);
}
void
CHotkeyOptions::CConditionDialog::doInit(HWND hwnd)
{
// subclass edit control
HWND child = getItem(hwnd, IDC_HOTKEY_CONDITION_HOTKEY);
s_editWndProc = (WNDPROC)GetWindowLong(child, GWL_WNDPROC);
SetWindowLong(child, GWL_WNDPROC, (LONG)editProc);
// fill control
fillHotkey(hwnd);
}
void
CHotkeyOptions::CConditionDialog::fillHotkey(HWND hwnd)
{
HWND child = getItem(hwnd, IDC_HOTKEY_CONDITION_HOTKEY);
if (s_condition != NULL) {
setWindowText(child, s_condition->format().c_str());
}
else {
setWindowText(child, "");
}
}
void
CHotkeyOptions::CConditionDialog::onButton(HWND hwnd, ButtonID button)
{
delete s_condition;
s_condition =
new CInputFilter::CMouseButtonCondition(button, getModifiers());
fillHotkey(GetParent(hwnd));
}
void
CHotkeyOptions::CConditionDialog::onKey(HWND hwnd, WPARAM wParam, LPARAM lParam)
{
// ignore key repeats
if ((lParam & 0xc0000000u) == 0x40000000u) {
return;
}
// ignore key releases if the condition is complete and for the tab
// key (in case we were just tabbed to)
if ((lParam & 0x80000000u) != 0) {
if (isGoodCondition() || wParam == VK_TAB) {
return;
}
}
KeyID key = kKeyNone;
KeyModifierMask mask = getModifiers();
switch (wParam) {
case VK_SHIFT:
case VK_LSHIFT:
case VK_RSHIFT:
case VK_CONTROL:
case VK_LCONTROL:
case VK_RCONTROL:
case VK_MENU:
case VK_LMENU:
case VK_RMENU:
case VK_LWIN:
case VK_RWIN:
break;
case VK_TAB:
// allow tabbing out of control
if ((mask & (KeyModifierControl |
KeyModifierAlt | KeyModifierSuper)) == 0) {
HWND next = hwnd;
if ((mask & KeyModifierShift) == 0) {
do {
next = GetWindow(next, GW_HWNDNEXT);
if (next == NULL) {
next = GetWindow(hwnd, GW_HWNDFIRST);
}
} while (next != hwnd &&
(!IsWindowVisible(next) ||
(GetWindowLong(next, GWL_STYLE) & WS_TABSTOP) == 0));
}
else {
do {
next = GetWindow(next, GW_HWNDPREV);
if (next == NULL) {
next = GetWindow(hwnd, GW_HWNDLAST);
}
} while (next != hwnd &&
(!IsWindowVisible(next) ||
(GetWindowLong(next, GWL_STYLE) & WS_TABSTOP) == 0));
}
SetFocus(next);
return;
}
// fall through
default:
key = CMSWindowsKeyState::getKeyID(wParam,
static_cast<KeyButton>((lParam & 0x1ff0000u) >> 16));
switch (key) {
case kKeyNone:
// could be a character
key = getChar(wParam, lParam);
if (key == kKeyNone) {
return;
}
break;
case kKeyShift_L:
case kKeyShift_R:
case kKeyControl_L:
case kKeyControl_R:
case kKeyAlt_L:
case kKeyAlt_R:
case kKeyMeta_L:
case kKeyMeta_R:
case kKeySuper_L:
case kKeySuper_R:
case kKeyCapsLock:
case kKeyNumLock:
case kKeyScrollLock:
// bogus
return;
}
break;
}
delete s_condition;
s_condition = new CInputFilter::CKeystrokeCondition(key, mask);
fillHotkey(GetParent(hwnd));
}
KeyID
CHotkeyOptions::CConditionDialog::getChar(WPARAM wParam, LPARAM lParam)
{
BYTE keyState[256];
UINT virtualKey = (UINT)wParam;
UINT scanCode = (UINT)((lParam & 0x0ff0000u) >> 16);
GetKeyboardState(keyState);
// reset modifier state
keyState[VK_SHIFT] = 0;
keyState[VK_LSHIFT] = 0;
keyState[VK_RSHIFT] = 0;
keyState[VK_CONTROL] = 0;
keyState[VK_LCONTROL] = 0;
keyState[VK_RCONTROL] = 0;
keyState[VK_MENU] = 0;
keyState[VK_LMENU] = 0;
keyState[VK_RMENU] = 0;
keyState[VK_LWIN] = 0;
keyState[VK_RWIN] = 0;
// translate virtual key to character
int n;
KeyID id;
if (CArchMiscWindows::isWindows95Family()) {
// XXX -- how do we get characters not in Latin-1?
WORD ascii;
n = ToAscii(virtualKey, scanCode, keyState, &ascii, 0);
id = static_cast<KeyID>(ascii & 0xffu);
}
else {
typedef int (WINAPI *ToUnicode_t)(UINT wVirtKey,
UINT wScanCode,
PBYTE lpKeyState,
LPWSTR pwszBuff,
int cchBuff,
UINT wFlags);
ToUnicode_t s_ToUnicode = NULL;
if (s_ToUnicode == NULL) {
HMODULE userModule = GetModuleHandle("user32.dll");
s_ToUnicode =
(ToUnicode_t)GetProcAddress(userModule, "ToUnicode");
}
WCHAR unicode[2];
n = s_ToUnicode(virtualKey, scanCode, keyState,
unicode, sizeof(unicode) / sizeof(unicode[0]),
0);
id = static_cast<KeyID>(unicode[0]);
}
switch (n) {
case -1:
// no hot keys on dead keys
return kKeyNone;
default:
case 0:
// unmapped
return kKeyNone;
case 1:
return id;
}
}
KeyModifierMask
CHotkeyOptions::CConditionDialog::getModifiers()
{
KeyModifierMask mask = 0;
if ((GetKeyState(VK_SHIFT) & 0x8000) != 0) {
mask |= KeyModifierShift;
}
if ((GetKeyState(VK_CONTROL) & 0x8000) != 0) {
mask |= KeyModifierControl;
}
if ((GetKeyState(VK_MENU) & 0x8000) != 0) {
mask |= KeyModifierAlt;
}
if ((GetKeyState(VK_LWIN) & 0x8000) != 0 ||
(GetKeyState(VK_RWIN) & 0x8000) != 0) {
mask |= KeyModifierSuper;
}
return mask;
}
bool
CHotkeyOptions::CConditionDialog::isGoodCondition()
{
CInputFilter::CKeystrokeCondition* keyCondition =
dynamic_cast<CInputFilter::CKeystrokeCondition*>(s_condition);
return (keyCondition == NULL || keyCondition->getKey() != kKeyNone);
}
BOOL CALLBACK
CHotkeyOptions::CConditionDialog::dlgProc(HWND hwnd,
UINT message, WPARAM wParam, LPARAM)
{
switch (message) {
case WM_INITDIALOG:
doInit(hwnd);
return TRUE;
case WM_COMMAND:
switch (LOWORD(wParam)) {
case IDOK:
EndDialog(hwnd, 1);
return TRUE;
case IDCANCEL:
EndDialog(hwnd, 0);
return TRUE;
}
break;
default:
break;
}
return FALSE;
}
LRESULT CALLBACK
CHotkeyOptions::CConditionDialog::editProc(HWND hwnd,
UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message) {
case WM_LBUTTONDOWN:
if (GetFocus() == hwnd) {
onButton(hwnd, kButtonLeft);
}
else {
SetFocus(hwnd);
}
return 0;
case WM_MBUTTONDOWN:
if (GetFocus() == hwnd) {
onButton(hwnd, kButtonMiddle);
}
return 0;
case WM_RBUTTONDOWN:
if (GetFocus() == hwnd) {
onButton(hwnd, kButtonRight);
}
return 0;
case WM_XBUTTONDOWN:
if (GetFocus() == hwnd) {
switch (HIWORD(wParam)) {
case XBUTTON1:
onButton(hwnd, kButtonExtra0 + 0);
break;
case XBUTTON2:
onButton(hwnd, kButtonExtra0 + 1);
break;
}
}
return 0;
case WM_KEYDOWN:
case WM_SYSKEYDOWN:
case WM_KEYUP:
case WM_SYSKEYUP:
onKey(hwnd, wParam, lParam);
return 0;
case WM_LBUTTONUP:
case WM_MBUTTONUP:
case WM_RBUTTONUP:
case WM_XBUTTONUP:
case WM_CHAR:
case WM_SYSCHAR:
case WM_DEADCHAR:
case WM_SYSDEADCHAR:
return 0;
case WM_SETFOCUS:
if (s_condition != NULL) {
delete s_lastGoodCondition;
s_lastGoodCondition = s_condition->clone();
}
break;
case WM_KILLFOCUS:
if (!isGoodCondition()) {
delete s_condition;
if (s_lastGoodCondition != NULL) {
s_condition = s_lastGoodCondition->clone();
}
else {
s_condition = NULL;
}
}
fillHotkey(GetParent(hwnd));
break;
case WM_GETDLGCODE:
return DLGC_WANTALLKEYS;
default:
break;
}
return CallWindowProc(s_editWndProc, hwnd, message, wParam, lParam);
}
//
// CHotkeyOptions::CActionDialog
//
CConfig* CHotkeyOptions::CActionDialog::s_config = NULL;
bool CHotkeyOptions::CActionDialog::s_onActivate = false;
CInputFilter::CAction*
CHotkeyOptions::CActionDialog::s_action = NULL;
CInputFilter::CAction*
CHotkeyOptions::CActionDialog::s_lastGoodAction = NULL;
std::set<CString>
CHotkeyOptions::CActionDialog::s_screens;
WNDPROC CHotkeyOptions::CActionDialog::s_editWndProc = NULL;
bool
CHotkeyOptions::CActionDialog::doModal(HWND parent, CConfig* config,
CInputFilter::CAction*& action, bool& onActivate)
{
s_config = config;
s_onActivate = onActivate;
s_action = action;
if (s_action != NULL) {
s_lastGoodAction = s_action->clone();
}
else {
s_lastGoodAction = NULL;
}
int n = DialogBox(s_instance, MAKEINTRESOURCE(IDD_HOTKEY_ACTION),
parent, dlgProc);
onActivate = s_onActivate;
action = s_action;
delete s_lastGoodAction;
s_action = NULL;
s_lastGoodAction = NULL;
return (n == 1);
}
void
CHotkeyOptions::CActionDialog::doInit(HWND hwnd)
{
// subclass edit control
HWND child = getItem(hwnd, IDC_HOTKEY_ACTION_HOTKEY);
s_editWndProc = (WNDPROC)GetWindowLong(child, GWL_WNDPROC);
SetWindowLong(child, GWL_WNDPROC, (LONG)editProc);
setWindowText(getItem(hwnd, IDC_HOTKEY_ACTION_HOTKEY), "");
fillHotkey(hwnd);
// fill screens
child = getItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_TO_LIST);
SendMessage(child, CB_RESETCONTENT, 0, 0);
for (CConfig::const_iterator index = s_config->begin();
index != s_config->end(); ) {
const CString& name = *index;
++index;
if (index != s_config->end()) {
SendMessage(child, CB_INSERTSTRING,
(WPARAM)-1, (LPARAM)name.c_str());
}
else {
SendMessage(child, CB_ADDSTRING, 0, (LPARAM)name.c_str());
}
}
SendMessage(child, CB_SETCURSEL, 0, 0);
// fill directions
child = getItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_IN_LIST);
SendMessage(child, CB_ADDSTRING, 0,
(LPARAM)getString(IDS_EDGE_LEFT).c_str());
SendMessage(child, CB_ADDSTRING, 0,
(LPARAM)getString(IDS_EDGE_RIGHT).c_str());
SendMessage(child, CB_ADDSTRING, 0,
(LPARAM)getString(IDS_EDGE_TOP).c_str());
SendMessage(child, CB_ADDSTRING, 0,
(LPARAM)getString(IDS_EDGE_BOTTOM).c_str());
SendMessage(child, CB_SETCURSEL, 0, 0);
// fill lock modes
child = getItem(hwnd, IDC_HOTKEY_ACTION_LOCK_LIST);
SendMessage(child, CB_ADDSTRING, 0,
(LPARAM)getString(IDS_MODE_OFF).c_str());
SendMessage(child, CB_ADDSTRING, 0,
(LPARAM)getString(IDS_MODE_ON).c_str());
SendMessage(child, CB_ADDSTRING, 0,
(LPARAM)getString(IDS_MODE_TOGGLE).c_str());
SendMessage(child, CB_SETCURSEL, 0, 0);
// fill keyboard broadcast modes
child = getItem(hwnd, IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST_LIST);
SendMessage(child, CB_ADDSTRING, 0,
(LPARAM)getString(IDS_MODE_OFF).c_str());
SendMessage(child, CB_ADDSTRING, 0,
(LPARAM)getString(IDS_MODE_ON).c_str());
SendMessage(child, CB_ADDSTRING, 0,
(LPARAM)getString(IDS_MODE_TOGGLE).c_str());
SendMessage(child, CB_SETCURSEL, 0, 0);
// select when
if (s_onActivate) {
child = getItem(hwnd, IDC_HOTKEY_ACTION_ON_ACTIVATE);
}
else {
child = getItem(hwnd, IDC_HOTKEY_ACTION_ON_DEACTIVATE);
}
setItemChecked(child, true);
// no screens by default
s_screens.clear();
// select mode
child = NULL;
CInputFilter::CKeystrokeAction* keyAction =
dynamic_cast<CInputFilter::CKeystrokeAction*>(s_action);
CInputFilter::CMouseButtonAction* mouseAction =
dynamic_cast<CInputFilter::CMouseButtonAction*>(s_action);
CInputFilter::CLockCursorToScreenAction* lockAction =
dynamic_cast<CInputFilter::CLockCursorToScreenAction*>(s_action);
CInputFilter::CSwitchToScreenAction* switchToAction =
dynamic_cast<CInputFilter::CSwitchToScreenAction*>(s_action);
CInputFilter::CSwitchInDirectionAction* switchInAction =
dynamic_cast<CInputFilter::CSwitchInDirectionAction*>(s_action);
CInputFilter::CKeyboardBroadcastAction* keyboardBroadcastAction=
dynamic_cast<CInputFilter::CKeyboardBroadcastAction*>(s_action);
if (keyAction != NULL) {
if (dynamic_cast<CKeystrokeDownUpAction*>(s_action) != NULL) {
child = getItem(hwnd, IDC_HOTKEY_ACTION_DOWNUP);
}
else if (keyAction->isOnPress()) {
child = getItem(hwnd, IDC_HOTKEY_ACTION_DOWN);
}
else {
child = getItem(hwnd, IDC_HOTKEY_ACTION_UP);
}
}
else if (mouseAction != NULL) {
if (dynamic_cast<CMouseButtonDownUpAction*>(s_action) != NULL) {
child = getItem(hwnd, IDC_HOTKEY_ACTION_DOWNUP);
}
else if (keyAction->isOnPress()) {
child = getItem(hwnd, IDC_HOTKEY_ACTION_DOWN);
}
else {
child = getItem(hwnd, IDC_HOTKEY_ACTION_UP);
}
}
else if (lockAction != NULL) {
child = getItem(hwnd, IDC_HOTKEY_ACTION_LOCK_LIST);
SendMessage(child, CB_SETCURSEL, lockAction->getMode(), 0);
child = getItem(hwnd, IDC_HOTKEY_ACTION_LOCK);
}
else if (switchToAction != NULL) {
child = getItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_TO_LIST);
DWORD i = SendMessage(child, CB_FINDSTRINGEXACT, (WPARAM)-1,
(LPARAM)switchToAction->getScreen().c_str());
if (i == CB_ERR) {
i = 0;
}
SendMessage(child, CB_SETCURSEL, i, 0);
child = getItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_TO);
}
else if (switchInAction != NULL) {
child = getItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_IN_LIST);
SendMessage(child, CB_SETCURSEL,
switchInAction->getDirection() - kLeft, 0);
child = getItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_IN);
}
else if (keyboardBroadcastAction != NULL) {
// Save the screens we're broadcasting to
s_screens = keyboardBroadcastAction->getScreens();
child = getItem(hwnd, IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST_LIST);
SendMessage(child, CB_SETCURSEL, keyboardBroadcastAction->getMode(), 0);
child = getItem(hwnd, IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST);
}
if (child != NULL) {
setItemChecked(child, true);
}
updateControls(hwnd);
}
void
CHotkeyOptions::CActionDialog::fillHotkey(HWND hwnd)
{
HWND child = getItem(hwnd, IDC_HOTKEY_ACTION_HOTKEY);
CInputFilter::CKeystrokeAction* keyAction =
dynamic_cast<CInputFilter::CKeystrokeAction*>(s_action);
CInputFilter::CMouseButtonAction* mouseAction =
dynamic_cast<CInputFilter::CMouseButtonAction*>(s_action);
if (keyAction != NULL || mouseAction != NULL) {
setWindowText(child, s_action->format().c_str());
}
else {
setWindowText(child, "");
}
// can only set screens in key actions
enableItem(hwnd, IDC_HOTKEY_ACTION_SCREENS, keyAction != NULL);
}
void
CHotkeyOptions::CActionDialog::updateControls(HWND hwnd)
{
// determine which mode we're in
UInt32 mode = 0;
if (isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_DOWNUP)) ||
isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_DOWN)) ||
isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_UP))) {
mode = 1;
}
else if (isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_TO))) {
mode = 2;
}
else if (isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_IN))) {
mode = 3;
}
else if (isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_LOCK))) {
mode = 4;
}
else if (isItemChecked(getItem(hwnd,
IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST))) {
mode = 5;
}
// enable/disable all mode specific controls
enableItem(hwnd, IDC_HOTKEY_ACTION_HOTKEY, mode == 1);
enableItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_TO_LIST, mode == 2);
enableItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_IN_LIST, mode == 3);
enableItem(hwnd, IDC_HOTKEY_ACTION_LOCK_LIST, mode == 4);
enableItem(hwnd, IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST_LIST, mode == 5);
enableItem(hwnd, IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST_SCREENS, mode == 5);
// can only set screens in key actions
CInputFilter::CKeystrokeAction* keyAction =
dynamic_cast<CInputFilter::CKeystrokeAction*>(s_action);
enableItem(hwnd, IDC_HOTKEY_ACTION_SCREENS, keyAction != NULL);
}
void
CHotkeyOptions::CActionDialog::onButton(HWND hwnd, ButtonID button)
{
IPlatformScreen::CButtonInfo* info =
IPrimaryScreen::CButtonInfo::alloc(button, getModifiers());
delete s_action;
HWND parent = GetParent(hwnd);
if (isItemChecked(getItem(parent, IDC_HOTKEY_ACTION_DOWNUP))) {
s_action = new CMouseButtonDownUpAction(info);
}
else if (isItemChecked(getItem(parent, IDC_HOTKEY_ACTION_DOWN))) {
s_action = new CInputFilter::CMouseButtonAction(info, true);
}
else if (isItemChecked(getItem(parent, IDC_HOTKEY_ACTION_UP))) {
s_action = new CInputFilter::CMouseButtonAction(info, false);
}
else {
s_action = NULL;
}
fillHotkey(parent);
}
void
CHotkeyOptions::CActionDialog::onKey(HWND hwnd, WPARAM wParam, LPARAM lParam)
{
// ignore key repeats
if ((lParam & 0xc0000000u) == 0x40000000u) {
return;
}
// ignore key releases if the action is complete and for the tab
// key (in case we were just tabbed to)
if ((lParam & 0x80000000u) != 0) {
if (isGoodAction() || wParam == VK_TAB) {
return;
}
}
KeyID key = kKeyNone;
KeyModifierMask mask = getModifiers();
switch (wParam) {
case VK_SHIFT:
case VK_LSHIFT:
case VK_RSHIFT:
case VK_CONTROL:
case VK_LCONTROL:
case VK_RCONTROL:
case VK_MENU:
case VK_LMENU:
case VK_RMENU:
case VK_LWIN:
case VK_RWIN:
break;
case VK_TAB:
// allow tabbing out of control
if ((mask & (KeyModifierControl |
KeyModifierAlt | KeyModifierSuper)) == 0) {
HWND next = hwnd;
if ((mask & KeyModifierShift) == 0) {
do {
next = GetWindow(next, GW_HWNDNEXT);
if (next == NULL) {
next = GetWindow(hwnd, GW_HWNDFIRST);
}
} while (next != hwnd &&
(!IsWindowVisible(next) ||
(GetWindowLong(next, GWL_STYLE) & WS_TABSTOP) == 0));
}
else {
do {
next = GetWindow(next, GW_HWNDPREV);
if (next == NULL) {
next = GetWindow(hwnd, GW_HWNDLAST);
}
} while (next != hwnd &&
(!IsWindowVisible(next) ||
(GetWindowLong(next, GWL_STYLE) & WS_TABSTOP) == 0));
}
SetFocus(next);
return;
}
// fall through
default:
key = CMSWindowsKeyState::getKeyID(wParam,
static_cast<KeyButton>((lParam & 0x1ff0000u) >> 16));
switch (key) {
case kKeyNone:
// could be a character
key = getChar(wParam, lParam);
if (key == kKeyNone) {
return;
}
break;
case kKeyShift_L:
case kKeyShift_R:
case kKeyControl_L:
case kKeyControl_R:
case kKeyAlt_L:
case kKeyAlt_R:
case kKeyMeta_L:
case kKeyMeta_R:
case kKeySuper_L:
case kKeySuper_R:
case kKeyCapsLock:
case kKeyNumLock:
case kKeyScrollLock:
// bogus
return;
}
break;
}
// get old screen list
std::set<CString> screens;
CInputFilter::CKeystrokeAction* keyAction =
dynamic_cast<CInputFilter::CKeystrokeAction*>(s_action);
if (keyAction == NULL) {
keyAction =
dynamic_cast<CInputFilter::CKeystrokeAction*>(s_lastGoodAction);
}
if (keyAction != NULL) {
IKeyState::CKeyInfo::split(keyAction->getInfo()->m_screens, screens);
}
// create new action
IPlatformScreen::CKeyInfo* info =
IKeyState::CKeyInfo::alloc(key, mask, 0, 0, screens);
delete s_action;
HWND parent = GetParent(hwnd);
if (isItemChecked(getItem(parent, IDC_HOTKEY_ACTION_DOWNUP))) {
s_action = new CKeystrokeDownUpAction(info);
}
else if (isItemChecked(getItem(parent, IDC_HOTKEY_ACTION_DOWN))) {
s_action = new CInputFilter::CKeystrokeAction(info, true);
}
else if (isItemChecked(getItem(parent, IDC_HOTKEY_ACTION_UP))) {
s_action = new CInputFilter::CKeystrokeAction(info, false);
}
else {
s_action = NULL;
}
fillHotkey(parent);
}
void
CHotkeyOptions::CActionDialog::onLockAction(HWND hwnd)
{
HWND child = getItem(hwnd, IDC_HOTKEY_ACTION_LOCK_LIST);
LRESULT index = SendMessage(child, CB_GETCURSEL, 0, 0);
if (index != CB_ERR) {
delete s_action;
s_action = new CInputFilter::CLockCursorToScreenAction(
(CInputFilter::CLockCursorToScreenAction::Mode)index);
}
}
void
CHotkeyOptions::CActionDialog::onSwitchToAction(HWND hwnd)
{
HWND child = getItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_TO_LIST);
CString screen = getWindowText(child);
delete s_action;
s_action = new CInputFilter::CSwitchToScreenAction(screen);
}
void
CHotkeyOptions::CActionDialog::onSwitchInAction(HWND hwnd)
{
HWND child = getItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_IN_LIST);
LRESULT index = SendMessage(child, CB_GETCURSEL, 0, 0);
if (index != CB_ERR) {
delete s_action;
s_action = new CInputFilter::CSwitchInDirectionAction(
(EDirection)(index + kLeft));
}
}
void
CHotkeyOptions::CActionDialog::onKeyboardBroadcastAction(HWND hwnd)
{
HWND child = getItem(hwnd, IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST_LIST);
LRESULT index = SendMessage(child, CB_GETCURSEL, 0, 0);
if (index != CB_ERR) {
delete s_action;
s_action = new CInputFilter::CKeyboardBroadcastAction(
(CInputFilter::CKeyboardBroadcastAction::Mode)index, s_screens);
}
}
KeyID
CHotkeyOptions::CActionDialog::getChar(WPARAM wParam, LPARAM lParam)
{
BYTE keyState[256];
UINT virtualKey = (UINT)wParam;
UINT scanCode = (UINT)((lParam & 0x0ff0000u) >> 16);
GetKeyboardState(keyState);
// reset modifier state
keyState[VK_SHIFT] = 0;
keyState[VK_LSHIFT] = 0;
keyState[VK_RSHIFT] = 0;
keyState[VK_CONTROL] = 0;
keyState[VK_LCONTROL] = 0;
keyState[VK_RCONTROL] = 0;
keyState[VK_MENU] = 0;
keyState[VK_LMENU] = 0;
keyState[VK_RMENU] = 0;
keyState[VK_LWIN] = 0;
keyState[VK_RWIN] = 0;
// translate virtual key to character
int n;
KeyID id;
if (CArchMiscWindows::isWindows95Family()) {
// XXX -- how do we get characters not in Latin-1?
WORD ascii;
n = ToAscii(virtualKey, scanCode, keyState, &ascii, 0);
id = static_cast<KeyID>(ascii & 0xffu);
}
else {
typedef int (WINAPI *ToUnicode_t)(UINT wVirtKey,
UINT wScanCode,
PBYTE lpKeyState,
LPWSTR pwszBuff,
int cchBuff,
UINT wFlags);
ToUnicode_t s_ToUnicode = NULL;
if (s_ToUnicode == NULL) {
HMODULE userModule = GetModuleHandle("user32.dll");
s_ToUnicode =
(ToUnicode_t)GetProcAddress(userModule, "ToUnicode");
}
WCHAR unicode[2];
n = s_ToUnicode(virtualKey, scanCode, keyState,
unicode, sizeof(unicode) / sizeof(unicode[0]),
0);
id = static_cast<KeyID>(unicode[0]);
}
switch (n) {
case -1:
// no hot keys on dead keys
return kKeyNone;
default:
case 0:
// unmapped
return kKeyNone;
case 1:
return id;
}
}
KeyModifierMask
CHotkeyOptions::CActionDialog::getModifiers()
{
KeyModifierMask mask = 0;
if ((GetKeyState(VK_SHIFT) & 0x8000) != 0) {
mask |= KeyModifierShift;
}
if ((GetKeyState(VK_CONTROL) & 0x8000) != 0) {
mask |= KeyModifierControl;
}
if ((GetKeyState(VK_MENU) & 0x8000) != 0) {
mask |= KeyModifierAlt;
}
if ((GetKeyState(VK_LWIN) & 0x8000) != 0 ||
(GetKeyState(VK_RWIN) & 0x8000) != 0) {
mask |= KeyModifierSuper;
}
return mask;
}
bool
CHotkeyOptions::CActionDialog::isGoodAction()
{
CInputFilter::CMouseButtonAction* mouseAction =
dynamic_cast<CInputFilter::CMouseButtonAction*>(s_action);
CInputFilter::CKeystrokeAction* keyAction =
dynamic_cast<CInputFilter::CKeystrokeAction*>(s_action);
return (mouseAction == NULL || keyAction == NULL ||
keyAction->getInfo()->m_key != kKeyNone);
}
void
CHotkeyOptions::CActionDialog::convertAction(HWND hwnd)
{
if (s_lastGoodAction != NULL) {
CInputFilter::CMouseButtonAction* mouseAction =
dynamic_cast<CInputFilter::CMouseButtonAction*>(s_lastGoodAction);
CInputFilter::CKeystrokeAction* keyAction =
dynamic_cast<CInputFilter::CKeystrokeAction*>(s_lastGoodAction);
if (mouseAction != NULL) {
IPlatformScreen::CButtonInfo* info =
IPrimaryScreen::CButtonInfo::alloc(*mouseAction->getInfo());
delete s_action;
if (isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_DOWNUP))) {
s_action = new CMouseButtonDownUpAction(info);
}
else if (isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_DOWN))) {
s_action = new CInputFilter::CMouseButtonAction(info, true);
}
else if (isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_UP))) {
s_action = new CInputFilter::CMouseButtonAction(info, false);
}
else {
free(info);
s_action = NULL;
}
}
else if (keyAction != NULL) {
IPlatformScreen::CKeyInfo* info =
IKeyState::CKeyInfo::alloc(*keyAction->getInfo());
delete s_action;
if (isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_DOWNUP))) {
s_action = new CKeystrokeDownUpAction(info);
}
else if (isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_DOWN))) {
s_action = new CInputFilter::CKeystrokeAction(info, true);
}
else if (isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_UP))) {
s_action = new CInputFilter::CKeystrokeAction(info, false);
}
else {
free(info);
s_action = NULL;
}
}
}
}
bool
CHotkeyOptions::CActionDialog::isDownUpAction()
{
return (dynamic_cast<CKeystrokeDownUpAction*>(s_action) != NULL ||
dynamic_cast<CMouseButtonDownUpAction*>(s_action) != NULL);
}
BOOL CALLBACK
CHotkeyOptions::CActionDialog::dlgProc(HWND hwnd,
UINT message, WPARAM wParam, LPARAM)
{
switch (message) {
case WM_INITDIALOG:
doInit(hwnd);
return TRUE;
case WM_COMMAND:
switch (LOWORD(wParam)) {
case IDOK:
if (isDownUpAction()) {
s_onActivate = true;
}
EndDialog(hwnd, 1);
return TRUE;
case IDCANCEL:
EndDialog(hwnd, 0);
return TRUE;
case IDC_HOTKEY_ACTION_ON_ACTIVATE:
s_onActivate = true;
return TRUE;
case IDC_HOTKEY_ACTION_ON_DEACTIVATE:
s_onActivate = false;
return TRUE;
case IDC_HOTKEY_ACTION_DOWNUP:
case IDC_HOTKEY_ACTION_DOWN:
case IDC_HOTKEY_ACTION_UP:
convertAction(hwnd);
fillHotkey(hwnd);
updateControls(hwnd);
return TRUE;
case IDC_HOTKEY_ACTION_LOCK:
onLockAction(hwnd);
updateControls(hwnd);
return TRUE;
case IDC_HOTKEY_ACTION_SWITCH_TO:
onSwitchToAction(hwnd);
updateControls(hwnd);
return TRUE;
case IDC_HOTKEY_ACTION_SWITCH_IN:
onSwitchInAction(hwnd);
updateControls(hwnd);
return TRUE;
case IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST:
onKeyboardBroadcastAction(hwnd);
updateControls(hwnd);
return TRUE;
case IDC_HOTKEY_ACTION_LOCK_LIST:
switch (HIWORD(wParam)) {
case LBN_SELCHANGE:
onLockAction(hwnd);
return TRUE;
}
break;
case IDC_HOTKEY_ACTION_SWITCH_TO_LIST:
switch (HIWORD(wParam)) {
case LBN_SELCHANGE:
onSwitchToAction(hwnd);
return TRUE;
}
break;
case IDC_HOTKEY_ACTION_SWITCH_IN_LIST:
switch (HIWORD(wParam)) {
case LBN_SELCHANGE:
onSwitchInAction(hwnd);
return TRUE;
}
break;
case IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST_LIST:
switch (HIWORD(wParam)) {
case LBN_SELCHANGE:
onKeyboardBroadcastAction(hwnd);
return TRUE;
}
break;
case IDC_HOTKEY_ACTION_SCREENS:
CScreensDialog::doModal(hwnd, s_config,
dynamic_cast<CInputFilter::CKeystrokeAction*>(s_action));
fillHotkey(hwnd);
return TRUE;
case IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST_SCREENS: {
// convert screens to form that CScreenDialog::doModal() wants
IPlatformScreen::CKeyInfo* tmpInfo =
IPlatformScreen::CKeyInfo::alloc(0, 0, 0, 1, s_screens);
CInputFilter::CKeystrokeAction tmpAction(tmpInfo, true);
// get the screens
CScreensDialog::doModal(hwnd, s_config, &tmpAction);
// convert screens back
IPlatformScreen::CKeyInfo::split(
tmpAction.getInfo()->m_screens, s_screens);
// update
onKeyboardBroadcastAction(hwnd);
return TRUE;
}
}
break;
default:
break;
}
return FALSE;
}
LRESULT CALLBACK
CHotkeyOptions::CActionDialog::editProc(HWND hwnd,
UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message) {
case WM_LBUTTONDOWN:
if (GetFocus() == hwnd) {
onButton(hwnd, kButtonLeft);
}
else {
SetFocus(hwnd);
}
return 0;
case WM_MBUTTONDOWN:
if (GetFocus() == hwnd) {
onButton(hwnd, kButtonMiddle);
}
return 0;
case WM_RBUTTONDOWN:
if (GetFocus() == hwnd) {
onButton(hwnd, kButtonRight);
}
return 0;
case WM_XBUTTONDOWN:
if (GetFocus() == hwnd) {
switch (HIWORD(wParam)) {
case XBUTTON1:
onButton(hwnd, kButtonExtra0 + 0);
break;
case XBUTTON2:
onButton(hwnd, kButtonExtra0 + 1);
break;
}
}
return 0;
case WM_KEYDOWN:
case WM_SYSKEYDOWN:
case WM_KEYUP:
case WM_SYSKEYUP:
onKey(hwnd, wParam, lParam);
return 0;
case WM_LBUTTONUP:
case WM_MBUTTONUP:
case WM_RBUTTONUP:
case WM_XBUTTONUP:
case WM_CHAR:
case WM_SYSCHAR:
case WM_DEADCHAR:
case WM_SYSDEADCHAR:
return 0;
case WM_SETFOCUS:
if (s_action != NULL) {
delete s_lastGoodAction;
s_lastGoodAction = s_action->clone();
}
break;
case WM_KILLFOCUS:
if (!isGoodAction()) {
delete s_action;
if (s_lastGoodAction != NULL) {
s_action = s_lastGoodAction->clone();
}
else {
s_action = NULL;
}
}
else if (s_action != NULL) {
delete s_lastGoodAction;
s_lastGoodAction = s_action->clone();
}
fillHotkey(GetParent(hwnd));
break;
case WM_GETDLGCODE:
return DLGC_WANTALLKEYS;
default:
break;
}
return CallWindowProc(s_editWndProc, hwnd, message, wParam, lParam);
}
//
// CHotkeyOptions::CScreensDialog
//
CConfig* CHotkeyOptions::CScreensDialog::s_config = NULL;
CInputFilter::CKeystrokeAction*
CHotkeyOptions::CScreensDialog::s_action = NULL;
CHotkeyOptions::CScreensDialog::CScreens
CHotkeyOptions::CScreensDialog::s_nonTargets;
CHotkeyOptions::CScreensDialog::CScreens
CHotkeyOptions::CScreensDialog::s_targets;
CString CHotkeyOptions::CScreensDialog::s_allScreens;
void
CHotkeyOptions::CScreensDialog::doModal(HWND parent, CConfig* config,
CInputFilter::CKeystrokeAction* action)
{
s_allScreens = getString(IDS_ALL_SCREENS);
s_config = config;
s_action = action;
DialogBox(s_instance, MAKEINTRESOURCE(IDD_HOTKEY_SCREENS),
parent, dlgProc);
s_config = NULL;
s_action = NULL;
}
void
CHotkeyOptions::CScreensDialog::doInit(HWND hwnd)
{
s_nonTargets.clear();
s_targets.clear();
// get screens from config
s_nonTargets.insert("*");
for (CConfig::const_iterator i = s_config->begin();
i != s_config->end(); ++i) {
s_nonTargets.insert(*i);
}
// get screens in action
IKeyState::CKeyInfo::split(s_action->getInfo()->m_screens, s_targets);
// remove screens in action from screens in config
for (CScreens::const_iterator i = s_targets.begin();
i != s_targets.end(); ++i) {
s_nonTargets.erase(*i);
}
// fill dialog
fillScreens(hwnd);
updateControls(hwnd);
}
void
CHotkeyOptions::CScreensDialog::doFini(HWND)
{
// put screens into action
const IPlatformScreen::CKeyInfo* oldInfo = s_action->getInfo();
IPlatformScreen::CKeyInfo* newInfo =
IKeyState::CKeyInfo::alloc(oldInfo->m_key,
oldInfo->m_mask, 0, 0, s_targets);
s_action->adoptInfo(newInfo);
}
void
CHotkeyOptions::CScreensDialog::fillScreens(HWND hwnd)
{
HWND child = getItem(hwnd, IDC_HOTKEY_SCREENS_SRC);
SendMessage(child, LB_RESETCONTENT, 0, 0);
for (CScreens::const_iterator i = s_nonTargets.begin();
i != s_nonTargets.end(); ++i) {
CString name = *i;
if (name == "*") {
name = s_allScreens;
}
SendMessage(child, LB_INSERTSTRING, (WPARAM)-1,
(LPARAM)name.c_str());
}
child = getItem(hwnd, IDC_HOTKEY_SCREENS_DST);
SendMessage(child, LB_RESETCONTENT, 0, 0);
for (CScreens::const_iterator i = s_targets.begin();
i != s_targets.end(); ++i) {
CString name = *i;
if (name == "*") {
name = s_allScreens;
}
SendMessage(child, LB_INSERTSTRING, (WPARAM)-1,
(LPARAM)name.c_str());
}
if (s_targets.empty()) {
// if no targets then add a special item so the user knows
// what'll happen
CString activeScreenLabel = getString(IDS_ACTIVE_SCREEN);
SendMessage(child, LB_INSERTSTRING, (WPARAM)-1,
(LPARAM)activeScreenLabel.c_str());
}
}
void
CHotkeyOptions::CScreensDialog::updateControls(HWND hwnd)
{
HWND child = getItem(hwnd, IDC_HOTKEY_SCREENS_SRC);
bool canAdd = (SendMessage(child, LB_GETSELCOUNT, 0, 0) != 0);
child = getItem(hwnd, IDC_HOTKEY_SCREENS_DST);
bool canRemove = (!s_targets.empty() &&
(SendMessage(child, LB_GETSELCOUNT, 0, 0) != 0));
enableItem(hwnd, IDC_HOTKEY_SCREENS_ADD, canAdd);
enableItem(hwnd, IDC_HOTKEY_SCREENS_REMOVE, canRemove);
}
void
CHotkeyOptions::CScreensDialog::add(HWND hwnd)
{
CScreens selected;
getSelected(hwnd, IDC_HOTKEY_SCREENS_SRC, s_nonTargets, selected);
for (CScreens::const_iterator i = selected.begin();
i != selected.end(); ++i) {
s_targets.insert(*i);
s_nonTargets.erase(*i);
}
fillScreens(hwnd);
updateControls(hwnd);
}
void
CHotkeyOptions::CScreensDialog::remove(HWND hwnd)
{
CScreens selected;
getSelected(hwnd, IDC_HOTKEY_SCREENS_DST, s_targets, selected);
for (CScreens::const_iterator i = selected.begin();
i != selected.end(); ++i) {
s_nonTargets.insert(*i);
s_targets.erase(*i);
}
fillScreens(hwnd);
updateControls(hwnd);
}
void
CHotkeyOptions::CScreensDialog::getSelected(HWND hwnd, UINT id,
const CScreens& inScreens, CScreens& outScreens)
{
// get the selected item indices
HWND child = getItem(hwnd, id);
UInt32 n = (UInt32)SendMessage(child, LB_GETSELCOUNT, 0, 0);
int* index = new int[n];
SendMessage(child, LB_GETSELITEMS, (WPARAM)n, (LPARAM)index);
// get the items in a vector
std::vector<CString> tmpList;
for (CScreens::const_iterator i = inScreens.begin();
i != inScreens.end(); ++i) {
tmpList.push_back(*i);
}
// get selected items into the output set
outScreens.clear();
for (UInt32 i = 0; i < n; ++i) {
outScreens.insert(tmpList[index[i]]);
}
// clean up
delete[] index;
}
BOOL CALLBACK
CHotkeyOptions::CScreensDialog::dlgProc(HWND hwnd,
UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message) {
case WM_INITDIALOG:
doInit(hwnd);
return TRUE;
case WM_COMMAND:
switch (LOWORD(wParam)) {
case IDOK:
doFini(hwnd);
EndDialog(hwnd, 0);
return TRUE;
case IDCANCEL:
EndDialog(hwnd, 0);
return TRUE;
case IDC_HOTKEY_SCREENS_ADD:
add(hwnd);
return TRUE;
case IDC_HOTKEY_SCREENS_REMOVE:
remove(hwnd);
return TRUE;
case IDC_HOTKEY_SCREENS_SRC:
case IDC_HOTKEY_SCREENS_DST:
switch (HIWORD(wParam)) {
case LBN_SELCANCEL:
case LBN_SELCHANGE:
updateControls(hwnd);
return TRUE;
}
break;
}
break;
case WM_CTLCOLORLISTBOX:
if (s_targets.empty() &&
(HWND)lParam == getItem(hwnd, IDC_HOTKEY_SCREENS_DST)) {
// override colors
HDC dc = (HDC)wParam;
SetTextColor(dc, GetSysColor(COLOR_GRAYTEXT));
return (BOOL)GetSysColorBrush(COLOR_WINDOW);
}
break;
default:
break;
}
return FALSE;
}