source: trunk/synergy/cmd/launcher/CHotkeyOptions.cpp@ 3232

Last change on this file since 3232 was 2749, checked in by bird, 19 years ago

synergy v1.3.1 sources (zip).

File size: 45.0 KB
Line 
1/*
2 * synergy -- mouse and keyboard sharing utility
3 * Copyright (C) 2006 Chris Schoeneman
4 *
5 * This package is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * found in the file COPYING that should have accompanied this file.
8 *
9 * This package is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 */
14
15#include "CArchMiscWindows.h"
16#include "CMSWindowsKeyState.h"
17#include "CConfig.h"
18#include "CHotkeyOptions.h"
19#include "CStringUtil.h"
20#include "LaunchUtil.h"
21#include "resource.h"
22
23#if !defined(WM_XBUTTONDOWN)
24#define WM_XBUTTONDOWN 0x020B
25#define WM_XBUTTONUP 0x020C
26#define WM_XBUTTONDBLCLK 0x020D
27#define XBUTTON1 0x0001
28#define XBUTTON2 0x0002
29#endif
30
31//
32// CAdvancedOptions
33//
34
35CHotkeyOptions* CHotkeyOptions::s_singleton = NULL;
36
37CHotkeyOptions::CHotkeyOptions(HWND parent, CConfig* config) :
38 m_parent(parent),
39 m_config(config)
40{
41 assert(s_singleton == NULL);
42 s_singleton = this;
43}
44
45CHotkeyOptions::~CHotkeyOptions()
46{
47 s_singleton = NULL;
48}
49
50void
51CHotkeyOptions::doModal()
52{
53 // do dialog
54 m_inputFilter = m_config->getInputFilter();
55 DialogBoxParam(s_instance, MAKEINTRESOURCE(IDD_HOTKEY_OPTIONS),
56 m_parent, dlgProc, (LPARAM)this);
57}
58
59void
60CHotkeyOptions::doInit(HWND hwnd)
61{
62 m_activeRuleIndex = (UInt32)-1;
63 fillHotkeys(hwnd);
64 openRule(hwnd);
65}
66
67void
68CHotkeyOptions::fillHotkeys(HWND hwnd, UInt32 select)
69{
70 HWND rules = getItem(hwnd, IDC_HOTKEY_HOTKEYS);
71
72 SendMessage(rules, LB_RESETCONTENT, 0, 0);
73 for (UInt32 i = 0, n = m_inputFilter->getNumRules(); i < n; ++i) {
74 CInputFilter::CRule& rule = m_inputFilter->getRule(i);
75 SendMessage(rules, LB_INSERTSTRING, (WPARAM)-1,
76 (LPARAM)rule.getCondition()->format().c_str());
77 }
78
79 if (select < m_inputFilter->getNumRules()) {
80 SendMessage(rules, LB_SETCURSEL, select, 0);
81 }
82
83 updateHotkeysControls(hwnd);
84}
85
86void
87CHotkeyOptions::updateHotkeysControls(HWND hwnd)
88{
89 HWND child = getItem(hwnd, IDC_HOTKEY_HOTKEYS);
90 bool selected = (SendMessage(child, LB_GETCURSEL, 0, 0) != LB_ERR);
91
92 enableItem(hwnd, IDC_HOTKEY_ADD_HOTKEY, TRUE);
93 enableItem(hwnd, IDC_HOTKEY_EDIT_HOTKEY, selected);
94 enableItem(hwnd, IDC_HOTKEY_REMOVE_HOTKEY, selected);
95}
96
97void
98CHotkeyOptions::addHotkey(HWND hwnd)
99{
100 closeRule(hwnd);
101 CInputFilter::CCondition* condition = NULL;
102 if (editCondition(hwnd, condition)) {
103 m_inputFilter->addFilterRule(CInputFilter::CRule(condition));
104 fillHotkeys(hwnd, m_inputFilter->getNumRules() - 1);
105 }
106 else {
107 delete condition;
108 }
109 openRule(hwnd);
110}
111
112void
113CHotkeyOptions::removeHotkey(HWND hwnd)
114{
115 UInt32 ruleIndex = m_activeRuleIndex;
116 closeRule(hwnd);
117
118 m_inputFilter->removeFilterRule(ruleIndex);
119 UInt32 n = m_inputFilter->getNumRules();
120 if (n > 0 && ruleIndex >= n) {
121 ruleIndex = n - 1;
122 }
123 fillHotkeys(hwnd, ruleIndex);
124
125 openRule(hwnd);
126}
127
128void
129CHotkeyOptions::editHotkey(HWND hwnd)
130{
131 // save selected item in action list
132 HWND actions = getItem(hwnd, IDC_HOTKEY_ACTIONS);
133 LRESULT aIndex = SendMessage(actions, LB_GETCURSEL, 0, 0);
134
135 UInt32 index = m_activeRuleIndex;
136 closeRule(hwnd);
137
138 CInputFilter::CRule& rule = m_inputFilter->getRule(index);
139 CInputFilter::CCondition* condition = rule.getCondition()->clone();
140 if (editCondition(hwnd, condition)) {
141 rule.setCondition(condition);
142 fillHotkeys(hwnd, index);
143 }
144 else {
145 delete condition;
146 }
147
148 openRule(hwnd);
149
150 // restore selected item in action list
151 if (aIndex != LB_ERR) {
152 SendMessage(actions, LB_SETCURSEL, aIndex, 0);
153 }
154}
155
156void
157CHotkeyOptions::fillActions(HWND hwnd, UInt32 select)
158{
159 HWND actions = getItem(hwnd, IDC_HOTKEY_ACTIONS);
160 SendMessage(actions, LB_RESETCONTENT, 0, 0);
161 if (m_activeRuleIndex != (UInt32)-1) {
162 UInt32 n = m_activeRule.getNumActions(true);
163 UInt32 n2 = m_activeRule.getNumActions(false);
164 for (UInt32 i = 0; i < n; ++i) {
165 const CInputFilter::CAction& action =
166 m_activeRule.getAction(true, i);
167 CString line("A ");
168 line += action.format();
169 SendMessage(actions, LB_INSERTSTRING, (WPARAM)-1,
170 (LPARAM)line.c_str());
171 SendMessage(actions, LB_SETITEMDATA, (WPARAM)i, (LPARAM)i);
172 }
173 for (UInt32 i = 0; i < n2; ++i) {
174 const CInputFilter::CAction& action =
175 m_activeRule.getAction(false, i);
176 CString line("D ");
177 line += action.format();
178 SendMessage(actions, LB_INSERTSTRING, (WPARAM)-1,
179 (LPARAM)line.c_str());
180 SendMessage(actions, LB_SETITEMDATA, (WPARAM)i + n,
181 (LPARAM)(i | 0x80000000u));
182 }
183
184 if (select < n + n2) {
185 SendMessage(actions, LB_SETCURSEL, select, 0);
186 }
187 }
188
189 updateActionsControls(hwnd);
190}
191
192void
193CHotkeyOptions::updateActionsControls(HWND hwnd)
194{
195 HWND child = getItem(hwnd, IDC_HOTKEY_HOTKEYS);
196 bool active = (m_activeRuleIndex != (UInt32)-1);
197
198 child = getItem(hwnd, IDC_HOTKEY_ACTIONS);
199 bool selected =
200 (active && (SendMessage(child, LB_GETCURSEL, 0, 0) != LB_ERR));
201
202 enableItem(hwnd, IDC_HOTKEY_ADD_ACTION, active);
203 enableItem(hwnd, IDC_HOTKEY_EDIT_ACTION, selected);
204 enableItem(hwnd, IDC_HOTKEY_REMOVE_ACTION, selected);
205}
206
207void
208CHotkeyOptions::addAction(HWND hwnd)
209{
210 CInputFilter::CAction* action = NULL;
211 bool onActivate = true;
212 if (editAction(hwnd, action, onActivate)) {
213 m_activeRule.adoptAction(action, onActivate);
214
215 UInt32 actionIndex = m_activeRule.getNumActions(true) - 1;
216 if (!onActivate) {
217 actionIndex += m_activeRule.getNumActions(false);
218 }
219 fillActions(hwnd, actionIndex);
220 }
221 else {
222 delete action;
223 }
224}
225
226void
227CHotkeyOptions::removeAction(HWND hwnd)
228{
229 HWND child = getItem(hwnd, IDC_HOTKEY_ACTIONS);
230 LRESULT index = SendMessage(child, LB_GETCURSEL, 0, 0);
231 if (index != LB_ERR) {
232 UInt32 actionIndex =
233 (UInt32)SendMessage(child, LB_GETITEMDATA, index, 0);
234 bool onActivate = ((actionIndex & 0x80000000u) == 0);
235 actionIndex &= ~0x80000000u;
236
237 m_activeRule.removeAction(onActivate, actionIndex);
238
239 actionIndex = static_cast<UInt32>(index);
240 UInt32 n = m_activeRule.getNumActions(true) +
241 m_activeRule.getNumActions(false);
242 if (n > 0 && actionIndex >= n) {
243 actionIndex = n - 1;
244 }
245 fillActions(hwnd, actionIndex);
246 }
247}
248
249void
250CHotkeyOptions::editAction(HWND hwnd)
251{
252 HWND child = getItem(hwnd, IDC_HOTKEY_ACTIONS);
253 LRESULT index = SendMessage(child, LB_GETCURSEL, 0, 0);
254 if (index != LB_ERR) {
255 UInt32 actionIndex =
256 (UInt32)SendMessage(child, LB_GETITEMDATA, index, 0);
257 bool onActivate = ((actionIndex & 0x80000000u) == 0);
258 actionIndex &= ~0x80000000u;
259
260 CInputFilter::CAction* action =
261 m_activeRule.getAction(onActivate, actionIndex).clone();
262 bool newOnActivate = onActivate;
263 if (editAction(hwnd, action, newOnActivate)) {
264 if (onActivate == newOnActivate) {
265 m_activeRule.replaceAction(action, onActivate, actionIndex);
266 actionIndex = static_cast<UInt32>(index);
267 }
268 else {
269 m_activeRule.removeAction(onActivate, actionIndex);
270 m_activeRule.adoptAction(action, newOnActivate);
271 actionIndex = m_activeRule.getNumActions(true) - 1;
272 if (!newOnActivate) {
273 actionIndex += m_activeRule.getNumActions(false);
274 }
275 }
276 fillActions(hwnd, actionIndex);
277 }
278 else {
279 delete action;
280 }
281 }
282}
283
284bool
285CHotkeyOptions::editCondition(HWND hwnd, CInputFilter::CCondition*& condition)
286{
287 return CConditionDialog::doModal(hwnd, condition);
288}
289
290bool
291CHotkeyOptions::editAction(HWND hwnd, CInputFilter::CAction*& action,
292 bool& onActivate)
293{
294 return CActionDialog::doModal(hwnd, m_config, action, onActivate);
295}
296
297void
298CHotkeyOptions::openRule(HWND hwnd)
299{
300 // get the active rule and copy it, merging down/up pairs of keystroke
301 // and mouse button actions into single actions for the convenience of
302 // of the user.
303 HWND rules = getItem(hwnd, IDC_HOTKEY_HOTKEYS);
304 LRESULT index = SendMessage(rules, LB_GETCURSEL, 0, 0);
305 if (index != LB_ERR) {
306 // copy the rule as is
307 m_activeRuleIndex = (SInt32)index;
308 m_activeRule = m_inputFilter->getRule(m_activeRuleIndex);
309
310 // look for actions to combine
311 for (UInt32 i = 0, n = m_activeRule.getNumActions(true); i < n; ++i) {
312 // get next activate action
313 const CInputFilter::CAction* action =
314 &m_activeRule.getAction(true, i);
315
316 // check if it's a key or mouse action
317 const CInputFilter::CKeystrokeAction* keyAction =
318 dynamic_cast<const CInputFilter::CKeystrokeAction*>(action);
319 const CInputFilter::CMouseButtonAction* mouseAction =
320 dynamic_cast<const CInputFilter::CMouseButtonAction*>(action);
321 if (keyAction == NULL && mouseAction == NULL) {
322 continue;
323 }
324
325 // check for matching deactivate action
326 UInt32 j = (UInt32)-1;
327 CInputFilter::CAction* newAction = NULL;
328 if (keyAction != NULL) {
329 j = findMatchingAction(keyAction);
330 if (j != (UInt32)-1) {
331 // found a match
332 const IPlatformScreen::CKeyInfo* oldInfo =
333 keyAction->getInfo();
334 IPlatformScreen::CKeyInfo* newInfo =
335 IKeyState::CKeyInfo::alloc(*oldInfo);
336 newAction = new CKeystrokeDownUpAction(newInfo);
337 }
338 }
339 else if (mouseAction != NULL) {
340 j = findMatchingAction(mouseAction);
341 if (j != (UInt32)-1) {
342 // found a match
343 const IPlatformScreen::CButtonInfo* oldInfo =
344 mouseAction->getInfo();
345 IPlatformScreen::CButtonInfo* newInfo =
346 IPrimaryScreen::CButtonInfo::alloc(*oldInfo);
347 newAction = new CMouseButtonDownUpAction(newInfo);
348 }
349 }
350
351 // perform merge
352 if (newAction != NULL) {
353 m_activeRule.replaceAction(newAction, true, i);
354 m_activeRule.removeAction(false, j);
355 }
356 }
357 }
358 else {
359 m_activeRuleIndex = (UInt32)-1;
360 }
361 fillActions(hwnd);
362}
363
364void
365CHotkeyOptions::closeRule(HWND)
366{
367 // copy rule back to input filter, expanding merged actions into the
368 // two component actions.
369 if (m_activeRuleIndex != (UInt32)-1) {
370 // expand merged rules
371 for (UInt32 i = 0, n = m_activeRule.getNumActions(true); i < n; ++i) {
372 // get action
373 const CInputFilter::CAction* action =
374 &m_activeRule.getAction(true, i);
375
376 // check if it's a merged key or mouse action
377 const CKeystrokeDownUpAction* keyAction =
378 dynamic_cast<const CKeystrokeDownUpAction*>(action);
379 const CMouseButtonDownUpAction* mouseAction =
380 dynamic_cast<const CMouseButtonDownUpAction*>(action);
381 if (keyAction == NULL && mouseAction == NULL) {
382 continue;
383 }
384
385 // expand
386 if (keyAction != NULL) {
387 const IPlatformScreen::CKeyInfo* oldInfo =
388 keyAction->getInfo();
389 IPlatformScreen::CKeyInfo* newInfo =
390 IKeyState::CKeyInfo::alloc(*oldInfo);
391 CInputFilter::CKeystrokeAction* downAction =
392 new CInputFilter::CKeystrokeAction(newInfo, true);
393 newInfo = IKeyState::CKeyInfo::alloc(*oldInfo);
394 CInputFilter::CKeystrokeAction* upAction =
395 new CInputFilter::CKeystrokeAction(newInfo, false);
396 m_activeRule.replaceAction(downAction, true, i);
397 m_activeRule.adoptAction(upAction, false);
398 }
399 else if (mouseAction != NULL) {
400 const IPlatformScreen::CButtonInfo* oldInfo =
401 mouseAction->getInfo();
402 IPlatformScreen::CButtonInfo* newInfo =
403 IPrimaryScreen::CButtonInfo::alloc(*oldInfo);
404 CInputFilter::CMouseButtonAction* downAction =
405 new CInputFilter::CMouseButtonAction(newInfo, true);
406 newInfo = IPrimaryScreen::CButtonInfo::alloc(*oldInfo);
407 CInputFilter::CMouseButtonAction* upAction =
408 new CInputFilter::CMouseButtonAction(newInfo, false);
409 m_activeRule.replaceAction(downAction, true, i);
410 m_activeRule.adoptAction(upAction, false);
411 }
412 }
413
414 // copy it back
415 m_inputFilter->getRule(m_activeRuleIndex) = m_activeRule;
416 }
417 m_activeRuleIndex = (UInt32)-1;
418}
419
420UInt32
421CHotkeyOptions::findMatchingAction(
422 const CInputFilter::CKeystrokeAction* src) const
423{
424 for (UInt32 i = 0, n = m_activeRule.getNumActions(false); i < n; ++i) {
425 const CInputFilter::CKeystrokeAction* dst =
426 dynamic_cast<const CInputFilter::CKeystrokeAction*>(
427 &m_activeRule.getAction(false, i));
428 if (dst != NULL &&
429 IKeyState::CKeyInfo::equal(src->getInfo(), dst->getInfo())) {
430 return i;
431 }
432 }
433 return (UInt32)-1;
434}
435
436UInt32
437CHotkeyOptions::findMatchingAction(
438 const CInputFilter::CMouseButtonAction* src) const
439{
440 for (UInt32 i = 0, n = m_activeRule.getNumActions(false); i < n; ++i) {
441 const CInputFilter::CMouseButtonAction* dst =
442 dynamic_cast<const CInputFilter::CMouseButtonAction*>(
443 &m_activeRule.getAction(false, i));
444 if (dst != NULL &&
445 IPrimaryScreen::CButtonInfo::equal(
446 src->getInfo(), dst->getInfo())) {
447 return i;
448 }
449 }
450 return (UInt32)-1;
451}
452
453BOOL
454CHotkeyOptions::doDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM)
455{
456 switch (message) {
457 case WM_INITDIALOG:
458 doInit(hwnd);
459 return TRUE;
460
461 case WM_COMMAND:
462 switch (LOWORD(wParam)) {
463 case IDOK:
464 case IDCANCEL:
465 closeRule(hwnd);
466 EndDialog(hwnd, 0);
467 return TRUE;
468
469 case IDC_HOTKEY_HOTKEYS:
470 switch (HIWORD(wParam)) {
471 case LBN_DBLCLK:
472 editHotkey(hwnd);
473 return TRUE;
474
475 case LBN_SELCHANGE: {
476 HWND rules = getItem(hwnd, IDC_HOTKEY_HOTKEYS);
477 LRESULT index = SendMessage(rules, LB_GETCURSEL, 0, 0);
478 if (m_activeRuleIndex != (UInt32)index) {
479 closeRule(hwnd);
480 updateHotkeysControls(hwnd);
481 openRule(hwnd);
482 }
483 return TRUE;
484 }
485 }
486 break;
487
488 case IDC_HOTKEY_ADD_HOTKEY:
489 addHotkey(hwnd);
490 return TRUE;
491
492 case IDC_HOTKEY_REMOVE_HOTKEY:
493 removeHotkey(hwnd);
494 return TRUE;
495
496 case IDC_HOTKEY_EDIT_HOTKEY:
497 editHotkey(hwnd);
498 return TRUE;
499
500 case IDC_HOTKEY_ACTIONS:
501 switch (HIWORD(wParam)) {
502 case LBN_DBLCLK:
503 editAction(hwnd);
504 return TRUE;
505
506 case LBN_SELCHANGE:
507 updateActionsControls(hwnd);
508 return TRUE;
509 }
510 break;
511
512 case IDC_HOTKEY_ADD_ACTION:
513 addAction(hwnd);
514 return TRUE;
515
516 case IDC_HOTKEY_REMOVE_ACTION:
517 removeAction(hwnd);
518 return TRUE;
519
520 case IDC_HOTKEY_EDIT_ACTION:
521 editAction(hwnd);
522 return TRUE;
523 }
524 break;
525
526 default:
527 break;
528 }
529
530 return FALSE;
531}
532
533BOOL CALLBACK
534CHotkeyOptions::dlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
535{
536 return s_singleton->doDlgProc(hwnd, message, wParam, lParam);
537}
538
539
540//
541// CHotkeyOptions::CConditionDialog
542//
543
544CInputFilter::CCondition*
545 CHotkeyOptions::CConditionDialog::s_condition = NULL;
546CInputFilter::CCondition*
547 CHotkeyOptions::CConditionDialog::s_lastGoodCondition = NULL;
548WNDPROC CHotkeyOptions::CConditionDialog::s_editWndProc = NULL;
549
550bool
551CHotkeyOptions::CConditionDialog::doModal(HWND parent,
552 CInputFilter::CCondition*& condition)
553{
554 s_condition = condition;
555 if (s_condition != NULL) {
556 s_lastGoodCondition = s_condition->clone();
557 }
558 else {
559 s_lastGoodCondition = NULL;
560 }
561 int n = DialogBox(s_instance, MAKEINTRESOURCE(IDD_HOTKEY_CONDITION),
562 parent, dlgProc);
563
564 condition = s_condition;
565 delete s_lastGoodCondition;
566 s_condition = NULL;
567 s_lastGoodCondition = NULL;
568
569 // user effectively cancelled if the condition is NULL
570 if (condition == NULL) {
571 n = 0;
572 }
573
574 return (n == 1);
575}
576
577void
578CHotkeyOptions::CConditionDialog::doInit(HWND hwnd)
579{
580 // subclass edit control
581 HWND child = getItem(hwnd, IDC_HOTKEY_CONDITION_HOTKEY);
582 s_editWndProc = (WNDPROC)GetWindowLong(child, GWL_WNDPROC);
583 SetWindowLong(child, GWL_WNDPROC, (LONG)editProc);
584
585 // fill control
586 fillHotkey(hwnd);
587}
588
589void
590CHotkeyOptions::CConditionDialog::fillHotkey(HWND hwnd)
591{
592 HWND child = getItem(hwnd, IDC_HOTKEY_CONDITION_HOTKEY);
593 if (s_condition != NULL) {
594 setWindowText(child, s_condition->format().c_str());
595 }
596 else {
597 setWindowText(child, "");
598 }
599}
600
601void
602CHotkeyOptions::CConditionDialog::onButton(HWND hwnd, ButtonID button)
603{
604 delete s_condition;
605 s_condition =
606 new CInputFilter::CMouseButtonCondition(button, getModifiers());
607
608 fillHotkey(GetParent(hwnd));
609}
610
611void
612CHotkeyOptions::CConditionDialog::onKey(HWND hwnd, WPARAM wParam, LPARAM lParam)
613{
614 // ignore key repeats
615 if ((lParam & 0xc0000000u) == 0x40000000u) {
616 return;
617 }
618
619 // ignore key releases if the condition is complete and for the tab
620 // key (in case we were just tabbed to)
621 if ((lParam & 0x80000000u) != 0) {
622 if (isGoodCondition() || wParam == VK_TAB) {
623 return;
624 }
625 }
626
627 KeyID key = kKeyNone;
628 KeyModifierMask mask = getModifiers();
629 switch (wParam) {
630 case VK_SHIFT:
631 case VK_LSHIFT:
632 case VK_RSHIFT:
633 case VK_CONTROL:
634 case VK_LCONTROL:
635 case VK_RCONTROL:
636 case VK_MENU:
637 case VK_LMENU:
638 case VK_RMENU:
639 case VK_LWIN:
640 case VK_RWIN:
641 break;
642
643 case VK_TAB:
644 // allow tabbing out of control
645 if ((mask & (KeyModifierControl |
646 KeyModifierAlt | KeyModifierSuper)) == 0) {
647 HWND next = hwnd;
648 if ((mask & KeyModifierShift) == 0) {
649 do {
650 next = GetWindow(next, GW_HWNDNEXT);
651 if (next == NULL) {
652 next = GetWindow(hwnd, GW_HWNDFIRST);
653 }
654 } while (next != hwnd &&
655 (!IsWindowVisible(next) ||
656 (GetWindowLong(next, GWL_STYLE) & WS_TABSTOP) == 0));
657 }
658 else {
659 do {
660 next = GetWindow(next, GW_HWNDPREV);
661 if (next == NULL) {
662 next = GetWindow(hwnd, GW_HWNDLAST);
663 }
664 } while (next != hwnd &&
665 (!IsWindowVisible(next) ||
666 (GetWindowLong(next, GWL_STYLE) & WS_TABSTOP) == 0));
667 }
668 SetFocus(next);
669 return;
670 }
671 // fall through
672
673 default:
674 key = CMSWindowsKeyState::getKeyID(wParam,
675 static_cast<KeyButton>((lParam & 0x1ff0000u) >> 16));
676 switch (key) {
677 case kKeyNone:
678 // could be a character
679 key = getChar(wParam, lParam);
680 if (key == kKeyNone) {
681 return;
682 }
683 break;
684
685 case kKeyShift_L:
686 case kKeyShift_R:
687 case kKeyControl_L:
688 case kKeyControl_R:
689 case kKeyAlt_L:
690 case kKeyAlt_R:
691 case kKeyMeta_L:
692 case kKeyMeta_R:
693 case kKeySuper_L:
694 case kKeySuper_R:
695 case kKeyCapsLock:
696 case kKeyNumLock:
697 case kKeyScrollLock:
698 // bogus
699 return;
700 }
701 break;
702 }
703
704 delete s_condition;
705 s_condition = new CInputFilter::CKeystrokeCondition(key, mask);
706
707 fillHotkey(GetParent(hwnd));
708}
709
710KeyID
711CHotkeyOptions::CConditionDialog::getChar(WPARAM wParam, LPARAM lParam)
712{
713 BYTE keyState[256];
714 UINT virtualKey = (UINT)wParam;
715 UINT scanCode = (UINT)((lParam & 0x0ff0000u) >> 16);
716 GetKeyboardState(keyState);
717
718 // reset modifier state
719 keyState[VK_SHIFT] = 0;
720 keyState[VK_LSHIFT] = 0;
721 keyState[VK_RSHIFT] = 0;
722 keyState[VK_CONTROL] = 0;
723 keyState[VK_LCONTROL] = 0;
724 keyState[VK_RCONTROL] = 0;
725 keyState[VK_MENU] = 0;
726 keyState[VK_LMENU] = 0;
727 keyState[VK_RMENU] = 0;
728 keyState[VK_LWIN] = 0;
729 keyState[VK_RWIN] = 0;
730
731 // translate virtual key to character
732 int n;
733 KeyID id;
734 if (CArchMiscWindows::isWindows95Family()) {
735 // XXX -- how do we get characters not in Latin-1?
736 WORD ascii;
737 n = ToAscii(virtualKey, scanCode, keyState, &ascii, 0);
738 id = static_cast<KeyID>(ascii & 0xffu);
739 }
740 else {
741 typedef int (WINAPI *ToUnicode_t)(UINT wVirtKey,
742 UINT wScanCode,
743 PBYTE lpKeyState,
744 LPWSTR pwszBuff,
745 int cchBuff,
746 UINT wFlags);
747 ToUnicode_t s_ToUnicode = NULL;
748 if (s_ToUnicode == NULL) {
749 HMODULE userModule = GetModuleHandle("user32.dll");
750 s_ToUnicode =
751 (ToUnicode_t)GetProcAddress(userModule, "ToUnicode");
752 }
753
754 WCHAR unicode[2];
755 n = s_ToUnicode(virtualKey, scanCode, keyState,
756 unicode, sizeof(unicode) / sizeof(unicode[0]),
757 0);
758 id = static_cast<KeyID>(unicode[0]);
759 }
760 switch (n) {
761 case -1:
762 // no hot keys on dead keys
763 return kKeyNone;
764
765 default:
766 case 0:
767 // unmapped
768 return kKeyNone;
769
770 case 1:
771 return id;
772 }
773}
774
775KeyModifierMask
776CHotkeyOptions::CConditionDialog::getModifiers()
777{
778 KeyModifierMask mask = 0;
779 if ((GetKeyState(VK_SHIFT) & 0x8000) != 0) {
780 mask |= KeyModifierShift;
781 }
782 if ((GetKeyState(VK_CONTROL) & 0x8000) != 0) {
783 mask |= KeyModifierControl;
784 }
785 if ((GetKeyState(VK_MENU) & 0x8000) != 0) {
786 mask |= KeyModifierAlt;
787 }
788 if ((GetKeyState(VK_LWIN) & 0x8000) != 0 ||
789 (GetKeyState(VK_RWIN) & 0x8000) != 0) {
790 mask |= KeyModifierSuper;
791 }
792 return mask;
793}
794
795bool
796CHotkeyOptions::CConditionDialog::isGoodCondition()
797{
798 CInputFilter::CKeystrokeCondition* keyCondition =
799 dynamic_cast<CInputFilter::CKeystrokeCondition*>(s_condition);
800 return (keyCondition == NULL || keyCondition->getKey() != kKeyNone);
801}
802
803BOOL CALLBACK
804CHotkeyOptions::CConditionDialog::dlgProc(HWND hwnd,
805 UINT message, WPARAM wParam, LPARAM)
806{
807 switch (message) {
808 case WM_INITDIALOG:
809 doInit(hwnd);
810 return TRUE;
811
812 case WM_COMMAND:
813 switch (LOWORD(wParam)) {
814 case IDOK:
815 EndDialog(hwnd, 1);
816 return TRUE;
817
818 case IDCANCEL:
819 EndDialog(hwnd, 0);
820 return TRUE;
821 }
822 break;
823
824 default:
825 break;
826 }
827
828 return FALSE;
829}
830
831LRESULT CALLBACK
832CHotkeyOptions::CConditionDialog::editProc(HWND hwnd,
833 UINT message, WPARAM wParam, LPARAM lParam)
834{
835 switch (message) {
836 case WM_LBUTTONDOWN:
837 if (GetFocus() == hwnd) {
838 onButton(hwnd, kButtonLeft);
839 }
840 else {
841 SetFocus(hwnd);
842 }
843 return 0;
844
845 case WM_MBUTTONDOWN:
846 if (GetFocus() == hwnd) {
847 onButton(hwnd, kButtonMiddle);
848 }
849 return 0;
850
851 case WM_RBUTTONDOWN:
852 if (GetFocus() == hwnd) {
853 onButton(hwnd, kButtonRight);
854 }
855 return 0;
856
857 case WM_XBUTTONDOWN:
858 if (GetFocus() == hwnd) {
859 switch (HIWORD(wParam)) {
860 case XBUTTON1:
861 onButton(hwnd, kButtonExtra0 + 0);
862 break;
863
864 case XBUTTON2:
865 onButton(hwnd, kButtonExtra0 + 1);
866 break;
867 }
868 }
869 return 0;
870
871 case WM_KEYDOWN:
872 case WM_SYSKEYDOWN:
873 case WM_KEYUP:
874 case WM_SYSKEYUP:
875 onKey(hwnd, wParam, lParam);
876 return 0;
877
878 case WM_LBUTTONUP:
879 case WM_MBUTTONUP:
880 case WM_RBUTTONUP:
881 case WM_XBUTTONUP:
882 case WM_CHAR:
883 case WM_SYSCHAR:
884 case WM_DEADCHAR:
885 case WM_SYSDEADCHAR:
886 return 0;
887
888 case WM_SETFOCUS:
889 if (s_condition != NULL) {
890 delete s_lastGoodCondition;
891 s_lastGoodCondition = s_condition->clone();
892 }
893 break;
894
895 case WM_KILLFOCUS:
896 if (!isGoodCondition()) {
897 delete s_condition;
898 if (s_lastGoodCondition != NULL) {
899 s_condition = s_lastGoodCondition->clone();
900 }
901 else {
902 s_condition = NULL;
903 }
904 }
905 fillHotkey(GetParent(hwnd));
906 break;
907
908 case WM_GETDLGCODE:
909 return DLGC_WANTALLKEYS;
910
911 default:
912 break;
913 }
914 return CallWindowProc(s_editWndProc, hwnd, message, wParam, lParam);
915}
916
917
918//
919// CHotkeyOptions::CActionDialog
920//
921
922CConfig* CHotkeyOptions::CActionDialog::s_config = NULL;
923bool CHotkeyOptions::CActionDialog::s_onActivate = false;
924CInputFilter::CAction*
925 CHotkeyOptions::CActionDialog::s_action = NULL;
926CInputFilter::CAction*
927 CHotkeyOptions::CActionDialog::s_lastGoodAction = NULL;
928WNDPROC CHotkeyOptions::CActionDialog::s_editWndProc = NULL;
929
930bool
931CHotkeyOptions::CActionDialog::doModal(HWND parent, CConfig* config,
932 CInputFilter::CAction*& action, bool& onActivate)
933{
934 s_config = config;
935 s_onActivate = onActivate;
936 s_action = action;
937 if (s_action != NULL) {
938 s_lastGoodAction = s_action->clone();
939 }
940 else {
941 s_lastGoodAction = NULL;
942 }
943
944 int n = DialogBox(s_instance, MAKEINTRESOURCE(IDD_HOTKEY_ACTION),
945 parent, dlgProc);
946
947 onActivate = s_onActivate;
948 action = s_action;
949 delete s_lastGoodAction;
950 s_action = NULL;
951 s_lastGoodAction = NULL;
952
953 return (n == 1);
954}
955
956void
957CHotkeyOptions::CActionDialog::doInit(HWND hwnd)
958{
959 // subclass edit control
960 HWND child = getItem(hwnd, IDC_HOTKEY_ACTION_HOTKEY);
961 s_editWndProc = (WNDPROC)GetWindowLong(child, GWL_WNDPROC);
962 SetWindowLong(child, GWL_WNDPROC, (LONG)editProc);
963 setWindowText(getItem(hwnd, IDC_HOTKEY_ACTION_HOTKEY), "");
964 fillHotkey(hwnd);
965
966 // fill screens
967 child = getItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_TO_LIST);
968 SendMessage(child, CB_RESETCONTENT, 0, 0);
969 for (CConfig::const_iterator index = s_config->begin();
970 index != s_config->end(); ) {
971 const CString& name = *index;
972 ++index;
973 if (index != s_config->end()) {
974 SendMessage(child, CB_INSERTSTRING,
975 (WPARAM)-1, (LPARAM)name.c_str());
976 }
977 else {
978 SendMessage(child, CB_ADDSTRING, 0, (LPARAM)name.c_str());
979 }
980 }
981 SendMessage(child, CB_SETCURSEL, 0, 0);
982
983 // fill directions
984 child = getItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_IN_LIST);
985 SendMessage(child, CB_ADDSTRING, 0,
986 (LPARAM)getString(IDS_EDGE_LEFT).c_str());
987 SendMessage(child, CB_ADDSTRING, 0,
988 (LPARAM)getString(IDS_EDGE_RIGHT).c_str());
989 SendMessage(child, CB_ADDSTRING, 0,
990 (LPARAM)getString(IDS_EDGE_TOP).c_str());
991 SendMessage(child, CB_ADDSTRING, 0,
992 (LPARAM)getString(IDS_EDGE_BOTTOM).c_str());
993 SendMessage(child, CB_SETCURSEL, 0, 0);
994
995 // fill lock modes
996 child = getItem(hwnd, IDC_HOTKEY_ACTION_LOCK_LIST);
997 SendMessage(child, CB_ADDSTRING, 0,
998 (LPARAM)getString(IDS_LOCK_MODE_OFF).c_str());
999 SendMessage(child, CB_ADDSTRING, 0,
1000 (LPARAM)getString(IDS_LOCK_MODE_ON).c_str());
1001 SendMessage(child, CB_ADDSTRING, 0,
1002 (LPARAM)getString(IDS_LOCK_MODE_TOGGLE).c_str());
1003 SendMessage(child, CB_SETCURSEL, 0, 0);
1004
1005 // select when
1006 if (s_onActivate) {
1007 child = getItem(hwnd, IDC_HOTKEY_ACTION_ON_ACTIVATE);
1008 }
1009 else {
1010 child = getItem(hwnd, IDC_HOTKEY_ACTION_ON_DEACTIVATE);
1011 }
1012 setItemChecked(child, true);
1013
1014 // select mode
1015 child = NULL;
1016 CInputFilter::CKeystrokeAction* keyAction =
1017 dynamic_cast<CInputFilter::CKeystrokeAction*>(s_action);
1018 CInputFilter::CMouseButtonAction* mouseAction =
1019 dynamic_cast<CInputFilter::CMouseButtonAction*>(s_action);
1020 CInputFilter::CLockCursorToScreenAction* lockAction =
1021 dynamic_cast<CInputFilter::CLockCursorToScreenAction*>(s_action);
1022 CInputFilter::CSwitchToScreenAction* switchToAction =
1023 dynamic_cast<CInputFilter::CSwitchToScreenAction*>(s_action);
1024 CInputFilter::CSwitchInDirectionAction* switchInAction =
1025 dynamic_cast<CInputFilter::CSwitchInDirectionAction*>(s_action);
1026 if (keyAction != NULL) {
1027 if (dynamic_cast<CKeystrokeDownUpAction*>(s_action) != NULL) {
1028 child = getItem(hwnd, IDC_HOTKEY_ACTION_DOWNUP);
1029 }
1030 else if (keyAction->isOnPress()) {
1031 child = getItem(hwnd, IDC_HOTKEY_ACTION_DOWN);
1032 }
1033 else {
1034 child = getItem(hwnd, IDC_HOTKEY_ACTION_UP);
1035 }
1036 }
1037 else if (mouseAction != NULL) {
1038 if (dynamic_cast<CMouseButtonDownUpAction*>(s_action) != NULL) {
1039 child = getItem(hwnd, IDC_HOTKEY_ACTION_DOWNUP);
1040 }
1041 else if (keyAction->isOnPress()) {
1042 child = getItem(hwnd, IDC_HOTKEY_ACTION_DOWN);
1043 }
1044 else {
1045 child = getItem(hwnd, IDC_HOTKEY_ACTION_UP);
1046 }
1047 }
1048 else if (lockAction != NULL) {
1049 child = getItem(hwnd, IDC_HOTKEY_ACTION_LOCK_LIST);
1050 SendMessage(child, CB_SETCURSEL, lockAction->getMode(), 0);
1051 child = getItem(hwnd, IDC_HOTKEY_ACTION_LOCK);
1052 }
1053 else if (switchToAction != NULL) {
1054 child = getItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_TO_LIST);
1055 DWORD i = SendMessage(child, CB_FINDSTRINGEXACT, (WPARAM)-1,
1056 (LPARAM)switchToAction->getScreen().c_str());
1057 if (i == CB_ERR) {
1058 i = 0;
1059 }
1060 SendMessage(child, CB_SETCURSEL, i, 0);
1061 child = getItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_TO);
1062 }
1063 else if (switchInAction != NULL) {
1064 child = getItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_IN_LIST);
1065 SendMessage(child, CB_SETCURSEL,
1066 switchInAction->getDirection() - kLeft, 0);
1067 child = getItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_IN);
1068 }
1069 if (child != NULL) {
1070 setItemChecked(child, true);
1071 }
1072
1073 updateControls(hwnd);
1074}
1075
1076void
1077CHotkeyOptions::CActionDialog::fillHotkey(HWND hwnd)
1078{
1079 HWND child = getItem(hwnd, IDC_HOTKEY_ACTION_HOTKEY);
1080 CInputFilter::CKeystrokeAction* keyAction =
1081 dynamic_cast<CInputFilter::CKeystrokeAction*>(s_action);
1082 CInputFilter::CMouseButtonAction* mouseAction =
1083 dynamic_cast<CInputFilter::CMouseButtonAction*>(s_action);
1084 if (keyAction != NULL || mouseAction != NULL) {
1085 setWindowText(child, s_action->format().c_str());
1086 }
1087 else {
1088 setWindowText(child, "");
1089 }
1090
1091 // can only set screens in key actions
1092 enableItem(hwnd, IDC_HOTKEY_ACTION_SCREENS, keyAction != NULL);
1093}
1094
1095void
1096CHotkeyOptions::CActionDialog::updateControls(HWND hwnd)
1097{
1098 // determine which mode we're in
1099 UInt32 mode = 0;
1100 if (isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_DOWNUP)) ||
1101 isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_DOWN)) ||
1102 isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_UP))) {
1103 mode = 1;
1104 }
1105 else if (isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_TO))) {
1106 mode = 2;
1107 }
1108 else if (isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_IN))) {
1109 mode = 3;
1110 }
1111 else if (isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_LOCK))) {
1112 mode = 4;
1113 }
1114
1115 // enable/disable all mode specific controls
1116 enableItem(hwnd, IDC_HOTKEY_ACTION_HOTKEY, mode == 1);
1117 enableItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_TO_LIST, mode == 2);
1118 enableItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_IN_LIST, mode == 3);
1119 enableItem(hwnd, IDC_HOTKEY_ACTION_LOCK_LIST, mode == 4);
1120
1121 // can only set screens in key actions
1122 CInputFilter::CKeystrokeAction* keyAction =
1123 dynamic_cast<CInputFilter::CKeystrokeAction*>(s_action);
1124 enableItem(hwnd, IDC_HOTKEY_ACTION_SCREENS, keyAction != NULL);
1125}
1126
1127void
1128CHotkeyOptions::CActionDialog::onButton(HWND hwnd, ButtonID button)
1129{
1130 IPlatformScreen::CButtonInfo* info =
1131 IPrimaryScreen::CButtonInfo::alloc(button, getModifiers());
1132 delete s_action;
1133 HWND parent = GetParent(hwnd);
1134 if (isItemChecked(getItem(parent, IDC_HOTKEY_ACTION_DOWNUP))) {
1135 s_action = new CMouseButtonDownUpAction(info);
1136 }
1137 else if (isItemChecked(getItem(parent, IDC_HOTKEY_ACTION_DOWN))) {
1138 s_action = new CInputFilter::CMouseButtonAction(info, true);
1139 }
1140 else if (isItemChecked(getItem(parent, IDC_HOTKEY_ACTION_UP))) {
1141 s_action = new CInputFilter::CMouseButtonAction(info, false);
1142 }
1143 else {
1144 s_action = NULL;
1145 }
1146
1147 fillHotkey(parent);
1148}
1149
1150void
1151CHotkeyOptions::CActionDialog::onKey(HWND hwnd, WPARAM wParam, LPARAM lParam)
1152{
1153 // ignore key repeats
1154 if ((lParam & 0xc0000000u) == 0x40000000u) {
1155 return;
1156 }
1157
1158 // ignore key releases if the action is complete and for the tab
1159 // key (in case we were just tabbed to)
1160 if ((lParam & 0x80000000u) != 0) {
1161 if (isGoodAction() || wParam == VK_TAB) {
1162 return;
1163 }
1164 }
1165
1166 KeyID key = kKeyNone;
1167 KeyModifierMask mask = getModifiers();
1168 switch (wParam) {
1169 case VK_SHIFT:
1170 case VK_LSHIFT:
1171 case VK_RSHIFT:
1172 case VK_CONTROL:
1173 case VK_LCONTROL:
1174 case VK_RCONTROL:
1175 case VK_MENU:
1176 case VK_LMENU:
1177 case VK_RMENU:
1178 case VK_LWIN:
1179 case VK_RWIN:
1180 break;
1181
1182 case VK_TAB:
1183 // allow tabbing out of control
1184 if ((mask & (KeyModifierControl |
1185 KeyModifierAlt | KeyModifierSuper)) == 0) {
1186 HWND next = hwnd;
1187 if ((mask & KeyModifierShift) == 0) {
1188 do {
1189 next = GetWindow(next, GW_HWNDNEXT);
1190 if (next == NULL) {
1191 next = GetWindow(hwnd, GW_HWNDFIRST);
1192 }
1193 } while (next != hwnd &&
1194 (!IsWindowVisible(next) ||
1195 (GetWindowLong(next, GWL_STYLE) & WS_TABSTOP) == 0));
1196 }
1197 else {
1198 do {
1199 next = GetWindow(next, GW_HWNDPREV);
1200 if (next == NULL) {
1201 next = GetWindow(hwnd, GW_HWNDLAST);
1202 }
1203 } while (next != hwnd &&
1204 (!IsWindowVisible(next) ||
1205 (GetWindowLong(next, GWL_STYLE) & WS_TABSTOP) == 0));
1206 }
1207 SetFocus(next);
1208 return;
1209 }
1210 // fall through
1211
1212 default:
1213 key = CMSWindowsKeyState::getKeyID(wParam,
1214 static_cast<KeyButton>((lParam & 0x1ff0000u) >> 16));
1215 switch (key) {
1216 case kKeyNone:
1217 // could be a character
1218 key = getChar(wParam, lParam);
1219 if (key == kKeyNone) {
1220 return;
1221 }
1222 break;
1223
1224 case kKeyShift_L:
1225 case kKeyShift_R:
1226 case kKeyControl_L:
1227 case kKeyControl_R:
1228 case kKeyAlt_L:
1229 case kKeyAlt_R:
1230 case kKeyMeta_L:
1231 case kKeyMeta_R:
1232 case kKeySuper_L:
1233 case kKeySuper_R:
1234 case kKeyCapsLock:
1235 case kKeyNumLock:
1236 case kKeyScrollLock:
1237 // bogus
1238 return;
1239 }
1240 break;
1241 }
1242
1243 // get old screen list
1244 std::set<CString> screens;
1245 CInputFilter::CKeystrokeAction* keyAction =
1246 dynamic_cast<CInputFilter::CKeystrokeAction*>(s_action);
1247 if (keyAction == NULL) {
1248 keyAction =
1249 dynamic_cast<CInputFilter::CKeystrokeAction*>(s_lastGoodAction);
1250 }
1251 if (keyAction != NULL) {
1252 IKeyState::CKeyInfo::split(keyAction->getInfo()->m_screens, screens);
1253 }
1254
1255 // create new action
1256 IPlatformScreen::CKeyInfo* info =
1257 IKeyState::CKeyInfo::alloc(key, mask, 0, 0, screens);
1258 delete s_action;
1259 HWND parent = GetParent(hwnd);
1260 if (isItemChecked(getItem(parent, IDC_HOTKEY_ACTION_DOWNUP))) {
1261 s_action = new CKeystrokeDownUpAction(info);
1262 }
1263 else if (isItemChecked(getItem(parent, IDC_HOTKEY_ACTION_DOWN))) {
1264 s_action = new CInputFilter::CKeystrokeAction(info, true);
1265 }
1266 else if (isItemChecked(getItem(parent, IDC_HOTKEY_ACTION_UP))) {
1267 s_action = new CInputFilter::CKeystrokeAction(info, false);
1268 }
1269 else {
1270 s_action = NULL;
1271 }
1272
1273 fillHotkey(parent);
1274}
1275
1276void
1277CHotkeyOptions::CActionDialog::onLockAction(HWND hwnd)
1278{
1279 HWND child = getItem(hwnd, IDC_HOTKEY_ACTION_LOCK_LIST);
1280 LRESULT index = SendMessage(child, CB_GETCURSEL, 0, 0);
1281 if (index != CB_ERR) {
1282 delete s_action;
1283 s_action = new CInputFilter::CLockCursorToScreenAction(
1284 (CInputFilter::CLockCursorToScreenAction::Mode)index);
1285 }
1286}
1287
1288void
1289CHotkeyOptions::CActionDialog::onSwitchToAction(HWND hwnd)
1290{
1291 HWND child = getItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_TO_LIST);
1292 CString screen = getWindowText(child);
1293 delete s_action;
1294 s_action = new CInputFilter::CSwitchToScreenAction(screen);
1295}
1296
1297void
1298CHotkeyOptions::CActionDialog::onSwitchInAction(HWND hwnd)
1299{
1300 HWND child = getItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_IN_LIST);
1301 LRESULT index = SendMessage(child, CB_GETCURSEL, 0, 0);
1302 if (index != CB_ERR) {
1303 delete s_action;
1304 s_action = new CInputFilter::CSwitchInDirectionAction(
1305 (EDirection)(index + kLeft));
1306 }
1307}
1308
1309KeyID
1310CHotkeyOptions::CActionDialog::getChar(WPARAM wParam, LPARAM lParam)
1311{
1312 BYTE keyState[256];
1313 UINT virtualKey = (UINT)wParam;
1314 UINT scanCode = (UINT)((lParam & 0x0ff0000u) >> 16);
1315 GetKeyboardState(keyState);
1316
1317 // reset modifier state
1318 keyState[VK_SHIFT] = 0;
1319 keyState[VK_LSHIFT] = 0;
1320 keyState[VK_RSHIFT] = 0;
1321 keyState[VK_CONTROL] = 0;
1322 keyState[VK_LCONTROL] = 0;
1323 keyState[VK_RCONTROL] = 0;
1324 keyState[VK_MENU] = 0;
1325 keyState[VK_LMENU] = 0;
1326 keyState[VK_RMENU] = 0;
1327 keyState[VK_LWIN] = 0;
1328 keyState[VK_RWIN] = 0;
1329
1330 // translate virtual key to character
1331 int n;
1332 KeyID id;
1333 if (CArchMiscWindows::isWindows95Family()) {
1334 // XXX -- how do we get characters not in Latin-1?
1335 WORD ascii;
1336 n = ToAscii(virtualKey, scanCode, keyState, &ascii, 0);
1337 id = static_cast<KeyID>(ascii & 0xffu);
1338 }
1339 else {
1340 typedef int (WINAPI *ToUnicode_t)(UINT wVirtKey,
1341 UINT wScanCode,
1342 PBYTE lpKeyState,
1343 LPWSTR pwszBuff,
1344 int cchBuff,
1345 UINT wFlags);
1346 ToUnicode_t s_ToUnicode = NULL;
1347 if (s_ToUnicode == NULL) {
1348 HMODULE userModule = GetModuleHandle("user32.dll");
1349 s_ToUnicode =
1350 (ToUnicode_t)GetProcAddress(userModule, "ToUnicode");
1351 }
1352
1353 WCHAR unicode[2];
1354 n = s_ToUnicode(virtualKey, scanCode, keyState,
1355 unicode, sizeof(unicode) / sizeof(unicode[0]),
1356 0);
1357 id = static_cast<KeyID>(unicode[0]);
1358 }
1359 switch (n) {
1360 case -1:
1361 // no hot keys on dead keys
1362 return kKeyNone;
1363
1364 default:
1365 case 0:
1366 // unmapped
1367 return kKeyNone;
1368
1369 case 1:
1370 return id;
1371 }
1372}
1373
1374KeyModifierMask
1375CHotkeyOptions::CActionDialog::getModifiers()
1376{
1377 KeyModifierMask mask = 0;
1378 if ((GetKeyState(VK_SHIFT) & 0x8000) != 0) {
1379 mask |= KeyModifierShift;
1380 }
1381 if ((GetKeyState(VK_CONTROL) & 0x8000) != 0) {
1382 mask |= KeyModifierControl;
1383 }
1384 if ((GetKeyState(VK_MENU) & 0x8000) != 0) {
1385 mask |= KeyModifierAlt;
1386 }
1387 if ((GetKeyState(VK_LWIN) & 0x8000) != 0 ||
1388 (GetKeyState(VK_RWIN) & 0x8000) != 0) {
1389 mask |= KeyModifierSuper;
1390 }
1391 return mask;
1392}
1393
1394bool
1395CHotkeyOptions::CActionDialog::isGoodAction()
1396{
1397 CInputFilter::CMouseButtonAction* mouseAction =
1398 dynamic_cast<CInputFilter::CMouseButtonAction*>(s_action);
1399 CInputFilter::CKeystrokeAction* keyAction =
1400 dynamic_cast<CInputFilter::CKeystrokeAction*>(s_action);
1401 return (mouseAction == NULL || keyAction == NULL ||
1402 keyAction->getInfo()->m_key != kKeyNone);
1403}
1404
1405void
1406CHotkeyOptions::CActionDialog::convertAction(HWND hwnd)
1407{
1408 if (s_lastGoodAction != NULL) {
1409 CInputFilter::CMouseButtonAction* mouseAction =
1410 dynamic_cast<CInputFilter::CMouseButtonAction*>(s_lastGoodAction);
1411 CInputFilter::CKeystrokeAction* keyAction =
1412 dynamic_cast<CInputFilter::CKeystrokeAction*>(s_lastGoodAction);
1413 if (mouseAction != NULL) {
1414 IPlatformScreen::CButtonInfo* info =
1415 IPrimaryScreen::CButtonInfo::alloc(*mouseAction->getInfo());
1416 delete s_action;
1417 if (isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_DOWNUP))) {
1418 s_action = new CMouseButtonDownUpAction(info);
1419 }
1420 else if (isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_DOWN))) {
1421 s_action = new CInputFilter::CMouseButtonAction(info, true);
1422 }
1423 else if (isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_UP))) {
1424 s_action = new CInputFilter::CMouseButtonAction(info, false);
1425 }
1426 else {
1427 free(info);
1428 s_action = NULL;
1429 }
1430 }
1431 else if (keyAction != NULL) {
1432 IPlatformScreen::CKeyInfo* info =
1433 IKeyState::CKeyInfo::alloc(*keyAction->getInfo());
1434 delete s_action;
1435 if (isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_DOWNUP))) {
1436 s_action = new CKeystrokeDownUpAction(info);
1437 }
1438 else if (isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_DOWN))) {
1439 s_action = new CInputFilter::CKeystrokeAction(info, true);
1440 }
1441 else if (isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_UP))) {
1442 s_action = new CInputFilter::CKeystrokeAction(info, false);
1443 }
1444 else {
1445 free(info);
1446 s_action = NULL;
1447 }
1448 }
1449 }
1450}
1451
1452bool
1453CHotkeyOptions::CActionDialog::isDownUpAction()
1454{
1455 return (dynamic_cast<CKeystrokeDownUpAction*>(s_action) != NULL ||
1456 dynamic_cast<CMouseButtonDownUpAction*>(s_action) != NULL);
1457}
1458
1459BOOL CALLBACK
1460CHotkeyOptions::CActionDialog::dlgProc(HWND hwnd,
1461 UINT message, WPARAM wParam, LPARAM)
1462{
1463 switch (message) {
1464 case WM_INITDIALOG:
1465 doInit(hwnd);
1466 return TRUE;
1467
1468 case WM_COMMAND:
1469 switch (LOWORD(wParam)) {
1470 case IDOK:
1471 if (isDownUpAction()) {
1472 s_onActivate = true;
1473 }
1474 EndDialog(hwnd, 1);
1475 return TRUE;
1476
1477 case IDCANCEL:
1478 EndDialog(hwnd, 0);
1479 return TRUE;
1480
1481 case IDC_HOTKEY_ACTION_ON_ACTIVATE:
1482 s_onActivate = true;
1483 return TRUE;
1484
1485 case IDC_HOTKEY_ACTION_ON_DEACTIVATE:
1486 s_onActivate = false;
1487 return TRUE;
1488
1489 case IDC_HOTKEY_ACTION_DOWNUP:
1490 case IDC_HOTKEY_ACTION_DOWN:
1491 case IDC_HOTKEY_ACTION_UP:
1492 convertAction(hwnd);
1493 fillHotkey(hwnd);
1494 updateControls(hwnd);
1495 return TRUE;
1496
1497 case IDC_HOTKEY_ACTION_LOCK:
1498 onLockAction(hwnd);
1499 updateControls(hwnd);
1500 return TRUE;
1501
1502 case IDC_HOTKEY_ACTION_SWITCH_TO:
1503 onSwitchToAction(hwnd);
1504 updateControls(hwnd);
1505 return TRUE;
1506
1507 case IDC_HOTKEY_ACTION_SWITCH_IN:
1508 onSwitchInAction(hwnd);
1509 updateControls(hwnd);
1510 return TRUE;
1511
1512 case IDC_HOTKEY_ACTION_LOCK_LIST:
1513 switch (HIWORD(wParam)) {
1514 case LBN_SELCHANGE:
1515 onLockAction(hwnd);
1516 return TRUE;
1517 }
1518 break;
1519
1520 case IDC_HOTKEY_ACTION_SWITCH_TO_LIST:
1521 switch (HIWORD(wParam)) {
1522 case LBN_SELCHANGE:
1523 onSwitchToAction(hwnd);
1524 return TRUE;
1525 }
1526 break;
1527
1528 case IDC_HOTKEY_ACTION_SWITCH_IN_LIST:
1529 switch (HIWORD(wParam)) {
1530 case LBN_SELCHANGE:
1531 onSwitchInAction(hwnd);
1532 return TRUE;
1533 }
1534 break;
1535
1536 case IDC_HOTKEY_ACTION_SCREENS:
1537 CScreensDialog::doModal(hwnd, s_config,
1538 dynamic_cast<CInputFilter::CKeystrokeAction*>(s_action));
1539 fillHotkey(hwnd);
1540 return TRUE;
1541 }
1542 break;
1543
1544 default:
1545 break;
1546 }
1547
1548 return FALSE;
1549}
1550
1551LRESULT CALLBACK
1552CHotkeyOptions::CActionDialog::editProc(HWND hwnd,
1553 UINT message, WPARAM wParam, LPARAM lParam)
1554{
1555 switch (message) {
1556 case WM_LBUTTONDOWN:
1557 if (GetFocus() == hwnd) {
1558 onButton(hwnd, kButtonLeft);
1559 }
1560 else {
1561 SetFocus(hwnd);
1562 }
1563 return 0;
1564
1565 case WM_MBUTTONDOWN:
1566 if (GetFocus() == hwnd) {
1567 onButton(hwnd, kButtonMiddle);
1568 }
1569 return 0;
1570
1571 case WM_RBUTTONDOWN:
1572 if (GetFocus() == hwnd) {
1573 onButton(hwnd, kButtonRight);
1574 }
1575 return 0;
1576
1577 case WM_XBUTTONDOWN:
1578 if (GetFocus() == hwnd) {
1579 switch (HIWORD(wParam)) {
1580 case XBUTTON1:
1581 onButton(hwnd, kButtonExtra0 + 0);
1582 break;
1583
1584 case XBUTTON2:
1585 onButton(hwnd, kButtonExtra0 + 1);
1586 break;
1587 }
1588 }
1589 return 0;
1590
1591 case WM_KEYDOWN:
1592 case WM_SYSKEYDOWN:
1593 case WM_KEYUP:
1594 case WM_SYSKEYUP:
1595 onKey(hwnd, wParam, lParam);
1596 return 0;
1597
1598 case WM_LBUTTONUP:
1599 case WM_MBUTTONUP:
1600 case WM_RBUTTONUP:
1601 case WM_XBUTTONUP:
1602 case WM_CHAR:
1603 case WM_SYSCHAR:
1604 case WM_DEADCHAR:
1605 case WM_SYSDEADCHAR:
1606 return 0;
1607
1608 case WM_SETFOCUS:
1609 if (s_action != NULL) {
1610 delete s_lastGoodAction;
1611 s_lastGoodAction = s_action->clone();
1612 }
1613 break;
1614
1615 case WM_KILLFOCUS:
1616 if (!isGoodAction()) {
1617 delete s_action;
1618 if (s_lastGoodAction != NULL) {
1619 s_action = s_lastGoodAction->clone();
1620 }
1621 else {
1622 s_action = NULL;
1623 }
1624 }
1625 else if (s_action != NULL) {
1626 delete s_lastGoodAction;
1627 s_lastGoodAction = s_action->clone();
1628 }
1629 fillHotkey(GetParent(hwnd));
1630 break;
1631
1632 case WM_GETDLGCODE:
1633 return DLGC_WANTALLKEYS;
1634
1635 default:
1636 break;
1637 }
1638 return CallWindowProc(s_editWndProc, hwnd, message, wParam, lParam);
1639}
1640
1641
1642//
1643// CHotkeyOptions::CScreensDialog
1644//
1645
1646CConfig* CHotkeyOptions::CScreensDialog::s_config = NULL;
1647CInputFilter::CKeystrokeAction*
1648 CHotkeyOptions::CScreensDialog::s_action = NULL;
1649CHotkeyOptions::CScreensDialog::CScreens
1650 CHotkeyOptions::CScreensDialog::s_nonTargets;
1651CHotkeyOptions::CScreensDialog::CScreens
1652 CHotkeyOptions::CScreensDialog::s_targets;
1653CString CHotkeyOptions::CScreensDialog::s_allScreens;
1654
1655void
1656CHotkeyOptions::CScreensDialog::doModal(HWND parent, CConfig* config,
1657 CInputFilter::CKeystrokeAction* action)
1658{
1659 s_allScreens = getString(IDS_ALL_SCREENS);
1660 s_config = config;
1661 s_action = action;
1662 DialogBox(s_instance, MAKEINTRESOURCE(IDD_HOTKEY_SCREENS),
1663 parent, dlgProc);
1664 s_config = NULL;
1665 s_action = NULL;
1666}
1667
1668void
1669CHotkeyOptions::CScreensDialog::doInit(HWND hwnd)
1670{
1671 s_nonTargets.clear();
1672 s_targets.clear();
1673
1674 // get screens from config
1675 s_nonTargets.insert("*");
1676 for (CConfig::const_iterator i = s_config->begin();
1677 i != s_config->end(); ++i) {
1678 s_nonTargets.insert(*i);
1679 }
1680
1681 // get screens in action
1682 IKeyState::CKeyInfo::split(s_action->getInfo()->m_screens, s_targets);
1683
1684 // remove screens in action from screens in config
1685 for (CScreens::const_iterator i = s_targets.begin();
1686 i != s_targets.end(); ++i) {
1687 s_nonTargets.erase(*i);
1688 }
1689
1690 // fill dialog
1691 fillScreens(hwnd);
1692 updateControls(hwnd);
1693}
1694
1695void
1696CHotkeyOptions::CScreensDialog::doFini(HWND)
1697{
1698 // put screens into action
1699 const IPlatformScreen::CKeyInfo* oldInfo = s_action->getInfo();
1700 IPlatformScreen::CKeyInfo* newInfo =
1701 IKeyState::CKeyInfo::alloc(oldInfo->m_key,
1702 oldInfo->m_mask, 0, 0, s_targets);
1703 s_action->adoptInfo(newInfo);
1704}
1705
1706void
1707CHotkeyOptions::CScreensDialog::fillScreens(HWND hwnd)
1708{
1709 HWND child = getItem(hwnd, IDC_HOTKEY_SCREENS_SRC);
1710 SendMessage(child, LB_RESETCONTENT, 0, 0);
1711 for (CScreens::const_iterator i = s_nonTargets.begin();
1712 i != s_nonTargets.end(); ++i) {
1713 CString name = *i;
1714 if (name == "*") {
1715 name = s_allScreens;
1716 }
1717 SendMessage(child, LB_INSERTSTRING, (WPARAM)-1,
1718 (LPARAM)name.c_str());
1719 }
1720
1721 child = getItem(hwnd, IDC_HOTKEY_SCREENS_DST);
1722 SendMessage(child, LB_RESETCONTENT, 0, 0);
1723 for (CScreens::const_iterator i = s_targets.begin();
1724 i != s_targets.end(); ++i) {
1725 CString name = *i;
1726 if (name == "*") {
1727 name = s_allScreens;
1728 }
1729 SendMessage(child, LB_INSERTSTRING, (WPARAM)-1,
1730 (LPARAM)name.c_str());
1731 }
1732 if (s_targets.empty()) {
1733 // if no targets then add a special item so the user knows
1734 // what'll happen
1735 CString activeScreenLabel = getString(IDS_ACTIVE_SCREEN);
1736 SendMessage(child, LB_INSERTSTRING, (WPARAM)-1,
1737 (LPARAM)activeScreenLabel.c_str());
1738 }
1739}
1740
1741void
1742CHotkeyOptions::CScreensDialog::updateControls(HWND hwnd)
1743{
1744 HWND child = getItem(hwnd, IDC_HOTKEY_SCREENS_SRC);
1745 bool canAdd = (SendMessage(child, LB_GETSELCOUNT, 0, 0) != 0);
1746 child = getItem(hwnd, IDC_HOTKEY_SCREENS_DST);
1747 bool canRemove = (!s_targets.empty() &&
1748 (SendMessage(child, LB_GETSELCOUNT, 0, 0) != 0));
1749
1750 enableItem(hwnd, IDC_HOTKEY_SCREENS_ADD, canAdd);
1751 enableItem(hwnd, IDC_HOTKEY_SCREENS_REMOVE, canRemove);
1752}
1753
1754void
1755CHotkeyOptions::CScreensDialog::add(HWND hwnd)
1756{
1757 CScreens selected;
1758 getSelected(hwnd, IDC_HOTKEY_SCREENS_SRC, s_nonTargets, selected);
1759 for (CScreens::const_iterator i = selected.begin();
1760 i != selected.end(); ++i) {
1761 s_targets.insert(*i);
1762 s_nonTargets.erase(*i);
1763 }
1764 fillScreens(hwnd);
1765 updateControls(hwnd);
1766}
1767
1768void
1769CHotkeyOptions::CScreensDialog::remove(HWND hwnd)
1770{
1771 CScreens selected;
1772 getSelected(hwnd, IDC_HOTKEY_SCREENS_DST, s_targets, selected);
1773 for (CScreens::const_iterator i = selected.begin();
1774 i != selected.end(); ++i) {
1775 s_nonTargets.insert(*i);
1776 s_targets.erase(*i);
1777 }
1778 fillScreens(hwnd);
1779 updateControls(hwnd);
1780}
1781
1782void
1783CHotkeyOptions::CScreensDialog::getSelected(HWND hwnd, UINT id,
1784 const CScreens& inScreens, CScreens& outScreens)
1785{
1786 // get the selected item indices
1787 HWND child = getItem(hwnd, id);
1788 UInt32 n = (UInt32)SendMessage(child, LB_GETSELCOUNT, 0, 0);
1789 int* index = new int[n];
1790 SendMessage(child, LB_GETSELITEMS, (WPARAM)n, (LPARAM)index);
1791
1792 // get the items in a vector
1793 std::vector<CString> tmpList;
1794 for (CScreens::const_iterator i = inScreens.begin();
1795 i != inScreens.end(); ++i) {
1796 tmpList.push_back(*i);
1797 }
1798
1799 // get selected items into the output set
1800 outScreens.clear();
1801 for (UInt32 i = 0; i < n; ++i) {
1802 outScreens.insert(tmpList[index[i]]);
1803 }
1804
1805 // clean up
1806 delete[] index;
1807}
1808
1809BOOL CALLBACK
1810CHotkeyOptions::CScreensDialog::dlgProc(HWND hwnd,
1811 UINT message, WPARAM wParam, LPARAM lParam)
1812{
1813 switch (message) {
1814 case WM_INITDIALOG:
1815 doInit(hwnd);
1816 return TRUE;
1817
1818 case WM_COMMAND:
1819 switch (LOWORD(wParam)) {
1820 case IDOK:
1821 doFini(hwnd);
1822 EndDialog(hwnd, 0);
1823 return TRUE;
1824
1825 case IDCANCEL:
1826 EndDialog(hwnd, 0);
1827 return TRUE;
1828
1829 case IDC_HOTKEY_SCREENS_ADD:
1830 add(hwnd);
1831 return TRUE;
1832
1833 case IDC_HOTKEY_SCREENS_REMOVE:
1834 remove(hwnd);
1835 return TRUE;
1836
1837 case IDC_HOTKEY_SCREENS_SRC:
1838 case IDC_HOTKEY_SCREENS_DST:
1839 switch (HIWORD(wParam)) {
1840 case LBN_SELCANCEL:
1841 case LBN_SELCHANGE:
1842 updateControls(hwnd);
1843 return TRUE;
1844 }
1845 break;
1846 }
1847 break;
1848
1849 case WM_CTLCOLORLISTBOX:
1850 if (s_targets.empty() &&
1851 (HWND)lParam == getItem(hwnd, IDC_HOTKEY_SCREENS_DST)) {
1852 // override colors
1853 HDC dc = (HDC)wParam;
1854 SetTextColor(dc, GetSysColor(COLOR_GRAYTEXT));
1855 return (BOOL)GetSysColorBrush(COLOR_WINDOW);
1856 }
1857 break;
1858
1859 default:
1860 break;
1861 }
1862
1863 return FALSE;
1864}
Note: See TracBrowser for help on using the repository browser.