/*******************************************************************************
*
*  (C) COPYRIGHT MICROSOFT CORP., 1993-1994
*
*  TITLE:       REGKEY.C
*
*  VERSION:     4.01
*
*  AUTHOR:      Tracy Sharpe
*
*  DATE:        05 Mar 1994
*
*  KeyTreeWnd TreeView routines for the Registry Editor.
*
*******************************************************************************/

#include "pch.h"
#include "regedit.h"
#include "regkey.h"
#include "regvalue.h"
#include "regresid.h"

#define MAX_KEYNAME_TEMPLATE_ID         100

#define SELCHANGE_TIMER_ID              1

#define REFRESH_DPA_GROW                16

VOID
PASCAL
RegEdit_OnKeyTreeDelete(
    HWND hWnd,
    HTREEITEM hTreeItem
    );

VOID
PASCAL
RegEdit_OnKeyTreeRename(
    HWND hWnd,
    HTREEITEM hTreeItem
    );

int
WINAPI
DPACompareKeyNames(
    LPVOID lpString1,
    LPVOID lpString2,
    LPARAM lParam
    );

HTREEITEM
PASCAL
KeyTree_InsertItem(
    HWND hKeyTreeWnd,
    HTREEITEM hParent,
    HTREEITEM hInsertAfter,
    LPCTSTR lpText,
    UINT fHasKids,
    LPARAM lParam
    );

BOOL
PASCAL
DoesKeyHaveKids(
    HKEY hKey,
    LPTSTR lpKeyName
    );

VOID
PASCAL
KeyTree_EditLabel(
    HWND hKeyTreeWnd,
    HTREEITEM hTreeItem
    );

BOOL
PASCAL
KeyTree_CanDeleteOrRenameItem(
    HWND hWnd,
    HTREEITEM hTreeItem
    );

/*******************************************************************************
*
*  RegEdit_OnNewKey
*
*  DESCRIPTION:
*
*  PARAMETERS:
*     hWnd, handle of RegEdit window.
*
*******************************************************************************/

VOID
PASCAL
RegEdit_OnNewKey(
                 HWND hWnd,
                 HTREEITEM hTreeItem
                 )
{
    TCHAR KeyName[MAXKEYNAME*2];
    UINT cchKeyName = 0;
    HKEY hRootKey;
    HKEY hKey;
    UINT ErrorStringID;
    BOOL fNewKeyIsOnlyChild;
    UINT NewKeyNameID;
    HKEY hNewKey;
    HTREEITEM hNewTreeItem;
    TV_ITEM TVItem;
    
    hRootKey = KeyTree_BuildKeyPath(g_RegEditData.hKeyTreeWnd, hTreeItem,
        KeyName, BKP_TOSUBKEY);
    cchKeyName = lstrlen(KeyName);

    if(RegOpenKeyEx(hRootKey,KeyName,0,KEY_CREATE_SUB_KEY,&hKey) != ERROR_SUCCESS) {
        
        //
        //  Get the text of the selected tree item so that we can display
        //  a more meaningful error message.
        //
        
        TVItem.mask = TVIF_TEXT;
        TVItem.hItem = hTreeItem;
        TVItem.pszText = (LPTSTR) KeyName;
        TVItem.cchTextMax = sizeof(KeyName)/sizeof(TCHAR);
        
        TreeView_GetItem(g_RegEditData.hKeyTreeWnd, &TVItem);
        
        ErrorStringID = IDS_NEWKEYPARENTOPENFAILED;
        goto error_ShowDialog;
        
    }
    
    TVItem.mask = TVIF_STATE | TVIF_CHILDREN;
    TVItem.hItem = hTreeItem;
    TreeView_GetItem(g_RegEditData.hKeyTreeWnd, &TVItem);
    
    fNewKeyIsOnlyChild = FALSE;
    
    if (TVItem.cChildren == FALSE) {
        
        //
        //  The selected key doesn't have any subkeys, so we can't do an expand
        //  on it just yet.  We'll just set a flag and later tag it with a
        //  plus/minus icon and expand it.
        //
        
        fNewKeyIsOnlyChild = TRUE;
        
    }
    
    else if (!(TVItem.state & TVIS_EXPANDED)) {
        
        //
        //  The selected key isn't expanded.  Do it now so that we can do an
        //  in-place edit and don't reenumerate the "New Key #xxx" after we do
        //  the RegCreateKey.
        //
        
        TreeView_Expand(g_RegEditData.hKeyTreeWnd, hTreeItem, TVE_EXPAND);
        
    }
    
    if (RegEdit_GetTemporaryKeyName(hWnd, KeyName, hKey))
    {
        if((cchKeyName + lstrlen(KeyName) + 1) < MAXKEYNAME)
        {
            if (RegCreateKey(hKey, KeyName, &hNewKey) != ERROR_SUCCESS)
            {
                ErrorStringID = IDS_NEWKEYCANNOTCREATE;
                goto error_CloseKey;
                
            }
            
            RegCloseKey(hNewKey);
            
            if (fNewKeyIsOnlyChild) 
            {
                TVItem.mask = TVIF_CHILDREN;
                TVItem.cChildren = TRUE;
                TreeView_SetItem(g_RegEditData.hKeyTreeWnd, &TVItem);
                
                TreeView_Expand(g_RegEditData.hKeyTreeWnd, hTreeItem, TVE_EXPAND);
                
                //  WARNING:  It is possible for our new item _not_ to be the only child
                //  if our view is out of date!
                hNewTreeItem = TreeView_GetChild(g_RegEditData.hKeyTreeWnd, hTreeItem);
                
            }
            
            else 
            {
                hNewTreeItem = KeyTree_InsertItem(g_RegEditData.hKeyTreeWnd, hTreeItem,
                    TVI_LAST, KeyName, FALSE, 0);
                
            }
            
            TreeView_SelectItem(g_RegEditData.hKeyTreeWnd, hNewTreeItem);
            KeyTree_EditLabel(g_RegEditData.hKeyTreeWnd, hNewTreeItem);
        }
        else
        {
            ErrorStringID = IDS_RENAMEKEYTOOLONG;
            goto error_CloseKey;
        }
    }
    else
    {
        ErrorStringID = IDS_NEWKEYNOUNIQUE;
        goto error_CloseKey;
    }

    RegCloseKey(hKey);
    return;
    
error_CloseKey:
    RegCloseKey(hKey);
    
    //  FEATURE:  For any errors that may crop up, we may need to turn off the
    //  child flag.
    
error_ShowDialog:
    InternalMessageBox(g_hInstance, hWnd, MAKEINTRESOURCE(ErrorStringID),
        MAKEINTRESOURCE(IDS_NEWKEYERRORTITLE), MB_ICONERROR | MB_OK,
        (LPTSTR) KeyName);
    
}


