CHotkeyOptions.cpp

00001 /*
00002  * synergy -- mouse and keyboard sharing utility
00003  * Copyright (C) 2006 Chris Schoeneman
00004  * 
00005  * This package is free software; you can redistribute it and/or
00006  * modify it under the terms of the GNU General Public License
00007  * found in the file COPYING that should have accompanied this file.
00008  * 
00009  * This package is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
00013  */
00014 
00015 #include "CArchMiscWindows.h"
00016 #include "CMSWindowsKeyState.h"
00017 #include "CConfig.h"
00018 #include "CHotkeyOptions.h"
00019 #include "CStringUtil.h"
00020 #include "LaunchUtil.h"
00021 #include "resource.h"
00022 
00023 #if !defined(WM_XBUTTONDOWN)
00024 #define WM_XBUTTONDOWN      0x020B
00025 #define WM_XBUTTONUP        0x020C
00026 #define WM_XBUTTONDBLCLK    0x020D
00027 #define XBUTTON1            0x0001
00028 #define XBUTTON2            0x0002
00029 #endif
00030 
00031 //
00032 // CAdvancedOptions
00033 //
00034 
00035 CHotkeyOptions*         CHotkeyOptions::s_singleton = NULL;
00036 
00037 CHotkeyOptions::CHotkeyOptions(HWND parent, CConfig* config) :
00038     m_parent(parent),
00039     m_config(config)
00040 {
00041     assert(s_singleton == NULL);
00042     s_singleton = this;
00043 }
00044 
00045 CHotkeyOptions::~CHotkeyOptions()
00046 {
00047     s_singleton = NULL;
00048 }
00049 
00050 void
00051 CHotkeyOptions::doModal()
00052 {
00053     // do dialog
00054     m_inputFilter = m_config->getInputFilter();
00055     DialogBoxParam(s_instance, MAKEINTRESOURCE(IDD_HOTKEY_OPTIONS),
00056                                 m_parent, (DLGPROC)dlgProc, (LPARAM)this);
00057 }
00058 
00059 void
00060 CHotkeyOptions::doInit(HWND hwnd)
00061 {
00062     m_activeRuleIndex = (UInt32)-1;
00063     fillHotkeys(hwnd);
00064     openRule(hwnd);
00065 }
00066 
00067 void
00068 CHotkeyOptions::fillHotkeys(HWND hwnd, UInt32 select)
00069 {
00070     HWND rules = getItem(hwnd, IDC_HOTKEY_HOTKEYS);
00071 
00072     SendMessage(rules, LB_RESETCONTENT, 0, 0);
00073     for (UInt32 i = 0, n = m_inputFilter->getNumRules(); i < n; ++i) {
00074         CInputFilter::CRule& rule = m_inputFilter->getRule(i);
00075         SendMessage(rules, LB_INSERTSTRING, (WPARAM)-1,
00076                             (LPARAM)rule.getCondition()->format().c_str());
00077     }
00078 
00079     if (select < m_inputFilter->getNumRules()) {
00080         SendMessage(rules, LB_SETCURSEL, select, 0);
00081     }
00082 
00083     updateHotkeysControls(hwnd);
00084 }
00085 
00086 void
00087 CHotkeyOptions::updateHotkeysControls(HWND hwnd)
00088 {
00089     HWND child = getItem(hwnd, IDC_HOTKEY_HOTKEYS);
00090     bool selected = (SendMessage(child, LB_GETCURSEL, 0, 0) != LB_ERR);
00091 
00092     enableItem(hwnd, IDC_HOTKEY_ADD_HOTKEY, TRUE);
00093     enableItem(hwnd, IDC_HOTKEY_EDIT_HOTKEY, selected);
00094     enableItem(hwnd, IDC_HOTKEY_REMOVE_HOTKEY, selected);
00095 }
00096 
00097 void
00098 CHotkeyOptions::addHotkey(HWND hwnd)
00099 {
00100     closeRule(hwnd);
00101     CInputFilter::CCondition* condition = NULL;
00102     if (editCondition(hwnd, condition)) {
00103         m_inputFilter->addFilterRule(CInputFilter::CRule(condition));
00104         fillHotkeys(hwnd, m_inputFilter->getNumRules() - 1);
00105     }
00106     else {
00107         delete condition;
00108     }
00109     openRule(hwnd);
00110 }
00111 
00112 void
00113 CHotkeyOptions::removeHotkey(HWND hwnd)
00114 {
00115     UInt32 ruleIndex = m_activeRuleIndex;
00116     closeRule(hwnd);
00117 
00118     m_inputFilter->removeFilterRule(ruleIndex);
00119     UInt32 n = m_inputFilter->getNumRules();
00120     if (n > 0 && ruleIndex >= n) {
00121         ruleIndex = n - 1;
00122     }
00123     fillHotkeys(hwnd, ruleIndex);
00124 
00125     openRule(hwnd);
00126 }
00127 
00128 void
00129 CHotkeyOptions::editHotkey(HWND hwnd)
00130 {
00131     // save selected item in action list
00132     HWND actions = getItem(hwnd, IDC_HOTKEY_ACTIONS);
00133     LRESULT aIndex = SendMessage(actions, LB_GETCURSEL, 0, 0);
00134 
00135     UInt32 index = m_activeRuleIndex;
00136     closeRule(hwnd);
00137 
00138     CInputFilter::CRule& rule = m_inputFilter->getRule(index);
00139     CInputFilter::CCondition* condition = rule.getCondition()->clone();
00140     if (editCondition(hwnd, condition)) {
00141         rule.setCondition(condition);
00142         fillHotkeys(hwnd, index);
00143     }
00144     else {
00145         delete condition;
00146     }
00147 
00148     openRule(hwnd);
00149 
00150     // restore selected item in action list
00151     if (aIndex != LB_ERR) {
00152         SendMessage(actions, LB_SETCURSEL, aIndex, 0);
00153     }
00154 }
00155 
00156 void
00157 CHotkeyOptions::fillActions(HWND hwnd, UInt32 select)
00158 {
00159     HWND actions = getItem(hwnd, IDC_HOTKEY_ACTIONS);
00160     SendMessage(actions, LB_RESETCONTENT, 0, 0);
00161     if (m_activeRuleIndex != (UInt32)-1) {
00162         UInt32 n  = m_activeRule.getNumActions(true);
00163         UInt32 n2 = m_activeRule.getNumActions(false);
00164         for (UInt32 i = 0; i < n; ++i) {
00165             const CInputFilter::CAction& action =
00166                 m_activeRule.getAction(true, i);
00167             CString line("A ");
00168             line += action.format();
00169             SendMessage(actions, LB_INSERTSTRING, (WPARAM)-1,
00170                                 (LPARAM)line.c_str());
00171             SendMessage(actions, LB_SETITEMDATA, (WPARAM)i, (LPARAM)i);
00172         }
00173         for (UInt32 i = 0; i < n2; ++i) {
00174             const CInputFilter::CAction& action =
00175                 m_activeRule.getAction(false, i);
00176             CString line("D ");
00177             line += action.format();
00178             SendMessage(actions, LB_INSERTSTRING, (WPARAM)-1,
00179                                 (LPARAM)line.c_str());
00180             SendMessage(actions, LB_SETITEMDATA, (WPARAM)i + n,
00181                                 (LPARAM)(i | 0x80000000u));
00182         }
00183 
00184         if (select < n + n2) {
00185             SendMessage(actions, LB_SETCURSEL, select, 0);
00186         }
00187     }
00188 
00189     updateActionsControls(hwnd);
00190 }
00191 
00192 void
00193 CHotkeyOptions::updateActionsControls(HWND hwnd)
00194 {
00195     HWND child = getItem(hwnd, IDC_HOTKEY_HOTKEYS);
00196     bool active = (m_activeRuleIndex != (UInt32)-1);
00197 
00198     child = getItem(hwnd, IDC_HOTKEY_ACTIONS);
00199     bool selected =
00200         (active && (SendMessage(child, LB_GETCURSEL, 0, 0) != LB_ERR));
00201 
00202     enableItem(hwnd, IDC_HOTKEY_ADD_ACTION, active);
00203     enableItem(hwnd, IDC_HOTKEY_EDIT_ACTION, selected);
00204     enableItem(hwnd, IDC_HOTKEY_REMOVE_ACTION, selected);
00205 }
00206 
00207 void
00208 CHotkeyOptions::addAction(HWND hwnd)
00209 {
00210     CInputFilter::CAction* action = NULL;
00211     bool onActivate = true;
00212     if (editAction(hwnd, action, onActivate)) {
00213         m_activeRule.adoptAction(action, onActivate);
00214 
00215         UInt32 actionIndex = m_activeRule.getNumActions(true) - 1;
00216         if (!onActivate) {
00217             actionIndex += m_activeRule.getNumActions(false);
00218         }
00219         fillActions(hwnd, actionIndex);
00220     }
00221     else {
00222         delete action;
00223     }
00224 }
00225 
00226 void
00227 CHotkeyOptions::removeAction(HWND hwnd)
00228 {
00229     HWND child = getItem(hwnd, IDC_HOTKEY_ACTIONS);
00230     LRESULT index = SendMessage(child, LB_GETCURSEL, 0, 0);
00231     if (index != LB_ERR) {
00232         UInt32 actionIndex =
00233             (UInt32)SendMessage(child, LB_GETITEMDATA, index, 0);
00234         bool onActivate = ((actionIndex & 0x80000000u) == 0);
00235         actionIndex    &= ~0x80000000u;
00236 
00237         m_activeRule.removeAction(onActivate, actionIndex);
00238 
00239         actionIndex = static_cast<UInt32>(index);
00240         UInt32 n = m_activeRule.getNumActions(true) +
00241                         m_activeRule.getNumActions(false);
00242         if (n > 0 && actionIndex >= n) {
00243             actionIndex = n - 1;
00244         }
00245         fillActions(hwnd, actionIndex);
00246     }
00247 }
00248 
00249 void
00250 CHotkeyOptions::editAction(HWND hwnd)
00251 {
00252     HWND child = getItem(hwnd, IDC_HOTKEY_ACTIONS);
00253     LRESULT index = SendMessage(child, LB_GETCURSEL, 0, 0);
00254     if (index != LB_ERR) {
00255         UInt32 actionIndex =
00256             (UInt32)SendMessage(child, LB_GETITEMDATA, index, 0);
00257         bool onActivate = ((actionIndex & 0x80000000u) == 0);
00258         actionIndex    &= ~0x80000000u;
00259 
00260         CInputFilter::CAction* action =
00261             m_activeRule.getAction(onActivate, actionIndex).clone();
00262         bool newOnActivate = onActivate;
00263         if (editAction(hwnd, action, newOnActivate)) {
00264             if (onActivate == newOnActivate) {
00265                 m_activeRule.replaceAction(action, onActivate, actionIndex);
00266                 actionIndex = static_cast<UInt32>(index);
00267             }
00268             else {
00269                 m_activeRule.removeAction(onActivate, actionIndex);
00270                 m_activeRule.adoptAction(action, newOnActivate);
00271                 actionIndex = m_activeRule.getNumActions(true) - 1;
00272                 if (!newOnActivate) {
00273                     actionIndex += m_activeRule.getNumActions(false);
00274                 }
00275             }
00276             fillActions(hwnd, actionIndex);
00277         }
00278         else {
00279             delete action;
00280         }
00281     }
00282 }
00283 
00284 bool
00285 CHotkeyOptions::editCondition(HWND hwnd, CInputFilter::CCondition*& condition)
00286 {
00287     return CConditionDialog::doModal(hwnd, condition);
00288 }
00289 
00290 bool
00291 CHotkeyOptions::editAction(HWND hwnd, CInputFilter::CAction*& action,
00292                 bool& onActivate)
00293 {
00294     return CActionDialog::doModal(hwnd, m_config, action, onActivate);
00295 }
00296 
00297 void
00298 CHotkeyOptions::openRule(HWND hwnd)
00299 {
00300     // get the active rule and copy it, merging down/up pairs of keystroke
00301     // and mouse button actions into single actions for the convenience of
00302     // of the user.
00303     HWND rules = getItem(hwnd, IDC_HOTKEY_HOTKEYS);
00304     LRESULT index = SendMessage(rules, LB_GETCURSEL, 0, 0);
00305     if (index != LB_ERR) {
00306         // copy the rule as is
00307         m_activeRuleIndex = (SInt32)index;
00308         m_activeRule = m_inputFilter->getRule(m_activeRuleIndex);
00309 
00310         // look for actions to combine
00311         for (UInt32 i = 0, n = m_activeRule.getNumActions(true); i < n; ++i) {
00312             // get next activate action
00313             const CInputFilter::CAction* action =
00314                 &m_activeRule.getAction(true, i);
00315 
00316             // check if it's a key or mouse action
00317             const CInputFilter::CKeystrokeAction* keyAction =
00318                 dynamic_cast<const CInputFilter::CKeystrokeAction*>(action);
00319             const CInputFilter::CMouseButtonAction* mouseAction =
00320                 dynamic_cast<const CInputFilter::CMouseButtonAction*>(action);
00321             if (keyAction == NULL && mouseAction == NULL) {
00322                 continue;
00323             }
00324 
00325             // check for matching deactivate action
00326             UInt32 j = (UInt32)-1;
00327             CInputFilter::CAction* newAction = NULL;
00328             if (keyAction != NULL) {
00329                 j = findMatchingAction(keyAction);
00330                 if (j != (UInt32)-1) {
00331                     // found a match
00332                     const IPlatformScreen::CKeyInfo* oldInfo =
00333                         keyAction->getInfo();
00334                     IPlatformScreen::CKeyInfo* newInfo =
00335                         IKeyState::CKeyInfo::alloc(*oldInfo);
00336                     newAction = new CKeystrokeDownUpAction(newInfo);
00337                 }
00338             }
00339             else if (mouseAction != NULL) {
00340                 j = findMatchingAction(mouseAction);
00341                 if (j != (UInt32)-1) {
00342                     // found a match
00343                     const IPlatformScreen::CButtonInfo* oldInfo =
00344                         mouseAction->getInfo();
00345                     IPlatformScreen::CButtonInfo* newInfo =
00346                         IPrimaryScreen::CButtonInfo::alloc(*oldInfo);
00347                     newAction = new CMouseButtonDownUpAction(newInfo);
00348                 }
00349             }
00350 
00351             // perform merge
00352             if (newAction != NULL) {
00353                 m_activeRule.replaceAction(newAction, true, i);
00354                 m_activeRule.removeAction(false, j);
00355             }
00356         }
00357     }
00358     else {
00359         m_activeRuleIndex = (UInt32)-1;
00360     }
00361     fillActions(hwnd);
00362 }
00363 
00364 void
00365 CHotkeyOptions::closeRule(HWND)
00366 {
00367     // copy rule back to input filter, expanding merged actions into the
00368     // two component actions.
00369     if (m_activeRuleIndex != (UInt32)-1) {
00370         // expand merged rules
00371         for (UInt32 i = 0, n = m_activeRule.getNumActions(true); i < n; ++i) {
00372             // get action
00373             const CInputFilter::CAction* action =
00374                 &m_activeRule.getAction(true, i);
00375 
00376             // check if it's a merged key or mouse action
00377             const CKeystrokeDownUpAction* keyAction =
00378                 dynamic_cast<const CKeystrokeDownUpAction*>(action);
00379             const CMouseButtonDownUpAction* mouseAction =
00380                 dynamic_cast<const CMouseButtonDownUpAction*>(action);
00381             if (keyAction == NULL && mouseAction == NULL) {
00382                 continue;
00383             }
00384 
00385             // expand
00386             if (keyAction != NULL) {
00387                 const IPlatformScreen::CKeyInfo* oldInfo =
00388                     keyAction->getInfo();
00389                 IPlatformScreen::CKeyInfo* newInfo =
00390                     IKeyState::CKeyInfo::alloc(*oldInfo);
00391                 CInputFilter::CKeystrokeAction* downAction =
00392                     new CInputFilter::CKeystrokeAction(newInfo, true);
00393                 newInfo = IKeyState::CKeyInfo::alloc(*oldInfo);
00394                 CInputFilter::CKeystrokeAction* upAction =
00395                     new CInputFilter::CKeystrokeAction(newInfo, false);
00396                 m_activeRule.replaceAction(downAction, true, i);
00397                 m_activeRule.adoptAction(upAction, false);
00398             }
00399             else if (mouseAction != NULL) {
00400                 const IPlatformScreen::CButtonInfo* oldInfo =
00401                     mouseAction->getInfo();
00402                 IPlatformScreen::CButtonInfo* newInfo =
00403                     IPrimaryScreen::CButtonInfo::alloc(*oldInfo);
00404                 CInputFilter::CMouseButtonAction* downAction =
00405                     new CInputFilter::CMouseButtonAction(newInfo, true);
00406                 newInfo = IPrimaryScreen::CButtonInfo::alloc(*oldInfo);
00407                 CInputFilter::CMouseButtonAction* upAction =
00408                     new CInputFilter::CMouseButtonAction(newInfo, false);
00409                 m_activeRule.replaceAction(downAction, true, i);
00410                 m_activeRule.adoptAction(upAction, false);
00411             }
00412         }
00413 
00414         // copy it back
00415         m_inputFilter->getRule(m_activeRuleIndex) = m_activeRule;
00416     }
00417     m_activeRuleIndex = (UInt32)-1;
00418 }
00419 
00420 UInt32
00421 CHotkeyOptions::findMatchingAction(
00422                 const CInputFilter::CKeystrokeAction* src) const
00423 {
00424     for (UInt32 i = 0, n = m_activeRule.getNumActions(false); i < n; ++i) {
00425         const CInputFilter::CKeystrokeAction* dst =
00426             dynamic_cast<const CInputFilter::CKeystrokeAction*>(
00427                 &m_activeRule.getAction(false, i));
00428         if (dst != NULL &&
00429             IKeyState::CKeyInfo::equal(src->getInfo(), dst->getInfo())) {
00430             return i;
00431         }
00432     }
00433     return (UInt32)-1;
00434 }
00435 
00436 UInt32
00437 CHotkeyOptions::findMatchingAction(
00438                 const CInputFilter::CMouseButtonAction* src) const
00439 {
00440     for (UInt32 i = 0, n = m_activeRule.getNumActions(false); i < n; ++i) {
00441         const CInputFilter::CMouseButtonAction* dst =
00442             dynamic_cast<const CInputFilter::CMouseButtonAction*>(
00443                 &m_activeRule.getAction(false, i));
00444         if (dst != NULL &&
00445             IPrimaryScreen::CButtonInfo::equal(
00446                                 src->getInfo(), dst->getInfo())) {
00447             return i;
00448         }
00449     }
00450     return (UInt32)-1;
00451 }
00452 
00453 BOOL
00454 CHotkeyOptions::doDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM)
00455 {
00456     switch (message) {
00457     case WM_INITDIALOG:
00458         doInit(hwnd);
00459         return TRUE;
00460 
00461     case WM_COMMAND:
00462         switch (LOWORD(wParam)) {
00463         case IDOK:
00464         case IDCANCEL:
00465             closeRule(hwnd);
00466             EndDialog(hwnd, 0);
00467             return TRUE;
00468 
00469         case IDC_HOTKEY_HOTKEYS:
00470             switch (HIWORD(wParam)) {
00471             case LBN_DBLCLK:
00472                 editHotkey(hwnd);
00473                 return TRUE;
00474 
00475             case LBN_SELCHANGE: {
00476                 HWND rules    = getItem(hwnd, IDC_HOTKEY_HOTKEYS);
00477                 LRESULT index = SendMessage(rules, LB_GETCURSEL, 0, 0);
00478                 if (m_activeRuleIndex != (UInt32)index) {
00479                     closeRule(hwnd);
00480                     updateHotkeysControls(hwnd);
00481                     openRule(hwnd);
00482                 }
00483                 return TRUE;
00484             }
00485             }
00486             break;
00487 
00488         case IDC_HOTKEY_ADD_HOTKEY:
00489             addHotkey(hwnd);
00490             return TRUE;
00491 
00492         case IDC_HOTKEY_REMOVE_HOTKEY:
00493             removeHotkey(hwnd);
00494             return TRUE;
00495 
00496         case IDC_HOTKEY_EDIT_HOTKEY:
00497             editHotkey(hwnd);
00498             return TRUE;
00499 
00500         case IDC_HOTKEY_ACTIONS:
00501             switch (HIWORD(wParam)) {
00502             case LBN_DBLCLK:
00503                 editAction(hwnd);
00504                 return TRUE;
00505 
00506             case LBN_SELCHANGE:
00507                 updateActionsControls(hwnd);
00508                 return TRUE;
00509             }
00510             break;
00511 
00512         case IDC_HOTKEY_ADD_ACTION:
00513             addAction(hwnd);
00514             return TRUE;
00515 
00516         case IDC_HOTKEY_REMOVE_ACTION:
00517             removeAction(hwnd);
00518             return TRUE;
00519 
00520         case IDC_HOTKEY_EDIT_ACTION:
00521             editAction(hwnd);
00522             return TRUE;
00523         }
00524         break;
00525 
00526     default:
00527         break;
00528     }
00529 
00530     return FALSE;
00531 }
00532 
00533 BOOL CALLBACK
00534 CHotkeyOptions::dlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
00535 {
00536     return s_singleton->doDlgProc(hwnd, message, wParam, lParam);
00537 }
00538 
00539 
00540 //
00541 // CHotkeyOptions::CConditionDialog
00542 //
00543 
00544 CInputFilter::CCondition*
00545                 CHotkeyOptions::CConditionDialog::s_condition         = NULL;
00546 CInputFilter::CCondition*
00547                 CHotkeyOptions::CConditionDialog::s_lastGoodCondition = NULL;
00548 WNDPROC         CHotkeyOptions::CConditionDialog::s_editWndProc       = NULL;
00549 
00550 bool
00551 CHotkeyOptions::CConditionDialog::doModal(HWND parent,
00552                 CInputFilter::CCondition*& condition)
00553 {
00554     s_condition = condition;
00555     if (s_condition != NULL) {
00556         s_lastGoodCondition = s_condition->clone();
00557     }
00558     else {
00559         s_lastGoodCondition = NULL;
00560     }
00561     int n = DialogBox(s_instance, MAKEINTRESOURCE(IDD_HOTKEY_CONDITION),
00562                                 parent, (DLGPROC) dlgProc);
00563 
00564     condition           = s_condition;
00565     delete s_lastGoodCondition;
00566     s_condition         = NULL;
00567     s_lastGoodCondition = NULL;
00568 
00569     // user effectively cancelled if the condition is NULL
00570     if (condition == NULL) {
00571         n = 0;
00572     }
00573 
00574     return (n == 1);
00575 }
00576 
00577 void
00578 CHotkeyOptions::CConditionDialog::doInit(HWND hwnd)
00579 {
00580     // subclass edit control
00581     HWND child = getItem(hwnd, IDC_HOTKEY_CONDITION_HOTKEY);
00582     s_editWndProc = (WNDPROC)GetWindowLongPtr(child, GWLP_WNDPROC);
00583     SetWindowLongPtr(child, GWLP_WNDPROC, (LONG_PTR) editProc);
00584 
00585     // fill control
00586     fillHotkey(hwnd);
00587 }
00588 
00589 void
00590 CHotkeyOptions::CConditionDialog::fillHotkey(HWND hwnd)
00591 {
00592     HWND child = getItem(hwnd, IDC_HOTKEY_CONDITION_HOTKEY);
00593     if (s_condition != NULL) {
00594         setWindowText(child, s_condition->format().c_str());
00595     }
00596     else {
00597         setWindowText(child, "");
00598     }
00599 }
00600 
00601 void
00602 CHotkeyOptions::CConditionDialog::onButton(HWND hwnd, ButtonID button)
00603 {
00604     delete s_condition;
00605     s_condition =
00606         new CInputFilter::CMouseButtonCondition(button, getModifiers());
00607 
00608     fillHotkey(GetParent(hwnd));
00609 }
00610 
00611 void
00612 CHotkeyOptions::CConditionDialog::onKey(HWND hwnd, WPARAM wParam, LPARAM lParam)
00613 {
00614     // ignore key repeats
00615     if ((lParam & 0xc0000000u) == 0x40000000u) {
00616         return;
00617     }
00618 
00619     // ignore key releases if the condition is complete and for the tab
00620     // key (in case we were just tabbed to)
00621     if ((lParam & 0x80000000u) != 0) {
00622         if (isGoodCondition() || wParam == VK_TAB) {
00623             return;
00624         }
00625     }
00626 
00627     KeyID key            = kKeyNone;
00628     KeyModifierMask mask = getModifiers();
00629     switch (wParam) {
00630     case VK_SHIFT:
00631     case VK_LSHIFT:
00632     case VK_RSHIFT:
00633     case VK_CONTROL:
00634     case VK_LCONTROL:
00635     case VK_RCONTROL:
00636     case VK_MENU:
00637     case VK_LMENU:
00638     case VK_RMENU:
00639     case VK_LWIN:
00640     case VK_RWIN:
00641         break;
00642 
00643     case VK_TAB:
00644         // allow tabbing out of control
00645         if ((mask & (KeyModifierControl |
00646                             KeyModifierAlt | KeyModifierSuper)) == 0) {
00647             HWND next = hwnd;
00648             if ((mask & KeyModifierShift) == 0) {
00649                 do {
00650                     next = GetWindow(next, GW_HWNDNEXT);
00651                     if (next == NULL) {
00652                         next = GetWindow(hwnd, GW_HWNDFIRST);
00653                     }
00654                 } while (next != hwnd &&
00655                     (!IsWindowVisible(next) ||
00656                     (GetWindowLong(next, GWL_STYLE) & WS_TABSTOP) == 0));
00657             }
00658             else {
00659                 do {
00660                     next = GetWindow(next, GW_HWNDPREV);
00661                     if (next == NULL) {
00662                         next = GetWindow(hwnd, GW_HWNDLAST);
00663                     }
00664                 } while (next != hwnd &&
00665                     (!IsWindowVisible(next) ||
00666                     (GetWindowLong(next, GWL_STYLE) & WS_TABSTOP) == 0));
00667             }
00668             SetFocus(next);
00669             return;
00670         }
00671         // fall through
00672 
00673     default:
00674         key = CMSWindowsKeyState::getKeyID(wParam,
00675                         static_cast<KeyButton>((lParam & 0x1ff0000u) >> 16));
00676         switch (key) {
00677         case kKeyNone:
00678             // could be a character
00679             key = getChar(wParam, lParam);
00680             if (key == kKeyNone) {
00681                 return;
00682             }
00683             break;
00684 
00685         case kKeyShift_L:
00686         case kKeyShift_R:
00687         case kKeyControl_L:
00688         case kKeyControl_R:
00689         case kKeyAlt_L:
00690         case kKeyAlt_R:
00691         case kKeyMeta_L:
00692         case kKeyMeta_R:
00693         case kKeySuper_L:
00694         case kKeySuper_R:
00695         case kKeyCapsLock:
00696         case kKeyNumLock:
00697         case kKeyScrollLock:
00698             // bogus
00699             return;
00700         }
00701         break;
00702     }
00703 
00704     delete s_condition;
00705     s_condition = new CInputFilter::CKeystrokeCondition(key, mask);
00706 
00707     fillHotkey(GetParent(hwnd));
00708 }
00709 
00710 KeyID
00711 CHotkeyOptions::CConditionDialog::getChar(WPARAM wParam, LPARAM lParam)
00712 {
00713     BYTE keyState[256];
00714     UINT virtualKey = (UINT)wParam;
00715     UINT scanCode   = (UINT)((lParam & 0x0ff0000u) >> 16);
00716     GetKeyboardState(keyState);
00717 
00718     // reset modifier state
00719     keyState[VK_SHIFT]    = 0;
00720     keyState[VK_LSHIFT]   = 0;
00721     keyState[VK_RSHIFT]   = 0;
00722     keyState[VK_CONTROL]  = 0;
00723     keyState[VK_LCONTROL] = 0;
00724     keyState[VK_RCONTROL] = 0;
00725     keyState[VK_MENU]     = 0;
00726     keyState[VK_LMENU]    = 0;
00727     keyState[VK_RMENU]    = 0;
00728     keyState[VK_LWIN]     = 0;
00729     keyState[VK_RWIN]     = 0;
00730 
00731     // translate virtual key to character
00732     int n;
00733     KeyID id;
00734     if (CArchMiscWindows::isWindows95Family()) {
00735         // XXX -- how do we get characters not in Latin-1?
00736         WORD ascii;
00737         n  = ToAscii(virtualKey, scanCode, keyState, &ascii, 0);
00738         id = static_cast<KeyID>(ascii & 0xffu);
00739     }
00740     else {
00741         typedef int (WINAPI *ToUnicode_t)(UINT wVirtKey,
00742                                             UINT wScanCode,
00743                                             PBYTE lpKeyState,
00744                                             LPWSTR pwszBuff,
00745                                             int cchBuff,
00746                                             UINT wFlags);
00747         ToUnicode_t s_ToUnicode = NULL;
00748         if (s_ToUnicode == NULL) {
00749             HMODULE userModule = GetModuleHandle("user32.dll");
00750             s_ToUnicode =
00751                 (ToUnicode_t)GetProcAddress(userModule, "ToUnicode");
00752         }
00753 
00754         WCHAR unicode[2];
00755         n  = s_ToUnicode(virtualKey, scanCode, keyState,
00756                                 unicode, sizeof(unicode) / sizeof(unicode[0]),
00757                                 0);
00758         id = static_cast<KeyID>(unicode[0]);
00759     }
00760     switch (n) {
00761     case -1:
00762         // no hot keys on dead keys
00763         return kKeyNone;
00764 
00765     default:
00766     case 0:
00767         // unmapped
00768         return kKeyNone;
00769 
00770     case 1:
00771         return id;
00772     }
00773 }
00774 
00775 KeyModifierMask
00776 CHotkeyOptions::CConditionDialog::getModifiers()
00777 {
00778     KeyModifierMask mask = 0;
00779     if ((GetKeyState(VK_SHIFT) & 0x8000) != 0) {
00780         mask |= KeyModifierShift;
00781     }
00782     if ((GetKeyState(VK_CONTROL) & 0x8000) != 0) {
00783         mask |= KeyModifierControl;
00784     }
00785     if ((GetKeyState(VK_MENU) & 0x8000) != 0) {
00786         mask |= KeyModifierAlt;
00787     }
00788     if ((GetKeyState(VK_LWIN) & 0x8000) != 0 ||
00789         (GetKeyState(VK_RWIN) & 0x8000) != 0) {
00790         mask |= KeyModifierSuper;
00791     }
00792     return mask;
00793 }
00794 
00795 bool
00796 CHotkeyOptions::CConditionDialog::isGoodCondition()
00797 {
00798     CInputFilter::CKeystrokeCondition* keyCondition =
00799         dynamic_cast<CInputFilter::CKeystrokeCondition*>(s_condition);
00800     return (keyCondition == NULL || keyCondition->getKey() != kKeyNone);
00801 }
00802 
00803 BOOL CALLBACK
00804 CHotkeyOptions::CConditionDialog::dlgProc(HWND hwnd,
00805                 UINT message, WPARAM wParam, LPARAM)
00806 {
00807     switch (message) {
00808     case WM_INITDIALOG:
00809         doInit(hwnd);
00810         return TRUE;
00811 
00812     case WM_COMMAND:
00813         switch (LOWORD(wParam)) {
00814         case IDOK:
00815             EndDialog(hwnd, 1);
00816             return TRUE;
00817 
00818         case IDCANCEL:
00819             EndDialog(hwnd, 0);
00820             return TRUE;
00821         }
00822         break;
00823 
00824     default:
00825         break;
00826     }
00827 
00828     return FALSE;
00829 }
00830 
00831 LRESULT CALLBACK
00832 CHotkeyOptions::CConditionDialog::editProc(HWND hwnd,
00833                 UINT message, WPARAM wParam, LPARAM lParam)
00834 {
00835     switch (message) {
00836     case WM_LBUTTONDOWN:
00837         if (GetFocus() == hwnd) {
00838             onButton(hwnd, kButtonLeft);
00839         }
00840         else {
00841             SetFocus(hwnd);
00842         }
00843         return 0;
00844 
00845     case WM_MBUTTONDOWN:
00846         if (GetFocus() == hwnd) {
00847             onButton(hwnd, kButtonMiddle);
00848         }
00849         return 0;
00850 
00851     case WM_RBUTTONDOWN:
00852         if (GetFocus() == hwnd) {
00853             onButton(hwnd, kButtonRight);
00854         }
00855         return 0;
00856 
00857     case WM_XBUTTONDOWN:
00858         if (GetFocus() == hwnd) {
00859             switch (HIWORD(wParam)) {
00860             case XBUTTON1:
00861                 onButton(hwnd, kButtonExtra0 + 0);
00862                 break;
00863 
00864             case XBUTTON2:
00865                 onButton(hwnd, kButtonExtra0 + 1);
00866                 break;
00867             }
00868         }
00869         return 0;
00870 
00871     case WM_KEYDOWN:
00872     case WM_SYSKEYDOWN:
00873     case WM_KEYUP:
00874     case WM_SYSKEYUP:
00875         onKey(hwnd, wParam, lParam);
00876         return 0;
00877 
00878     case WM_LBUTTONUP:
00879     case WM_MBUTTONUP:
00880     case WM_RBUTTONUP:
00881     case WM_XBUTTONUP:
00882     case WM_CHAR:
00883     case WM_SYSCHAR:
00884     case WM_DEADCHAR:
00885     case WM_SYSDEADCHAR:
00886         return 0;
00887 
00888     case WM_SETFOCUS:
00889         if (s_condition != NULL) {
00890             delete s_lastGoodCondition;
00891             s_lastGoodCondition = s_condition->clone();
00892         }
00893         break;
00894 
00895     case WM_KILLFOCUS:
00896         if (!isGoodCondition()) {
00897             delete s_condition;
00898             if (s_lastGoodCondition != NULL) {
00899                 s_condition = s_lastGoodCondition->clone();
00900             }
00901             else {
00902                 s_condition = NULL;
00903             }
00904         }
00905         fillHotkey(GetParent(hwnd));
00906         break;
00907 
00908     case WM_GETDLGCODE:
00909         return DLGC_WANTALLKEYS;
00910 
00911     default:
00912         break;
00913     }
00914     return CallWindowProc(s_editWndProc, hwnd, message, wParam, lParam);
00915 }
00916 
00917 
00918 //
00919 // CHotkeyOptions::CActionDialog
00920 //
00921 
00922 CConfig*        CHotkeyOptions::CActionDialog::s_config         = NULL;
00923 bool            CHotkeyOptions::CActionDialog::s_onActivate     = false;
00924 CInputFilter::CAction*
00925                 CHotkeyOptions::CActionDialog::s_action         = NULL;
00926 CInputFilter::CAction*
00927                 CHotkeyOptions::CActionDialog::s_lastGoodAction = NULL;
00928 std::set<CString>
00929                 CHotkeyOptions::CActionDialog::s_screens;
00930 WNDPROC         CHotkeyOptions::CActionDialog::s_editWndProc    = NULL;
00931 
00932 bool
00933 CHotkeyOptions::CActionDialog::doModal(HWND parent, CConfig* config,
00934                 CInputFilter::CAction*& action, bool& onActivate)
00935 {
00936     s_config     = config;
00937     s_onActivate = onActivate;
00938     s_action     = action;
00939     if (s_action != NULL) {
00940         s_lastGoodAction = s_action->clone();
00941     }
00942     else {
00943         s_lastGoodAction = NULL;
00944     }
00945 
00946     int n = DialogBox(s_instance, MAKEINTRESOURCE(IDD_HOTKEY_ACTION),
00947                                 parent, (DLGPROC) dlgProc);
00948 
00949     onActivate       = s_onActivate;
00950     action           = s_action;
00951     delete s_lastGoodAction;
00952     s_action         = NULL;
00953     s_lastGoodAction = NULL;
00954 
00955     return (n == 1);
00956 }
00957 
00958 void
00959 CHotkeyOptions::CActionDialog::doInit(HWND hwnd)
00960 {
00961     // subclass edit control
00962     HWND child = getItem(hwnd, IDC_HOTKEY_ACTION_HOTKEY);
00963     s_editWndProc = (WNDPROC)GetWindowLongPtr(child, GWLP_WNDPROC);
00964     SetWindowLongPtr(child, GWLP_WNDPROC, (LONG_PTR)editProc);
00965     setWindowText(getItem(hwnd, IDC_HOTKEY_ACTION_HOTKEY), "");
00966     fillHotkey(hwnd);
00967 
00968     // fill screens
00969     child = getItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_TO_LIST);
00970     SendMessage(child, CB_RESETCONTENT, 0, 0);
00971     for (CConfig::const_iterator index = s_config->begin();
00972                             index != s_config->end(); ) {
00973         const CString& name = *index;
00974         ++index;
00975         if (index != s_config->end()) {
00976             SendMessage(child, CB_INSERTSTRING,
00977                             (WPARAM)-1, (LPARAM)name.c_str());
00978         }
00979         else {
00980             SendMessage(child, CB_ADDSTRING, 0, (LPARAM)name.c_str());
00981         }
00982     }
00983     SendMessage(child, CB_SETCURSEL, 0, 0);
00984 
00985     // fill directions
00986     child = getItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_IN_LIST);
00987     SendMessage(child, CB_ADDSTRING, 0,
00988                             (LPARAM)getString(IDS_EDGE_LEFT).c_str());
00989     SendMessage(child, CB_ADDSTRING, 0,
00990                             (LPARAM)getString(IDS_EDGE_RIGHT).c_str());
00991     SendMessage(child, CB_ADDSTRING, 0,
00992                             (LPARAM)getString(IDS_EDGE_TOP).c_str());
00993     SendMessage(child, CB_ADDSTRING, 0,
00994                             (LPARAM)getString(IDS_EDGE_BOTTOM).c_str());
00995     SendMessage(child, CB_SETCURSEL, 0, 0);
00996 
00997     // fill lock modes
00998     child = getItem(hwnd, IDC_HOTKEY_ACTION_LOCK_LIST);
00999     SendMessage(child, CB_ADDSTRING, 0,
01000                             (LPARAM)getString(IDS_MODE_OFF).c_str());
01001     SendMessage(child, CB_ADDSTRING, 0,
01002                             (LPARAM)getString(IDS_MODE_ON).c_str());
01003     SendMessage(child, CB_ADDSTRING, 0,
01004                             (LPARAM)getString(IDS_MODE_TOGGLE).c_str());
01005     SendMessage(child, CB_SETCURSEL, 0, 0);
01006 
01007     // fill keyboard broadcast modes
01008     child = getItem(hwnd, IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST_LIST);
01009     SendMessage(child, CB_ADDSTRING, 0,
01010                             (LPARAM)getString(IDS_MODE_OFF).c_str());
01011     SendMessage(child, CB_ADDSTRING, 0,
01012                             (LPARAM)getString(IDS_MODE_ON).c_str());
01013     SendMessage(child, CB_ADDSTRING, 0,
01014                             (LPARAM)getString(IDS_MODE_TOGGLE).c_str());
01015     SendMessage(child, CB_SETCURSEL, 0, 0);
01016 
01017     // select when
01018     if (s_onActivate) {
01019         child = getItem(hwnd, IDC_HOTKEY_ACTION_ON_ACTIVATE);
01020     }
01021     else {
01022         child = getItem(hwnd, IDC_HOTKEY_ACTION_ON_DEACTIVATE);
01023     }
01024     setItemChecked(child, true);
01025 
01026     // no screens by default
01027     s_screens.clear();
01028 
01029     // select mode
01030     child = NULL;
01031     CInputFilter::CKeystrokeAction* keyAction =
01032         dynamic_cast<CInputFilter::CKeystrokeAction*>(s_action);
01033     CInputFilter::CMouseButtonAction* mouseAction =
01034         dynamic_cast<CInputFilter::CMouseButtonAction*>(s_action);
01035     CInputFilter::CLockCursorToScreenAction* lockAction =
01036         dynamic_cast<CInputFilter::CLockCursorToScreenAction*>(s_action);
01037     CInputFilter::CSwitchToScreenAction* switchToAction =
01038         dynamic_cast<CInputFilter::CSwitchToScreenAction*>(s_action);
01039     CInputFilter::CSwitchInDirectionAction* switchInAction =
01040         dynamic_cast<CInputFilter::CSwitchInDirectionAction*>(s_action);
01041     CInputFilter::CKeyboardBroadcastAction* keyboardBroadcastAction=
01042         dynamic_cast<CInputFilter::CKeyboardBroadcastAction*>(s_action);
01043     if (keyAction != NULL) {
01044         if (dynamic_cast<CKeystrokeDownUpAction*>(s_action) != NULL) {
01045             child = getItem(hwnd, IDC_HOTKEY_ACTION_DOWNUP);
01046         }
01047         else if (keyAction->isOnPress()) {
01048             child = getItem(hwnd, IDC_HOTKEY_ACTION_DOWN);
01049         }
01050         else {
01051             child = getItem(hwnd, IDC_HOTKEY_ACTION_UP);
01052         }
01053     }
01054     else if (mouseAction != NULL) {
01055         if (dynamic_cast<CMouseButtonDownUpAction*>(s_action) != NULL) {
01056             child = getItem(hwnd, IDC_HOTKEY_ACTION_DOWNUP);
01057         }
01058         else if (keyAction->isOnPress()) {
01059             child = getItem(hwnd, IDC_HOTKEY_ACTION_DOWN);
01060         }
01061         else {
01062             child = getItem(hwnd, IDC_HOTKEY_ACTION_UP);
01063         }
01064     }
01065     else if (lockAction != NULL) {
01066         child = getItem(hwnd, IDC_HOTKEY_ACTION_LOCK_LIST);
01067         SendMessage(child, CB_SETCURSEL, lockAction->getMode(), 0);
01068         child = getItem(hwnd, IDC_HOTKEY_ACTION_LOCK);
01069     }
01070     else if (switchToAction != NULL) {
01071         child = getItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_TO_LIST);
01072         DWORD i = SendMessage(child, CB_FINDSTRINGEXACT, (WPARAM)-1,
01073                                 (LPARAM)switchToAction->getScreen().c_str());
01074         if (i == CB_ERR) {
01075             i = 0;
01076         }
01077         SendMessage(child, CB_SETCURSEL, i, 0);
01078         child = getItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_TO);
01079     }
01080     else if (switchInAction != NULL) {
01081         child = getItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_IN_LIST);
01082         SendMessage(child, CB_SETCURSEL,
01083                             switchInAction->getDirection() - kLeft, 0);
01084         child = getItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_IN);
01085     }
01086     else if (keyboardBroadcastAction != NULL) {
01087         // Save the screens we're broadcasting to
01088         s_screens = keyboardBroadcastAction->getScreens();
01089 
01090         child = getItem(hwnd, IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST_LIST);
01091         SendMessage(child, CB_SETCURSEL, keyboardBroadcastAction->getMode(), 0);
01092         child = getItem(hwnd, IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST);
01093     }
01094     if (child != NULL) {
01095         setItemChecked(child, true);
01096     }
01097 
01098     updateControls(hwnd);
01099 }
01100 
01101 void
01102 CHotkeyOptions::CActionDialog::fillHotkey(HWND hwnd)
01103 {
01104     HWND child = getItem(hwnd, IDC_HOTKEY_ACTION_HOTKEY);
01105     CInputFilter::CKeystrokeAction* keyAction =
01106         dynamic_cast<CInputFilter::CKeystrokeAction*>(s_action);
01107     CInputFilter::CMouseButtonAction* mouseAction =
01108         dynamic_cast<CInputFilter::CMouseButtonAction*>(s_action);
01109     if (keyAction != NULL || mouseAction != NULL) {
01110         setWindowText(child, s_action->format().c_str());
01111     }
01112     else {
01113         setWindowText(child, "");
01114     }
01115 
01116     // can only set screens in key actions
01117     enableItem(hwnd, IDC_HOTKEY_ACTION_SCREENS, keyAction != NULL);
01118 }
01119 
01120 void
01121 CHotkeyOptions::CActionDialog::updateControls(HWND hwnd)
01122 {
01123     // determine which mode we're in
01124     UInt32 mode = 0;
01125     if (isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_DOWNUP)) ||
01126         isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_DOWN)) ||
01127         isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_UP))) {
01128         mode = 1;
01129     }
01130     else if (isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_TO))) {
01131         mode = 2;
01132     }
01133     else if (isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_IN))) {
01134         mode = 3;
01135     }
01136     else if (isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_LOCK))) {
01137         mode = 4;
01138     }
01139     else if (isItemChecked(getItem(hwnd,
01140                                     IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST))) {
01141         mode = 5;
01142     }
01143 
01144     // enable/disable all mode specific controls
01145     enableItem(hwnd, IDC_HOTKEY_ACTION_HOTKEY, mode == 1);
01146     enableItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_TO_LIST, mode == 2);
01147     enableItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_IN_LIST, mode == 3);
01148     enableItem(hwnd, IDC_HOTKEY_ACTION_LOCK_LIST, mode == 4);
01149     enableItem(hwnd, IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST_LIST, mode == 5);
01150     enableItem(hwnd, IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST_SCREENS, mode == 5);
01151 
01152     // can only set screens in key actions
01153     CInputFilter::CKeystrokeAction* keyAction =
01154         dynamic_cast<CInputFilter::CKeystrokeAction*>(s_action);
01155     enableItem(hwnd, IDC_HOTKEY_ACTION_SCREENS, keyAction != NULL);
01156 }
01157 
01158 void
01159 CHotkeyOptions::CActionDialog::onButton(HWND hwnd, ButtonID button)
01160 {
01161     IPlatformScreen::CButtonInfo* info =
01162         IPrimaryScreen::CButtonInfo::alloc(button, getModifiers());
01163     delete s_action;
01164     HWND parent = GetParent(hwnd);
01165     if (isItemChecked(getItem(parent, IDC_HOTKEY_ACTION_DOWNUP))) {
01166         s_action = new CMouseButtonDownUpAction(info);
01167     }
01168     else if (isItemChecked(getItem(parent, IDC_HOTKEY_ACTION_DOWN))) {
01169         s_action = new CInputFilter::CMouseButtonAction(info, true);
01170     }
01171     else if (isItemChecked(getItem(parent, IDC_HOTKEY_ACTION_UP))) {
01172         s_action = new CInputFilter::CMouseButtonAction(info, false);
01173     }
01174     else {
01175         s_action = NULL;
01176     }
01177 
01178     fillHotkey(parent);
01179 }
01180 
01181 void
01182 CHotkeyOptions::CActionDialog::onKey(HWND hwnd, WPARAM wParam, LPARAM lParam)
01183 {
01184     // ignore key repeats
01185     if ((lParam & 0xc0000000u) == 0x40000000u) {
01186         return;
01187     }
01188 
01189     // ignore key releases if the action is complete and for the tab
01190     // key (in case we were just tabbed to)
01191     if ((lParam & 0x80000000u) != 0) {
01192         if (isGoodAction() || wParam == VK_TAB) {
01193             return;
01194         }
01195     }
01196 
01197     KeyID key            = kKeyNone;
01198     KeyModifierMask mask = getModifiers();
01199     switch (wParam) {
01200     case VK_SHIFT:
01201     case VK_LSHIFT:
01202     case VK_RSHIFT:
01203     case VK_CONTROL:
01204     case VK_LCONTROL:
01205     case VK_RCONTROL:
01206     case VK_MENU:
01207     case VK_LMENU:
01208     case VK_RMENU:
01209     case VK_LWIN:
01210     case VK_RWIN:
01211         break;
01212 
01213     case VK_TAB:
01214         // allow tabbing out of control
01215         if ((mask & (KeyModifierControl |
01216                             KeyModifierAlt | KeyModifierSuper)) == 0) {
01217             HWND next = hwnd;
01218             if ((mask & KeyModifierShift) == 0) {
01219                 do {
01220                     next = GetWindow(next, GW_HWNDNEXT);
01221                     if (next == NULL) {
01222                         next = GetWindow(hwnd, GW_HWNDFIRST);
01223                     }
01224                 } while (next != hwnd &&
01225                     (!IsWindowVisible(next) ||
01226                     (GetWindowLong(next, GWL_STYLE) & WS_TABSTOP) == 0));
01227             }
01228             else {
01229                 do {
01230                     next = GetWindow(next, GW_HWNDPREV);
01231                     if (next == NULL) {
01232                         next = GetWindow(hwnd, GW_HWNDLAST);
01233                     }
01234                 } while (next != hwnd &&
01235                     (!IsWindowVisible(next) ||
01236                     (GetWindowLong(next, GWL_STYLE) & WS_TABSTOP) == 0));
01237             }
01238             SetFocus(next);
01239             return;
01240         }
01241         // fall through
01242 
01243     default:
01244         key = CMSWindowsKeyState::getKeyID(wParam,
01245                         static_cast<KeyButton>((lParam & 0x1ff0000u) >> 16));
01246         switch (key) {
01247         case kKeyNone:
01248             // could be a character
01249             key = getChar(wParam, lParam);
01250             if (key == kKeyNone) {
01251                 return;
01252             }
01253             break;
01254 
01255         case kKeyShift_L:
01256         case kKeyShift_R:
01257         case kKeyControl_L:
01258         case kKeyControl_R:
01259         case kKeyAlt_L:
01260         case kKeyAlt_R:
01261         case kKeyMeta_L:
01262         case kKeyMeta_R:
01263         case kKeySuper_L:
01264         case kKeySuper_R:
01265         case kKeyCapsLock:
01266         case kKeyNumLock:
01267         case kKeyScrollLock:
01268             // bogus
01269             return;
01270         }
01271         break;
01272     }
01273 
01274     // get old screen list
01275     std::set<CString> screens;
01276     CInputFilter::CKeystrokeAction* keyAction =
01277         dynamic_cast<CInputFilter::CKeystrokeAction*>(s_action);
01278     if (keyAction == NULL) {
01279         keyAction =
01280             dynamic_cast<CInputFilter::CKeystrokeAction*>(s_lastGoodAction);
01281     }
01282     if (keyAction != NULL) {
01283         IKeyState::CKeyInfo::split(keyAction->getInfo()->m_screens, screens);
01284     }
01285 
01286     // create new action
01287     IPlatformScreen::CKeyInfo* info =
01288         IKeyState::CKeyInfo::alloc(key, mask, 0, 0, screens);
01289     delete s_action;
01290     HWND parent = GetParent(hwnd);
01291     if (isItemChecked(getItem(parent, IDC_HOTKEY_ACTION_DOWNUP))) {
01292         s_action = new CKeystrokeDownUpAction(info);
01293     }
01294     else if (isItemChecked(getItem(parent, IDC_HOTKEY_ACTION_DOWN))) {
01295         s_action = new CInputFilter::CKeystrokeAction(info, true);
01296     }
01297     else if (isItemChecked(getItem(parent, IDC_HOTKEY_ACTION_UP))) {
01298         s_action = new CInputFilter::CKeystrokeAction(info, false);
01299     }
01300     else {
01301         s_action = NULL;
01302     }
01303 
01304     fillHotkey(parent);
01305 }
01306 
01307 void
01308 CHotkeyOptions::CActionDialog::onLockAction(HWND hwnd)
01309 {
01310     HWND child = getItem(hwnd, IDC_HOTKEY_ACTION_LOCK_LIST);
01311     LRESULT index = SendMessage(child, CB_GETCURSEL, 0, 0);
01312     if (index != CB_ERR) {
01313         delete s_action;
01314         s_action = new CInputFilter::CLockCursorToScreenAction(
01315             (CInputFilter::CLockCursorToScreenAction::Mode)index);
01316     }
01317 }
01318 
01319 void
01320 CHotkeyOptions::CActionDialog::onSwitchToAction(HWND hwnd)
01321 {
01322     HWND child = getItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_TO_LIST);
01323     CString screen = getWindowText(child);
01324     delete s_action;
01325     s_action = new CInputFilter::CSwitchToScreenAction(screen);
01326 }
01327 
01328 void
01329 CHotkeyOptions::CActionDialog::onSwitchInAction(HWND hwnd)
01330 {
01331     HWND child = getItem(hwnd, IDC_HOTKEY_ACTION_SWITCH_IN_LIST);
01332     LRESULT index = SendMessage(child, CB_GETCURSEL, 0, 0);
01333     if (index != CB_ERR) {
01334         delete s_action;
01335         s_action = new CInputFilter::CSwitchInDirectionAction(
01336                             (EDirection)(index + kLeft));
01337     }
01338 }
01339 
01340 void
01341 CHotkeyOptions::CActionDialog::onKeyboardBroadcastAction(HWND hwnd)
01342 {
01343     HWND child = getItem(hwnd, IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST_LIST);
01344     LRESULT index = SendMessage(child, CB_GETCURSEL, 0, 0);
01345     if (index != CB_ERR) {
01346         delete s_action;
01347         s_action = new CInputFilter::CKeyboardBroadcastAction(
01348             (CInputFilter::CKeyboardBroadcastAction::Mode)index, s_screens);
01349     }
01350 }
01351 
01352 KeyID
01353 CHotkeyOptions::CActionDialog::getChar(WPARAM wParam, LPARAM lParam)
01354 {
01355     BYTE keyState[256];
01356     UINT virtualKey = (UINT)wParam;
01357     UINT scanCode   = (UINT)((lParam & 0x0ff0000u) >> 16);
01358     GetKeyboardState(keyState);
01359 
01360     // reset modifier state
01361     keyState[VK_SHIFT]    = 0;
01362     keyState[VK_LSHIFT]   = 0;
01363     keyState[VK_RSHIFT]   = 0;
01364     keyState[VK_CONTROL]  = 0;
01365     keyState[VK_LCONTROL] = 0;
01366     keyState[VK_RCONTROL] = 0;
01367     keyState[VK_MENU]     = 0;
01368     keyState[VK_LMENU]    = 0;
01369     keyState[VK_RMENU]    = 0;
01370     keyState[VK_LWIN]     = 0;
01371     keyState[VK_RWIN]     = 0;
01372 
01373     // translate virtual key to character
01374     int n;
01375     KeyID id;
01376     if (CArchMiscWindows::isWindows95Family()) {
01377         // XXX -- how do we get characters not in Latin-1?
01378         WORD ascii;
01379         n  = ToAscii(virtualKey, scanCode, keyState, &ascii, 0);
01380         id = static_cast<KeyID>(ascii & 0xffu);
01381     }
01382     else {
01383         typedef int (WINAPI *ToUnicode_t)(UINT wVirtKey,
01384                                             UINT wScanCode,
01385                                             PBYTE lpKeyState,
01386                                             LPWSTR pwszBuff,
01387                                             int cchBuff,
01388                                             UINT wFlags);
01389         ToUnicode_t s_ToUnicode = NULL;
01390         if (s_ToUnicode == NULL) {
01391             HMODULE userModule = GetModuleHandle("user32.dll");
01392             s_ToUnicode =
01393                 (ToUnicode_t)GetProcAddress(userModule, "ToUnicode");
01394         }
01395 
01396         WCHAR unicode[2];
01397         n  = s_ToUnicode(virtualKey, scanCode, keyState,
01398                                 unicode, sizeof(unicode) / sizeof(unicode[0]),
01399                                 0);
01400         id = static_cast<KeyID>(unicode[0]);
01401     }
01402     switch (n) {
01403     case -1:
01404         // no hot keys on dead keys
01405         return kKeyNone;
01406 
01407     default:
01408     case 0:
01409         // unmapped
01410         return kKeyNone;
01411 
01412     case 1:
01413         return id;
01414     }
01415 }
01416 
01417 KeyModifierMask
01418 CHotkeyOptions::CActionDialog::getModifiers()
01419 {
01420     KeyModifierMask mask = 0;
01421     if ((GetKeyState(VK_SHIFT) & 0x8000) != 0) {
01422         mask |= KeyModifierShift;
01423     }
01424     if ((GetKeyState(VK_CONTROL) & 0x8000) != 0) {
01425         mask |= KeyModifierControl;
01426     }
01427     if ((GetKeyState(VK_MENU) & 0x8000) != 0) {
01428         mask |= KeyModifierAlt;
01429     }
01430     if ((GetKeyState(VK_LWIN) & 0x8000) != 0 ||
01431         (GetKeyState(VK_RWIN) & 0x8000) != 0) {
01432         mask |= KeyModifierSuper;
01433     }
01434     return mask;
01435 }
01436 
01437 bool
01438 CHotkeyOptions::CActionDialog::isGoodAction()
01439 {
01440     CInputFilter::CMouseButtonAction* mouseAction =
01441         dynamic_cast<CInputFilter::CMouseButtonAction*>(s_action);
01442     CInputFilter::CKeystrokeAction* keyAction =
01443         dynamic_cast<CInputFilter::CKeystrokeAction*>(s_action);
01444     return (mouseAction == NULL || keyAction == NULL ||
01445             keyAction->getInfo()->m_key != kKeyNone);
01446 }
01447 
01448 void
01449 CHotkeyOptions::CActionDialog::convertAction(HWND hwnd)
01450 {
01451     if (s_lastGoodAction != NULL) {
01452         CInputFilter::CMouseButtonAction* mouseAction =
01453             dynamic_cast<CInputFilter::CMouseButtonAction*>(s_lastGoodAction);
01454         CInputFilter::CKeystrokeAction* keyAction =
01455             dynamic_cast<CInputFilter::CKeystrokeAction*>(s_lastGoodAction);
01456         if (mouseAction != NULL) {
01457             IPlatformScreen::CButtonInfo* info =
01458                 IPrimaryScreen::CButtonInfo::alloc(*mouseAction->getInfo());
01459             delete s_action;
01460             if (isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_DOWNUP))) {
01461                 s_action = new CMouseButtonDownUpAction(info);
01462             }
01463             else if (isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_DOWN))) {
01464                 s_action = new CInputFilter::CMouseButtonAction(info, true);
01465             }
01466             else if (isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_UP))) {
01467                 s_action = new CInputFilter::CMouseButtonAction(info, false);
01468             }
01469             else {
01470                 free(info);
01471                 s_action = NULL;
01472             }
01473         }
01474         else if (keyAction != NULL) {
01475             IPlatformScreen::CKeyInfo* info =
01476                 IKeyState::CKeyInfo::alloc(*keyAction->getInfo());
01477             delete s_action;
01478             if (isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_DOWNUP))) {
01479                 s_action = new CKeystrokeDownUpAction(info);
01480             }
01481             else if (isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_DOWN))) {
01482                 s_action = new CInputFilter::CKeystrokeAction(info, true);
01483             }
01484             else if (isItemChecked(getItem(hwnd, IDC_HOTKEY_ACTION_UP))) {
01485                 s_action = new CInputFilter::CKeystrokeAction(info, false);
01486             }
01487             else {
01488                 free(info);
01489                 s_action = NULL;
01490             }
01491         }
01492     }
01493 }
01494 
01495 bool
01496 CHotkeyOptions::CActionDialog::isDownUpAction()
01497 {
01498     return (dynamic_cast<CKeystrokeDownUpAction*>(s_action) != NULL ||
01499             dynamic_cast<CMouseButtonDownUpAction*>(s_action) != NULL);
01500 }
01501 
01502 BOOL CALLBACK
01503 CHotkeyOptions::CActionDialog::dlgProc(HWND hwnd,
01504                 UINT message, WPARAM wParam, LPARAM)
01505 {
01506     switch (message) {
01507     case WM_INITDIALOG:
01508         doInit(hwnd);
01509         return TRUE;
01510 
01511     case WM_COMMAND:
01512         switch (LOWORD(wParam)) {
01513         case IDOK:
01514             if (isDownUpAction()) {
01515                 s_onActivate = true;
01516             }
01517             EndDialog(hwnd, 1);
01518             return TRUE;
01519 
01520         case IDCANCEL:
01521             EndDialog(hwnd, 0);
01522             return TRUE;
01523 
01524         case IDC_HOTKEY_ACTION_ON_ACTIVATE:
01525             s_onActivate = true;
01526             return TRUE;
01527 
01528         case IDC_HOTKEY_ACTION_ON_DEACTIVATE:
01529             s_onActivate = false;
01530             return TRUE;
01531 
01532         case IDC_HOTKEY_ACTION_DOWNUP:
01533         case IDC_HOTKEY_ACTION_DOWN:
01534         case IDC_HOTKEY_ACTION_UP:
01535             convertAction(hwnd);
01536             fillHotkey(hwnd);
01537             updateControls(hwnd);
01538             return TRUE;
01539 
01540         case IDC_HOTKEY_ACTION_LOCK:
01541             onLockAction(hwnd);
01542             updateControls(hwnd);
01543             return TRUE;
01544 
01545         case IDC_HOTKEY_ACTION_SWITCH_TO:
01546             onSwitchToAction(hwnd);
01547             updateControls(hwnd);
01548             return TRUE;
01549 
01550         case IDC_HOTKEY_ACTION_SWITCH_IN:
01551             onSwitchInAction(hwnd);
01552             updateControls(hwnd);
01553             return TRUE;
01554 
01555         case IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST:
01556             onKeyboardBroadcastAction(hwnd);
01557             updateControls(hwnd);
01558             return TRUE;
01559 
01560         case IDC_HOTKEY_ACTION_LOCK_LIST:
01561             switch (HIWORD(wParam)) {
01562             case LBN_SELCHANGE:
01563                 onLockAction(hwnd);
01564                 return TRUE;
01565             }
01566             break;
01567 
01568         case IDC_HOTKEY_ACTION_SWITCH_TO_LIST:
01569             switch (HIWORD(wParam)) {
01570             case LBN_SELCHANGE:
01571                 onSwitchToAction(hwnd);
01572                 return TRUE;
01573             }
01574             break;
01575 
01576         case IDC_HOTKEY_ACTION_SWITCH_IN_LIST:
01577             switch (HIWORD(wParam)) {
01578             case LBN_SELCHANGE:
01579                 onSwitchInAction(hwnd);
01580                 return TRUE;
01581             }
01582             break;
01583 
01584         case IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST_LIST:
01585             switch (HIWORD(wParam)) {
01586             case LBN_SELCHANGE:
01587                 onKeyboardBroadcastAction(hwnd);
01588                 return TRUE;
01589             }
01590             break;
01591 
01592         case IDC_HOTKEY_ACTION_SCREENS:
01593             CScreensDialog::doModal(hwnd, s_config,
01594                 dynamic_cast<CInputFilter::CKeystrokeAction*>(s_action));
01595             fillHotkey(hwnd);
01596             return TRUE;
01597 
01598         case IDC_HOTKEY_ACTION_KEYBOARD_BROADCAST_SCREENS: {
01599             // convert screens to form that CScreenDialog::doModal() wants
01600             IPlatformScreen::CKeyInfo* tmpInfo =
01601                 IPlatformScreen::CKeyInfo::alloc(0, 0, 0, 1, s_screens);
01602             CInputFilter::CKeystrokeAction tmpAction(tmpInfo, true);
01603 
01604             // get the screens
01605             CScreensDialog::doModal(hwnd, s_config, &tmpAction);
01606 
01607             // convert screens back
01608             IPlatformScreen::CKeyInfo::split(
01609                                 tmpAction.getInfo()->m_screens, s_screens);
01610 
01611             // update
01612             onKeyboardBroadcastAction(hwnd);
01613             return TRUE;
01614         }
01615         }
01616         break;
01617 
01618     default:
01619         break;
01620     }
01621 
01622     return FALSE;
01623 }
01624 
01625 LRESULT CALLBACK
01626 CHotkeyOptions::CActionDialog::editProc(HWND hwnd,
01627                 UINT message, WPARAM wParam, LPARAM lParam)
01628 {
01629     switch (message) {
01630     case WM_LBUTTONDOWN:
01631         if (GetFocus() == hwnd) {
01632             onButton(hwnd, kButtonLeft);
01633         }
01634         else {
01635             SetFocus(hwnd);
01636         }
01637         return 0;
01638 
01639     case WM_MBUTTONDOWN:
01640         if (GetFocus() == hwnd) {
01641             onButton(hwnd, kButtonMiddle);
01642         }
01643         return 0;
01644 
01645     case WM_RBUTTONDOWN:
01646         if (GetFocus() == hwnd) {
01647             onButton(hwnd, kButtonRight);
01648         }
01649         return 0;
01650 
01651     case WM_XBUTTONDOWN:
01652         if (GetFocus() == hwnd) {
01653             switch (HIWORD(wParam)) {
01654             case XBUTTON1:
01655                 onButton(hwnd, kButtonExtra0 + 0);
01656                 break;
01657 
01658             case XBUTTON2:
01659                 onButton(hwnd, kButtonExtra0 + 1);
01660                 break;
01661             }
01662         }
01663         return 0;
01664 
01665     case WM_KEYDOWN:
01666     case WM_SYSKEYDOWN:
01667     case WM_KEYUP:
01668     case WM_SYSKEYUP:
01669         onKey(hwnd, wParam, lParam);
01670         return 0;
01671 
01672     case WM_LBUTTONUP:
01673     case WM_MBUTTONUP:
01674     case WM_RBUTTONUP:
01675     case WM_XBUTTONUP:
01676     case WM_CHAR:
01677     case WM_SYSCHAR:
01678     case WM_DEADCHAR:
01679     case WM_SYSDEADCHAR:
01680         return 0;
01681 
01682     case WM_SETFOCUS:
01683         if (s_action != NULL) {
01684             delete s_lastGoodAction;
01685             s_lastGoodAction = s_action->clone();
01686         }
01687         break;
01688 
01689     case WM_KILLFOCUS:
01690         if (!isGoodAction()) {
01691             delete s_action;
01692             if (s_lastGoodAction != NULL) {
01693                 s_action = s_lastGoodAction->clone();
01694             }
01695             else {
01696                 s_action = NULL;
01697             }
01698         }
01699         else if (s_action != NULL) {
01700             delete s_lastGoodAction;
01701             s_lastGoodAction = s_action->clone();
01702         }
01703         fillHotkey(GetParent(hwnd));
01704         break;
01705 
01706     case WM_GETDLGCODE:
01707         return DLGC_WANTALLKEYS;
01708 
01709     default:
01710         break;
01711     }
01712     return CallWindowProc(s_editWndProc, hwnd, message, wParam, lParam);
01713 }
01714 
01715 
01716 //
01717 // CHotkeyOptions::CScreensDialog
01718 //
01719 
01720 CConfig*                CHotkeyOptions::CScreensDialog::s_config = NULL;
01721 CInputFilter::CKeystrokeAction*
01722                         CHotkeyOptions::CScreensDialog::s_action = NULL;
01723 CHotkeyOptions::CScreensDialog::CScreens
01724                         CHotkeyOptions::CScreensDialog::s_nonTargets;
01725 CHotkeyOptions::CScreensDialog::CScreens
01726                         CHotkeyOptions::CScreensDialog::s_targets;
01727 CString             CHotkeyOptions::CScreensDialog::s_allScreens;
01728 
01729 void
01730 CHotkeyOptions::CScreensDialog::doModal(HWND parent, CConfig* config,
01731                 CInputFilter::CKeystrokeAction* action)
01732 {
01733     s_allScreens = getString(IDS_ALL_SCREENS);
01734     s_config = config;
01735     s_action = action;
01736     DialogBox(s_instance, MAKEINTRESOURCE(IDD_HOTKEY_SCREENS),
01737                                 parent, (DLGPROC) dlgProc);
01738     s_config = NULL;
01739     s_action = NULL;
01740 }
01741 
01742 void
01743 CHotkeyOptions::CScreensDialog::doInit(HWND hwnd)
01744 {
01745     s_nonTargets.clear();
01746     s_targets.clear();
01747 
01748     // get screens from config
01749     s_nonTargets.insert("*");
01750     for (CConfig::const_iterator i = s_config->begin();
01751                             i != s_config->end(); ++i) {
01752         s_nonTargets.insert(*i);
01753     }
01754 
01755     // get screens in action
01756     IKeyState::CKeyInfo::split(s_action->getInfo()->m_screens, s_targets);
01757 
01758     // remove screens in action from screens in config
01759     for (CScreens::const_iterator i = s_targets.begin();
01760                                 i != s_targets.end(); ++i) {
01761         s_nonTargets.erase(*i);
01762     }
01763 
01764     // fill dialog
01765     fillScreens(hwnd);
01766     updateControls(hwnd);
01767 }
01768 
01769 void
01770 CHotkeyOptions::CScreensDialog::doFini(HWND)
01771 {
01772     // put screens into action
01773     const IPlatformScreen::CKeyInfo* oldInfo = s_action->getInfo();
01774     IPlatformScreen::CKeyInfo* newInfo =
01775         IKeyState::CKeyInfo::alloc(oldInfo->m_key,
01776                                 oldInfo->m_mask, 0, 0, s_targets);
01777     s_action->adoptInfo(newInfo);
01778 }
01779 
01780 void
01781 CHotkeyOptions::CScreensDialog::fillScreens(HWND hwnd)
01782 {
01783     HWND child = getItem(hwnd, IDC_HOTKEY_SCREENS_SRC);
01784     SendMessage(child, LB_RESETCONTENT, 0, 0);
01785     for (CScreens::const_iterator i = s_nonTargets.begin();
01786                                 i != s_nonTargets.end(); ++i) {
01787         CString name = *i;
01788         if (name == "*") {
01789             name = s_allScreens;
01790         }
01791         SendMessage(child, LB_INSERTSTRING, (WPARAM)-1,
01792                             (LPARAM)name.c_str());
01793     }
01794 
01795     child = getItem(hwnd, IDC_HOTKEY_SCREENS_DST);
01796     SendMessage(child, LB_RESETCONTENT, 0, 0);
01797     for (CScreens::const_iterator i = s_targets.begin();
01798                                 i != s_targets.end(); ++i) {
01799         CString name = *i;
01800         if (name == "*") {
01801             name = s_allScreens;
01802         }
01803         SendMessage(child, LB_INSERTSTRING, (WPARAM)-1,
01804                             (LPARAM)name.c_str());
01805     }
01806     if (s_targets.empty()) {
01807         // if no targets then add a special item so the user knows
01808         // what'll happen
01809         CString activeScreenLabel = getString(IDS_ACTIVE_SCREEN);
01810         SendMessage(child, LB_INSERTSTRING, (WPARAM)-1,
01811                             (LPARAM)activeScreenLabel.c_str());
01812     }
01813 }
01814 
01815 void
01816 CHotkeyOptions::CScreensDialog::updateControls(HWND hwnd)
01817 {
01818     HWND child     = getItem(hwnd, IDC_HOTKEY_SCREENS_SRC);
01819     bool canAdd    = (SendMessage(child, LB_GETSELCOUNT, 0, 0) != 0);
01820     child          = getItem(hwnd, IDC_HOTKEY_SCREENS_DST);
01821     bool canRemove = (!s_targets.empty() &&
01822                         (SendMessage(child, LB_GETSELCOUNT, 0, 0) != 0));
01823 
01824     enableItem(hwnd, IDC_HOTKEY_SCREENS_ADD, canAdd);
01825     enableItem(hwnd, IDC_HOTKEY_SCREENS_REMOVE, canRemove);
01826 }
01827 
01828 void
01829 CHotkeyOptions::CScreensDialog::add(HWND hwnd)
01830 {
01831     CScreens selected;
01832     getSelected(hwnd, IDC_HOTKEY_SCREENS_SRC, s_nonTargets, selected);
01833     for (CScreens::const_iterator i = selected.begin();
01834                                 i != selected.end(); ++i) {
01835         s_targets.insert(*i);
01836         s_nonTargets.erase(*i);
01837     }
01838     fillScreens(hwnd);
01839     updateControls(hwnd);
01840 }
01841 
01842 void
01843 CHotkeyOptions::CScreensDialog::remove(HWND hwnd)
01844 {
01845     CScreens selected;
01846     getSelected(hwnd, IDC_HOTKEY_SCREENS_DST, s_targets, selected);
01847     for (CScreens::const_iterator i = selected.begin();
01848                                 i != selected.end(); ++i) {
01849         s_nonTargets.insert(*i);
01850         s_targets.erase(*i);
01851     }
01852     fillScreens(hwnd);
01853     updateControls(hwnd);
01854 }
01855 
01856 void
01857 CHotkeyOptions::CScreensDialog::getSelected(HWND hwnd, UINT id,
01858                 const CScreens& inScreens, CScreens& outScreens)
01859 {
01860     // get the selected item indices
01861     HWND child = getItem(hwnd, id);
01862     UInt32 n   = (UInt32)SendMessage(child, LB_GETSELCOUNT, 0, 0);
01863     int* index = new int[n];
01864     SendMessage(child, LB_GETSELITEMS, (WPARAM)n, (LPARAM)index);
01865 
01866     // get the items in a vector
01867     std::vector<CString> tmpList;
01868     for (CScreens::const_iterator i = inScreens.begin();
01869                                 i != inScreens.end(); ++i) {
01870         tmpList.push_back(*i);
01871     }
01872 
01873     // get selected items into the output set
01874     outScreens.clear();
01875     for (UInt32 i = 0; i < n; ++i) {
01876         outScreens.insert(tmpList[index[i]]);
01877     }
01878 
01879     // clean up
01880     delete[] index;
01881 }
01882 
01883 BOOL CALLBACK
01884 CHotkeyOptions::CScreensDialog::dlgProc(HWND hwnd,
01885                 UINT message, WPARAM wParam, LPARAM lParam)
01886 {
01887     switch (message) {
01888     case WM_INITDIALOG:
01889         doInit(hwnd);
01890         return TRUE;
01891 
01892     case WM_COMMAND:
01893         switch (LOWORD(wParam)) {
01894         case IDOK:
01895             doFini(hwnd);
01896             EndDialog(hwnd, 0);
01897             return TRUE;
01898 
01899         case IDCANCEL:
01900             EndDialog(hwnd, 0);
01901             return TRUE;
01902 
01903         case IDC_HOTKEY_SCREENS_ADD:
01904             add(hwnd);
01905             return TRUE;
01906 
01907         case IDC_HOTKEY_SCREENS_REMOVE:
01908             remove(hwnd);
01909             return TRUE;
01910 
01911         case IDC_HOTKEY_SCREENS_SRC:
01912         case IDC_HOTKEY_SCREENS_DST:
01913             switch (HIWORD(wParam)) {
01914             case LBN_SELCANCEL:
01915             case LBN_SELCHANGE:
01916                 updateControls(hwnd);
01917                 return TRUE;
01918             }
01919             break;
01920         }
01921         break;
01922 
01923     case WM_CTLCOLORLISTBOX:
01924         if (s_targets.empty() &&
01925             (HWND)lParam == getItem(hwnd, IDC_HOTKEY_SCREENS_DST)) {
01926             // override colors
01927             HDC dc = (HDC)wParam;
01928             SetTextColor(dc, GetSysColor(COLOR_GRAYTEXT));
01929             return (BOOL)GetSysColorBrush(COLOR_WINDOW);
01930         }
01931         break;
01932 
01933     default:
01934         break;
01935     }
01936 
01937     return FALSE;
01938 }

Generated on Fri Nov 6 00:18:45 2009 for synergy-plus by  doxygen 1.4.7