/*++

Copyright (c) 1994-1995,  Microsoft Corporation  All rights reserved.

Module Name:

    locdlg.c

Abstract:

    This module implements the input locale property sheet for the Regional
    Settings applet.

Revision History:

--*/



//
//  Include Files.
//

#include "intl.h"
#include <windowsx.h>
#include <regstr.h>
#include <setupapi.h>
#include <syssetup.h>
#include <winuserp.h>
#include <help.h>
#include "locdlg.h"

#ifdef DBCS
  #include <imm.h>
#endif




//
//  Context Help Ids.
//

static int aLocaleHelpIds[] =
{
    IDC_KBDL_LOCALE,         IDH_KEYB_INPUT_LIST,
    IDC_KBDL_LAYOUT_TEXT,    IDH_KEYB_INPUT_LIST,
    IDC_KBDL_LOCALE_LIST,    IDH_KEYB_INPUT_LIST,
    IDC_KBDL_ADD,            IDH_KEYB_INPUT_ADD,
    IDC_KBDL_EDIT,           IDH_KEYB_INPUT_PROP,
    IDC_KBDL_DELETE,         IDH_KEYB_INPUT_DEL,
    IDC_KBDL_DISABLED,       NO_HELP,
    IDC_KBDL_DISABLED_2,     NO_HELP,
    IDC_KBDL_DEFAULT_LABEL,  IDH_KEYB_INPUT_DEF_LANG,
    IDC_KBDL_DEFAULT,        IDH_KEYB_INPUT_DEF_LANG,
    IDC_KBDL_INPUT_FRAME,    IDH_COMM_GROUPBOX,
    IDC_KBDL_SET_DEFAULT,    IDH_KEYB_INPUT_DEFAULT,
    IDC_KBDL_SHORTCUT_FRAME, IDH_KEYB_INPUT_SHORTCUT,
    IDC_KBDL_ALT_SHIFT,      IDH_KEYB_INPUT_SHORTCUT,
    IDC_KBDL_CTRL_SHIFT,     IDH_KEYB_INPUT_SHORTCUT,
    IDC_KBDL_NO_SHIFT,       IDH_KEYB_INPUT_SHORTCUT,
    IDC_KBDL_INDICATOR,      IDH_KEYB_INPUT_INDICATOR,
    IDC_KBDL_ONSCRNKBD,      IDH_KEYB_INPUT_ONSCRN_KEYB,

    0, 0
};

#define IDH_KEYB_DEF_KEYB_FOR_LOCALE 4033
static int aAddLocaleHelpIds[] =
{
    IDC_KBDLA_LOCALE,        IDH_KEYB_INPUT_LANG,
    IDC_KBDLA_DEFAULT,       IDH_KEYB_DEF_KEYB_FOR_LOCALE,

    0, 0
};


static int aLocalePropHelpIDs[] =
{
    IDC_KBDLE_LOCALE_TXT,    IDH_KEYB_INPUT_PROP_LANG,
    IDC_KBDLE_LOCALE,        IDH_KEYB_INPUT_PROP_LANG,
    IDC_KBDLE_LAYOUT,        IDH_KEYB_INPUT_PROP_KEYLAY,

    0, 0
};




//
//  Global Variables.
//

TCHAR szPropHwnd[] = TEXT("PROP_HWND");
TCHAR szPropIdx[]  = TEXT("PROP_IDX");





////////////////////////////////////////////////////////////////////////////
//
//  GetKbdSwitchHotkey
//
//  Gets the hotkey keyboard switch value from the registry and then
//  sets the appropriate radio button in the dialog.
//
////////////////////////////////////////////////////////////////////////////

int GetKbdSwitchHotkey(
    HWND hwnd)
{
    TCHAR sz[10];
    DWORD cb;
    HKEY hkey;

    //
    //  Get the hotkey value from the registry.
    //
    sz[0] = 0;
    if (RegOpenKey(HKEY_CURRENT_USER, szKbdToggleKey, &hkey) == ERROR_SUCCESS)
    {
        cb = sizeof(sz);
        RegQueryValueEx(hkey, TEXT("Hotkey"), NULL, NULL, (LPBYTE)sz, &cb);
        RegCloseKey(hkey);
    }

    //
    //  Set the appropriate radio button in the dialog.
    //
    if ((sz[0] != 0) && (sz[1] == 0))
    {
        switch (sz[0])
        {
            case ( TEXT('2') ) :
            {
                CheckRadioButton( hwnd,
                                  IDC_KBDL_ALT_SHIFT,
                                  IDC_KBDL_NO_SHIFT,
                                  IDC_KBDL_CTRL_SHIFT );
                return (2);
            }
            case ( TEXT('3') ) :
            {
                CheckRadioButton( hwnd,
                                  IDC_KBDL_ALT_SHIFT,
                                  IDC_KBDL_NO_SHIFT,
                                  IDC_KBDL_NO_SHIFT );
                return (3);
            }
        }
    }

    //
    //  Default case.
    //
    CheckRadioButton( hwnd,
                      IDC_KBDL_ALT_SHIFT,
                      IDC_KBDL_NO_SHIFT,
                      IDC_KBDL_ALT_SHIFT );
    return (1);
}


////////////////////////////////////////////////////////////////////////////
//
//  TransNum
//
//  Converts a number string to a dword value.
//
////////////////////////////////////////////////////////////////////////////

DWORD TransNum(
    LPTSTR lpsz)
{
    DWORD dw = 0L;
    TCHAR c;

    while (*lpsz)
    {
        c = *lpsz++;

        if (c >= TEXT('A') && c <= TEXT('F'))
        {
            c -= TEXT('A') - 0xa;
        }
        else if (c >= TEXT('0') && c <= TEXT('9'))
        {
            c -= TEXT('0');
        }
        else if (c >= TEXT('a') && c <= TEXT('f'))
        {
            c -= TEXT('a') - 0xa;
        }
        else
        {
            break;
        }
        dw *= 0x10;
        dw += c;
    }
    return (dw);
}


////////////////////////////////////////////////////////////////////////////
//
//  ErrorMsg
//
//  Sound a beep and put up the given error message.
//
////////////////////////////////////////////////////////////////////////////

void ErrorMsg(
    HWND hwnd,
    UINT iErr)
{
    TCHAR sz[DESC_MAX];

    //
    //  Sound a beep.
    //
    MessageBeep(MB_OK);

    //
    //  Put up the appropriate error message box.
    //
    if (LoadString(hInstance, iErr, sz, DESC_MAX))
    {
        MessageBox(hwnd, sz, NULL, MB_OK_OOPS);
    }
}


////////////////////////////////////////////////////////////////////////////
//
//  ApplyError
//
//  Put up the given error message with the language name in it.
//
//  NOTE: This error is NOT fatal - as we could be half way through the
//        list before an error occurs.  The registry will already have 
//        some information and we should let them have what comes next
//        as well.
//
////////////////////////////////////////////////////////////////////////////

int ApplyError(
    HWND hwnd,
    LPLANGNODE pLangNode,
    UINT iErr,
    UINT iStyle)
{
    UINT idxLang, idxLayout;
    TCHAR sz[MAX_PATH];
    TCHAR szTemp[MAX_PATH];
    TCHAR szLangName[MAX_PATH * 2];
    LPTSTR pszLang;

    //
    //  Load in the string for the given string id.
    //
    LoadString(hInstance, iErr, sz, MAX_PATH);

    //
    //  Get the language name to fill into the above string.
    //
    if (pLangNode)
    {
        idxLang = pLangNode->iLang;
        idxLayout = pLangNode->iLayout;
        GetAtomName(lpLang[idxLang].atmLanguageName, szLangName, MAX_PATH);
        if (lpLang[idxLang].dwID != lpLayout[idxLayout].dwID)
        {
            pszLang = szLangName + lstrlen(szLangName);
            pszLang[0] = TEXT(' ');
            pszLang[1] = TEXT('-');
            pszLang[2] = TEXT(' ');
            GetAtomName( lpLayout[idxLayout].atmLayoutText,
                         pszLang + 3,
                         MAX_PATH - 3 );
        }
    }
    else
    {
        LoadString(hInstance, IDS_UNKNOWN, szLangName, MAX_PATH);
    }

    //
    //  Put up the error message box.
    //
    wsprintf(szTemp, sz, szLangName);
    return ( MessageBox(hwnd, szTemp, NULL, iStyle) );
}


////////////////////////////////////////////////////////////////////////////
//
//  FetchIndicator
//
//  Saves the two letter indicator symbol for the given language in the
//  lpLang array.
//
////////////////////////////////////////////////////////////////////////////

void FetchIndicator(
    LPLANGNODE pLangNode)
{
    TCHAR szData[MAX_PATH];
    LPINPUTLANG pInpLang = &lpLang[pLangNode->iLang];

    pLangNode->wStatus |= ICON_LOADED;

#ifdef DBCS
    if (pLangNode->wStatus & LANG_IME)
    {
        TCHAR szFileName[MAX_PATH];
        HICON hIcon = NULL;

        if (himIndicators != NULL)
        {
            GetAtomName( lpLayout[pLangNode->iLayout].atmIMEFile,
                         szFileName,
                         MAX_PATH );
            ExtractIconEx(szFileName, 0, (HICON *)&hIcon, NULL, 1);

            if (hIcon)
            {
                pLangNode->niconIME = ImageList_AddIcon( himIndicators,
                                                         hIcon );
            }
            else
            {
                pLangNode->niconIME = -1;
            }
            DestroyIcon(hIcon);
            if (pLangNode->niconIME != -1)
            {
                return;
            }
        }
    }
#endif

    //
    //  Get the indicator by using the first 2 characters of the
    //  abbreviated language name.
    //
    if (GetLocaleInfo( LOWORD(pInpLang->dwID),
                       LOCALE_SABBREVLANGNAME | LOCALE_NOUSEROVERRIDE,
                       szData,
                       MAX_PATH ))
    {
        //
        //  Save the first two characters.
        //
        pInpLang->szSymbol[0] = szData[0];
        pInpLang->szSymbol[1] = szData[1];
        pInpLang->szSymbol[2] = TEXT('\0');
    }
    else
    {
        //
        //  Id wasn't found.  Return question marks.
        //
        pInpLang->szSymbol[0] = TEXT('?');
        pInpLang->szSymbol[1] = TEXT('?');
        pInpLang->szSymbol[2] = TEXT('\0');
    }
}


////////////////////////////////////////////////////////////////////////////
//
//  SetSecondaryControls
//
//  Sets the secondary controls to either be enabled or disabled.  When
//  there is only 1 active keyboard layout, then this function will be
//  called to disable these controls.
//
////////////////////////////////////////////////////////////////////////////

void SetSecondaryControls(
    HWND hwndMain,
    BOOL bOn)
{
    EnableWindow(GetDlgItem(hwndMain, IDC_KBDL_INDICATOR), bOn);
    CheckDlgButton(hwndMain, IDC_KBDL_INDICATOR, bOn);

    EnableWindow(GetDlgItem(hwndMain, IDC_KBDL_DELETE), bOn);

    EnableWindow(GetDlgItem(hwndMain, IDC_KBDL_SET_DEFAULT), bOn);

    EnableWindow(GetDlgItem(hwndMain, IDC_KBDL_ALT_SHIFT), bOn);
    CheckDlgButton(hwndMain, IDC_KBDL_ALT_SHIFT, bOn);

    EnableWindow(GetDlgItem(hwndMain, IDC_KBDL_CTRL_SHIFT), bOn);
    CheckDlgButton(hwndMain, IDC_KBDL_CTRL_SHIFT, 0);

    EnableWindow(GetDlgItem(hwndMain, IDC_KBDL_NO_SHIFT), bOn);
    CheckDlgButton(hwndMain, IDC_KBDL_NO_SHIFT, 0);
}


////////////////////////////////////////////////////////////////////////////
//
//  Locale_AddToLinkedList
//
//  Adds an Input Locale to the main lpLang array.
//
////////////////////////////////////////////////////////////////////////////

LPLANGNODE Locale_AddToLinkedList(
    UINT idx,
    HKL hkl)
{
    LPINPUTLANG pInpLang = &lpLang[idx];
    LPLANGNODE pLangNode;
    LPLANGNODE pTemp;
    HANDLE hLangNode;

    //
    //  Create the new node.
    //
    if (!(hLangNode = GlobalAlloc(GHND, sizeof(LANGNODE))))
    {
        return (NULL);
    }
    pLangNode = GlobalLock(hLangNode);

    //
    //  Fill in the new node with the appropriate info.
    //
    pLangNode->wStatus = 0;
    pLangNode->iLayout = (UINT)(-1);
    pLangNode->hkl = hkl;
    pLangNode->hklUnload = hkl;
    pLangNode->iLang = idx;
    pLangNode->hLangNode = hLangNode;
    pLangNode->pNext = NULL;

    //
    //  Put the new node in the list.
    //
    pTemp = pInpLang->pNext;
    if (pTemp == NULL)
    {
        pInpLang->pNext = pLangNode;
    }
    else
    {
        while (pTemp->pNext != NULL)
        {
            pTemp = pTemp->pNext;
        }
        pTemp->pNext = pLangNode;
    }

    //
    //  Increment the count.
    //
    pInpLang->iNumCount++;

    //
    //  Return the pointer to the new node.
    //
    return (pLangNode);
}


////////////////////////////////////////////////////////////////////////////
//
//  Locale_RemoveFromLinkedList
//
//  Removes a link from the linked list.
//
////////////////////////////////////////////////////////////////////////////

void Locale_RemoveFromLinkedList(
    LPLANGNODE pLangNode)
{
    LPINPUTLANG pInpLang;
    LPLANGNODE pPrev;
    LPLANGNODE pCur;
    HANDLE hCur;

    pInpLang = &lpLang[pLangNode->iLang];

    //
    //  Find the node in the list.
    //
    pPrev = NULL;
    pCur = pInpLang->pNext;

    while (pCur && (pCur != pLangNode))
    {
        pPrev = pCur;
        pCur = pCur->pNext;
    }

    if (pPrev == NULL)
    {
        pInpLang->pNext = NULL;
    }
    else if (pCur)
    {
        pPrev->pNext = pCur->pNext;
    }

    //
    //  Remove the node from the list.
    //
    if (pCur)
    {
        hCur = pCur->hLangNode;
        GlobalUnlock(hCur);
        GlobalFree(hCur);
    }
}


////////////////////////////////////////////////////////////////////////////
//
//  AddLanguage
//
//  Adds the new input locale to the list in the property page.
//
////////////////////////////////////////////////////////////////////////////

