// ************************************************************************** // Filterkeys dialogs // Process the filterkeys dialogs // ************************************************************************** #include "Access.h" extern DWORD g_dwOrigFKFlags; extern BOOL g_bFKOn; extern LPTSTR HelpFile(); #define SWAP(A, B) ( A ^= B, B ^= A, A ^= B ) // Prototypes INT_PTR WINAPI BKDlg (HWND, UINT, WPARAM, LPARAM); INT_PTR WINAPI RKDlg (HWND, UINT, WPARAM, LPARAM); BOOL WINAPI NotificationDlg (HWND, UINT, WPARAM, LPARAM); BOOL SubclassFilterKeysTestBox (UINT uIdTestBox,HWND hDlg); BOOL SubclassRepeatKeysTestBox (UINT uIdTestBox,HWND hDlg); // All these are for subclassing, so that pressing TAB stops at the next // control after test areas. a-anilk LRESULT CALLBACK SubclassWndProcFKPrev(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam); LRESULT CALLBACK SubclassWndProcFKNext(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam); LRESULT CALLBACK SubclassWndProcRKPrev(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam); LRESULT CALLBACK SubclassWndProcRKNext(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam); // Times are in milliseconds #define DELAYSIZE 5 UINT uDelayTable[] = { 300, 700, 1000, 1500, 2000 }; // Times are in milliseconds #define RATESIZE 6 UINT uRateTable[] = { 300, 500, 700, 1000, 1500, 2000 }; // Times are in milliseconds #define BOUNCESIZE 5 UINT uBounceTable[] = { 500, 700, 1000, 1500, 2000 }; // Times are in milliseconds // TODO 5, 10, and 20 sec needs change to kernel code (SystemParametersInfo) #define ACCEPTSIZE 10 UINT uAcceptTable[] = { 0, 300, 500, 700, 1000, 1400, 2000, 5000, 10000, 20000 }; // these are wndprocs for subclassed windows to ignore repeated tab keys in // some situations. WNDPROC g_WndProcFKPrev = NULL; WNDPROC g_WndProcFKNext = NULL; WNDPROC g_WndProcRKPrev = NULL; WNDPROC g_WndProcRKNext = NULL; // other definitions for the keyboard // UP means key was up before this message, DOWN means key was down // PRESS means the key is being pressed, RELEASE means key being released #define KEY_UP 0 #define KEY_DOWN 1 #define KEY_PRESS 0 #define KEY_RELEASE 1 // Macros to look at the lParam of keyboard messages // #define SCAN_CODE(theParam) (LOBYTE (HIWORD(theParam))) #define EXTENDED(theParam) ( (HIWORD (theParam) & 0x0100) > 0) #define SYSKEY(theParam) ( (HIWORD (theParam) & 0x2000) > 0) #define MENUMODE(theParam) ( (HIWORD (theParam) & 0x1000) > 0) #define PREV_STATE(theParam) ( (HIWORD (theParam) & 0x4000) > 0) #define TRAN_STATE(theParam) ( (HIWORD (theParam) & 0x8000) > 0) #define MAKE(theParam) (TRAN_STATE(theParam) == KEY_PRESS) #define BREAK(theParam) (TRAN_STATE(theParam) == KEY_RELEASE) #define WASUP(theParam) (PREV_STATE(theParam) == KEY_UP) #define WASDOWN(theParam) (PREV_STATE(theParam) == KEY_DOWN) #define FIRSTHIT(theParam) (WASUP(theParam) && MAKE(theParam)) // ************************************************************************* // Process the scrolling messages from our trackbars. // GENERIC CODE - called for any TrackBar handler. // Passed in the hwnd, wParam, hwndScroll // we can do all handling and return the new trackbar value without // knowing what control it is. // Returns -1 to mean don't do anything // ************************************************************************* int HandleScroll (HWND hwnd, WPARAM wParam, HWND hwndScroll) { int nCurSliderPos = (int) SendMessage( hwndScroll, TBM_GETPOS, 0, 0); int nMaxVal = (int) SendMessage( hwndScroll, TBM_GETRANGEMAX, 0, 0); int nMinVal = (int) SendMessage( hwndScroll, TBM_GETRANGEMIN, 0, 0); switch (LOWORD(wParam)) { case TB_LINEUP: case TB_LINEDOWN: case TB_THUMBTRACK: case TB_THUMBPOSITION: case SB_ENDSCROLL: break; case TB_BOTTOM: nCurSliderPos = nMaxVal; break; case TB_TOP: nCurSliderPos = nMinVal; break; } if (nCurSliderPos < nMinVal) { nCurSliderPos = nMinVal; } if (nCurSliderPos > nMaxVal) { nCurSliderPos = nMaxVal; } SendMessage(GetParent(hwnd), PSM_CHANGED, (WPARAM) hwnd, 0); return(nCurSliderPos); } // Helper functions __inline WriteFloat(LPTSTR pszBuf, UINT uVal, LPCTSTR pszUnits) { wsprintf(pszBuf, TEXT("%d.%d %s"), uVal/1000, (uVal % 1000)/100, pszUnits); } __inline void HandleSelection(HWND hwnd, UINT *puTable, DWORD *pdwNewValue) { LRESULT i = SendMessage(hwnd, CB_GETCURSEL, 0, 0); *pdwNewValue = (i != CB_ERR)?puTable[i]:0; } int GetIndex(DWORD dwValue, UINT *puTable, int cSize) { int i; for (i = 0; i < cSize; i++) { if (puTable[i] >= dwValue) break; } if (i >= cSize) i = cSize - 1; return i; } void FillAndSetCombo(HWND hwnd, UINT *puTable, int cItems, int iCurPos, LPCTSTR pszUnits) { int i; TCHAR pszItem[100]; SendMessage(hwnd, CB_RESETCONTENT, 0, 0); for (i=0;ihItemHandle, HelpFile(), HELP_WM_HELP, (DWORD_PTR) (LPSTR) g_aIds); break; case WM_CONTEXTMENU: WinHelp((HWND) wParam, HelpFile(), HELP_CONTEXTMENU, (DWORD_PTR) (LPSTR) g_aIds); break; case WM_COMMAND: switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDC_FK_HOTKEY: g_fk.dwFlags ^= FKF_HOTKEYACTIVE; break; case IDC_FK_REPEAT: g_fk.iBounceMSec = 0; if (g_fk.iDelayMSec == 0) { g_fk.iDelayMSec = g_nLastRepeatDelay; g_fk.iRepeatMSec = g_nLastRepeatRate; g_fk.iWaitMSec = g_nLastWait; } CheckRadioButton(hwnd, IDC_FK_REPEAT, IDC_FK_BOUNCE, IDC_FK_REPEAT); EnableWindow(GetDlgItem(hwnd, IDC_BK_SETTINGS), FALSE); EnableWindow(GetDlgItem(hwnd, IDC_RK_SETTINGS), TRUE); break; case IDC_FK_BOUNCE: g_fk.iDelayMSec = 0; g_fk.iRepeatMSec = 0; g_fk.iWaitMSec = 0; if (g_fk.iBounceMSec == 0) { g_fk.iBounceMSec = g_dwLastBounceKeySetting; } CheckRadioButton(hwnd, IDC_FK_REPEAT, IDC_FK_BOUNCE, IDC_FK_BOUNCE); EnableWindow(GetDlgItem(hwnd, IDC_BK_SETTINGS), TRUE); EnableWindow(GetDlgItem(hwnd, IDC_RK_SETTINGS), FALSE); break; // Settings dialogs case IDC_RK_SETTINGS: // This is RepeatKeys fk = g_fk; if (DialogBox(g_hinst, MAKEINTRESOURCE(IDD_ADVCHARREPEAT), hwnd, RKDlg) == IDCANCEL) { g_fk = fk; } break; case IDC_BK_SETTINGS: // This is BounceKeys fk = g_fk; if (DialogBox(g_hinst, MAKEINTRESOURCE(IDD_ADVKEYBOUNCE), hwnd, BKDlg) == IDCANCEL) { g_fk = fk; } break; case IDC_FK_SOUND: g_fk.dwFlags ^= FKF_CLICKON; break; case IDC_FK_STATUS: g_fk.dwFlags ^= FKF_INDICATOR; break; // The test edit box is a special control for us. When we get the // focus we turn on the current filterkeys settings, when we // leave the text box, we turn them back to what they were. case IDC_FK_TESTBOX: switch (HIWORD(wParam)) { case EN_SETFOCUS: TestFilterKeys(TRUE); break; case EN_KILLFOCUS: TestFilterKeys(FALSE); break; } break; case IDOK: if (g_dwLastBounceKeySetting == 0) g_dwLastBounceKeySetting = uBounceTable[0]; EndDialog(hwnd, IDOK); break; case IDCANCEL: EndDialog(hwnd, IDCANCEL); break; } break; default: fProcessed = FALSE; break; } return(fProcessed); } void PutNumInEdit (HWND hwndEdit, int nNum) { TCHAR szBuf[10], szBuf2[20]; wsprintf(szBuf, __TEXT("%d.%d"), nNum / 1000, (nNum % 1000) / 100); GetNumberFormat(LOCALE_USER_DEFAULT, 0, szBuf, NULL, szBuf2, 20); SetWindowText(hwndEdit, szBuf2); } // ************************************************************************** // BKDlg // Process the BounceKeys dialog. // ************************************************************************** INT_PTR WINAPI BKDlg (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { int i; BOOL fProcessed = TRUE; TCHAR pszSeconds[50]; int ctch; switch (uMsg) { case WM_INITDIALOG: ctch = LoadString(g_hinst, IDS_SECONDS, pszSeconds, ARRAY_SIZE(pszSeconds)); Assert(ctch); // Determine the bounce. Make sure its a valide value. if (g_dwLastBounceKeySetting == 0) g_dwLastBounceKeySetting = 500; if (g_fk.iBounceMSec == 0) g_fk.iBounceMSec = g_dwLastBounceKeySetting; i = GetIndex(g_fk.iBounceMSec, uBounceTable, BOUNCESIZE); FillAndSetCombo(GetDlgItem(hwnd, IDC_CMB_BK_BOUNCERATE), uBounceTable, BOUNCESIZE, i, pszSeconds); break; case WM_HELP: // F1 WinHelp(((LPHELPINFO) lParam)->hItemHandle, HelpFile(), HELP_WM_HELP, (DWORD_PTR) (LPSTR) g_aIds); break; case WM_CONTEXTMENU: // right mouse click WinHelp((HWND) wParam, HelpFile(), HELP_CONTEXTMENU, (DWORD_PTR) (LPSTR) g_aIds); break; case WM_COMMAND: switch (GET_WM_COMMAND_ID(wParam, lParam)) { // The test edit box is a special control for us. When we get the // focus we turn on the current filterkeys settings, when we // leave the text box, we turn them back to what they were. case IDC_BK_TESTBOX: switch (HIWORD(wParam)) { case EN_SETFOCUS: TestFilterKeys(TRUE); break; case EN_KILLFOCUS: TestFilterKeys(FALSE); break; } break; case IDC_CMB_BK_BOUNCERATE: switch (HIWORD(wParam)) { case CBN_SELCHANGE: HandleSelection(GetDlgItem(hwnd, IDC_CMB_BK_BOUNCERATE), uBounceTable, &g_fk.iBounceMSec); break; } break; case IDOK: // Save the last known valid setting. g_dwLastBounceKeySetting = g_fk.iBounceMSec; EndDialog(hwnd, IDOK); break; case IDCANCEL: EndDialog(hwnd, IDCANCEL); break; } break; default: fProcessed = FALSE; break; } return(fProcessed); } // ************************************************************************** // RKDlg // Process the RepeatKeys dialog. // ************************************************************************** INT_PTR WINAPI RKDlg (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { int i; BOOL fProcessed = TRUE; static s_fRepeating = TRUE; static DWORD s_nLastRepeatDelayOld; static DWORD s_nLastRepeatRateOld; static DWORD s_nLastWaitOld; TCHAR pszItem[100]; TCHAR pszSeconds[50]; int ctch; LPARAM lParamT; switch(uMsg) { case WM_INITDIALOG: ctch = LoadString(g_hinst, IDS_SECONDS, pszSeconds, ARRAY_SIZE(pszSeconds)); Assert(ctch); s_nLastRepeatDelayOld = g_nLastRepeatDelay; s_nLastRepeatRateOld = g_nLastRepeatRate; s_nLastWaitOld = g_nLastWait; s_fRepeating = (0 != g_fk.iDelayMSec); CheckRadioButton(hwnd, IDC_RK_NOREPEAT, IDC_RK_REPEAT, s_fRepeating ? IDC_RK_REPEAT : IDC_RK_NOREPEAT); if (!s_fRepeating) { // Set FilterKey values to LastRepeat values // so the sliders will still get initialized correctly g_fk.iDelayMSec = g_nLastRepeatDelay; g_fk.iRepeatMSec = g_nLastRepeatRate; } // Initialize the Acceptance combo box to last valid state i = GetIndex(g_fk.iWaitMSec, uAcceptTable, ACCEPTSIZE); FillAndSetCombo(GetDlgItem(hwnd, IDC_CMB_RK_ACCEPTRATE), uAcceptTable, ACCEPTSIZE, i, pszSeconds); g_fk.iWaitMSec = uAcceptTable[i]; // Initialize the Delay combo box i = GetIndex(g_fk.iDelayMSec, uDelayTable, DELAYSIZE); FillAndSetCombo(GetDlgItem(hwnd, IDC_CMB_RK_DELAYRATE), uDelayTable, DELAYSIZE, i, pszSeconds); g_fk.iDelayMSec = uDelayTable[i]; // Initialize the Repeat Rate Slider Note -1 is set via the checkbox. i = GetIndex(g_fk.iRepeatMSec, uRateTable, RATESIZE); FillAndSetCombo(GetDlgItem(hwnd, IDC_CMB_RK_REPEATRATE), uRateTable, RATESIZE, i, pszSeconds); g_fk.iRepeatMSec = uRateTable[i]; // Now cleanup from initialization. Disable controls // that usable... Swap back any params needed if (!s_fRepeating) { EnableWindow(GetDlgItem(hwnd, IDC_CMB_RK_REPEATRATE), FALSE); EnableWindow(GetDlgItem(hwnd, IDC_CMB_RK_DELAYRATE), FALSE); // If we're not repeating, now set the value to 0 // which indicates max repeat rate. g_fk.iDelayMSec = 0; g_fk.iRepeatMSec = 0; } // // SteveDon 5/15/98 // If the focus is in the TestBox and "Ignore Quick Keystrokes" is on, // you have to hold down tab to get out. But as soon as focus leaves, // Ignore Quick Keystrokes gets turned off and the tab keys ends up // autorepeating very quickly, which (usually) lands you back in the // TestBox. // Solution: ignore repeated tabs in this dialog. // Problem: keys don't go to the dialog, they go to the focused // control. So: we can try to ignore repeated tab keys for the controls // just after the test box and just before the test box, which means // that we need to subclass those window procs. if (!SubclassRepeatKeysTestBox (IDC_RK_TESTBOX,hwnd)) return (FALSE); break; case WM_HELP: // F1 WinHelp(((LPHELPINFO) lParam)->hItemHandle, HelpFile(), HELP_WM_HELP, (DWORD_PTR) (LPSTR) g_aIds); break; case WM_CONTEXTMENU: // right mouse click WinHelp((HWND) wParam, HelpFile(), HELP_CONTEXTMENU, (DWORD_PTR) (LPSTR) g_aIds); break; case WM_COMMAND: switch (GET_WM_COMMAND_ID(wParam, lParam)) { // Turn on repeat keys - We're disabling via CPL rather than any flags in the call case IDC_RK_REPEAT: if (!s_fRepeating) { g_fk.iDelayMSec = g_nLastRepeatDelay; g_fk.iRepeatMSec = g_nLastRepeatRate; } // Now that we have valid parameters, continue with setting the sliders. s_fRepeating = TRUE; CheckRadioButton(hwnd, IDC_RK_NOREPEAT, IDC_RK_REPEAT, IDC_RK_REPEAT); if (g_fk.iRepeatMSec == 0) g_fk.iRepeatMSec = uRateTable[0]; if (g_fk.iDelayMSec == 0) g_fk.iDelayMSec = uDelayTable[0]; i = GetIndex(g_fk.iRepeatMSec, uRateTable, RATESIZE); EnableWindow(GetDlgItem(hwnd, IDC_CMB_RK_REPEATRATE), TRUE); SendDlgItemMessage(hwnd, IDC_CMB_RK_REPEATRATE, CB_SETCURSEL, i, 0); i = GetIndex(g_fk.iDelayMSec, uDelayTable, DELAYSIZE); EnableWindow(GetDlgItem(hwnd, IDC_CMB_RK_DELAYRATE), TRUE); SendDlgItemMessage(hwnd, IDC_CMB_RK_DELAYRATE, CB_SETCURSEL, i, 0); break; // Turn OFF repeat keys case IDC_RK_NOREPEAT: s_fRepeating = FALSE; CheckRadioButton(hwnd, IDC_RK_NOREPEAT, IDC_RK_REPEAT, IDC_RK_NOREPEAT); g_fk.iDelayMSec = 0; g_fk.iRepeatMSec = 0; SendDlgItemMessage(hwnd, IDC_CMB_RK_DELAYRATE, CB_SETCURSEL, -1, 0); EnableWindow(GetDlgItem(hwnd, IDC_CMB_RK_DELAYRATE), FALSE); SendDlgItemMessage(hwnd, IDC_CMB_RK_REPEATRATE, CB_SETCURSEL, -1, 0); EnableWindow(GetDlgItem(hwnd, IDC_CMB_RK_REPEATRATE), FALSE); break; // Process the test box - turnon filterkeys while inside it. case IDC_RK_TESTBOX: switch (HIWORD(wParam)) { case EN_SETFOCUS: TestFilterKeys(TRUE); break; case EN_KILLFOCUS: TestFilterKeys(FALSE); break; } break; case IDC_CMB_RK_DELAYRATE: switch (HIWORD(wParam)) { case CBN_SELCHANGE: HandleSelection(GetDlgItem(hwnd, IDC_CMB_RK_DELAYRATE), uDelayTable, &g_fk.iDelayMSec); break; } break; case IDC_CMB_RK_REPEATRATE: switch (HIWORD(wParam)) { case CBN_SELCHANGE: HandleSelection(GetDlgItem(hwnd, IDC_CMB_RK_REPEATRATE), uRateTable, &g_fk.iRepeatMSec); break; } break; case IDC_CMB_RK_ACCEPTRATE: switch (HIWORD(wParam)) { case CBN_SELCHANGE: HandleSelection(GetDlgItem(hwnd, IDC_CMB_RK_ACCEPTRATE), uAcceptTable, &g_fk.iWaitMSec); break; } break; case IDOK: // Save off repeating values to registry EndDialog(hwnd, IDOK); break; case IDCANCEL: g_nLastRepeatDelay = s_nLastRepeatDelayOld; g_nLastRepeatRate = s_nLastRepeatRateOld; g_nLastWait = s_nLastWaitOld; EndDialog(hwnd, IDCANCEL); break; } break; default: fProcessed = FALSE; break; } return(fProcessed); } // ************************************************************************** // SubclassFilterKeysTestBox // // This takes the dialog ID of an edit field, and then finds the controls // near the edit field (the controls 2 windows before and 2 windows after the // edit control in the z-order). These are the nearest controls that get // keyboard messages. It subclasses both of these controls // so that they ignore any WM_KEYDOWN messages when the key being pressed is // the tab key and the key is already down (i.e. this is a repeated message) // // ************************************************************************** BOOL SubclassFilterKeysTestBox (UINT uIdTestBox,HWND hDlg) { HWND hwndPrev, hwndNext, hwndTestBox; hwndTestBox = GetDlgItem (hDlg,uIdTestBox); // BE CAREFUL IF DIALOG CHANGES! Right now the // Previous Previous window is the "S&ettings" push button, // and the Next Next is the "&Beep when keys pressed..." // checkbox. If the order changes, this code might have to change too. // Could make it more general where it searches for controls before // and after that can get keyboard focus. hwndPrev = GetNextDlgTabItem (hDlg,hwndTestBox,TRUE); if (!hwndPrev) return FALSE; g_WndProcFKPrev = (WNDPROC) GetWindowLongPtr (hwndPrev, GWLP_WNDPROC); SetWindowLongPtr (hwndPrev,GWLP_WNDPROC,(LPARAM)SubclassWndProcFKPrev); hwndNext = GetNextDlgTabItem (hDlg,hwndTestBox,FALSE); if (!hwndNext) return FALSE; g_WndProcFKNext = (WNDPROC) GetWindowLongPtr (hwndNext, GWLP_WNDPROC); SetWindowLongPtr (hwndNext,GWLP_WNDPROC,(LPARAM)SubclassWndProcFKNext); return TRUE; } // ************************************************************************** // SubclassWndProcFKPrev // // This is the WndProc used to ignore repeated presses of the tab key for // the first focusable control that precedes the test box. // // ************************************************************************** LRESULT CALLBACK SubclassWndProcFKPrev(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam) { switch (uMsg) { case WM_KEYDOWN: if ((int)wParam == VK_TAB) { if (WASDOWN (lParam)) { return (0); } // if not a repeat, need to move the focus. For some reason, // just calling CallWindowProc won't do it for us. if (GetKeyState(VK_SHIFT) < 0) { SendMessage (GetParent(hwnd),WM_NEXTDLGCTL,1,0); } else { SendMessage (GetParent(hwnd),WM_NEXTDLGCTL,0,0); } } break; case WM_GETDLGCODE: return (DLGC_WANTTAB | CallWindowProc (g_WndProcFKPrev,hwnd,uMsg,wParam,lParam)); break; } return (CallWindowProc(g_WndProcFKPrev,hwnd,uMsg,wParam,lParam)); } // ************************************************************************** // SubclassWndProcFKNext // // This is the WndProc used to ignore repeated presses of the tab key for // the first focusable control that follows the test box. // // ************************************************************************** LRESULT CALLBACK SubclassWndProcFKNext(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam) { switch (uMsg) { case WM_KEYDOWN: if ((int)wParam == VK_TAB) { if (WASDOWN(lParam)) { return (0); } // if not a repeat, need to move the focus. For some reason, // just calling CallWindowProc won't do it for us. if (GetKeyState(VK_SHIFT) < 0) { SendMessage (GetParent(hwnd),WM_NEXTDLGCTL,1,0); } else { SendMessage (GetParent(hwnd),WM_NEXTDLGCTL,0,0); } } break; case WM_GETDLGCODE: return (DLGC_WANTTAB | CallWindowProc (g_WndProcFKNext,hwnd,uMsg,wParam,lParam)); break; } return (CallWindowProc(g_WndProcFKNext,hwnd,uMsg,wParam,lParam)); } // ************************************************************************** // SubclassRepeatKeysTestBox // // Same as SubclassFilterKeysTestBox, but keeps it's info in different // globals so that one doesn't overwrite the other. // // ************************************************************************** BOOL SubclassRepeatKeysTestBox (UINT uIdTestBox,HWND hDlg) { HWND hwndPrev, hwndNext, hwndTestBox; hwndTestBox = GetDlgItem (hDlg,uIdTestBox); // BE CAREFUL IF DIALOG CHANGES! Right now the // Previous Previous window is the "S&ettings" push button, // and the Next Next is the "&Beep when keys pressed..." // checkbox. If the order changes, this code might have to change too. // Could make it more general where it searches for controls before // and after that can get keyboard focus. hwndPrev = GetNextDlgTabItem (hDlg,hwndTestBox,TRUE); g_WndProcRKPrev = (WNDPROC) GetWindowLongPtr (hwndPrev,GWLP_WNDPROC); SetWindowLongPtr (hwndPrev,GWLP_WNDPROC,(LPARAM)SubclassWndProcRKPrev); hwndNext = GetNextDlgTabItem (hDlg,hwndTestBox,FALSE); g_WndProcRKNext = (WNDPROC) GetWindowLongPtr (hwndNext,GWLP_WNDPROC); SetWindowLongPtr (hwndNext,GWLP_WNDPROC,(LPARAM)SubclassWndProcRKNext); return (TRUE); } // ************************************************************************** // SubclassWndProcRKPrev // // ************************************************************************** LRESULT CALLBACK SubclassWndProcRKPrev(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam) { switch (uMsg) { case WM_KEYDOWN: if ((int)wParam == VK_TAB) { if (WASDOWN (lParam)) { return (0); } // if not a repeat, need to move the focus. For some reason, // just calling CallWindowProc won't do it for us. if (GetKeyState(VK_SHIFT) < 0) { SendMessage (GetParent(hwnd),WM_NEXTDLGCTL,1,0); } else { SendMessage (GetParent(hwnd),WM_NEXTDLGCTL,0,0); } } break; case WM_GETDLGCODE: return (DLGC_WANTTAB | CallWindowProc (g_WndProcRKPrev,hwnd,uMsg,wParam,lParam)); break; } return (CallWindowProc(g_WndProcRKPrev,hwnd,uMsg,wParam,lParam)); } // ************************************************************************** // SubclassWndProcRKNext // // ************************************************************************** LRESULT CALLBACK SubclassWndProcRKNext(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam) { switch (uMsg) { case WM_KEYDOWN: if ((int)wParam == VK_TAB) { if (WASDOWN(lParam)) { return (0); } // if not a repeat, need to move the focus. For some reason, // just calling CallWindowProc won't do it for us. if (GetKeyState(VK_SHIFT) < 0) { SendMessage (GetParent(hwnd),WM_NEXTDLGCTL,1,0); } else { SendMessage (GetParent(hwnd),WM_NEXTDLGCTL,0,0); } } break; case WM_GETDLGCODE: return (DLGC_WANTTAB | CallWindowProc (g_WndProcRKNext,hwnd,uMsg,wParam,lParam)); break; } return (CallWindowProc(g_WndProcRKNext,hwnd,uMsg,wParam,lParam)); } ///////////////////////////////// End of File /////////////////////////////////