//------------------------------------------------------------------------------
//  RegEdit_GetTemporaryKeyName
//  
//  DESCRIPTION: Loop through the registry trying to find a valid temporary name 
//               until the user renames the key.
//
//  PARAMETERS:  HWND hWnd - handle to window
//               PTSTR pszKeyName
//
//  RETURN:      True, if unique name is found
//------------------------------------------------------------------------------
BOOL RegEdit_GetTemporaryKeyName(HWND hWnd, PTSTR pszKeyName, HKEY hKey)
{
    HKEY hNewKey;
    UINT uNewKeyNameID = 1;

    while (uNewKeyNameID < MAX_KEYNAME_TEMPLATE_ID) 
    {
        wsprintf(pszKeyName, g_RegEditData.pNewKeyTemplate, uNewKeyNameID);

        if(RegOpenKeyEx(hKey, pszKeyName, 0, 0, &hNewKey) == ERROR_FILE_NOT_FOUND) 
        {
            break;
        }
        RegCloseKey(hNewKey);

        uNewKeyNameID++;
    }

    if (uNewKeyNameID == MAX_KEYNAME_TEMPLATE_ID) 
    {
        InternalMessageBox(g_hInstance, hWnd, MAKEINTRESOURCE(IDS_NEWKEYNOUNIQUE),
        MAKEINTRESOURCE(IDS_NEWKEYERRORTITLE), MB_ICONERROR | MB_OK, pszKeyName);
    }

    return (uNewKeyNameID != MAX_KEYNAME_TEMPLATE_ID);
}


/*******************************************************************************
*
*  RegEdit_OnKeyTreeItemExpanding
*
*  DESCRIPTION:
*
*  PARAMETERS:
*     hWnd, handle of RegEdit window.
*     lpNMTreeView, TreeView notification data.
*
*******************************************************************************/

LRESULT
PASCAL
RegEdit_OnKeyTreeItemExpanding(
    HWND hWnd,
    LPNM_TREEVIEW lpNMTreeView
    )
{

    HWND hKeyTreeWnd;
    HTREEITEM hExpandingTreeItem;
    TCHAR KeyName[MAXKEYNAME];
    TV_ITEM TVItem;

    hKeyTreeWnd = g_RegEditData.hKeyTreeWnd;
    hExpandingTreeItem = lpNMTreeView-> itemNew.hItem;

    //
    //  Check if we're to expand the given tree item for the first time.  If so,
    //  delve into the registry to get all of the key's subkeys.
    //

    if (lpNMTreeView-> action & TVE_EXPAND && !(lpNMTreeView-> itemNew.state &
        TVIS_EXPANDEDONCE)) {

        if (TreeView_GetChild(hKeyTreeWnd, hExpandingTreeItem) != NULL)
            return FALSE;

        RegEdit_SetWaitCursor(TRUE);

        if (!KeyTree_ExpandBranch(hKeyTreeWnd, hExpandingTreeItem)) {

            //
            //  Get the text of the selected tree item so that we can display
            //  a more meaningful error message.
            //

            TVItem.mask = TVIF_TEXT;
            TVItem.hItem = hExpandingTreeItem;
            TVItem.pszText = (LPTSTR) KeyName;
            TVItem.cchTextMax = sizeof(KeyName)/sizeof(TCHAR);

            TreeView_GetItem(hKeyTreeWnd, &TVItem);

            InternalMessageBox(g_hInstance, hWnd,
                MAKEINTRESOURCE(IDS_OPENKEYCANNOTOPEN),
                MAKEINTRESOURCE(IDS_OPENKEYERRORTITLE), MB_ICONERROR | MB_OK,
                (LPTSTR) KeyName);

        }

	RegEdit_SetWaitCursor(FALSE);

    }

    return FALSE;

}

/*******************************************************************************
*
*  RegEdit_OnKeyTreeSelChanged
*
*  DESCRIPTION:
*     Depending on how the user has selected the new item in the KeyTreeWnd,
*     we call to the real worker routine, RegEdit_KeyTreeSelChanged, or delay
*     the call for several milliseconds.
*
*  PARAMETERS:
*     hWnd, handle of RegEdit window.
*     lpNMTreeView, TreeView notification data.
*
*******************************************************************************/

VOID
PASCAL
RegEdit_OnKeyTreeSelChanged(
    HWND hWnd,
    LPNM_TREEVIEW lpNMTreeView
    )
{

    UINT TimerDelay;

    //
    //  We delay the actual update of the selection and thus of the
    //  ValueListWnd for several milliseconds.  This avoids unnecessary flashing
    //  as the user scrolls through the tree.  (This behavior is directly taken
    //	from the Explorer.)
    //

    switch (g_RegEditData.SelChangeTimerState) {

        case SCTS_TIMERSET:
            KillTimer(hWnd, SELCHANGE_TIMER_ID);
            //  FALL THROUGH

        case SCTS_TIMERCLEAR:
#ifdef WINNT
        //
        // This behavior is extremely annoying so I am changing it.
        //
	    TimerDelay = 1;
#else
	    TimerDelay = (lpNMTreeView != NULL && lpNMTreeView-> action ==
		TVC_BYMOUSE) ? (1) : (GetDoubleClickTime() * 3 / 2);
#endif
	    SetTimer(hWnd, SELCHANGE_TIMER_ID, TimerDelay, NULL);
            g_RegEditData.SelChangeTimerState = SCTS_TIMERSET;
            break;

        //
        //  We want to punt the first selection change notification that comes
        //  through.
        //

        case SCTS_INITIALIZING:
            RegEdit_KeyTreeSelChanged(hWnd);
            break;

    }

}

/*******************************************************************************
*
*  RegEdit_OnSelChangedTimer
*
*  DESCRIPTION:
*     Called several milliseconds after a keyboard operation has selected a new
*     item in the KeyTreeWnd.  Act as if a new selection has just been made in
*     the KeyTreeWnd.
*
*  PARAMETERS:
*     hWnd, handle of RegEdit window.
*
*******************************************************************************/

VOID
PASCAL
RegEdit_OnSelChangedTimer(
    HWND hWnd
    )
{

    KillTimer(hWnd, SELCHANGE_TIMER_ID);
    g_RegEditData.SelChangeTimerState = SCTS_TIMERCLEAR;

    RegEdit_KeyTreeSelChanged(hWnd);

}

/*******************************************************************************
*
*  RegEdit_KeyTreeSelChanged
*
*  DESCRIPTION:
*     Called after a new item has been selected in the KeyTreeWnd.  Opens a
*     registry key to the new branch and notifies the ValueListWnd to update
*     itself.
*
*  PARAMETERS:
*     hWnd, handle of RegEdit window.
*
*******************************************************************************/