BOOL AddLanguage(
    HWND hwndMain,
    LPLANGNODE pLangNode)
{
    HWND hwndLang;
    UINT iCount;

    //
    //  See if the user has Admin privileges.  If not, then don't allow
    //  them to install any NEW layouts.
    //
    if ((!g_bAdmin_Privileges) &&
        (!lpLayout[pLangNode->iLayout].bInstalled))
    {
        //
        //  The layout is not currently installed, so don't allow it
        //  to be added.
        //
        ErrorMsg(hwndMain, IDS_ML_LAYOUTFAILED);
        return (FALSE);
    }

    //
    //  Set the language to active.
    //  Also, set the status to changed so that the layout will be added.
    //
    pLangNode->wStatus |= (LANG_CHANGED | LANG_ACTIVE);

    //
    //  Get the number of items in the input locale list box.
    //
    hwndLang = GetDlgItem(hwndMain, IDC_KBDL_LOCALE_LIST);
    iCount = ListBox_GetCount(hwndLang);

    //
    //  Add the new item data to the list box.
    //
    ListBox_AddItemData(hwndLang, pLangNode);

    //
    //  Get the indicator symbol.
    //
    if ((pLangNode->wStatus & ICON_LOADED))
    {
        FetchIndicator(pLangNode);
    }

    //
    //  See if the original count (before the addition) was 1.  If so,
    //  enable the secondary controls, since there are now 2 items in
    //  the list box.
    //
    if (iCount == 1)
    {
        SetSecondaryControls(hwndMain, TRUE);
    }

    //
    //  Return success.
    //
    return (TRUE);
}


////////////////////////////////////////////////////////////////////////////
//
//  Locale_SetupKeyboardLayouts
//
//  Calls setup to get all of the new keyboard layout files.
//
////////////////////////////////////////////////////////////////////////////

BOOL Locale_SetupKeyboardLayouts(
    HWND hwnd,
    HWND hwndList,
    UINT nLocales)
{
    HINF hKbdInf;
    HSPFILEQ FileQueue;
    PVOID QueueContext;
    UINT i;
    LPLANGNODE pLangNode;
    int count;
    BOOL bInitInf = FALSE;
    TCHAR szSection[MAX_PATH];
    BOOL bRet = TRUE;

    for (i = 0; i < nLocales; i++)
    {
        pLangNode = (LPLANGNODE)ListBox_GetItemData(hwndList, i);
        if ((pLangNode->wStatus & LANG_CHANGED) && 
            (pLangNode->wStatus & LANG_ACTIVE))
        {
            if (!bInitInf)
            {
                //
                //  Open the Inf file.
                //
                hKbdInf = SetupOpenInfFile(szKbdInf, NULL, INF_STYLE_WIN4, NULL);
                if (hKbdInf == INVALID_HANDLE_VALUE)
                {
                    return (FALSE);
                }
            
                if (!SetupOpenAppendInfFile(NULL, hKbdInf, NULL))
                {
                    SetupCloseInfFile(hKbdInf);
                    return (FALSE);
                }
            
                //
                //  Create a setup file queue and initialize default setup
                //  copy queue callback context.
                //
                FileQueue = SetupOpenFileQueue();
                if ((!FileQueue) || (FileQueue == INVALID_HANDLE_VALUE))
                {
                    SetupCloseInfFile(hKbdInf);
                    return (FALSE);
                }
            
                QueueContext = SetupInitDefaultQueueCallback(hwnd);
                if (!QueueContext)
                {
                    SetupCloseFileQueue(FileQueue);
                    SetupCloseInfFile(hKbdInf);
                    return (FALSE);
                }

                bInitInf = TRUE;
            }

            //
            //  Get the layout name.
            //
            wsprintf( szSection,
                      TEXT("%ws%8.8lx"),
                      szPrefixCopy,
                      lpLayout[pLangNode->iLayout].dwID );

            //
            //  Enqueue the keyboard layout files so that they may be
            //  copied.  This only handles the CopyFiles entries in the
            //  inf file.
            //
            if (!SetupInstallFilesFromInfSection( hKbdInf,
                                                  NULL,
                                                  FileQueue,
                                                  szSection,
                                                  NULL,
                                                  SP_COPY_NEWER ))
            {
                //
                //  Setup failed to find the keyboard.  Make it inactive
                //  and remove it from the list.
                //
                //  This shouldn't happen - the inf file is messed up.
                //
                ErrorMsg(hwnd, IDS_ML_SETUPFAILED);

                pLangNode->wStatus & ~(LANG_CHANGED | LANG_ACTIVE);
                if ((count = ListBox_GetCount(hwndList)) > 1)
                {
                    ListBox_DeleteString(hwndList, i);
                    ListBox_SetCurSel(hwndList, 0);
                    if (count == 2)
                    {
                        SetSecondaryControls(hwnd, FALSE);
                    }
                    nLocales = count - 1;

                    (lpLang[pLangNode->iLang].iNumCount)--;
                    Locale_RemoveFromLinkedList(pLangNode);
                }
            }
        }
    }

    if (bInitInf)
    {
        DWORD d;

        //
        //  See if we need to install any files.
        //
        //  d = 0: User wants new files or some files were missing;
        //         Must commit queue.
        //
        //  d = 1: User wants to use existing files and queue is empty;
        //         Can skip committing queue.
        //
        //  d = 2: User wants to use existing files, but del/ren queues
        //         not empty.  Must commit queue.  The copy queue will
        //         have been emptied, so only del/ren functions will be
        //         performed.
        //
        if ((SetupScanFileQueue( FileQueue,
                                 SPQ_SCAN_FILE_VALIDITY | SPQ_SCAN_INFORM_USER,
                                 hwnd,
                                 NULL,
                                 NULL,
                                 &d )) && (d != 1))
        {
            //
            //  Copy the files in the queue.
            //
            if (!SetupCommitFileQueue( hwnd,
                                       FileQueue,
                                       SetupDefaultQueueCallback,
                                       QueueContext ))
            {
                //
                //  This can happen if the user hits Cancel from within
                //  the setup dialog.
                //
                ErrorMsg(hwnd, IDS_ML_SETUPFAILED);
                bRet = FALSE;
                goto Locale_SetupError;
            }
        }

        //
        //  Execute all of the other entries in the inf file.
        //
        //  Currently, there are no other entries within the inf file
        //  other than the CopyFiles entry.  Therefore, this loop does
        //  nothing at the moment.
        //
        for (i = 0; i < nLocales; i++)
        {
            pLangNode = (LPLANGNODE)ListBox_GetItemData(hwndList, i);
            if ((pLangNode->wStatus & LANG_CHANGED) && 
                (pLangNode->wStatus & LANG_ACTIVE))
            {
                //
                //  Get the layout name.
                //
                wsprintf( szSection,
                          TEXT("%ws%8.8lx"),
                          szPrefixCopy,
                          lpLayout[pLangNode->iLayout].dwID );

                //
                //  Call setup to copy the keyboard layout file.
                //
                if (!SetupInstallFromInfSection( hwnd,
                                                 hKbdInf,
                                                 szSection,
                                                 SPINST_ALL & ~SPINST_FILES,
                                                 NULL,
                                                 NULL,
                                                 0,
                                                 NULL,
                                                 NULL,
                                                 NULL,
                                                 NULL ))
                {
                    //
                    //  Setup failed.
                    //
                    //  Already copied the keyboard layout file, so no
                    //  need to change the status of the keyboard info here.
                    //
                    //  This shouldn't happen - the inf file is messed up.
                    //
                    ErrorMsg(hwnd, IDS_ML_SETUPFAILED);
                }
            }
        }

Locale_SetupError:
        //
        //  Terminate the Queue.
        //
        SetupTermDefaultQueueCallback(QueueContext);

        //
        //  Close the file queue.
        //
        SetupCloseFileQueue(FileQueue);

        //
        //  Close the Inf file.
        //
        SetupCloseInfFile(hKbdInf);
    }

    //
    //  Return success.
    //
    return (bRet);
}


////////////////////////////////////////////////////////////////////////////
//
//  Locale_ApplyInputs
//
//  1. make sure we have all the layout files required.
//  2. write the information into the registry
//  3. call Load/UnloadKeyboardLayout where relevant
//
//  Note that this will trash the previous preload and substitutes sections,
//  based on what is actually loaded.  Thus if something was wrong before in
//  the registry, it will be corrected now.
//
////////////////////////////////////////////////////////////////////////////

