// **************************************************************************
// Filterkeys dialogs
// Process the filterkeys dialogs
// **************************************************************************


#include "Access.h"

extern DWORD g_dwOrigFKFlags;
extern BOOL g_bFKOn;

#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;i<cItems;i++)
    {
        WriteFloat(pszItem, puTable[i], pszUnits);
        SendMessage(hwnd, CB_ADDSTRING, 0, (LPARAM)pszItem);
    }
    SendMessage(hwnd, CB_SETCURSEL, iCurPos, 0);
}

void TestFilterKeys (BOOL fTurnTestOn)
{
	if (fTurnTestOn) 
	{
		g_fk.dwFlags &= ~FKF_INDICATOR;
		g_fk.dwFlags |= FKF_FILTERKEYSON;
	} 
	else 
	{
		if (g_dwOrigFKFlags & FKF_FILTERKEYSON)
		{
			g_fk.dwFlags |= FKF_FILTERKEYSON;
		}
		else
		{
			g_fk.dwFlags &= ~FKF_FILTERKEYSON;
		}

		if (g_dwOrigFKFlags & FKF_INDICATOR)
		{
			g_fk.dwFlags |= FKF_INDICATOR;
		}
		else
		{
			g_fk.dwFlags &= ~FKF_INDICATOR;
		}
	}

	AccessSystemParametersInfo(SPI_SETFILTERKEYS, sizeof(g_fk), &g_fk, 0);
}


// ****************************************************************************
// Main filter keys dialog handler
// ****************************************************************************

INT_PTR WINAPI FilterKeyDlg (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    FILTERKEYS fk;
    BOOL fProcessed = TRUE;

    switch (uMsg) {
    case WM_INITDIALOG:
        // Setup hotkey
        CheckDlgButton(hwnd, IDC_FK_HOTKEY, (g_fk.dwFlags & FKF_HOTKEYACTIVE) ? TRUE : FALSE);

        // Setup the radio buttons for SLOW vs BOUNCE keys
        if (0 != g_fk.iBounceMSec) {
            // Bounce keys enabeled
            CheckRadioButton(hwnd, IDC_FK_BOUNCE, IDC_FK_REPEAT, IDC_FK_BOUNCE);
            EnableWindow(GetDlgItem(hwnd, IDC_BK_SETTINGS), TRUE);
            EnableWindow(GetDlgItem(hwnd, IDC_RK_SETTINGS), FALSE);
        }
        else
        {
            // Slow key enabled
            CheckRadioButton(hwnd, IDC_FK_BOUNCE, IDC_FK_REPEAT, IDC_FK_REPEAT);
            EnableWindow(GetDlgItem(hwnd, IDC_BK_SETTINGS), FALSE);
            EnableWindow(GetDlgItem(hwnd, IDC_RK_SETTINGS), TRUE);
        }

        CheckDlgButton(hwnd, IDC_FK_SOUND, (g_fk.dwFlags & FKF_CLICKON) ? TRUE : FALSE);
        CheckDlgButton(hwnd, IDC_FK_STATUS, (g_fk.dwFlags & FKF_INDICATOR) ? TRUE : FALSE);
        // 
        // 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 (!SubclassFilterKeysTestBox (IDC_FK_TESTBOX,hwnd))
            return (FALSE);

        break;

    case WM_HELP:
        WinHelp(((LPHELPINFO) lParam)->hItemHandle, __TEXT("access.hlp"), HELP_WM_HELP, (DWORD_PTR) (LPSTR) g_aIds);
        break;
         
    case WM_CONTEXTMENU:
        WinHelp((HWND) wParam, __TEXT("access.hlp"), 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, __TEXT("access.hlp"), HELP_WM_HELP, (DWORD_PTR) (LPSTR) g_aIds);
        break;

    case WM_CONTEXTMENU:      // right mouse click
        WinHelp((HWND) wParam, __TEXT("access.hlp"), 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, __TEXT("access.hlp"), HELP_WM_HELP, (DWORD_PTR) (LPSTR) g_aIds);
        break;

    case WM_CONTEXTMENU:      // right mouse click
        WinHelp((HWND) wParam, __TEXT("access.hlp"), 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 /////////////////////////////////