VOID
PASCAL
RegEdit_KeyTreeSelChanged(
    HWND hWnd
    )
{

    HWND hKeyTreeWnd;
    HTREEITEM hSelectedTreeItem;
    RECT ItemRect;
    RECT ClientRect;
    RECT FromRect;
    RECT ToRect;
    HKEY hRootKey;
    TCHAR KeyName[MAXKEYNAME];
    TV_ITEM TVItem;

    hKeyTreeWnd = g_RegEditData.hKeyTreeWnd;
    hSelectedTreeItem = TreeView_GetSelection(hKeyTreeWnd);

    if (g_RegEditData.SelChangeTimerState != SCTS_INITIALIZING) {

        //
        //  Draw an animation that shows the "expansion" of the newly selected
        //  tree item to the ListView.
        //

        TreeView_GetItemRect(hKeyTreeWnd, hSelectedTreeItem, &ItemRect, TRUE);
        GetClientRect(hKeyTreeWnd, &ClientRect);
        IntersectRect(&FromRect, &ClientRect, &ItemRect);
        MapWindowPoints(hKeyTreeWnd, hWnd, (LPPOINT) &FromRect, 2);

        GetWindowRect(g_RegEditData.hValueListWnd, &ToRect);
        MapWindowPoints(NULL, hWnd, (LPPOINT) &ToRect, 2);

        DrawAnimatedRects(hWnd, IDANI_OPEN, &FromRect, &ToRect);

    }

    //
    //  Close the previously selected item's key handle, if appropriate.
    //

    if (g_RegEditData.hCurrentSelectionKey != NULL) {

        RegCloseKey(g_RegEditData.hCurrentSelectionKey);
        g_RegEditData.hCurrentSelectionKey = NULL;

    }

    RegEdit_UpdateStatusBar();

    //
    //  Simple case-- we're changing to one of the top-level labels, such as
    //  "My Computer" or a network computer name.  Right now, nothing is
    //  displayed in the ListView, so just empty it and return.
    //

    if (TreeView_GetParent(hKeyTreeWnd, hSelectedTreeItem) != NULL) {

        //
        //  Build a registry path to the selected tree item and open a registry
        //  key.
        //

        hRootKey = KeyTree_BuildKeyPath(hKeyTreeWnd, hSelectedTreeItem,
            KeyName, BKP_TOSUBKEY);

        if(RegOpenKeyEx(hRootKey,KeyName, 0, MAXIMUM_ALLOWED,
            &g_RegEditData.hCurrentSelectionKey) != ERROR_SUCCESS) {

            //
            //  Get the text of the selected tree item so that we can display
            //  a more meaningful error message.
            //

            TVItem.mask = TVIF_TEXT;
            TVItem.hItem = hSelectedTreeItem;
            TVItem.pszText = (LPTSTR) KeyName;
            TVItem.cchTextMax = sizeof(KeyName)/sizeof(TCHAR);

            TreeView_GetItem(hKeyTreeWnd, &TVItem);

            InternalMessageBox(g_hInstance, hWnd,
                MAKEINTRESOURCE(IDS_OPENKEYCANNOTOPEN),
                MAKEINTRESOURCE(IDS_OPENKEYERRORTITLE), MB_ICONERROR | MB_OK,
                (LPTSTR) KeyName);

        }

    }

    RegEdit_OnValueListRefresh(hWnd);

}

/*******************************************************************************
*
*  RegEdit_OnKeyTreeBeginLabelEdit
*
*  DESCRIPTION:
*
*  PARAMETERS:
*     hWnd, handle of RegEdit window.
*     lpTVDispInfo,
*
*******************************************************************************/

BOOL
PASCAL
RegEdit_OnKeyTreeBeginLabelEdit(
    HWND hWnd,
    TV_DISPINFO FAR* lpTVDispInfo
    )
{

    //
    //  B#7933:  We don't want the user to hurt themselves by making it too easy
    //  to rename keys and values.  Only allow renames via the menus.
    //

    //
    //  We don't get any information on the source of this editing action, so
    //  we must maintain a flag that tells us whether or not this is "good".
    //

    if (!g_RegEditData.fAllowLabelEdits)
        return TRUE;

    //
    //  All other labels are fair game.  We need to disable our keyboard
    //  accelerators so that the edit control can "see" them.
    //

    g_fDisableAccelerators = TRUE;

    return FALSE;

}

/*******************************************************************************
*
*  RegEdit_OnKeyTreeEndLabelEdit
*
*  DESCRIPTION:
*
*  PARAMETERS:
*     hWnd, handle of RegEdit window.
*     lpTVDispInfo,
*
*******************************************************************************/

BOOL
PASCAL
RegEdit_OnKeyTreeEndLabelEdit(
    HWND hWnd,
    TV_DISPINFO FAR* lpTVDispInfo
    )
{

    HWND hKeyTreeWnd;
    HKEY hRootKey;
    TCHAR SourceKeyName[MAXKEYNAME*2];
    TCHAR DestinationKeyName[MAXKEYNAME];
    HKEY hSourceKey;
    HKEY hDestinationKey;
    LPTSTR lpEndOfParentKey;
    UINT ErrorStringID;
    TV_ITEM TVItem;

    //
    //  We can reenable our keyboard accelerators now that the edit control no
    //  longer needs to "see" them.
    //

    g_fDisableAccelerators = FALSE;

    hKeyTreeWnd = g_RegEditData.hKeyTreeWnd;

    //
    //  Check to see if the user cancelled the edit.  If so, we don't care so
    //  just return.
    //

    if (lpTVDispInfo-> item.pszText == NULL)
        return FALSE;

    //
    //  Attempt to open the key to be renamed.  This may or may not be the same
    //  key that is already open.
    //

    hRootKey = KeyTree_BuildKeyPath(hKeyTreeWnd, lpTVDispInfo-> item.hItem,
        SourceKeyName, BKP_TOSUBKEY);
    if (lstrlen(SourceKeyName) >= MAXKEYNAME) {
        ErrorStringID = IDS_RENAMEKEYTOOLONG;
        goto error_ShowDialog;

    }

    if(RegOpenKeyEx(hRootKey,SourceKeyName,0,KEY_ENUMERATE_SUB_KEYS|KEY_QUERY_VALUE,&hSourceKey) != ERROR_SUCCESS) {

        ErrorStringID = IDS_RENAMEKEYOTHERERROR;
        goto error_ShowDialog;

    }

    //
    //  Take the full path name of the key (relative to a predefined root key)
    //  and replace the old key name with the new.  Make sure that this key
    //  doesn't exceed our internal buffers.
    //

    lstrcpy(DestinationKeyName, SourceKeyName);

    if ((lpEndOfParentKey = StrRChr(DestinationKeyName, NULL, TEXT('\\'))) != NULL)
        lpEndOfParentKey++;

    else
        lpEndOfParentKey = DestinationKeyName;

    *lpEndOfParentKey = 0;

    if (lstrlen(DestinationKeyName) + lstrlen(lpTVDispInfo-> item.pszText) >= MAXKEYNAME) {
        ErrorStringID = IDS_RENAMEKEYTOOLONG;
        goto error_CloseSourceKey;
    }

    lstrcpy(lpEndOfParentKey, lpTVDispInfo-> item.pszText);

    //
    //  Make sure there are no backslashes in the name.
    //

    if (StrChr(lpEndOfParentKey, TEXT('\\')) != NULL) {

        ErrorStringID = IDS_RENAMEKEYBADCHARS;
        goto error_CloseSourceKey;

    }

    //
    //  Make sure there the name isn't empty
    //

    if (DestinationKeyName[0] == 0) {
        ErrorStringID = IDS_RENAMEKEYEMPTY;
        goto error_CloseSourceKey;
    }

    //
    //  Make sure that the destination doesn't already exist.
    //
    if(RegOpenKeyEx(hRootKey,DestinationKeyName,0,KEY_QUERY_VALUE,&hDestinationKey) ==
        ERROR_SUCCESS) {

        RegCloseKey(hDestinationKey);

        ErrorStringID = IDS_RENAMEKEYEXISTS;
        goto error_CloseSourceKey;

    }

    //
    //  Create the destination key and do the copy.
    //

    if (RegCreateKey(hRootKey, DestinationKeyName, &hDestinationKey) !=
        ERROR_SUCCESS) {

        ErrorStringID = IDS_RENAMEKEYOTHERERROR;
        goto error_CloseSourceKey;

    }

    //  FEATURE:  Check this return (when it gets one!)
    if (!CopyRegistry(hSourceKey, hDestinationKey))
    {
        RegCloseKey(hDestinationKey);
        RegCloseKey(hSourceKey);

        ErrorStringID = IDS_RENAMEKEYOTHERERROR;
        goto error_ShowDialog;
    }

    RegCloseKey(hSourceKey);

    //
    //  Check to see if we're renaming the currently selected key.  If so, toss
    //  our cached key handle and change to our source key.
    //

    if (TreeView_GetSelection(hKeyTreeWnd) == lpTVDispInfo-> item.hItem) {

        RegCloseKey(g_RegEditData.hCurrentSelectionKey);

        g_RegEditData.hCurrentSelectionKey = hDestinationKey;

        //
        //  We can't just call RegEdit_UpdateStatusBar here... the tree item
        //  won't be updated until we return TRUE from this message.  So we must
        //  post a message to tell ourselves to do the update later on.
        //

        PostMessage(hWnd, REM_UPDATESTATUSBAR, 0, 0);

    }

    else
        RegCloseKey(hDestinationKey);

    if (RegDeleteKeyRecursive(hRootKey, SourceKeyName) != ERROR_SUCCESS) {

        ErrorStringID = IDS_RENAMEKEYOTHERERROR;
        goto error_ShowDialog;

    }

    return TRUE;

error_CloseSourceKey:
    RegCloseKey(hSourceKey);

error_ShowDialog:
    TVItem.hItem = lpTVDispInfo-> item.hItem;
    TVItem.mask = TVIF_TEXT;
    TVItem.pszText = SourceKeyName;
    TVItem.cchTextMax = sizeof(SourceKeyName)/sizeof(TCHAR);

    TreeView_GetItem(hKeyTreeWnd, &TVItem);

    InternalMessageBox(g_hInstance, hWnd, MAKEINTRESOURCE(ErrorStringID),
        MAKEINTRESOURCE(IDS_RENAMEKEYERRORTITLE), MB_ICONERROR | MB_OK,
        (LPTSTR) SourceKeyName);

    return FALSE;

}