BOOL Locale_ApplyInputs(
    HWND hwnd)
{
    HKL *pLangs = NULL;
    UINT nLangs;
    UINT idx;
    LPLANGNODE pLangNode, pTemp;
    LPINPUTLANG pInpLang;
    UINT nLocales;
    UINT i, j;
    UINT iPreload = 0;
    UINT iVal;
    DWORD dwID;
    TCHAR sz[DESC_MAX];            // temp - build the name of the reg entry
    TCHAR szPreload10[10];
    TCHAR szTemp[MAX_PATH];
    HWND hwndIndicate;
    HKEY hkeyLayouts;
    HKEY hkeySubst;
    HKEY hkeyPreload;
    HKEY hkeyToggle;
    HWND hwndList = GetDlgItem(hwnd, IDC_KBDL_LOCALE_LIST);
    HKL hklDefault = 0;
    HKL hklLoad, hklUnload;
    HCURSOR hcurSave;
#ifdef DBCS
    HKEY hkeyScanCode;
    DWORD cb;
    TCHAR szShiftL[8];
    TCHAR szShiftR[8];
#endif

    //
    //  See if the pane is disabled.  If so, then there is nothing to
    //  Apply.
    //
    if (!IsWindowEnabled(hwndList))
    {
        return (TRUE);
    }

    //
    //  First make sure we are left with a layout.
    //
    //  This actually shouldn't happen, since the "Remove" button is
    //  disabled when there is only one input locale left in the list.
    //
    nLocales = ListBox_GetCount(hwndList);
    if (nLocales < 1)
    {
        ErrorMsg(hwnd, IDS_ML_NEEDLAYOUT);
        return (FALSE);
    }

    //
    //  Put up the hour glass.
    //
    hcurSave = SetCursor(LoadCursor(NULL, IDC_WAIT));

    //
    //  Make sure there are actually changes since the last save when
    //  OK is selected.  If the user hits OK without anything to Apply,
    //  then we should do nothing.
    //
    if (!bSwitchChange && !bDefaultChange)
    {
        pLangNode = NULL;
        for (idx = 0; idx < iLangBuff; idx++)
        {
            pLangNode = lpLang[idx].pNext;
            while (pLangNode != NULL)
            {
                if (pLangNode->wStatus & (LANG_CHANGED | LANG_DEF_CHANGE))
                {
                    break;
                }
                pLangNode = pLangNode->pNext;
            }
            if (pLangNode != NULL)
            {
                break;
            }
        }
        if ((idx == iLangBuff) && (pLangNode == NULL))
        {
            SetCursor(hcurSave);
            PropSheet_UnChanged(GetParent(hwnd), hwnd);
            return (TRUE);
        }
    }

    //
    //  Queue up the new layouts and copy the appropriate files to
    //  disk using the setup apis.  Only do this if the user has
    //  Admin privileges.
    //
    if (g_bAdmin_Privileges &&
        !Locale_SetupKeyboardLayouts(hwnd, hwndList, nLocales))
    {
        SetCursor(hcurSave);
        return (FALSE);
    }

    //
    //  Clean up the registry.
    //

#ifdef DBCS
    //
    //  In the DBCS world, there is a keyboard which has a different
    //  scan code for shift keys - eg. NEC PC9801.
    //  We have to keep information about scan codes for shift keys in
    //  the registry under the 'toggle' sub key as named values.
    //
    szShiftL[0] = TEXT('\0');
    szShiftR[0] = TEXT('\0');
    if (RegOpenKey( HKEY_CURRENT_USER,
                    szScanCodeKey,
                    &hkeyScanCode ) == ERROR_SUCCESS)
    {
        cb = sizeof(szShiftL);
        RegQueryValueEx( hkeyScanCode,
                         szValueShiftLeft,
                         NULL,
                         NULL,
                         (LPBYTE)szShiftL,
                         &cb );

        cb = sizeof(szShiftR);
        RegQueryValueEx( hkeyScanCode,
                         szValueShiftRight,
                         NULL,
                         NULL,
                         (LPBYTE)szShiftR,
                         &cb );

        RegCloseKey(hkeyScanCode);
    }
#endif

    //
    //  Delete the HKCU\Keyboard Layout key and all subkeys.
    //
    if (RegOpenKeyEx( HKEY_CURRENT_USER,
                      szKbdLayouts,
                      0,
                      KEY_ALL_ACCESS,
                      &hkeyLayouts ) == ERROR_SUCCESS)
    {
        //
        //  Delete the HKCU\Keyboard Layout\Preload, Substitutes, and Toggle
        //  keys in the registry so that the Keyboard Layout section can be
        //  rebuilt.
        //
        RegDeleteKey(hkeyLayouts, szPreloadKey);
        RegDeleteKey(hkeyLayouts, szSubstKey);
        RegDeleteKey(hkeyLayouts, szToggleKey);

        RegCloseKey(hkeyLayouts);

        RegDeleteKey(HKEY_CURRENT_USER, szKbdLayouts);
    }

    //
    //  Create the HKCU\Keyboard Layout key.
    //
    if (RegCreateKey( HKEY_CURRENT_USER,
                      szKbdLayouts,
                      &hkeyLayouts ) == ERROR_SUCCESS)
    {
        //
        //  Create the HKCU\Keyboard Layout\Substitutes key.
        //
        if (RegCreateKey( hkeyLayouts,
                          szSubstKey,
                          &hkeySubst ) == ERROR_SUCCESS)
        {
            //
            //  Create the HKCU\Keyboard Layout\Preload key.
            //
            if (RegCreateKey( hkeyLayouts,
                              szPreloadKey,
                              &hkeyPreload ) == ERROR_SUCCESS)
            {
                //
                //  Initialize the iPreload variable to 1 to show
                //  that the key has been created.
                //
                iPreload = 1;

                //
                //  Create the HKCU\Keyboard Layout\Toggle key.
                //
                RegCreateKey(hkeyLayouts, szToggleKey, &hkeyToggle);
            }
            else
            {
                RegCloseKey(hkeySubst);
            }
        }

        RegCloseKey(hkeyLayouts);
    }
    if (!iPreload)
    {
        //
        //  Registry keys could not be created.  Now what?
        //
        MessageBeep(MB_OK);
        SetCursor(hcurSave);
        return (FALSE);
    }

    //
    //  Get the list of the currently active keyboard layouts from
    //  the system.
    //
    nLangs = GetKeyboardLayoutList(0, NULL);
    if (nLangs != 0)
    {
        pLangs = (HKL *)LocalAlloc(LPTR, sizeof(DWORD) * nLangs);
        GetKeyboardLayoutList(nLangs, (HKL *)pLangs);
    }

    //
    //  Set all usage counts to zero in the language array.
    //
    for (idx = 0; idx < iLangBuff; idx++)
    {
        lpLang[idx].iUseCount = 0;
    }

    //
    //  The order in the registry is based on the order in which they
    //  appear in the list box.
    //
    //  The only exception to this is that the default will be number 1.
    //
    //  If no default is found, the last one in the list will be used as
    //  the default.
    //
    iVal = 2;
    for (i = 0; i < nLocales; i++)
    {
        //
        //  Get the pointer to the lang node from the list box
        //  item data.
        //
        pLangNode = (LPLANGNODE)ListBox_GetItemData(hwndList, i);
        pInpLang = &(lpLang[pLangNode->iLang]);

        //
        //  See if it's the default input locale.
        //
        if (pLangNode->wStatus & LANG_DEFAULT)
        {
            //
            //  Default input locale, so the preload value should be
            //  set to 1.
            //
            iPreload = 1;
        }
        else if (i == (nLocales - 1))
        {
            //
            //  We're on the last one.  Make sure there was a default.
            //
            iPreload = (iVal <= nLocales) ? iVal : 1;
        }
        else
        {
            //
            //  Set the preload value to the next value.
            //
            iPreload = iVal;
            iVal++;
        }

        //
        //  Store the preload value as a string so that it can be written
        //  into the registry (as a value name).
        //
        wsprintf(sz, TEXT("%d"), iPreload);

        //
        //  Store the locale id as a string so that it can be written
        //  into the registry (as a value).
        //
#ifdef DBCS
        if (pLangNode->wStatus & LANG_IME)
        {
            wsprintf( szPreload10,
                      TEXT("%8.8lx"),
                      lpLayout[pLangNode->iLayout].dwID );
        }
        else
#endif
        {
        dwID = pInpLang->dwID;
        idx = pInpLang->iUseCount;
        if ((idx == 0) || (idx > 0xfff))
        {
            idx = 0;
            wsprintf(szPreload10, TEXT("%8.8x"), dwID);
        }
        else
        {
            dwID |= ((DWORD)(0xd000 | ((WORD)(idx - 1))) << 16);
            wsprintf(szPreload10, TEXT("%8.8x"), dwID);
        }
        (pInpLang->iUseCount)++;
        }

        //
        //  Set the new entry in the registry.  It is of the form:
        //
        //  HKCU\Keyboard Layout
        //      Preload:    1 = <locale id>
        //                  2 = <locale id>
        //                      etc...
        //
        RegSetValueEx( hkeyPreload,
                       sz,
                       0,
                       REG_SZ,
                       (LPBYTE)szPreload10,
                       (DWORD)(lstrlen(szPreload10) + 1) * sizeof(TCHAR) );

        //
        //  See if we need to add a substitute for this input locale.
        //
        if ((pInpLang->dwID != lpLayout[pLangNode->iLayout].dwID) || idx)
        {
            //
            //  Get the layout id as a string so that it can be written
            //  into the registry (as a value).
            //
            wsprintf( szTemp,
                      TEXT("%8.8lx"),
                      lpLayout[pLangNode->iLayout].dwID );

#ifdef DBCS
            if (!(pLangNode->wStatus & LANG_IME))
#endif
            //
            //  Set the new entry in the registry.  It is of the form:
            //
            //  HKCU\Keyboard Layout
            //      Substitutes:    <locale id> = <layout id>
            //                      <locale id> = <layout id>
            //                          etc...
            //
            RegSetValueEx( hkeySubst,
                           szPreload10,
                           0,
                           REG_SZ,
                           (LPBYTE)szTemp,
                           (DWORD)(lstrlen(szTemp) + 1) * sizeof(TCHAR) );
        }

        //
        //  Make sure all of the changes are written to disk.
        //
        RegFlushKey(hkeySubst);
        RegFlushKey(hkeyPreload);
        RegFlushKey(HKEY_CURRENT_USER);

        //
        //  See if the keyboard layout needs to be loaded.
        //
        if (pLangNode->wStatus & (LANG_CHANGED | LANG_DEF_CHANGE))
        {
            //
            //  Load the keyboard layout into the system.
            //
            if (pLangNode->hklUnload)
            {
                hklLoad = LoadKeyboardLayoutEx( pLangNode->hklUnload,
                                                szPreload10,
                                                KLF_SUBSTITUTE_OK |
                                                  KLF_NOTELLSHELL );
            }
            else
            {
                hklLoad = LoadKeyboardLayout( szPreload10,
                                              KLF_SUBSTITUTE_OK |
                                                KLF_NOTELLSHELL );
            }
            if (hklLoad)
            {
                pLangNode->wStatus &= ~(LANG_CHANGED | LANG_DEF_CHANGE);
                pLangNode->wStatus |= (LANG_ACTIVE | LANG_ORIGACTIVE);

                if (pLangNode->wStatus & LANG_DEFAULT)
                {
                    hklDefault = hklLoad;
                }

                pLangNode->hkl = hklLoad;
                pLangNode->hklUnload = hklLoad;
            }
            else
            {
                ApplyError(hwnd, pLangNode, IDS_ML_LOADKBDFAILED, MB_OK_OOPS);
            }
        }
    }

    //
    //  Close the handles to the registry keys.
    //
    RegCloseKey(hkeySubst);
    RegCloseKey(hkeyPreload);

    //
    //  Make sure the default is set properly.  The layout id for the
    //  current default input locale may have been changed.
    //
    //  NOTE: This should be done before the Unloads occur in case one
    //        of the layouts to unload is the old default layout.
    //
    if (hklDefault != 0)
    {
        if (!SystemParametersInfo( SPI_SETDEFAULTINPUTLANG,
                                   0,
                                   (LPVOID)((LPDWORD)&hklDefault),
                                   0 ))
        {
            //
            //  Failure is not fatal.  The old default language will
            //  still work.
            //
            ErrorMsg(hwnd, IDS_ML_NODEFLANG2);
        }
        else
        {
            //
            //  Activate the new default keyboard layout.
            //
//          ActivateKeyboardLayout(hklDefault, KLF_REORDER);
        }
    }

    //
    //  Search through the list to see if any keyboard layouts need to be
    //  unloaded from the system.
    //
    for (idx = 0; idx < iLangBuff; idx++)
    {
        pLangNode = lpLang[idx].pNext;
        while (pLangNode != NULL)
        {
            if ( (pLangNode->wStatus & LANG_ORIGACTIVE) &&
                 !(pLangNode->wStatus & LANG_ACTIVE) )
            {
                //
                //  Started off with this active, deleting it now.
                //  Failure is not fatal.
                //
                if (!UnloadKeyboardLayout(pLangNode->hkl))
                {
                    ApplyError( hwnd,
                                pLangNode,
                                IDS_ML_UNLOADKBDFAILED,
                                MB_OK_OOPS );

                    //
                    //  Failed to unload layout, put it back in the list,
                    //  and turn ON the indicator whether it needs it or not.
                    //
                    if (AddLanguage(hwnd, pLangNode))
                    {
                        CheckDlgButton(hwnd, IDC_KBDL_INDICATOR, TRUE);
                    }

                    pLangNode = pLangNode->pNext;
                }
                else
                {
                    //
                    //  Succeeded, no longer in USER's list.
                    //
                    //  Reset flag, this could be from ApplyInput and we'll
                    //  fail on the OK if we leave it marked as original
                    //  active.
                    //
                    pLangNode->wStatus &= ~(LANG_ORIGACTIVE | LANG_CHANGED);

                    //
                    //  Remove the link in the language array.
                    //
                    //  NOTE: pLangNode could be null here.
                    //
                    pTemp = pLangNode->pNext;
                    Locale_RemoveFromLinkedList(pLangNode);
                    pLangNode = pTemp;
                }
            }
            else
            {
                pLangNode = pLangNode->pNext;
            }
        }
    }

    //
    //  Handle the task bar indicator option.
    //
    hwndIndicate = FindWindow(szIndicator, NULL);

    if (RegCreateKey( HKEY_CURRENT_USER,
                      REGSTR_PATH_RUN,
                      &hkeySubst ) != ERROR_SUCCESS)
    {
        ErrorMsg(hwnd, IDS_ML_LOADLINEBAD);
        hkeySubst = NULL;
    }

    //
    //  See if the task bar indicator check box is set.
    //
    if (IsDlgButtonChecked(hwnd, IDC_KBDL_INDICATOR))
    {
        //
        //  User wants the indicator.
        //
        //  See if the indicator is already enabled.
        //
        if (hwndIndicate && IsWindow(hwndIndicate))
        {
            SendMessage(hwndIndicate, WM_COMMAND, IDM_NEWSHELL, 0L);
        }
        else
        {
            WinExec(szInternatA, SW_SHOWMINNOACTIVE);
        }

        if (hkeySubst)
        {
            RegSetValueEx( hkeySubst,
                           szInternat,
                           0,
                           REG_SZ,
                           (LPBYTE)szInternat,
                           sizeof(szInternat) );
        }
    }
    else
    {
        //
        //  Either the user doesn't want the indicator or there are less
        //  than two input locales.
        //
        if (hwndIndicate && IsWindow(hwndIndicate))
        {
            //
            //  It's on, turn it off again.
            //
            SendMessage(hwndIndicate, WM_COMMAND, IDM_EXIT, 0L);
        }
        if (hkeySubst)
        {
            //
            //  Clean up the registry.
            //
            RegDeleteValue(hkeySubst, szInternat);
        }
    }
    if (hkeySubst)
    {
        RegCloseKey(hkeySubst);
    }

    //
    //  See which of the toggle hotkeys is set.
    //
    idx = 1;
    if (IsDlgButtonChecked(hwnd, IDC_KBDL_CTRL_SHIFT))
    {
        idx = 2;
    }
    else if (IsDlgButtonChecked(hwnd, IDC_KBDL_NO_SHIFT))
    {
        idx = 3;
    }

    //
    //  Get the toggle hotkey as a string so that it can be written
    //  into the registry (as data).
    //
    wsprintf(szTemp, TEXT("%d"), idx);

    //
    //  Set the new entry in the registry.  It is of the form:
    //
    //  HKCU\Keyboard Layout
    //      Toggle:    Hotkey = <hotkey number>
    //
    if (hkeyToggle)
    {
        RegSetValueEx( hkeyToggle,
                       TEXT("Hotkey"),
                       0,
                       REG_SZ,
                       (LPBYTE)szTemp,
                       (DWORD)(lstrlen(szTemp) + 1) * sizeof(TCHAR) );
        RegCloseKey(hkeyToggle);
    }

#ifdef DBCS
    //
    //  Set the scan code entries in the registry.
    //
    if (RegCreateKey( HKEY_CURRENT_USER,
                      szScanCodeKey,
                      &hkeyScanCode ) == ERROR_SUCCESS)
    {
        if (szShiftL[0])
        {
            RegSetValueEx( hkeyScanCode,
                           szValueShiftLeft,
                           0,
                           REG_SZ,
                           (LPBYTE)szShiftL,
                           (DWORD)(lstrlen(szShiftL) + 1) * sizeof(TCHAR) );
        }

        if (szShiftR[0])
        {
            RegSetValueEx( hkeyScanCode,
                           szValueShiftRight,
                           0,
                           REG_SZ,
                           (LPBYTE)szShiftR,
                           (DWORD)(lstrlen(szShiftR) + 1) * sizeof(TCHAR) );
        }

        RegCloseKey(hkeyScanCode);
    }
#endif

    //
    //  Call SystemParametersInfo to enable the toggle.
    //
    SystemParametersInfo(SPI_SETLANGTOGGLE, 0, NULL, 0);

    //
    //  Turn off the hourglass.
    //
    SetCursor(hcurSave);

    //
    //  Free any allocated memory.
    //
    if (pLangs != NULL)
    {
        LocalFree((HANDLE)pLangs);
    }

    //
    //  Return success.
    //
    bSwitchChange = FALSE;
    bDefaultChange = FALSE;
    PropSheet_UnChanged(GetParent(hwnd), hwnd);
    return (TRUE);
}


////////////////////////////////////////////////////////////////////////////
//
//  EnablePane
//
//  The controls in "iControl" are the controls that get disabled if the
//  pane can't come up.
//
////////////////////////////////////////////////////////////////////////////

static UINT iControls[] =
{
    IDC_KBDL_LOCALE,         IDC_KBDL_LAYOUT_TEXT,   IDC_KBDL_LOCALE_LIST,
    IDC_KBDL_ADD,            IDC_KBDL_EDIT,          IDC_KBDL_DELETE,
    IDC_KBDL_DEFAULT,        IDC_KBDL_INPUT_FRAME,   IDC_KBDL_SET_DEFAULT,
    IDC_KBDL_SHORTCUT_FRAME, IDC_KBDL_ALT_SHIFT,     IDC_KBDL_CTRL_SHIFT,
    IDC_KBDL_NO_SHIFT,       IDC_KBDL_DEFAULT_LABEL, IDC_KBDL_INDICATOR,
    IDC_KBDL_ONSCRNKBD,      IDC_KBDL_DISABLED,      IDC_KBDL_DISABLED_2
};
#define NCONTROLS sizeof(iControls) / sizeof(UINT)


void EnablePane(
    HWND hwnd,
    BOOL bEnable,
    UINT DisableId)
{
    HWND hwndItem;
    int i;

    if (bEnable)
    {
        //
        //  Enable all of the controls except for the "pane disabled"
        //  strings.
        //
        for (i = 0; i < NCONTROLS; i++)
        {
            hwndItem = GetDlgItem(hwnd, iControls[i]);
            ShowWindow(hwndItem, SW_SHOW);
            EnableWindow(hwndItem, TRUE);
        }

        //
        //  Disable the "pane disabled" strings.
        //
        EnableWindow(GetDlgItem(hwnd, IDC_KBDL_DISABLED), FALSE);
        ShowWindow(GetDlgItem(hwnd, IDC_KBDL_DISABLED), SW_HIDE);
        EnableWindow(GetDlgItem(hwnd, IDC_KBDL_DISABLED_2), FALSE);
        ShowWindow(GetDlgItem(hwnd, IDC_KBDL_DISABLED_2), SW_HIDE);
    }
    else
    {
        //
        //  Disable all of the controls except for the "pane disabled"
        //  string.
        //
        for (i = 0; i < NCONTROLS; i++)
        {
            hwndItem = GetDlgItem(hwnd, iControls[i]);
            EnableWindow(hwndItem, FALSE);
            ShowWindow(hwndItem, SW_HIDE);
        }

        hwndItem = GetDlgItem(hwnd, DisableId);
        ShowWindow(hwndItem, SW_SHOW);
        EnableWindow(hwndItem, TRUE);
    }
}


////////////////////////////////////////////////////////////////////////////
//
//  FileExists
//
//  Determines if the file exists and is accessible.
//
////////////////////////////////////////////////////////////////////////////

BOOL FileExists(
    LPTSTR pFileName)
{
    WIN32_FIND_DATA FindData;
    HANDLE FindHandle;
    BOOL bRet;
    UINT OldMode;

    OldMode = SetErrorMode(SEM_FAILCRITICALERRORS);

    FindHandle = FindFirstFile(pFileName, &FindData);
    if (FindHandle == INVALID_HANDLE_VALUE)
    {
        bRet = FALSE;
    }
    else
    {
        FindClose(FindHandle);
        bRet = TRUE;
    }

    SetErrorMode(OldMode);

    return (bRet);
}


////////////////////////////////////////////////////////////////////////////
//
//  Locale_LoadLayouts
//
//  Loads the layouts from the registry.
//
////////////////////////////////////////////////////////////////////////////

BOOL Locale_LoadLayouts(
    HWND hwnd)
{
    HKEY hKey;
    HKEY hkey1;
    DWORD cb;
    DWORD dwIndex;
    LONG dwRetVal;
    DWORD dwValue;
    DWORD dwType;
    TCHAR szValue[MAX_PATH];           // language id (number)
    TCHAR szData[MAX_PATH];            // language name
    TCHAR szSystemDir[MAX_PATH * 2];
    UINT SysDirLen;


    //
    //  Now read all of the layouts from the registry.
    //
    if (RegOpenKey(HKEY_LOCAL_MACHINE, szLayoutPath, &hKey) != ERROR_SUCCESS)
    {
        EnablePane(hwnd, FALSE, IDC_KBDL_DISABLED);
        return (FALSE);
    }

    dwIndex = 0;
    dwRetVal = RegEnumKey( hKey,
                           dwIndex,
                           szValue,
                           sizeof(szValue) / sizeof(TCHAR) );

    if (dwRetVal != ERROR_SUCCESS)
    {
        EnablePane(hwnd, FALSE, IDC_KBDL_DISABLED);
        RegCloseKey(hKey);
        return (FALSE);
    }

    hLayout = GlobalAlloc(GHND, ALLOCBLOCK * sizeof(LAYOUT));
    nLayoutBuffSize = ALLOCBLOCK;
    iLayoutBuff = 0;
    lpLayout = GlobalLock(hLayout);

    if (!hLayout)
    {
        EnablePane(hwnd, FALSE, IDC_KBDL_DISABLED);
        RegCloseKey(hKey);
        return (FALSE);
    }

    //
    //  Save the system directory string.
    //
    szSystemDir[0] = 0;
    if (SysDirLen = GetSystemDirectory(szSystemDir, MAX_PATH))
    {
        if (SysDirLen > MAX_PATH)
        {
            SysDirLen = 0;
            szSystemDir[0] = 0;
        }
        else if (szSystemDir[SysDirLen - 1] != TEXT('\\'))
        {
            szSystemDir[SysDirLen] = TEXT('\\');
            szSystemDir[SysDirLen + 1] = 0;
            SysDirLen++;
        }
    }

    do
    {
        //
        //  New language - get the language name, the language
        //  description, and the language id.
        //
        if (iLayoutBuff + 1 == nLayoutBuffSize)
        {
            HANDLE hTemp;

            GlobalUnlock(hLayout);

            nLayoutBuffSize += ALLOCBLOCK;
            hTemp = GlobalReAlloc( hLayout,
                                   nLayoutBuffSize * sizeof(LAYOUT),
                                   GHND );
            if (hTemp == NULL)
            {
                break;
            }

            hLayout = hTemp;
            lpLayout = GlobalLock(hLayout);
        }

        lpLayout[iLayoutBuff].dwID = TransNum(szValue);

        lstrcpy(szData, szLayoutPath);
        lstrcat(szData, TEXT("\\"));
        lstrcat(szData, szValue);

        if (RegOpenKey(HKEY_LOCAL_MACHINE, szData, &hkey1) == ERROR_SUCCESS)
        {
            szValue[0] = TEXT('\0');
            cb = sizeof(szValue);
            if ((RegQueryValueEx( hkey1,
                                  szLayoutFile,
                                  NULL,
                                  NULL,
                                  (LPBYTE)szValue,
                                  &cb ) == ERROR_SUCCESS) &&
                (cb > sizeof(TCHAR)))
            {
                //
                //  Grab the layout, this one is a language only.
                //  NULL terminated string gives cb == sizeof(TCHAR), so
                //  we need to have the check.
                //
                lpLayout[iLayoutBuff].atmLayoutFile = AddAtom(szValue);

                //
                //  See if the layout file exists already.
                //
                lpLayout[iLayoutBuff].bInstalled = FALSE;
                lstrcpy(szSystemDir + SysDirLen, szValue);
                if (FileExists(szSystemDir))
                {
                    lpLayout[iLayoutBuff].bInstalled = TRUE;
                }

                szValue[0] = TEXT('\0');
                cb = sizeof(szValue);
                lpLayout[iLayoutBuff].iSpecialID = 0;
                if (RegQueryValueEx( hkey1,
                                     szLayoutText,
                                     NULL,
                                     NULL,
                                     (LPBYTE)szValue,
                                     &cb ) == ERROR_SUCCESS)
                {
                    lpLayout[iLayoutBuff].atmLayoutText = AddAtom(szValue);

                    szValue[0] = TEXT('\0');
                    cb = sizeof(szValue);
#ifdef DBCS
                    if ((HIWORD(lpLayout[iLayoutBuff].dwID) & 0xf000) == 0xe000)
                    {
                        if (RegQueryValueEx( hkey1,
                                             szIMEFile,
                                             NULL,
                                             NULL,
                                             (LPBYTE)szValue,
                                             &cb ) == ERROR_SUCCESS)
                        {
                            lpLayout[iLayoutBuff].atmIMEFile = AddAtom(szValue);
                            szValue[0] = TEXT('\0');
                            cb = sizeof(szValue);
                            iLayoutBuff++;
                        }
                    }
                    else
#endif
                    {
                    if (RegQueryValueEx( hkey1,
                                         szLayoutID,
                                         NULL,
                                         NULL,
                                         (LPBYTE)szValue,
                                         &cb ) == ERROR_SUCCESS)
                    {
                        //
                        //  This may not exist!
                        //
                        lpLayout[iLayoutBuff].iSpecialID =
                            (UINT)TransNum(szValue);
                    }
                    iLayoutBuff++;
                    }
                }
            }
            RegCloseKey(hkey1);
        }

        dwIndex++;
        szValue[0] = TEXT('\0');
        dwRetVal = RegEnumKey( hKey,
                               dwIndex,
                               szValue,
                               sizeof(szValue) / sizeof(TCHAR) );

    } while (dwRetVal == ERROR_SUCCESS);

    RegCloseKey(hKey);

    return (iLayoutBuff);
}


////////////////////////////////////////////////////////////////////////////
//
//  Locale_LoadLocales
//
//  Loads the locales from the registry.
//
////////////////////////////////////////////////////////////////////////////

BOOL Locale_LoadLocales(
    HWND hwnd)
{
    HKEY hKey;
    DWORD cch;
    DWORD dwIndex;
    LONG dwRetVal;
#ifdef DBCS
    UINT i, j = 0;
#endif

    TCHAR szValue[MAX_PATH];           // language id (number)
    TCHAR szData[MAX_PATH];            // language name

    if (!(hLang = GlobalAlloc(GHND, ALLOCBLOCK * sizeof(INPUTLANG))))
    {
        EnablePane(hwnd, FALSE, IDC_KBDL_DISABLED);
        return (FALSE);
    }

    nLangBuffSize = ALLOCBLOCK;
    iLangBuff = 0;
    lpLang = GlobalLock(hLang);

    //
    //  Now read all of the locales from the registry.
    //
    if (RegOpenKey(HKEY_LOCAL_MACHINE, szLocaleInfo, &hKey) != ERROR_SUCCESS)
    {
        EnablePane(hwnd, FALSE, IDC_KBDL_DISABLED);
        return (FALSE);
    }

    dwIndex = 0;
    cch = sizeof(szValue) / sizeof(TCHAR);
    dwRetVal = RegEnumValue( hKey,
                             dwIndex,
                             szValue,
                             &cch,
                             NULL,
                             NULL,
                             NULL,
                             NULL );

    if (dwRetVal != ERROR_SUCCESS)
    {
        EnablePane(hwnd, FALSE, IDC_KBDL_DISABLED);
        RegCloseKey(hKey);
        return (FALSE);
    }

    do
    {
        if ((cch > 1) && (cch < HKL_LEN))
        {
            //
            //  Check for cch > 1: an empty string will be enumerated,
            //  and will come back with cch == 1 for the null terminator.
            //
            //  New language - get the language name, the language
            //  description, and the language id.
            //
            if ((iLangBuff + 1) == nLangBuffSize)
            {
                HANDLE hTemp;

                GlobalUnlock(hLang);

                nLangBuffSize += ALLOCBLOCK;
                hTemp = GlobalReAlloc( hLang,
                                       nLangBuffSize * sizeof(INPUTLANG),
                                       GHND );
                if (hTemp == NULL)
                {
                    break;
                }

                hLang = hTemp;
                lpLang = GlobalLock(hLang);
            }

            lpLang[iLangBuff].dwID = TransNum(szValue);
            lpLang[iLangBuff].iUseCount = 0;
            lpLang[iLangBuff].iNumCount = 0;
            lpLang[iLangBuff].pNext = NULL;

            //
            //  Get the full localized name of the language.
            //
            if (GetLocaleInfo( LOWORD(lpLang[iLangBuff].dwID),
                               LOCALE_SLANGUAGE,
                               szData,
                               MAX_PATH ))
            {
                lpLang[iLangBuff].atmLanguageName = AddAtom(szData);

                iLangBuff++;
#ifdef DBCS
                //
                //  We have a valid language, so now get the layout data.
                //
                for (i = j; i < iLayoutBuff; i++)
                {
                    if (LOWORD(lpLayout[i].dwID) ==
                        LOWORD(lpLang[iLangBuff - 1].dwID) &&
                       (HIWORD(lpLayout[i].dwID) & 0xf000) == 0xe000)
                    {
                        lpLang[iLangBuff - 1].wStatus |= LANG_IME;
                        lpLang[iLangBuff - 1].hkl = (HKL)lpLayout[i].dwID;
                        lpLang[iLangBuff - 1].iLayout = i;

                        break;
                    }
                }
                for (j = i + 1; j < iLayoutBuff; j++)
                {
                    if ((HIWORD(lpLayout[j].dwID) & 0xf000) == 0xe000 &&
                        LOWORD(lpLayout[j].dwID) ==
                        LOWORD(lpLang[iLangBuff - 1].dwID))
                    {
                        break;
                    }
                }

                //
                //  See if there are more IME layouts for this language.
                //
                if (j < iLayoutBuff)
                {
                    continue;
                }
                else
                {
                    j = 0;
                }
#endif
            }
        }

        dwIndex++;
        cch = sizeof(szValue) / sizeof(TCHAR);
        szValue[0] = TEXT('\0');
        dwRetVal = RegEnumValue( hKey,
                                 dwIndex,
                                 szValue,
                                 &cch,
                                 NULL,
                                 NULL,
                                 NULL,
                                 NULL );

    } while (dwRetVal == ERROR_SUCCESS);

    RegCloseKey(hKey);
    return (TRUE);
}


////////////////////////////////////////////////////////////////////////////
//
//  Locale_GetActiveLocales
//
//  Gets the active locales.
//
////////////////////////////////////////////////////////////////////////////

BOOL Locale_GetActiveLocales(
    HWND hwnd)
{
    HKL *pLangs;
    UINT nLangs, i, j, k, id;
    HWND hwndList = GetDlgItem(hwnd, IDC_KBDL_LOCALE_LIST);
    HKL hklSystem;
    int idxListBox;
    DWORD langLay;
    HANDLE hLangNode;
    LPLANGNODE pLangNode;

    //
    //  Initialize US layout option.
    //
    iUsLayout = -1;

    //
    //  Get the active keyboard layout list from the system.
    //
    if (!SystemParametersInfo( SPI_GETDEFAULTINPUTLANG,
                               0,
                               (LPVOID)((LPDWORD)&hklSystem),
                               0 ))
    {
        hklSystem = GetKeyboardLayout(0);
    }

    nLangs = GetKeyboardLayoutList(0, NULL);
    if (nLangs == 0)
    {
        EnablePane(hwnd, FALSE, IDC_KBDL_DISABLED);
        return (FALSE);
    }
    pLangs = (HKL *)LocalAlloc(LPTR, sizeof(DWORD) * nLangs);
    GetKeyboardLayoutList(nLangs, (HKL *)pLangs);

#ifdef DBCS
    himIndicators = ImageList_Create( GetSystemMetrics(SM_CXSMICON),
                                      GetSystemMetrics(SM_CYSMICON),
                                      TRUE,
                                      0,
                                      0 );
#endif

    //
    //  Replace default with US (default).
    //
    for (i = 0; i < iLayoutBuff; i++)
    {
        if (lpLayout[i].dwID == US_LOCALE)
        {
            iUsLayout = i;
            break;
        }
    }
    if (i == iLayoutBuff)
    {
        EnablePane(hwnd, FALSE, IDC_KBDL_DISABLED);
        return (FALSE);
    }

    //
    //  Get the active keyboard information and put it in the internal
    //  language structure.
    //
    for (j = 0; j < nLangs; j++)
    {
        for (i = 0; i < iLangBuff; i++)
        {
            if (LOWORD(pLangs[j]) == LOWORD(lpLang[i].dwID))
            {
                //
                //  Found a match.
                //
#ifdef DBCS
                if (lpLang[i].wStatus & LANG_IME)
                {
                    if (lpLang[i].hkl == pLangs[j])
                    {
                        idxListBox = ListBox_AddItemData(hwndList, i);
                        lpLang[i].wStatus |= (LANG_ORIGACTIVE | LANG_ACTIVE);
                        FetchIndicator(pLangNode);
                    }
                    else
                    {
                       continue;
                    }
                }
                else
#endif
                {
                //
                //  Create a node for this language.
                //
                pLangNode = Locale_AddToLinkedList(i, pLangs[j]);
                if (!pLangNode)
                {
                    EnablePane(hwnd, FALSE, IDC_KBDL_DISABLED);
                    return (FALSE);
                }

                //
                //  Add the item data to the list box, mark the
                //  language as original and active, save the pointer
                //  to the match in the layout list, and get the
                //  2 letter indicator symbol.
                //
                idxListBox = ListBox_AddItemData(hwndList, pLangNode);
                pLangNode->wStatus |= (LANG_ORIGACTIVE | LANG_ACTIVE);
                pLangNode->hkl = pLangs[j];
                pLangNode->hklUnload = pLangs[j];
                FetchIndicator(pLangNode);

                //
                //  Match the language to the layout.
                //
                pLangNode->iLayout = 0;
                langLay = (DWORD)HIWORD(pLangs[j]);

                if ((HIWORD(pLangs[j]) == 0xffff) ||
                    (HIWORD(pLangs[j]) == 0xfffe))
                {
                    //
                    //  Mark default or previous error as US - this
                    //  means that the layout will be that of the basic
                    //  keyboard driver (the US one).
                    //
                    pLangNode->wStatus |= LANG_CHANGED;
                    pLangNode->iLayout = iUsLayout;
                    langLay = 0;
                }
                else if ((HIWORD(pLangs[j]) & 0xf000) == 0xf000)
                {
                    //
                    //  Layout is special, need to search for the ID
                    //  number.
                    //
                    id = HIWORD(pLangs[j]) & 0x0fff;
                    for (k = 0; k < iLayoutBuff; k++)
                    {
                        if (id == lpLayout[k].iSpecialID)
                        {
                            pLangNode->iLayout = k;
                            langLay = 0;
                            break;
                        }
                    }
                    if (langLay)
                    {
                        //
                        //  Didn't find the id, so reset to basic for
                        //  the language.
                        //
                        langLay = (DWORD)LOWORD(pLangs[j]);
                    }
                }

                if (langLay)
                {
                    //
                    //  Search for the id.
                    //
                    for (k = 0; k < iLayoutBuff; k++)
                    {
                        if (langLay == (DWORD)LOWORD(lpLayout[k].dwID))
                        {
                            pLangNode->iLayout = k;
                            break;
                        }
                    }

                    if (k == iLayoutBuff)
                    {
                        //
                        //  Something went wrong or didn't load from
                        //  the registry correctly.
                        //
                        MessageBeep(MB_ICONEXCLAMATION);
                        pLangNode->wStatus |= LANG_CHANGED;
                        pLangNode->iLayout = iUsLayout;
                    }
                }
                }

                //
                //  If this is the current language, then it's the default
                //  one.
                //
                if (pLangNode->hkl == hklSystem)
                {
                    TCHAR sz[DESC_MAX];
                    LPINPUTLANG pInpLang = &lpLang[i];

                    //
                    //  Found the default.  Set the Default input locale
                    //  text in the property sheet.
                    //
#ifdef DBCS
                    if (pLangNode->wStatus & LANG_IME)
                    {
                        GetAtomName( lpLayout[pLangNode->iLayout].atmLayoutText,
                                     sz,
                                     DESC_MAX );
                    }
                    else
#endif
                    GetAtomName(pInpLang->atmLanguageName, sz, DESC_MAX);
                    pLangNode->wStatus |= LANG_DEFAULT;

                    ListBox_SetCurSel( GetDlgItem(hwnd, IDC_KBDL_LOCALE_LIST),
                                       idxListBox );
                    SetDlgItemText(hwnd, IDC_KBDL_DEFAULT, sz);
                }

                //
                //  Break out of inner loop - we've found it.
                //
                break;
            }
        }
    }

    LocalFree((HANDLE)pLangs);
    return (TRUE);
}