/*******************************************************************************
*
*  RegEdit_OnKeyTreeCommand
*
*  DESCRIPTION:
*     Handles the selection of a menu item by the user intended for the
*     KeyTree child window.
*
*  PARAMETERS:
*     hWnd, handle of RegEdit window.
*     MenuCommand, identifier of menu command.
*     hTreeItem,
*
*******************************************************************************/

VOID
PASCAL
RegEdit_OnKeyTreeCommand(
    HWND hWnd,
    int MenuCommand,
    HTREEITEM hTreeItem
    )
{

    HWND hKeyTreeWnd;

    hKeyTreeWnd = g_RegEditData.hKeyTreeWnd;

    //
    //  Assume that the we mean the current selection if we're to dispatch a
    //  command that requires a tree item.  This is necessary because the tree
    //  control will let you activate the context menu of one tree item while
    //  another one is really the selected tree item.
    //

    if (hTreeItem == NULL)
        hTreeItem = TreeView_GetSelection(hKeyTreeWnd);

    switch (MenuCommand) {

        case ID_CONTEXTMENU:
            RegEdit_OnKeyTreeContextMenu(hWnd, TRUE);
            break;

        case ID_TOGGLE:
            TreeView_Expand(hKeyTreeWnd, hTreeItem, TVE_TOGGLE);
            break;

        case ID_DELETE:
            RegEdit_OnKeyTreeDelete(hWnd, hTreeItem);
            break;

        case ID_RENAME:
            RegEdit_OnKeyTreeRename(hWnd, hTreeItem);
            break;

        case ID_DISCONNECT:
            RegEdit_OnKeyTreeDisconnect(hWnd, hTreeItem);
            break;

        case ID_COPYKEYNAME:
            RegEdit_OnCopyKeyName(hWnd, hTreeItem);
            break;

        case ID_NEWKEY:
            RegEdit_OnNewKey(hWnd, hTreeItem);
            break;

        case ID_NEWSTRINGVALUE:
        case ID_NEWBINARYVALUE:
            if (hTreeItem != TreeView_GetSelection(hKeyTreeWnd)) {

                //
                //  Force the selection to occur now, so that we're dealing
                //  with the right open key.
                //

                TreeView_SelectItem(hKeyTreeWnd, hTreeItem);
                RegEdit_OnSelChangedTimer(hWnd);

            }
            //  FALL THROUGH

        default:
            //
            //  Check to see if this menu command should be handled by the main
            //  window's command handler.
            //

            if (MenuCommand >= ID_FIRSTMAINMENUITEM && MenuCommand <=
                ID_LASTMAINMENUITEM)
                RegEdit_OnCommand(hWnd, MenuCommand, NULL, 0);
            break;

    }

}

/*******************************************************************************
*
*  RegEdit_OnKeyTreeContextMenu
*
*  DESCRIPTION:
*
*  PARAMETERS:
*
*******************************************************************************/

VOID
PASCAL
RegEdit_OnKeyTreeContextMenu(
    HWND hWnd,
    BOOL fByAccelerator
    )
{

    HWND hKeyTreeWnd;
    DWORD MessagePos;
    POINT MessagePoint;
    TV_HITTESTINFO TVHitTestInfo;
    UINT MenuID;
    HMENU hContextMenu;
    HMENU hContextPopupMenu;
    TV_ITEM TVItem;
    int MenuCommand;

    hKeyTreeWnd = g_RegEditData.hKeyTreeWnd;

    //
    //  If fByAcclerator is TRUE, then the user hit Shift-F10 to bring up the
    //  context menu.  Following the Cabinet's convention, this menu is
    //  placed at (0,0) of the KeyTree client area.
    //

    if (fByAccelerator) {

        MessagePoint.x = 0;
        MessagePoint.y = 0;

        ClientToScreen(hKeyTreeWnd, &MessagePoint);

        TVItem.hItem = TreeView_GetSelection(hKeyTreeWnd);

    }

    else {

        MessagePos = GetMessagePos();

        MessagePoint.x = GET_X_LPARAM(MessagePos);
        MessagePoint.y = GET_Y_LPARAM(MessagePos);

        TVHitTestInfo.pt = MessagePoint;
        ScreenToClient(hKeyTreeWnd, &TVHitTestInfo.pt);
        TVItem.hItem = TreeView_HitTest(hKeyTreeWnd, &TVHitTestInfo);

    }

    //
    //  Determine which context menu to use and load it up.
    //

    if (TVItem.hItem == NULL)
    {
        return;     //  No context menu for now
    }
    else 
    {
        // Select the item to be dragged
        TreeView_Select(g_RegEditData.hKeyTreeWnd, TVItem.hItem, TVGN_CARET);

        if (TreeView_GetParent(hKeyTreeWnd, TVItem.hItem) == NULL)
            MenuID = IDM_COMPUTER_CONTEXT;

        else
            MenuID = IDM_KEY_CONTEXT;

    }

    if ((hContextMenu = LoadMenu(g_hInstance, MAKEINTRESOURCE(MenuID))) == NULL)
        return;

    hContextPopupMenu = GetSubMenu(hContextMenu, 0);

    TVItem.mask = TVIF_STATE | TVIF_CHILDREN;
    TreeView_GetItem(hKeyTreeWnd, &TVItem);

    if (TVItem.state & TVIS_EXPANDED)
        ModifyMenu(hContextPopupMenu, ID_TOGGLE, MF_BYCOMMAND | MF_STRING,
            ID_TOGGLE, g_RegEditData.pCollapse);

    if (MenuID == IDM_COMPUTER_CONTEXT) {

        if (g_RegEditData.fHaveNetwork) {

            if (TreeView_GetPrevSibling(hKeyTreeWnd, TVItem.hItem) == NULL)
                EnableMenuItem(hContextPopupMenu, ID_DISCONNECT, MF_GRAYED |
                    MF_BYCOMMAND);

        }

        else {

            DeleteMenu(hContextPopupMenu, ID_DISCONNECT, MF_BYCOMMAND);
            DeleteMenu(hContextPopupMenu, ID_NETSEPARATOR, MF_BYCOMMAND);

        }

    }

    else {

        RegEdit_SetKeyTreeEditMenuItems(hContextPopupMenu, TVItem.hItem);

        if (TVItem.cChildren == 0)
            EnableMenuItem(hContextPopupMenu, ID_TOGGLE, MF_GRAYED |
                MF_BYCOMMAND);

    }

    SetMenuDefaultItem(hContextPopupMenu, ID_TOGGLE, MF_BYCOMMAND);

    MenuCommand = TrackPopupMenuEx(hContextPopupMenu, TPM_RETURNCMD |
        TPM_RIGHTBUTTON | TPM_LEFTALIGN | TPM_TOPALIGN, MessagePoint.x,
        MessagePoint.y, hWnd, NULL);

    DestroyMenu(hContextMenu);

    RegEdit_OnKeyTreeCommand(hWnd, MenuCommand, TVItem.hItem);

}