////////////////////////////////////////////////////////////////////////////
//
//  Locale_InitPropSheet
//
//  Processing for a WM_INITDIALOG message for the Input Locales
//  property sheet.
//
////////////////////////////////////////////////////////////////////////////

void Locale_InitPropSheet(
    HWND hwnd)
{
    HKEY hKey;
    HANDLE hlib;
#ifdef DBCS
    HWND hwndList;
    LPLANGNODE pLangNode;
    WORD wLangID;
#endif

    //
    //  See if there are any other instances of this property page.
    //  If so, disable this page.
    //
    if (g_hMutex && (WaitForSingleObject(g_hMutex, 0) != WAIT_OBJECT_0))
    {
        EnablePane(hwnd, FALSE, IDC_KBDL_DISABLED_2);
        return;
    }
    else
    {
        EnablePane(hwnd, TRUE, 0);
    }

    //
    //  See if the user has Administrative privileges by checking for
    //  write permission to the registry key.
    //
    if (RegOpenKeyEx( HKEY_LOCAL_MACHINE,
                      szLocaleInfo,
                      0L,
                      KEY_WRITE,
                      &hKey ) == ERROR_SUCCESS)
    {
        //
        //  We can write to the HKEY_LOCAL_MACHINE key, so the user
        //  has Admin privileges.
        //
        g_bAdmin_Privileges = TRUE;
        RegCloseKey(hKey);
    }
    else
    {
        //
        //  The user does not have admin privileges.
        //
        g_bAdmin_Privileges = FALSE;
    }

    //
    //  Initialize all of the global variables.
    //
    if ((!Locale_LoadLayouts(hwnd)) ||
        (!Locale_LoadLocales(hwnd)) ||
        (!Locale_GetActiveLocales(hwnd)))
    {
        return;
    }

    cxIcon = GetSystemMetrics(SM_CXSMICON);
    cyIcon = GetSystemMetrics(SM_CYSMICON);

    GetKbdSwitchHotkey(hwnd);
    bSwitchChange = FALSE;
    bDefaultChange = FALSE;

    //
    //  See how many active keyboard layouts are in the input locale list.
    //
    if (ListBox_GetCount(GetDlgItem(hwnd, IDC_KBDL_LOCALE_LIST)) < 2)
    {
        //
        //  Only 1 active keyboard, so disable the secondary controls.
        //
        SetSecondaryControls(hwnd, FALSE);

#ifdef DBCS
        hwndList = GetDlgItem(hwnd, IDC_KBDL_LOCALE_LIST);
        pLangNode = (LPLANGNODE)ListBox_GetItemData(hwndList, 0);
        wLangID = LOWORD(pLangNode->hkl);
        if (wLangID = 0x0404 || wLangID == 0x0411 ||
            wLangID == 0x0412 || wLangID == 0x0804)
        {
            //
            //  Enable the indicator symbol check box and check it.
            //
            EnableWindow(GetDlgItem(hwnd, IDC_KBDL_INDICATOR), TRUE);
            CheckDlgButton( hwnd,
                            IDC_KBDL_INDICATOR,
                            FindWindow(szIndicator, NULL) != NULL );
        }
#endif
    }
    else
    {
        //
        //  Set the indicator symbol check box to the "checked" state
        //  if the check box is enabled.
        //
        CheckDlgButton( hwnd,
                        IDC_KBDL_INDICATOR,
                        FindWindow(szIndicator, NULL) != NULL );
    }
}


////////////////////////////////////////////////////////////////////////////
//
//  Locale_CommandConfigIME
//
//  Configures the IME.
//
////////////////////////////////////////////////////////////////////////////

#ifdef DBCS
void Locale_CommandConfigIME(
    HWND hwnd)
{
    LPLANGNODE pLangNode;
    int idxList;
    HWND hwndList = GetDlgItem(hwnd, IDC_KBDL_LOCALE_LIST);

    if ((idxList = ListBox_GetCurSel(hwndList)) == LB_ERR)
    {
        MessageBeep(MB_ICONEXCLAMATION);
        return;
    }

    pLangNode = (LPLANGNODE)ListBox_GetItemData(hwndList, idxList);
    if ((!(pLangNode->wStatus & LANG_IME)) ||
        (!(pLangNode->wStatus & LANG_ORIGACTIVE)))
    {
        MessageBeep(MB_ICONEXCLAMATION);
        return;
    }
    ImmConfigureIME(pLangNode->hkl, hwnd, IME_CONFIG_GENERAL, NULL);
}
#endif


////////////////////////////////////////////////////////////////////////////
//
//  Locale_DrawItem
//
//  Processing for a WM_DRAWITEM message.
//
////////////////////////////////////////////////////////////////////////////

BOOL Locale_DrawItem(
    HWND hwnd,
    LPDRAWITEMSTRUCT lpdi)
{
    switch (lpdi->CtlID)
    {
#ifdef ON_SCREEN_KEYBOARD
        case ( IDC_KBDL_UP ) :
        case ( IDC_KBDL_DOWN ) :
        {
            UINT wFlags;

            wFlags = ((lpdi->CtlID == IDC_KBDL_UP)
                         ? DFCS_SCROLLUP
                         : DFCS_SCROLLDOWN);

            if (lpdi->itemState & ODS_SELECTED)
            {
                wFlags |= DFCS_PUSHED;
            }
            else if (lpdi->itemState & ODS_DISABLED)
            {
                wFlags |= DFCS_INACTIVE;
            }

            DrawFrameControl(lpdi->hDC, &lpdi->rcItem, DFC_SCROLL, wFlags);
            break;
        }
#endif
        case ( IDC_KBDL_LOCALE_LIST ) :
        {
            LPLANGNODE pLangNode;
            LPINPUTLANG pInpLang;
            TCHAR sz[DESC_MAX];
            UINT len;
            DWORD rgbBk;
            DWORD rgbText;
            UINT oldAlign;
            RECT rc;

            if (ListBox_GetCount(lpdi->hwndItem) == 0)
            {
                break;
            }

            pLangNode = (LPLANGNODE)lpdi->itemData;
            pInpLang = &lpLang[pLangNode->iLang];
            rgbBk = SetBkColor( lpdi->hDC,
                                (lpdi->itemState & ODS_SELECTED)
                                    ? GetSysColor(COLOR_HIGHLIGHT)
                                    : GetSysColor(COLOR_WINDOW) );

            rgbText = SetTextColor( lpdi->hDC,
                                    (lpdi->itemState & ODS_SELECTED)
                                        ? GetSysColor(COLOR_HIGHLIGHTTEXT)
                                        : GetSysColor(COLOR_WINDOWTEXT) );

            len = GetAtomName(pInpLang->atmLanguageName, sz, DESC_MAX);

            ExtTextOut( lpdi->hDC,
                        lpdi->rcItem.left+cyIcon+ 3 * LIST_MARGIN + 2,
                        lpdi->rcItem.top + (cyListItem - cyText) / 2,
                        ETO_OPAQUE,
                        &lpdi->rcItem,
                        sz,
                        len,
                        NULL );

            oldAlign = GetTextAlign(lpdi->hDC);
            SetTextAlign(lpdi->hDC, TA_RIGHT | (oldAlign & ~TA_CENTER));

            len = GetAtomName( lpLayout[pLangNode->iLayout].atmLayoutText,
                               sz,
                               DESC_MAX );

            ExtTextOut( lpdi->hDC,
                        lpdi->rcItem.right -  LIST_MARGIN,
                        lpdi->rcItem.top + (cyListItem - cyText) / 2,
                        0,
                        NULL,
                        sz,
                        len,
                        NULL );

            SetTextAlign(lpdi->hDC, oldAlign);

            if (!(pLangNode->wStatus & ICON_LOADED))
            {
                FetchIndicator(pLangNode);
            }

#ifdef DBCS
            if ((himIndicators != NULL) &&
                (pLangNode->wStatus & LANG_IME) &&
                (pLangNode->niconIME != -1))
            {
                ImageList_Draw( himIndicators,
                                pLangNode->niconIME,
                                lpdi->hDC,
                                lpdi->rcItem.left + 3 * LIST_MARGIN,
                                lpdi->rcItem.top + LIST_MARGIN,
                                ILD_TRANSPARENT );
            }
            else
#endif
            {
            rgbBk = SetBkColor( lpdi->hDC,
                                (lpdi->itemState & ODS_SELECTED)
                                    ? GetSysColor(COLOR_WINDOW)
                                    : GetSysColor(COLOR_HIGHLIGHT) );

            rgbText = SetTextColor( lpdi->hDC,
                                    (lpdi->itemState & ODS_SELECTED)
                                        ? GetSysColor(COLOR_WINDOWTEXT)
                                        : GetSysColor(COLOR_HIGHLIGHTTEXT) );
            rc.left = lpdi->rcItem.left + 3 * LIST_MARGIN;
            rc.right = rc.left + cxIcon;
            rc.top = lpdi->rcItem.top + LIST_MARGIN;
            rc.bottom = rc.top + cyIcon;
            ExtTextOut( lpdi->hDC,
                        rc.left,
                        rc.top,
                        ETO_OPAQUE,
                        &rc,
                        TEXT(""),
                        0,
                        NULL );
            DrawText( lpdi->hDC,
                      pInpLang->szSymbol,
                      2,
                      &rc,
                      DT_CENTER | DT_VCENTER | DT_SINGLELINE );
            }

            SetBkColor(lpdi->hDC, rgbBk);
            SetTextColor(lpdi->hDC, rgbText);

            if (lpdi->itemState & ODS_FOCUS)
            {
                DrawFocusRect(lpdi->hDC, &lpdi->rcItem);
            }

            break;
        }
        default :
        {
            return (FALSE);
        }
    }
    return (TRUE);
}


////////////////////////////////////////////////////////////////////////////
//
//  Locale_KillPaneDialog
//
//  Processing for a WM_DESTROY message.
//
////////////////////////////////////////////////////////////////////////////

void Locale_KillPaneDialog(
    HWND hwnd)
{
    UINT i;
    HANDLE hCur;
    LPLANGNODE pCur;

    //
    //  Delete all Language Name atoms and free the lpLang array.
    //
    for (i = 0; i < iLangBuff; i++)
    {
        if (lpLang[i].atmLanguageName)
        {
            DeleteAtom(lpLang[i].atmLanguageName);
        }

        pCur = lpLang[i].pNext;
        lpLang[i].pNext = NULL;
        while (pCur)
        {
            hCur = pCur->hLangNode;
            pCur = pCur->pNext;
            GlobalUnlock(hCur);
            GlobalFree(hCur);
        }
    }
#ifdef DBCS
    if (himIndicators != NULL)
    {
        ImageList_Destroy(himIndicators);
    }
#endif
    GlobalUnlock(hLang);
    GlobalFree(hLang);

    //
    //  Delete all layout text and layout file atoms and free the
    //  lpLayout array.
    //
    for (i = 0; i < iLayoutBuff; i++)
    {
        if (lpLayout[i].atmLayoutText)
        {
            DeleteAtom(lpLayout[i].atmLayoutText);
        }

        if (lpLayout[i].atmLayoutFile)
        {
            DeleteAtom(lpLayout[i].atmLayoutFile);
        }
#ifdef DBCS
        if (lpLayout[i].atmIMEFile)
        {
            DeleteAtom(lpLayout[i].atmIMEFile);
        }
#endif
    }
    GlobalUnlock(hLayout);
    GlobalFree(hLayout);

    if (g_hMutex)
    {
        ReleaseMutex(g_hMutex);
    }
}


////////////////////////////////////////////////////////////////////////////
//
//  Locale_MeasureItem
//
//  Processing for a WM_MEASUREITEM message.
//
////////////////////////////////////////////////////////////////////////////

void Locale_MeasureItem(
    HWND hwnd,
    LPMEASUREITEMSTRUCT lpmi)
{
    HFONT hfont;
    HDC hdc;
    TEXTMETRIC tm;

    switch (lpmi->CtlID)
    {
        case ( IDC_KBDL_LOCALE_LIST ) :
        {
            hfont = (HFONT) SendMessage(hwnd, WM_GETFONT, 0, 0);
            hdc = GetDC(NULL);
            hfont = SelectObject(hdc, hfont);

            GetTextMetrics(hdc, &tm);
            SelectObject(hdc, hfont);
            ReleaseDC(NULL, hdc);

            cyText = tm.tmHeight;
            lpmi->itemHeight = cyListItem =
                MAX(cyText, GetSystemMetrics(SM_CYSMICON)) + 2 * LIST_MARGIN;

            break;
        }
    }
}


////////////////////////////////////////////////////////////////////////////
//
//  Locale_CommandAddEdit
//
//  Invokes either the Add dialog or the Properties dialog.
//
//  Returns 1 if a dialog box was invoked and the dialog returned IDOK.
//  Otherwise, it returns 0.
//
////////////////////////////////////////////////////////////////////////////

int Locale_CommandAddEdit(
    HWND hwnd,
    UINT iRes,
    LPLANGNODE pLangNode,
    FARPROC lpfnDlg)
{
    DLGPROC lpDialog;
    HWND hwndList;
    int idxList;
    UINT nList;
    int rc = 0;
    INITINFO InitInfo;

    //
    //  Initialize hwndList and pLangNode.
    //
    if (pLangNode == NULL)
    {
        hwndList = GetDlgItem(hwnd, IDC_KBDL_LOCALE_LIST);
        if (hwndList == NULL)
        {
            return (0);
        }
        idxList = ListBox_GetCurSel(hwndList);
        pLangNode = (LPLANGNODE)ListBox_GetItemData(hwndList, idxList);
    }
    else
    {
        hwndList = GetDlgItem(hwnd, IDC_KBDLA_LOCALE);
        if (hwndList == NULL)
        {
            return (0);
        }
        idxList = ListBox_GetCurSel(hwndList);
    }

    //
    //  Make sure we haven't added all possible combinations to the system.
    //
    if (iRes == DLG_KEYBOARD_LOCALE_ADD)
    {
        nList = ListBox_GetCount(hwndList);

        if (nList == (iLangBuff * iLayoutBuff))
        {
            //
            //  No languages left!
            //
            ErrorMsg(hwnd, IDS_ML_NOMORETOADD);
            return (rc);
        }
    }

    //
    //  Bring up the appropriate dialog box.
    //
    if ((pLangNode != (LPLANGNODE)LB_ERR) && (pLangNode != NULL))
    {
        lpDialog = (DLGPROC)MakeProcInstance(lpfnDlg, hInstance);

#ifdef DBCS
        if ((pLangNode->wStatus & LANG_IME) &&
            (iRes == DLG_KEYBOARD_LOCALE_EDIT))
        {
            Locale_CommandConfigIME(hwnd);
        }
        else
#endif
        {
        //
        //  Return value can be 1:IDOK, 2:IDCANCEL or -1:Error (from USER)
        //
        //  If adding a language, it goes at the end of the list, so get
        //  the end and make that the current selection.
        //
        InitInfo.hwndMain = hwnd;
        InitInfo.pLangNode = pLangNode;
        if ((rc = DialogBoxParam( hInstance,
                                  MAKEINTRESOURCE(iRes),
                                  hwnd,
                                  lpDialog,
                                  (LPARAM)(&InitInfo) )) == IDOK)
        {
            //
            //  See if it's the Add dialog box.
            //
            if (iRes == DLG_KEYBOARD_LOCALE_ADD)
            {
                //
                //  Get the number of items in the input locale list and
                //  enable the Properties and Remove push buttons.
                //
                nList = ListBox_GetCount(hwndList) - 1;
                EnableWindow(GetDlgItem(hwnd, IDC_KBDL_EDIT), TRUE);
                EnableWindow(GetDlgItem(hwnd, IDC_KBDL_DELETE), TRUE);

                //
                //  Set the current selection to be the last one in the
                //  list (the one that was just added).
                //
                ListBox_SetCurSel(hwndList, nList);

#ifdef DBCS
                pLangNode = (LPLANGNODE)ListBox_GetItemData(hwndList, nList);
                if (pLangNode->wStatus & LANG_IME)
                {
                    EnableWindow(GetDlgItem(hwnd, IDC_KBDL_EDIT), FALSE);
                }
#endif
            }
            else
            {
                //
                //  Reset the current selection to be the one that was
                //  previously set.
                //
                ListBox_SetCurSel(hwndList, idxList);
            }

            //
            //  Enable the Apply button.
            //
            PropSheet_Changed(GetParent(hwnd), hwnd);
        }
        else
        {
            //
            //  Failure, so need to return 0.
            //
            ListBox_SetCurSel(hwndList, idxList);
            rc = 0;
        }
        }

        FreeProcInstance((FARPROC)lpDialog);
    }
    else
    {
        MessageBeep(MB_ICONEXCLAMATION);
    }

    return (rc);
}


////////////////////////////////////////////////////////////////////////////
//
//  Locale_CommandLocaleList
//
//  Handles changes to the input locales list box in the property sheet.
//
////////////////////////////////////////////////////////////////////////////

void Locale_CommandLocaleList(
    HWND hwnd,
    WPARAM wParam,
    LPARAM lParam)
{
#ifdef DBCS
    HWND hwndList = (HWND)LOWORD(lParam);
    LPLANGNODE pLangNode;
    BOOL bOn;

    if (HIWORD(wParam) == LBN_SELCHANGE || HIWORD(wParam) == LBN_SETFOCUS)
    {
        pLangNode = (LPLANGNODE)ListBox_GetItemData( hwndList,
                                                     ListBox_GetCurSel(hwndList) );
        bOn = (pLangNode->wStatus & LANG_IME) &&
              (!(pLangNode->wStatus & LANG_ORIGACTIVE)) ? FALSE : TRUE;
        EnableWindow(GetDlgItem(hwnd, IDC_KBDL_EDIT), bOn);
    }
    else
#endif

    if (HIWORD(wParam) == LBN_DBLCLK)
    {
        //
        //  User double clicked on an input locale.  Invoke the Properties
        //  dialog.
        //
        Locale_CommandAddEdit( hwnd,
                               DLG_KEYBOARD_LOCALE_EDIT,
                               NULL,
                               (FARPROC)KbdLocaleEditDlg);
    }
}


////////////////////////////////////////////////////////////////////////////
//
//  Locale_CommandSetDefault
//
//  Sets the new default when the Set as Default button is pressed.
//
////////////////////////////////////////////////////////////////////////////

void Locale_CommandSetDefault(
    HWND hwnd)
{
    UINT idx;
    int idxList;
    LPLANGNODE pLangNode;
    HWND hwndList = GetDlgItem(hwnd, IDC_KBDL_LOCALE_LIST);
    TCHAR sz[DESC_MAX];

    if ((idxList = ListBox_GetCurSel(hwndList)) == LB_ERR)
    {
        MessageBeep(MB_ICONEXCLAMATION);
        return;
    }

    //
    //  Remove the LANG_DEFAULT flag from the current default.
    //
    pLangNode = NULL;
    for (idx = 0; idx < iLangBuff; idx++)
    {
        pLangNode = lpLang[idx].pNext;
        while (pLangNode != NULL)
        {
            if (pLangNode->wStatus & LANG_DEFAULT)
            {
                if (pLangNode ==
                     (LPLANGNODE)ListBox_GetItemData(hwndList, idxList))
                {
                    return;
                }
                pLangNode->wStatus &= ~(LANG_DEFAULT | LANG_DEF_CHANGE);
                break;
            }
            pLangNode = pLangNode->pNext;
        }
        if (pLangNode != NULL)
        {
            break;
        }
    }

    //
    //  Mark the current selection as the new default.
    //
    pLangNode = (LPLANGNODE)ListBox_GetItemData(hwndList, idxList);
    pLangNode->wStatus |= (LANG_DEFAULT | LANG_DEF_CHANGE);

    //
    //  Update the "Default input locale" text in the dialog.
    //
#ifdef  DBCS
    if ((pLangNode->wStatus & LANG_IME) != 0)
    {
        GetAtomName(lpLayout[pLangNode->iLayout].atmLayoutText, sz, DESC_MAX);
    }
    else
#endif
    GetAtomName(lpLang[pLangNode->iLang].atmLanguageName, sz, DESC_MAX);
    SetDlgItemText(hwnd, IDC_KBDL_DEFAULT, sz);

    //
    //  Enable the Apply button.
    //
    bDefaultChange = TRUE;
    PropSheet_Changed(GetParent(hwnd), hwnd);
}


////////////////////////////////////////////////////////////////////////////
//
//  Locale_CommandDelete
//
//  Removes the currently selected input locale from the list.
//
////////////////////////////////////////////////////////////////////////////

void Locale_CommandDelete(
    HWND hwnd)
{
    LPLANGNODE pLangNode;
    int idxList;
    HWND hwndList = GetDlgItem(hwnd, IDC_KBDL_LOCALE_LIST);
    int count;
#ifdef DBCS
    WORD wLangID;
#endif

    //
    //  Get the current selection in the input locale list.
    //
    if ((idxList = ListBox_GetCurSel(hwndList)) == LB_ERR)
    {
        MessageBeep(MB_ICONEXCLAMATION);
        return;
    }

    //
    //  Make sure we're not removing the only entry in the list.
    //
    if (ListBox_GetCount(hwndList) == 1)
    {
        MessageBeep(MB_ICONEXCLAMATION);
        return;
    }

    //
    //  Get the pointer to the lang node from the list box
    //  item data.
    //
    pLangNode = (LPLANGNODE)ListBox_GetItemData(hwndList, idxList);

    //
    //  Set the input locale to be not active and show that its state
    //  has changed.  Also, delete the string from the input locale list
    //  in the property sheet.
    //
    //  Decrement the number of nodes for this input locale.
    //
    pLangNode->wStatus &= ~LANG_ACTIVE;
    pLangNode->wStatus |= LANG_CHANGED;
    ListBox_DeleteString(hwndList, idxList);

    lpLang[pLangNode->iLang].iNumCount--;

    //
    //  See how many entries are left in the input locale list box.
    //
    if (count = ListBox_GetCount(hwndList))
    {
        //
        //  Set the new current selection.
        //
        ListBox_SetCurSel(hwndList, (count <= idxList) ? (count - 1) : idxList);

        //
        //  See if there is only one entry left in the list.
        //
        if (count < 2)
        {
            //
            //  Only 1 entry in list.  Disable the secondary controls.
            //
            SetSecondaryControls(hwnd, FALSE);

#ifdef DBCS
            pLangNode = (LPLANGNODE)ListBox_GetItemData(hwndList, 0);
            wLangID = LOWORD(pLangNode->hkl);
            if (wLangID == 0x0404 || wLangID == 0x0411 ||
                wLangID == 0x0412 || wLangID == 0x0804)
            {
                EnableWindow(GetDlgItem(hwnd, IDC_KBDL_INDICATOR), TRUE);
                CheckDlgButton( hwnd,
                                IDC_KBDL_INDICATOR,
                                FindWindow(szIndicator, NULL) != NULL );
            }
#endif
        }
    }
    else
    {
        //
        //  No entries left.  Disable the secondary controls.
        //  This should never happen since we check for this above.
        //
        SetSecondaryControls(hwnd, FALSE);
    }

    //
    //  If it was the default input locale, change the default to something
    //  else.
    //
    if (pLangNode->wStatus & LANG_DEFAULT)
    {
        pLangNode->wStatus &= ~LANG_DEFAULT;
        Locale_CommandSetDefault(hwnd);
    }

    //
    //  If it wasn't originally active, then remove it from the list.
    //  There's nothing more to do with this node.
    //
    if (!(pLangNode->wStatus & LANG_ORIGACTIVE))
    {
        Locale_RemoveFromLinkedList(pLangNode);
    }

    //
    //  Enable the Apply button.
    //
    PropSheet_Changed(GetParent(hwnd), hwnd);
}


////////////////////////////////////////////////////////////////////////////
//
//  Locale_UpdateActiveLocales
//
//  Updates the active locales.
//
////////////////////////////////////////////////////////////////////////////