/*******************************************************************************
*
*  RegEdit_SetKeyTreeEditMenuItems
*
*  DESCRIPTION:
*     Shared routine between the main menu and the context menu to setup the
*     edit menu items.
*
*  PARAMETERS:
*     hPopupMenu, handle of popup menu to modify.
*     hTreeItem, handle of selected tree item.
*
*******************************************************************************/

VOID
PASCAL
RegEdit_SetKeyTreeEditMenuItems(
    HMENU hPopupMenu,
    HTREEITEM hSelectedTreeItem
    )
{

    UINT EnableFlags;

    EnableFlags = KeyTree_CanDeleteOrRenameItem(g_RegEditData.hKeyTreeWnd,
        hSelectedTreeItem) ? (MF_ENABLED | MF_BYCOMMAND) :
        (MF_GRAYED | MF_BYCOMMAND);

    EnableMenuItem(hPopupMenu, ID_DELETE, EnableFlags);
    EnableMenuItem(hPopupMenu, ID_RENAME, EnableFlags);

}

/*******************************************************************************
*
*  RegEdit_OnKeyTreeDelete
*
*  DESCRIPTION:
*
*  PARAMETERS:
*
*******************************************************************************/

VOID
PASCAL
RegEdit_OnKeyTreeDelete(
    HWND hWnd,
    HTREEITEM hTreeItem
    )
{

    HWND hKeyTreeWnd;
    HKEY hRootKey;
    TCHAR KeyName[MAXKEYNAME];
    HTREEITEM hParentTreeItem;
    TV_ITEM TVItem;

    hKeyTreeWnd = g_RegEditData.hKeyTreeWnd;

    if (!KeyTree_CanDeleteOrRenameItem(hKeyTreeWnd, hTreeItem))
        return;

    if (InternalMessageBox(g_hInstance, hWnd,
        MAKEINTRESOURCE(IDS_CONFIRMDELKEYTEXT),
        MAKEINTRESOURCE(IDS_CONFIRMDELKEYTITLE), MB_ICONWARNING | MB_YESNO) !=
        IDYES)
        return;

    if (hTreeItem == TreeView_GetSelection(hKeyTreeWnd)) {

        if (g_RegEditData.hCurrentSelectionKey != NULL) {

            RegCloseKey(g_RegEditData.hCurrentSelectionKey);
            g_RegEditData.hCurrentSelectionKey = NULL;

        }

    }

    hRootKey = KeyTree_BuildKeyPath(hKeyTreeWnd, hTreeItem, KeyName,
        BKP_TOSUBKEY);

    if (RegDeleteKeyRecursive(hRootKey, KeyName) == ERROR_SUCCESS) {

        SetWindowRedraw(hKeyTreeWnd, FALSE);

        hParentTreeItem = TreeView_GetParent(hKeyTreeWnd, hTreeItem);

        TreeView_DeleteItem(hKeyTreeWnd, hTreeItem);

        //
        //  See if the key that we just deleted was the last child of its
        //  parent.  If so, remove the expand/collapse button.
        //

        if (TreeView_GetChild(hKeyTreeWnd, hParentTreeItem) == NULL) {

            TVItem.mask = TVIF_CHILDREN | TVIF_STATE;
            TVItem.hItem = hParentTreeItem;
            TVItem.cChildren = 0;
            TVItem.state = 0;
            TVItem.stateMask = TVIS_EXPANDED | TVIS_EXPANDEDONCE;
            TreeView_SetItem(hKeyTreeWnd, &TVItem);

        }

        //
        //  Make sure we can see the selected tree item now since it may be
        //  currently off-screen.
        //

        TreeView_EnsureVisible(hKeyTreeWnd, TreeView_GetSelection(hKeyTreeWnd));

        SetWindowRedraw(hKeyTreeWnd, TRUE);

        UpdateWindow(hKeyTreeWnd);

    }

    else {

        TVItem.hItem = hTreeItem;
        TVItem.mask = TVIF_TEXT;
        TVItem.pszText = KeyName;
        TVItem.cchTextMax = sizeof(KeyName)/sizeof(TCHAR);

        TreeView_GetItem(hKeyTreeWnd, &TVItem);

        InternalMessageBox(g_hInstance, hWnd,
            MAKEINTRESOURCE(IDS_DELETEKEYDELETEFAILED),
            MAKEINTRESOURCE(IDS_DELETEKEYERRORTITLE), MB_ICONERROR | MB_OK,
            KeyName);

        //
        //  Need to refresh the tree at this point, as some subkeys may have
        //  been deleted successfully even if we didn't have sufficient
        //  permissions to delete all of them.
        //
        RegEdit_OnKeyTreeRefresh(hWnd);
    }

}

/*******************************************************************************
*
*  RegEdit_OnKeyTreeRename
*
*  DESCRIPTION:
*
*  PARAMETERS:
*
*******************************************************************************/

VOID
PASCAL
RegEdit_OnKeyTreeRename(
    HWND hWnd,
    HTREEITEM hTreeItem
    )
{

    if (KeyTree_CanDeleteOrRenameItem(g_RegEditData.hKeyTreeWnd, hTreeItem))
        KeyTree_EditLabel(g_RegEditData.hKeyTreeWnd, hTreeItem);

}

/*******************************************************************************
*
*  RegEdit_OnKeyTreeRefresh
*
*  DESCRIPTION:
*
*  PARAMETERS:
*
*******************************************************************************/