BOOL Locale_UpdateActiveLocales(
    HWND hwnd)
{
    HKL *pLangs;
    UINT nLangs, i, j;
    UINT iOldLayout;
    HWND hwndList = GetDlgItem(hwnd, IDC_KBDL_LOCALE_LIST);
    HKL hklSystem;
    int idxListBox;
    DWORD langLay;
    BOOL bApply;
    int iOldCount;
    LPLANGNODE pDefault = NULL;
    LPLANGNODE pLangNode, pTemp;

    //
    //  See if the pane is disabled.  If so, then there is nothing to
    //  update.
    //
    if (!IsWindowEnabled(hwndList))
    {
        return (TRUE);
    }

    //
    //  Clear out the combo box.
    //
    iOldCount = ListBox_GetCount(hwndList);
    SendMessage(hwndList, LB_RESETCONTENT, 0, 0L);

    //
    //  Get the active keyboard layout list from the system.
    //
    if (!SystemParametersInfo( SPI_GETDEFAULTINPUTLANG,
                               0,
                               (LPVOID)((LPDWORD)&hklSystem),
                               0 ))
    {
        hklSystem = GetKeyboardLayout(0);
    }
    nLangs = GetKeyboardLayoutList(0, NULL);
    if (nLangs == 0)
    {
        EnablePane(hwnd, FALSE, IDC_KBDL_DISABLED);
        return (FALSE);
    }
    pLangs = (HKL *)LocalAlloc(LPTR, sizeof(DWORD) * nLangs);
    GetKeyboardLayoutList(nLangs, (HKL *)pLangs);

    //
    //  Mark all of the Original & Active entries with the LANG_UPDATE
    //  value so that these can be added to the list if they were deleted
    //  from the original list by another process.
    //
    for (i = 0; i < iLangBuff; i++)
    {
        pLangNode = lpLang[i].pNext;
        while (pLangNode)
        {
            if ((pLangNode->wStatus & LANG_ORIGACTIVE) &&
                (pLangNode->wStatus & LANG_ACTIVE))
            {
                pLangNode->wStatus |= LANG_UPDATE;
            }

            if ((pLangNode->wStatus & LANG_DEFAULT) &&
                (pLangNode->wStatus & LANG_DEF_CHANGE))
            {
                pDefault = pLangNode;
            }

            pLangNode = pLangNode->pNext;
        }
    }

    //
    //  Get the active keyboard information and put it in the internal
    //  language structure.
    //
    for (j = 0; j < nLangs; j++)
    {
        for (i = 0; i < iLangBuff; i++)
        {
            if (LOWORD(pLangs[j]) == LOWORD(lpLang[i].dwID))
            {
                //
                //  Found a match.
                //
#ifdef DBCS
                if ((pLangNode->wStatus & LANG_IME) &&
                    (pLangNode->hkl != pLangs[j]))
                {
                    continue;
                }
#endif
                //
                //  Find the correct entry for the hkl.
                //
                pLangNode = lpLang[i].pNext;
                while (pLangNode)
                {
                    if (pLangNode->hkl == pLangs[j])
                    {
                        break;
                    }
                    pLangNode = pLangNode->pNext;
                }
                if (pLangNode == NULL)
                {
                    pLangNode = Locale_AddToLinkedList(i, pLangs[j]);
                    if (!pLangNode)
                    {
                        continue;
                    }
                }

                //
                //  Make sure it wasn't one that was removed by the user
                //  before the Apply button was hit.
                //
                if ((pLangNode->wStatus & LANG_ORIGACTIVE) &&
                    (!(pLangNode->wStatus & LANG_ACTIVE)))
                {
                    if ((pLangNode->hkl == hklSystem) &&
                        (!pDefault || (pLangNode == pDefault)))
                    {
                        //
                        //  Override the user's removal if it is now the
                        //  system default.
                        //
                        pLangNode->wStatus |= LANG_ACTIVE;
                        pLangNode->wStatus &= ~LANG_CHANGED;

                        lpLang[i].iNumCount++;
                    }
                    else
                    {
                        //
                        //  Want to break out of the inner loop so that this
                        //  one won't be added to the list.
                        //
                        break;
                    }
                }

                //
                //  Add the item data to the list box, mark the
                //  language as original and active, save the pointer
                //  to the match in the layout list, and get the
                //  2 letter indicator symbol.
                //
                idxListBox = ListBox_AddItemData(hwndList, pLangNode);
                pLangNode->wStatus |= (LANG_ORIGACTIVE | LANG_ACTIVE);
                pLangNode->wStatus &= ~LANG_UPDATE;
                pLangNode->hkl = pLangs[j];
                pLangNode->hklUnload = pLangs[j];
                FetchIndicator(pLangNode);

                //
                //  Save the iLayout value to see if it's changed.
                //
                iOldLayout = pLangNode->iLayout;

                //
                //  Match the language to the layout.
                //  The hiword of pLangs[j] is the layout id.
                //
                pLangNode->iLayout = 0;
                langLay = (DWORD)HIWORD(pLangs[j]);

                if ((HIWORD(pLangs[j]) == 0xffff) ||
                    (HIWORD(pLangs[j]) == 0xfffe))
                {
                    //
                    //  Mark default or previous error as US - this
                    //  means that the layout will be that of the basic
                    //  keyboard driver (the US one).
                    //
                    pLangNode->wStatus |= LANG_CHANGED;
                    pLangNode->iLayout = iUsLayout;
                    langLay = 0;
                }
                else if ((HIWORD(pLangs[j]) & 0xf000) == 0xf000)
                {
                    //
                    //  Layout is special, need to search for the ID
                    //  number.
                    //
                    UINT k;
                    UINT id;

                    id = HIWORD(pLangs[j]) & 0x0fff;

                    for (k = 0; k < iLayoutBuff; k++)
                    {
                        if (id == lpLayout[k].iSpecialID)
                        {
                            pLangNode->iLayout = k;
                            langLay = 0;
                            break;
                        }
                    }
                    if (langLay)
                    {
                        //
                        //  Didn't find the id, so reset to basic for
                        //  the language.
                        //
                        langLay = (DWORD)LOWORD(pLangs[j]);
                    }
                }

                if (langLay)
                {
                    UINT k;

                    for (k = 0; k < iLayoutBuff; k++)
                    {
                        if (langLay == (DWORD)LOWORD(lpLayout[k].dwID))
                        {
                            pLangNode->iLayout = k;
                            break;
                        }
                    }

                    if (k == iLayoutBuff)
                    {
                        //
                        //  Something went wrong or didn't load from
                        //  the registry correctly.
                        //
                        MessageBeep(MB_ICONEXCLAMATION);
                        pLangNode->wStatus |= LANG_CHANGED;
                        pLangNode->iLayout = iUsLayout;
                    }
                }

                //
                //  See if the user changed the layout.
                //
                if ((pLangNode->wStatus & LANG_OAC) == LANG_OAC)
                {
                    if ((pLangNode->iLayout == iOldLayout) ||
                        ((pLangNode->hkl == hklSystem) &&
                         (!(pLangNode->wStatus & LANG_DEFAULT))))
                    {
                        pLangNode->wStatus &= ~LANG_CHANGED;
                    }
                    else
                    {
                        pLangNode->iLayout = iOldLayout;
                    }
                }

                //
                //  If this is the current language, then it's the default
                //  one.
                //
                if ((pLangNode == pDefault) ||
                    ((pLangNode->hkl == hklSystem) && !pDefault))
                {
                    TCHAR sz[DESC_MAX];

                    //
                    //  Found the default.  Set the Default input locale
                    //  text in the property sheet.
                    //
#ifdef DBCS
                    if (pLangNode->wStatus & LANG_IME)
                    {
                        GetAtomName( lpLayout[pLangNode->iLayout].atmLayoutText,
                                     sz,
                                     DESC_MAX );
                    }
                    else
#endif
                    GetAtomName(lpLang[i].atmLanguageName, sz, DESC_MAX);
                    pLangNode->wStatus |= LANG_DEFAULT;
                    if (pLangNode->hkl == hklSystem)
                    {
                        pLangNode->wStatus &= ~LANG_DEF_CHANGE;
                        bDefaultChange = FALSE;
                    }
                    pDefault = pLangNode;

                    ListBox_SetCurSel( GetDlgItem(hwnd, IDC_KBDL_LOCALE_LIST),
                                       idxListBox );
                    SetDlgItemText(hwnd, IDC_KBDL_DEFAULT, sz);
                }

                //
                //  Break out of inner loop - we've found it.
                //
                break;
            }
        }
    }

    //
    //  Need to see if any items are marked to be updated, if any were
    //  added to the list before the Apply button was hit, and if
    //  the default has changed.
    //
    bApply = FALSE;
    for (i = 0; i < iLangBuff; i++)
    {
        lpLang[i].iNumCount = 0;
        pLangNode = lpLang[i].pNext;
        while (pLangNode)
        {
            //
            //  See if this item is an update item.
            //
            if (pLangNode->wStatus & LANG_UPDATE)
            {
                pLangNode->wStatus = 0;
            }

            //
            //  See if this item needs to be added to the combo box.
            //
            else if ((pLangNode->wStatus & LANG_ACTIVE) &&
                     (!(pLangNode->wStatus & LANG_ORIGACTIVE)))
            {
                //
                //  In this case, the Apply button should already be enabled.
                //
                ListBox_AddItemData(hwndList, pLangNode);
                FetchIndicator(pLangNode);
            }

            //
            //  See if the default has changed.
            //
            if ((pLangNode->wStatus & LANG_DEFAULT) &&
                (pDefault) && (pLangNode != pDefault))
            {
                pLangNode->wStatus &= ~LANG_DEFAULT;
            }

            //
            //  See if the Apply button should be enabled or disabled.
            //
            if (pLangNode->wStatus & LANG_CHANGED)
            {
                bApply = TRUE;
            }

            //
            //  Advance the pointer.
            //
            if (pLangNode->wStatus == 0)
            {
                //
                //  Remove the node - it's no longer needed.
                //
                pTemp = pLangNode;
                pLangNode = pLangNode->pNext;
                Locale_RemoveFromLinkedList(pTemp);
            }
            else
            {
                //
                //  Increment the active count.
                //
                if (pLangNode->wStatus & LANG_ACTIVE)
                {
                    (lpLang[i].iNumCount)++;
                }
                pLangNode = pLangNode->pNext;
            }
        }
    }

    //
    //  See if the user specifically changed the "Switch locales" choice
    //  or the "Enable indicator on taskbar" check box.
    //
    if (bSwitchChange || bDefaultChange)
    {
        bApply = TRUE;
    }

    //
    //  Enable or Disable the Apply button.
    //
    SendMessage( GetParent(hwnd),
                 bApply ? PSM_CHANGED : PSM_UNCHANGED,
                 (WPARAM)hwnd,
                 0L );

    //
    //  See if we need to enable the secondary controls.
    //
    if ((iOldCount < 2) && (ListBox_GetCount(hwndList) > 1))
    {
        //
        //  Enable the secondary controls.
        //
        SetSecondaryControls(hwnd, TRUE);

        //
        //  Set the appropriate toggle key.
        //
        GetKbdSwitchHotkey(hwnd);

        //
        //  See if the taskbar indicator should be on or off.
        //
        CheckDlgButton( hwnd,
                        IDC_KBDL_INDICATOR,
                        FindWindow(szIndicator, NULL) != NULL );
    }

    //
    //  Return success.
    //
    LocalFree((HANDLE)pLangs);
    return (TRUE);
}


////////////////////////////////////////////////////////////////////////////
//
//  LocaleDlgProc
//
//  This is the dialog proc for the Input Locales property sheet.
//
////////////////////////////////////////////////////////////////////////////

BOOL CALLBACK LocaleDlgProc(
    HWND hDlg,
    UINT message,
    WPARAM wParam,
    LPARAM lParam)
{
    switch (message)
    {
        case ( WM_DESTROY ) :
        {
            Locale_KillPaneDialog(hDlg);
            break;
        }
        case ( WM_INITDIALOG ) :
        {
            Locale_InitPropSheet(hDlg);
            break;
        }
        case ( WM_MEASUREITEM ) :
        {
            Locale_MeasureItem(hDlg, (LPMEASUREITEMSTRUCT)lParam);
            break;
        }
        case ( WM_DRAWITEM ) :
        {
            return Locale_DrawItem(hDlg, (LPDRAWITEMSTRUCT)lParam);
        }
        case ( WM_ACTIVATE ) :
        {
            if (IsWindowEnabled(GetDlgItem(hDlg, IDC_KBDL_DISABLED_2)))
            {
                Locale_InitPropSheet(hDlg);
            }
            else
            {
                Locale_UpdateActiveLocales(hDlg);
            }
            break;
        }
        case ( WM_NOTIFY ) :
        {
            switch (((NMHDR *)lParam)->code)
            {
                case ( PSN_SETACTIVE ) :
                {
                    if (IsWindowEnabled(GetDlgItem(hDlg, IDC_KBDL_DISABLED_2)))
                    {
                        Locale_InitPropSheet(hDlg);
                    }
                    else
                    {
                        Locale_UpdateActiveLocales(hDlg);
                    }
                    break;
                }
                case ( PSN_APPLY ) :
                {
                    Locale_ApplyInputs(hDlg);
                    break;
                }
                default :
                {
                    return (FALSE);
                }
            }
            break;
        }
        case ( PSM_QUERYSIBLINGS ) :
        {
            Locale_UpdateActiveLocales(hDlg);
            break;
        }
        case ( WM_COMMAND ) :
        {
            switch (LOWORD(wParam))
            {
                case ( IDC_KBDL_ALT_SHIFT ) :
                case ( IDC_KBDL_CTRL_SHIFT ) :
                case ( IDC_KBDL_NO_SHIFT ) :
                case ( IDC_KBDL_INDICATOR ) :
                {
                    //
                    //  Care about these for "ApplyNow" only.
                    //
                    bSwitchChange = TRUE;
                    PropSheet_Changed(GetParent(hDlg), hDlg);
                    break;
                }
                case ( IDC_KBDL_LOCALE_LIST ) :
                {
                    Locale_CommandLocaleList(hDlg, wParam, lParam);
                    break;
                }
                case ( IDC_KBDL_ADD ) :
                {
                    Locale_CommandAddEdit( hDlg,
                                           DLG_KEYBOARD_LOCALE_ADD,
                                           NULL,
                                           (FARPROC)KbdLocaleAddDlg );
                    break;
                }
                case ( IDC_KBDL_EDIT ) :
                {
                    Locale_CommandAddEdit( hDlg,
                                           DLG_KEYBOARD_LOCALE_EDIT,
                                           NULL,
                                           (FARPROC)KbdLocaleEditDlg);
                    break;
                }
                case ( IDC_KBDL_DELETE ) :
                {
                    Locale_CommandDelete(hDlg);
                    break;
                }
                case ( IDC_KBDL_SET_DEFAULT ) :
                {
                    Locale_CommandSetDefault(hDlg);
                    break;
                }
                case ( IDOK ) :
                {
                    if (!Locale_ApplyInputs(hDlg))
                    {
                        break;
                    }

                    // fall thru...
                }
                case ( IDCANCEL ) :
                {
                    EndDialog(hDlg, TRUE);
                    break;
                }
                default :
                {
                    return (FALSE);
                }
            }
            break;
        }
        case ( WM_HELP ) :
        {
            WinHelp( (HWND)((LPHELPINFO)lParam)->hItemHandle,
                     NULL,
                     HELP_WM_HELP,
                     (DWORD)(LPTSTR)aLocaleHelpIds );
            break;
        }
        case ( WM_CONTEXTMENU ) :      // right mouse click
        {
            WinHelp( (HWND)wParam,
                     NULL,
                     HELP_CONTEXTMENU,
                     (DWORD)(LPTSTR)aLocaleHelpIds );
            break;
        }
        default :
        {
            return (FALSE);
        }
    }

    return (TRUE);
}


////////////////////////////////////////////////////////////////////////////
//
//  Locale_AddDlgInit
//
//  Processing for a WM_INITDIALOG message for the Add dialog box.
//
////////////////////////////////////////////////////////////////////////////

void Locale_AddDlgInit(
    HWND hwnd,
    LPARAM lParam)
{
    UINT i;
    HWND hwndLang = GetDlgItem(hwnd, IDC_KBDLA_LOCALE);
    UINT idx;
    TCHAR sz[DESC_MAX];

    //
    //  Go through all of the input locales.  Display all of them,
    //  since we can have multiple layouts per locale.
    //
    //  Do NOT go down the links in this case.  We don't want to display
    //  the language choice multiple times.
    //
    for (i = 0; i < iLangBuff; i++)
    {
        //
        //  Make sure there are layouts to be added for this
        //  input locale.
        //
        if (lpLang[i].iNumCount != iLayoutBuff)
        {
            //
            //  Get the language name, add the string to the
            //  combo box, and set the index into the lpLang
            //  array as the item data.
            //
#ifdef DBCS
            if (lpLang[i].wStatus & LANG_IME)
            {
                GetAtomName( lpLayout[lpLang[i].iLayout].atmLayoutText,
                             sz,
                             DESC_MAX );
            }
            else
#endif
            GetAtomName(lpLang[i].atmLanguageName, sz, DESC_MAX);
            idx = ComboBox_AddString(hwndLang, sz);
            ComboBox_SetItemData(hwndLang, idx, MAKELONG(i, 0));
        }
    }

    //
    //  Set the current selection to the first one in the list.
    //
    ComboBox_SetCurSel(hwndLang, 0);
    idx = (UINT)ComboBox_GetItemData(hwndLang, 0);

    SetProp(hwnd, szPropHwnd, (HANDLE)((LPINITINFO)lParam)->hwndMain);
    SetProp(hwnd, szPropIdx, (HANDLE)idx);

    //
    //  Always check the "Use default properties" check box.
    //
    CheckDlgButton(hwnd, IDC_KBDLA_DEFAULT, BST_CHECKED);
}