VOID
PASCAL
RegEdit_OnKeyTreeRefresh(
    HWND hWnd
    )
{

    HDPA hDPA;
    HWND hKeyTreeWnd;
    HTREEITEM hPrevSelectedTreeItem;
    TV_ITEM EnumTVItem;
    TV_ITEM CurrentTVItem;
    HKEY hRootKey;
    TCHAR KeyName[MAXKEYNAME];
    int MaximumSubKeyLength;
    int Index;
    HKEY hEnumKey;
    int CompareResult;
    LPTSTR lpDPAKeyName;
    HTREEITEM hTempTreeItem;

    if ((hDPA = DPA_CreateEx(REFRESH_DPA_GROW, GetProcessHeap())) == NULL)
        return;

    hKeyTreeWnd = g_RegEditData.hKeyTreeWnd;

    RegEdit_SetWaitCursor(TRUE);
    SetWindowRedraw(hKeyTreeWnd, FALSE);

    hPrevSelectedTreeItem = TreeView_GetSelection(hKeyTreeWnd);

    EnumTVItem.mask = TVIF_TEXT;
    EnumTVItem.pszText = KeyName;
    EnumTVItem.cchTextMax = sizeof(KeyName)/sizeof(TCHAR);

    CurrentTVItem.mask = TVIF_STATE | TVIF_CHILDREN;
    CurrentTVItem.stateMask = 0;
    CurrentTVItem.hItem = TreeView_GetRoot(hKeyTreeWnd);

    while (TRUE) {

        TreeView_GetItem(hKeyTreeWnd, &CurrentTVItem);
        hRootKey = KeyTree_BuildKeyPath(hKeyTreeWnd, CurrentTVItem.hItem,
            KeyName, BKP_TOSUBKEY);

        if (CurrentTVItem.state & TVIS_EXPANDED) {

            //
            //  If this isn't a top-level label (and it won't be if hRootKey is
            //  not NULL), then compare the actual contents of the registry
            //  against what we're showing.
            //
            if(hRootKey && RegOpenKeyEx(hRootKey,KeyName,0,KEY_ENUMERATE_SUB_KEYS,&hEnumKey) ==
                ERROR_SUCCESS) {

                //
                //  As a result of adding new keys and renaming existing ones,
                //  the children of this item may be out of order.  For the
                //  following algorithm to work correctly, we must now sort
                //  these keys.
                //

                TreeView_SortChildren(hKeyTreeWnd, CurrentTVItem.hItem, FALSE);

                //
                //  Build a sorted dynamic array of strings that represent the
                //  keys actually in the registry at this time.
                //

                MaximumSubKeyLength = MAXKEYNAME - (lstrlen(KeyName) + 1);
                Index = 0;

                while (RegEnumKey(hEnumKey, Index, KeyName,
                    MaximumSubKeyLength) == ERROR_SUCCESS) {

                    lpDPAKeyName = NULL;
                    Str_SetPtr(&lpDPAKeyName, KeyName);
                    DPA_InsertPtr(hDPA, Index++, lpDPAKeyName);

                }

                RegCloseKey(hEnumKey);
                DPA_Sort(hDPA, DPACompareKeyNames, 0);

                //
                //  Does this key have subkeys anymore?  If not, then we need
                //  to reset it's child flag and remove all of it's children.
                //

                if (Index == 0) {

                    DPA_DeleteAllPtrs(hDPA);

                    TreeView_Expand(hKeyTreeWnd, CurrentTVItem.hItem,
                        TVE_COLLAPSE | TVE_COLLAPSERESET);

                    CurrentTVItem.cChildren = 0;
                    goto SetCurrentTreeItem;

                }

                //
                //  Merge the keys that we found during our above enumeration
                //  with the keys that our key tree lists.  Add and remove
                //  elements from the tree as appropriate.
                //

                lpDPAKeyName = DPA_FastGetPtr(hDPA, --Index);

                EnumTVItem.hItem = TreeView_GetChild(hKeyTreeWnd,
                    CurrentTVItem.hItem);
                if (EnumTVItem.hItem)
                    TreeView_GetItem(hKeyTreeWnd, &EnumTVItem);

                while (Index >= 0 && EnumTVItem.hItem != NULL) {

                    CompareResult = lstrcmpi(KeyName, lpDPAKeyName);

                    if (CompareResult == 0) {

                        EnumTVItem.hItem = TreeView_GetNextSibling(hKeyTreeWnd,
                            EnumTVItem.hItem);
                        if (EnumTVItem.hItem)
                            TreeView_GetItem(hKeyTreeWnd, &EnumTVItem);

                        goto GetNextDPAPointer;

                    }

                    else if (CompareResult > 0) {

                        KeyTree_InsertItem(hKeyTreeWnd, CurrentTVItem.hItem,
                            TVI_SORT, lpDPAKeyName, DoesKeyHaveKids(hEnumKey,
                            lpDPAKeyName), 0);

GetNextDPAPointer:
                        Str_SetPtr(&lpDPAKeyName, NULL);

                        if (--Index >= 0)
                            lpDPAKeyName = DPA_FastGetPtr(hDPA, Index);

                    }

                    else {

                        hTempTreeItem = TreeView_GetNextSibling(hKeyTreeWnd,
                            EnumTVItem.hItem);
                        TreeView_DeleteItem(hKeyTreeWnd, EnumTVItem.hItem);
                        EnumTVItem.hItem = hTempTreeItem;
                        if (EnumTVItem.hItem)
                            TreeView_GetItem(hKeyTreeWnd, &EnumTVItem);

                    }

                }

                //
                //  Once we drop to here, we may have extra items in the key
                //  tree or in the dynamic array.  Process them accordingly.
                //

                if (Index >= 0) {

                    while (TRUE) {

                        KeyTree_InsertItem(hKeyTreeWnd, CurrentTVItem.hItem,
                            TVI_SORT, lpDPAKeyName, DoesKeyHaveKids(hEnumKey,
                            lpDPAKeyName), 0);

                        Str_SetPtr(&lpDPAKeyName, NULL);

                        if (--Index < 0)
                            break;

                        lpDPAKeyName = DPA_FastGetPtr(hDPA, Index);

                    }

                }

                else {

                    while (EnumTVItem.hItem != NULL) {

                        hTempTreeItem = TreeView_GetNextSibling(hKeyTreeWnd,
                            EnumTVItem.hItem);
                        TreeView_DeleteItem(hKeyTreeWnd, EnumTVItem.hItem);
                        EnumTVItem.hItem = hTempTreeItem;

                    }

                }

                DPA_DeleteAllPtrs(hDPA);

            }

            CurrentTVItem.hItem = TreeView_GetChild(hKeyTreeWnd,
                CurrentTVItem.hItem);

        }

        else {

            //
            //  If this isn't a top-level label (and it won't be if hRootKey is
            //  not NULL), then re-check if this key has any children.
            //

            if (hRootKey != NULL) {

                TreeView_Expand(hKeyTreeWnd, CurrentTVItem.hItem, TVE_COLLAPSE |
                    TVE_COLLAPSERESET);

                CurrentTVItem.cChildren = DoesKeyHaveKids(hRootKey, KeyName);

SetCurrentTreeItem:
                TreeView_SetItem(hKeyTreeWnd, &CurrentTVItem);

            }

            //
            //  Because we're at the "bottom" of the TreeView, we now need to
            //  walk to the siblings of this tree item.  And if no siblings
            //  exist, we walk back to the parent and check again for siblings.
            //

            while (TRUE) {

                if ((hTempTreeItem = TreeView_GetNextSibling(hKeyTreeWnd,
                    CurrentTVItem.hItem)) != NULL) {

                    CurrentTVItem.hItem = hTempTreeItem;
                    break;

                }

                if ((CurrentTVItem.hItem = TreeView_GetParent(hKeyTreeWnd,
                    CurrentTVItem.hItem)) == NULL) {

                    //
                    //  We've now walked over all of the tree items, so do any
                    //  cleanup here and exit.
                    //

                    DPA_Destroy(hDPA);

                    SetWindowRedraw(hKeyTreeWnd, TRUE);

                    //
                    //  The selection may have changed as a result of having
                    //  the focus on an nonexistent key.
                    //

                    if (TreeView_GetSelection(hKeyTreeWnd) != hPrevSelectedTreeItem) {
                        RegEdit_OnKeyTreeSelChanged(hWnd, NULL);
                    } else {
                        if (RegEdit_OnValueListRefresh(hWnd) != ERROR_SUCCESS) {
                            //
                            // Its possible that the registry key was deleted and replaced with
                            // an identically-named key.  We should just trigger a selection
                            // change in this case.
                            //
                            RegEdit_OnKeyTreeSelChanged(hWnd, NULL);
                        }
                    }

                    RegEdit_SetWaitCursor(FALSE);

                    return;

                }

            }

        }

    }

}

/*******************************************************************************
*
*  DPACompareKeyNames
*
*  DESCRIPTION:
*     Callback comparision routine for refresh's DPA_Sort call.  Simply returns
*     the result of lstrcmpi.
*
*  PARAMETERS:
*     lpString1,
*     lpString2,
*     lParam, ignored optional data.
*
*******************************************************************************/

int
WINAPI
DPACompareKeyNames(
    LPVOID lpString1,
    LPVOID lpString2,
    LPARAM lParam
    )
{

    return lstrcmpi((LPTSTR) lpString2, (LPTSTR) lpString1);

}

/*******************************************************************************
*
*  RegEdit_OnKeyTreeDisconnect
*
*  DESCRIPTION:
*
*  PARAMETERS:
*
*******************************************************************************/

VOID
PASCAL
RegEdit_OnKeyTreeDisconnect(
    HWND hWnd,
    HTREEITEM hTreeItem
    )
{

    HWND hKeyTreeWnd;
    TV_ITEM TVItem;

    hKeyTreeWnd = g_RegEditData.hKeyTreeWnd;

    //
    //  Disconnect all of the root registry handles that we've opened.
    //

    TVItem.mask = TVIF_PARAM;
    TVItem.hItem = TreeView_GetChild(hKeyTreeWnd, hTreeItem);

    while (TVItem.hItem != NULL) {

        TreeView_GetItem(hKeyTreeWnd, &TVItem);

        RegCloseKey((HKEY) TVItem.lParam);

        TVItem.hItem = TreeView_GetNextSibling(hKeyTreeWnd, TVItem.hItem);

    }

    TreeView_DeleteItem(hKeyTreeWnd, hTreeItem);

}

/*******************************************************************************
*
*  RegEdit_UpdateStatusBar
*
*  DESCRIPTION:
*     Show the full registry path in the status bar, for lack of anything
*     better to do with it.
*
*  PARAMETERS:
*     (none).
*
*******************************************************************************/

VOID
PASCAL
RegEdit_UpdateStatusBar(
    VOID
    )
{

    HWND hKeyTreeWnd;
    TCHAR KeyName[MAXKEYNAME*2];

    hKeyTreeWnd = g_RegEditData.hKeyTreeWnd;
    KeyTree_BuildKeyPath(hKeyTreeWnd, TreeView_GetSelection(hKeyTreeWnd),
        KeyName, BKP_TOCOMPUTER);
    SetWindowText(g_RegEditData.hStatusBarWnd, KeyName);

}

/*******************************************************************************
*
*  RegEdit_OnCopyKeyName
*
*  DESCRIPTION:
*
*  PARAMETERS:
*
*******************************************************************************/

VOID
PASCAL
RegEdit_OnCopyKeyName(
    HWND hWnd,
    HTREEITEM hTreeItem
    )
{

    TCHAR KeyName[MAXKEYNAME*2];
    UINT KeyNameLength;
    HANDLE hClipboardData;
    LPTSTR lpClipboardData;

    KeyTree_BuildKeyPath(g_RegEditData.hKeyTreeWnd, hTreeItem, KeyName,
        BKP_TOSYMBOLICROOT);
    KeyNameLength = (lstrlen(KeyName) + 1) * sizeof(TCHAR);

    if (OpenClipboard(hWnd)) {

        if ((hClipboardData = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE,
            KeyNameLength)) != NULL) {

            lpClipboardData = (LPTSTR) GlobalLock(hClipboardData);
            CopyMemory(lpClipboardData, KeyName, KeyNameLength);
            GlobalUnlock(hClipboardData);

            EmptyClipboard();
            SetClipboardData(CF_UNICODETEXT, hClipboardData);

        }

        CloseClipboard();

    }

}

/*******************************************************************************
*
*  KeyTree_BuildKeyPath
*
*  DESCRIPTION:
*
*  PARAMETERS:
*     hTreeViewWnd, handle of KeyTree window.
*     hTreeItem, handle of tree item to begin building from.
*     lpKeyPath, buffer to store path in.
*     fIncludeSymbolicRootName, TRUE if root key's name should be included
*        (e.g., HKEY_LOCAL_MACHINE), else FALSE.
*
*******************************************************************************/

HKEY
PASCAL
KeyTree_BuildKeyPath(
    HWND hTreeViewWnd,
    HTREEITEM hTreeItem,
    LPTSTR lpKeyPath,
    UINT ToFlags
    )
{

    TV_ITEM TVItem;
    TCHAR SubKeyName[MAXKEYNAME*2];

    *lpKeyPath = '\0';

    TVItem.mask = TVIF_TEXT | TVIF_PARAM;
    TVItem.hItem = hTreeItem;
    TVItem.pszText = (LPTSTR) SubKeyName;
    TVItem.cchTextMax = sizeof(SubKeyName)/sizeof(TCHAR);

    while (TRUE) {

        TreeView_GetItem(hTreeViewWnd, &TVItem);

        if (TVItem.lParam != 0 && !(ToFlags & BKP_TOSYMBOLICROOT))
            break;

        if (*lpKeyPath != '\0') {

            lstrcat(SubKeyName, TEXT("\\"));
            lstrcat(SubKeyName, lpKeyPath);

        }

        lstrcpy(lpKeyPath, SubKeyName);

        if (TVItem.lParam != 0 && (ToFlags & BKP_TOCOMPUTER) != BKP_TOCOMPUTER)
            break;

        TVItem.hItem = TreeView_GetParent(hTreeViewWnd, TVItem.hItem);

        if (TVItem.hItem == NULL) {

            if ((ToFlags & BKP_TOCOMPUTER) != BKP_TOCOMPUTER)
                *lpKeyPath = '\0';

            break;

        }

    }

    return ((HKEY) TVItem.lParam);

}

/*******************************************************************************
*
*  KeyTree_InsertItem
*
*  DESCRIPTION:
*
*  PARAMETERS:
*
*******************************************************************************/

HTREEITEM
PASCAL
KeyTree_InsertItem(
    HWND hKeyTreeWnd,
    HTREEITEM hParent,
    HTREEITEM hInsertAfter,
    LPCTSTR lpText,
    UINT fHasKids,
    LPARAM lParam
    )
{

    TV_INSERTSTRUCT TVInsertStruct;

    TVInsertStruct.hParent = hParent;
    TVInsertStruct.hInsertAfter = hInsertAfter;
    TVInsertStruct.item.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE |
        TVIF_PARAM | TVIF_CHILDREN;
    //  TVInsertStruct.item.hItem = NULL;
    //  TVInsertStruct.item.state = 0;
    //  TVInsertStruct.item.stateMask = 0;
    TVInsertStruct.item.pszText = (LPTSTR) lpText;
    //  TVInsertStruct.item.cchTextMax = lstrlen(lpText);
    TVInsertStruct.item.iImage = IMAGEINDEX(IDI_FOLDER);
    TVInsertStruct.item.iSelectedImage = IMAGEINDEX(IDI_FOLDEROPEN);
    TVInsertStruct.item.cChildren = fHasKids;
    TVInsertStruct.item.lParam = lParam;

    return TreeView_InsertItem(hKeyTreeWnd, &TVInsertStruct);

}