////////////////////////////////////////////////////////////////////////////
//
//  Locale_GetProperLayout
//
//  Returns the offset to the layout selection that matches the given
//  input locale selection.
//
//  NOTE: This function will return -1 if the offset has already been
//        used by the given input locale.
//
////////////////////////////////////////////////////////////////////////////

int Locale_GetProperLayout(
    UINT idxLang)
{
    DWORD dwID = lpLang[idxLang].dwID;
    UINT i;
    int idxusa = -1;
    int idxBaseLang = -1;
    int idxSel = -1;
    int idxOther = -1;
    UINT iBaseLang = (LOWORD(dwID) & 0xff) | 0x400;
    LPLANGNODE pTemp;

    //
    //  Search through all of the layouts to try to find a match.
    //
    for (i = 0; i < iLayoutBuff; i++)
    {
        if (lpLayout[i].dwID == US_LOCALE)
        {
            idxusa = i;
        }

        if (lpLayout[i].dwID == iBaseLang)
        {
            idxBaseLang = i;
        }

        if (LOWORD(lpLayout[i].dwID) == LOWORD(dwID))
        {
            if (HIWORD(lpLayout[i].dwID) == 0)
            {
                idxSel = i;
                break;
            }
            else
            {
                idxOther = i;
            }
        }
    }

    //
    //  Take the best fit.
    //
    if (idxSel == -1)
    {
        if (idxOther != -1)
        {
            //
            // Locale has nonstandard layout.
            //
            idxSel = idxOther;
        }
        else if (idxBaseLang != -1)
        {
            //
            //  Other locale in language has a layout, take that.
            //
            idxSel = idxBaseLang;
        }
        else if (idxusa != -1)
        {
            //
            //  We found the standard usa layout.
            //
            idxSel = idxusa;
        }
        else
        {
            //
            //  Found nothing, use first.  (should never get here!)
            //
            idxSel = 0;
        }
    }

    //
    //  Make sure this isn't a duplicate entry.  If so, return -1.
    //
    pTemp = lpLang[idxLang].pNext;
    while (pTemp)
    {
        if ((pTemp->wStatus & LANG_ACTIVE) &&
            (pTemp->iLayout == (UINT)idxSel))
        {
            idxSel = -1;
            break;
        }
        pTemp = pTemp->pNext;
    }

    //
    //  Return the selection.
    //
    return (idxSel);
}


////////////////////////////////////////////////////////////////////////////
//
//  Locale_AddCommandOK
//
//  Gets the currently selected input locale from the combo box and marks
//  it as active in the lpLang list.  It then gets the requested layout
//  and sets that in the list.  It then adds the new input locale string
//  to the input locale list in the property sheet.
//
////////////////////////////////////////////////////////////////////////////

int Locale_AddCommandOK(
    HWND hwnd)
{
    HWND hwndLang = GetDlgItem(hwnd, IDC_KBDLA_LOCALE);
    int idxLang = ComboBox_GetCurSel(hwndLang);
    LPLANGNODE pLangNode;
    HANDLE hLangNode;

    //
    //  Get the offset for the language to add.
    //
    idxLang = (int)ComboBox_GetItemData(hwndLang, idxLang);

    //
    //  Insert a new language node.
    //
    pLangNode = Locale_AddToLinkedList(idxLang, 0);
    if (!pLangNode)
    {
        return (0);
    }

#ifdef DBCS
    if (pLangNode->wStatus & LANG_IME)
    {
        //
        //  IME.  Add the new language.
        //
        if (!AddLanguage(GetProp(hwnd, szPropHwnd), pLangNode))
        {
            //
            //  Unable to add the language.  Need to return the user back
            //  to the Add dialog.
            //
            Locale_RemoveFromLinkedList(pLangNode);
            return (0);
        }
        return (1);
    }
#endif

    //
    //  Get the layout that best fits the current language.
    //
    pLangNode->iLayout = Locale_GetProperLayout(idxLang);

    //
    //  See if the "Use default properties" button is still checked.
    //  If it's not, then bring up the Properties dialog.
    //
    if ((IsDlgButtonChecked(hwnd, IDC_KBDLA_DEFAULT) == BST_UNCHECKED) ||
        (pLangNode->iLayout == (UINT)(-1)))
    {
        if (!Locale_CommandAddEdit( hwnd,
                                    DLG_KEYBOARD_LOCALE_EDIT,
                                    pLangNode,
                                    (FARPROC)KbdLocaleEditDlg ))
        {
            //
            //  The user hit cancel in the properties dialog box or the
            //  properties dialog failed to be invoked.  Need to return
            //  the user back to the Add dialog.
            //
            Locale_RemoveFromLinkedList(pLangNode);
            return (0);
        }
    }

    //
    //  Add the new language.
    //
    if (!AddLanguage(GetProp(hwnd, szPropHwnd), pLangNode))
    {
        //
        //  Unable to add the language.  Need to return the user back
        //  to the Add dialog.
        //
        Locale_RemoveFromLinkedList(pLangNode);
        return (0);
    }

    //
    //  Return success.
    //
    return (1);
}


////////////////////////////////////////////////////////////////////////////
//
//  KbdLocaleAddDlg
//
//  This is the dialog proc for the Add button of the Input Locales
//  property sheet.
//
////////////////////////////////////////////////////////////////////////////

BOOL CALLBACK KbdLocaleAddDlg(
    HWND hwnd,
    UINT uMsg,
    WPARAM wParam,
    LPARAM lParam)
{
    switch (uMsg)
    {
        case ( WM_INITDIALOG ) :
        {
            Locale_AddDlgInit(hwnd, lParam);
            break;
        }
        case ( WM_DESTROY ) :
        {
            RemoveProp(hwnd, szPropHwnd);
            RemoveProp(hwnd, szPropIdx);
            break;
        }
        case ( WM_COMMAND ) :
        {
            switch (LOWORD(wParam))
            {
                case ( IDOK ) :
                {
                    if (!Locale_AddCommandOK(hwnd))
                    {
                        //
                        //  This means the properties dialog was cancelled.
                        //  The Add dialog should remain active.
                        //
                        break;
                    }

                    // fall thru...
                }
                case ( IDCANCEL ) :
                {
                    EndDialog(hwnd, (wParam == IDOK) ? 1 : 0);
                    break;
                }
                default :
                {
                    return (FALSE);
                }
            }
            break;
        }
        case ( WM_HELP ) :
        {
            WinHelp( (HWND)((LPHELPINFO)lParam)->hItemHandle,
                     NULL,
                     HELP_WM_HELP,
                     (DWORD)(LPTSTR)aAddLocaleHelpIds );
            break;
        }
        case ( WM_CONTEXTMENU ) :      // right mouse click
        {
            WinHelp( (HWND)wParam,
                     NULL,
                     HELP_CONTEXTMENU,
                     (DWORD)(LPTSTR)aAddLocaleHelpIds );
            break;
        }
        default :
        {
            return (FALSE);
        }
    }

    return (TRUE);
}


////////////////////////////////////////////////////////////////////////////
//
//  Locale_EditDlgInit
//
//  Processing for a WM_INITDIALOG message for the Edit dialog box.
//
////////////////////////////////////////////////////////////////////////////

void Locale_EditDlgInit(
    HWND hwnd,
    LPARAM lParam)
{
    UINT i;
    UINT idx;
    int idxSel;
    int idxUSA;
    TCHAR sz[DESC_MAX];
    HWND hwndLayout = GetDlgItem(hwnd, IDC_KBDLE_LAYOUT);
    HWND hwndMain = ((LPINITINFO)lParam)->hwndMain;
    LPLANGNODE pLangNode, pTemp;

    //
    //  Get the language name for the currently selected input locale
    //  and display it in the dialog.
    //
    pLangNode = ((LPINITINFO)lParam)->pLangNode;
    GetAtomName(lpLang[pLangNode->iLang].atmLanguageName, sz, DESC_MAX);
    SetDlgItemText(hwnd, IDC_KBDLE_LOCALE, sz);

    //
    //  Search through all of the layouts.
    //
    idxSel = -1;
    idxUSA = -1;        // last resort default

    for (i = 0; i < iLayoutBuff; i++)
    {
#ifdef DBCS
        //
        //  We don't show IME layout in change layout list.
        //
        if ((HIWORD(lpLayout[i].dwID) & 0xf000) == 0xe000)
        {
            continue;
        }
#endif
        //
        //  Make sure this layout isn't already used for this input locale.
        //  If it is, then don't display it in the properties dialog.
        //
        if (i != pLangNode->iLayout)
        {
            pTemp = lpLang[pLangNode->iLang].pNext;
            while (pTemp)
            {
                if (pTemp->wStatus & LANG_ACTIVE)
                {
                    if (i == pTemp->iLayout)
                    {
                        break;
                    }
           
                }
                pTemp = pTemp->pNext;
            }
            if (pTemp && (i == pTemp->iLayout))
            {
                continue;
            }
        }

        //
        //  Get the layout text.  If it doesn't already exist in the
        //  combo box, then add it to the list of possible layouts.
        //
        GetAtomName(lpLayout[i].atmLayoutText, sz, DESC_MAX);
        if ((idx = ComboBox_FindStringExact(hwndLayout, 0, sz)) == CB_ERR)
        {
            //
            //  Add the layout string and set the item data to be the
            //  index into the lpLayout array.
            //
            idx = ComboBox_AddString(hwndLayout, sz);
            ComboBox_SetItemData(hwndLayout, idx, MAKELONG(i, 0));

            //
            //  See if it's the US layout.  If so, save the index.
            //
            if (lpLayout[i].dwID == US_LOCALE)
            {
                idxUSA = i;
            }
        }

        //
        //  Edit box, we want the one ALREADY associated.
        //
        if (i == pLangNode->iLayout)
        {
            idxSel = i;
        }
    }

    //
    //  If a default layout was not found, then set it to the US layout.
    //
    if (idxSel == -1)
    {
        idxSel = idxUSA;
    }

    //
    //  Set the current selection.
    //
    if (idxSel == -1)
    {
        //
        //  Simply set the current selection to be the first entry
        //  in the list.
        //
        ComboBox_SetCurSel(hwndLayout, 0);
    }
    else
    {
        //
        //  The combo box is sorted, but we need to know where
        //  lpLayout[idxSel] was stored.  So, get the atom again, and
        //  search the list.
        //
        GetAtomName(lpLayout[idxSel].atmLayoutText, sz, DESC_MAX);
        idx = ComboBox_FindStringExact(hwndLayout, 0, sz);
        ComboBox_SetCurSel(hwndLayout, idx);
    }

    SetProp(hwnd, szPropHwnd, (HANDLE)hwndMain);
    SetProp(hwnd, szPropIdx, (HANDLE)pLangNode);
}


////////////////////////////////////////////////////////////////////////////
//
//  Locale_EditCommandOK
//
//  Gets the new keyboard layout selection.  If it's different from the
//  current one for that locale, then it updates the lpLang array and
//  redraws the input locale combo box in the main property sheet.
//
////////////////////////////////////////////////////////////////////////////

void Locale_EditCommandOK(
    HWND hwnd)
{
    UINT idx;
    UINT idxLay;
    LPLANGNODE pLangNode;
    HWND hwndLay = GetDlgItem(hwnd, IDC_KBDLE_LAYOUT);
    HWND hwndMain;

    //
    //  Get the currently selected layout from the combo box.
    //
    idx = (UINT)ComboBox_GetCurSel(hwndLay);
    idxLay = (UINT)ComboBox_GetItemData(hwndLay, idx);
    pLangNode = (LPLANGNODE)GetProp(hwnd, szPropIdx);

    //
    //  See if the selected layout has changed.
    //
    if ((pLangNode->iLayout == (UINT)(-1)) || (pLangNode->iLayout != idxLay))
    {
        //
        //  Reset the lpLang array to contain the new layout and show
        //  that there is a change.
        //
        pLangNode->iLayout = idxLay;
        pLangNode->wStatus |= LANG_CHANGED;

        //
        //  Redraw the input locale list in the property sheet.
        //
        hwndMain = GetProp(hwnd, szPropHwnd);
        InvalidateRect(GetDlgItem(hwndMain, IDC_KBDL_LOCALE_LIST), NULL, FALSE);
    }
}


////////////////////////////////////////////////////////////////////////////
//
//  KbdLocaleEditDlg
//
//  This is the dialog proc for the Properties button of the Input Locales
//  property sheet.
//
////////////////////////////////////////////////////////////////////////////

BOOL CALLBACK KbdLocaleEditDlg(
    HWND hwnd,
    UINT uMsg,
    WPARAM wParam,
    LPARAM lParam)
{
    switch (uMsg)
    {
        case ( WM_INITDIALOG ) :
        {
            Locale_EditDlgInit(hwnd, lParam);
            break;
        }
        case ( WM_DESTROY ) :
        {
            RemoveProp(hwnd, szPropHwnd);
            RemoveProp(hwnd, szPropIdx);
            break;
        }
        case ( WM_HELP ) :
        {
            WinHelp( (HWND)((LPHELPINFO)lParam)->hItemHandle,
                     NULL,
                     HELP_WM_HELP,
                     (DWORD)(LPTSTR)aLocalePropHelpIDs );
            break;
        }
        case ( WM_CONTEXTMENU ) :
        {
            WinHelp( (HWND)wParam,
                     NULL,
                     HELP_CONTEXTMENU,
                     (DWORD)(LPTSTR)aLocalePropHelpIDs );
            break;
        }
        case ( WM_COMMAND ) :
        {
            switch (LOWORD(wParam))
            {
                case ( IDOK ) :
                {
                    Locale_EditCommandOK(hwnd);

                    // fall thru...
                }
                case ( IDCANCEL ) :
                {
                    EndDialog(hwnd, (wParam == IDOK) ? 1 : 0);
                    break;
                }
                default :
                {
                    return (FALSE);
                }
            }
            break;
        }
        default :
        {
            return (FALSE);
        }
    }

    return (TRUE);
}