/*******************************************************************************
*
*  KeyTree_ExpandBranch
*
*  DESCRIPTION:
*
*  PARAMETERS:
*     hTreeViewWnd, handle of KeyTree window.
*     hTreeItem, handle of tree item to edit.
*
*******************************************************************************/

BOOL
PASCAL
KeyTree_ExpandBranch(
    HWND hKeyTreeWnd,
    HTREEITEM hExpandingTreeItem
    )
{

    TCHAR KeyName[MAXKEYNAME];
    HKEY hRootKey;
    HKEY hEnumKey;
    int Index;
    int MaximumSubKeyLength;

    //
    //  Nothing special needs to be done with a top-level label such as "My
    //  Computer" or a network computer name.  It's children are already filled
    //  in and are always valid.
    //

    if (TreeView_GetParent(hKeyTreeWnd, hExpandingTreeItem) == NULL)
        return TRUE;

    hRootKey = KeyTree_BuildKeyPath(hKeyTreeWnd, hExpandingTreeItem,
        KeyName, FALSE);

    if(RegOpenKeyEx(hRootKey,KeyName,0,KEY_ENUMERATE_SUB_KEYS,&hEnumKey) != ERROR_SUCCESS)
        return FALSE;

    MaximumSubKeyLength = MAXKEYNAME - (lstrlen(KeyName) + 1);
    Index = 0;

    while (RegEnumKey(hEnumKey, Index++, KeyName, MaximumSubKeyLength) ==
        ERROR_SUCCESS) {

        KeyTree_InsertItem(hKeyTreeWnd, hExpandingTreeItem, TVI_FIRST,
            KeyName, DoesKeyHaveKids(hEnumKey, KeyName), 0);

    }

    RegCloseKey(hEnumKey);

    //
    //  Sort the subkeys _after_ inserting all the items.  The above insert
    //  used to specify TVI_SORT, but on NT, expanding a key with several
    //  subkeys (e.g., HKEY_CLASSES_ROOT) would take several seconds!
    //

    TreeView_SortChildren(hKeyTreeWnd, hExpandingTreeItem, FALSE);


    return TRUE;

}

/*******************************************************************************
*
*  DoesKeyHaveKids
*
*  DESCRIPTION:
*     Checks if the given key path has any subkeys or not.
*
*  PARAMETERS:
*
*******************************************************************************/

BOOL
PASCAL
DoesKeyHaveKids(
    HKEY hKey,
    LPTSTR lpKeyName
    )
{

    BOOL fHasKids;
    HKEY hCheckChildrenKey;
    DWORD cSubKeys;

    fHasKids = FALSE;

    if(RegOpenKeyEx(hKey,lpKeyName,0,KEY_QUERY_VALUE|KEY_ENUMERATE_SUB_KEYS,
        &hCheckChildrenKey) == ERROR_SUCCESS) {

        if (RegQueryInfoKey(hCheckChildrenKey, NULL, NULL, NULL, &cSubKeys,
            NULL, NULL, NULL, NULL, NULL, NULL, NULL) == ERROR_SUCCESS &&
            cSubKeys > 0)
            fHasKids = TRUE;

        RegCloseKey(hCheckChildrenKey);

    }

    return fHasKids;

}

/*******************************************************************************
*
*  KeyTree_EditLabel
*
*  DESCRIPTION:
*
*  PARAMETERS:
*     hTreeViewWnd, handle of KeyTree window.
*     hTreeItem, handle of tree item to edit.
*
*******************************************************************************/

VOID
PASCAL
KeyTree_EditLabel(
    HWND hKeyTreeWnd,
    HTREEITEM hTreeItem
    )
{

    g_RegEditData.fAllowLabelEdits = TRUE;

    TreeView_EditLabel(hKeyTreeWnd, hTreeItem);

    g_RegEditData.fAllowLabelEdits = FALSE;

}

/*******************************************************************************
*
*  KeyTree_CanDeleteOrRenameItem
*
*  DESCRIPTION:
*
*  PARAMETERS:
*     hTreeViewWnd, handle of KeyTree window.
*     hTreeItem, handle of tree item to check.
*
*******************************************************************************/

BOOL
PASCAL
KeyTree_CanDeleteOrRenameItem(
    HWND hWnd,
    HTREEITEM hTreeItem
    )
{

    TV_ITEM TVItem;

    //
    //  Check if the selected tree item is null.  This will occur when viewing
    //  the Edit popup from the main menu with no selection made.
    //

    if (hTreeItem != NULL) {

        //
        //  Check if this tree item has any reference data indicating that it
        //  is a predefined root.  Predefined roots cannot be renamed or
        //  deleted.
        //

        TVItem.hItem = hTreeItem;
        TVItem.mask = TVIF_PARAM;
        TreeView_GetItem(hWnd, &TVItem);

        if ((HKEY) TVItem.lParam == NULL) {

            //
            //  Check that this isn't a top-level item such as "My Computer" or
            //  a remote registry connection.
            //

            if (TreeView_GetParent(hWnd, hTreeItem) != NULL)
                return TRUE;

        }

    }

    return FALSE;

}


//------------------------------------------------------------------------------
//  KeyTree_GetRootKey
//
//  DESCRIPTION: Returns the root key of the item (HKEY_ ...)
//
//  PARAMETERS:  hTreeItem - treeview item
//------------------------------------------------------------------------------
HKEY KeyTree_GetRootKey(HTREEITEM hTreeItem)
{
    TV_ITEM TVItem;
    TVItem.mask = TVIF_PARAM;
    TVItem.hItem = hTreeItem;

    TreeView_GetItem(g_RegEditData.hKeyTreeWnd, &TVItem);

    while (!TVItem.lParam)
    {
        TVItem.hItem = TreeView_GetParent(g_RegEditData.hKeyTreeWnd, TVItem.hItem);
        TreeView_GetItem(g_RegEditData.hKeyTreeWnd, &TVItem);
    }

    return ((HKEY) TVItem.lParam);
}


//------------------------------------------------------------------------------
//  KeyTree_GetKeyName
//
//  DESCRIPTION: Returns the TEXT of an item
//
//  PARAMETERS:  hTreeItem - treeview item
//               pszText - pointer to an TCHAR array
//               cchMax - number of characters in the array
//------------------------------------------------------------------------------
PTSTR KeyTree_GetKeyName(HTREEITEM hTreeItem, PTSTR pszName, int cchNameMax)
{
    TV_ITEM TVItem;

    pszName[0] = TEXT('\0');

    TVItem.mask = TVIF_TEXT;
    TVItem.hItem = hTreeItem;
    TVItem.pszText = pszName;
    TVItem.cchTextMax = cchNameMax;

    TreeView_GetItem(g_RegEditData.hKeyTreeWnd, &TVItem);

    return TVItem.pszText;
}

