//
//  Folder.C
//
//  Copyright (C) Microsoft, 1994,1995 All Rights Reserved.
//
//  History:
//  ral 6/23/94 - First pass
//  3/20/95  [stevecat] - NT port & real clean up, unicode, etc.
//
//

#include "priv.h"
#include "appwiz.h"
#include "help.h"        // Help context IDs

typedef struct _FILEITEMDATA {
    DWORD   dwFlags;
    TCHAR   szPath[1];
} FILEITEMDATA, * LPFILEITEMDATA;

#define FIDFLAG_CANADDNEW      0x00000001
#define FIDFLAG_CANDEL         0x00000002
#define FIDFLAG_ISFOLDER       0x00000004
#define FIDFLAG_ISPROGS        0x00000008


//
//
//

int CALLBACK CompareFolderCB(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
{
    #define lpfid1  ((LPFILEITEMDATA)lParam1)
    #define lpfid2  ((LPFILEITEMDATA)lParam2)
    #define b1IsDir (lpfid1->dwFlags & FIDFLAG_ISFOLDER)
    #define b2IsDir (lpfid2->dwFlags & FIDFLAG_ISFOLDER)

    //
    // Programs folder always goes to top
    //

    if (lpfid1->dwFlags & FIDFLAG_ISPROGS)
    {
        return(-1);
    }

    if (lpfid2->dwFlags & FIDFLAG_ISPROGS)
    {
        return(1);
    }

    if (b1IsDir == b2IsDir)
    {
        return(lstrcmpi(lpfid1->szPath, lpfid2->szPath));
    }
    else
    {
        if (b1IsDir)
        {
            return(-1);
        }
        else
        {
            return(1);
        }
    }

    #undef  b1IsDir
    #undef  b2IsDir
    #undef  lpfid1
    #undef  lpfid2
}


//
//  Sorts the specified folder so that folders appear at the top and all
//  files appear in alphabetical order below.
//

void SortFolder(HWND hwndTree, HTREEITEM hParent)
{
    TV_SORTCB sSortCB;

    sSortCB.hParent = hParent;
    sSortCB.lpfnCompare = CompareFolderCB;
    sSortCB.lParam = 0;

    TreeView_SortChildrenCB(hwndTree, &sSortCB, FALSE);
}



//
//  Adds a new folder for the specifed path and returns its HTREEITEM.        If
//  it is unable to add the item then NULL is returned.
//  NOTE:  If dwFileAttributes == AI_NOATTRIB (-1) then no attributes specified.
//           If pidl is NULL then no pidl specified.
//

HTREEITEM AddItem(HWND hwndTree, LPCTSTR pszPath,
                    HTREEITEM hParent, LPITEMIDLIST pidl,
                    DWORD dwFlags)
{
    HTREEITEM       newhti = NULL;
    LPFILEITEMDATA  lpfid = (LPFILEITEMDATA)LocalAlloc(LMEM_FIXED,
                             sizeof(FILEITEMDATA) + (lstrlen(pszPath) + 1)*sizeof(TCHAR));
    if (lpfid)
    {
        TV_INSERTSTRUCT tvis;
        lpfid->dwFlags = dwFlags;
        lstrcpy(lpfid->szPath, pszPath);

        tvis.item.pszText = LPSTR_TEXTCALLBACK;
        tvis.item.iImage = tvis.item.iSelectedImage = I_IMAGECALLBACK;
        tvis.item.mask = TVIF_TEXT | TVIF_PARAM | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
        tvis.item.lParam = (LPARAM)lpfid;

        tvis.hParent = hParent;
        tvis.hInsertAfter = TVI_LAST;

        newhti = TreeView_InsertItem(hwndTree, &tvis);
        if (!newhti)
            LocalFree((LPVOID)lpfid);
    }
    return newhti;
}

//
//  Flags for FillFolder
//

#define FFF_AddFiles                1
#define FFF_AddDirs                2

//
//  Recursively add all folders below CurDir to the tree below hParent
//

BOOL IsFolderShortcut(LPCTSTR pszName)
{
    SHFOLDERCUSTOMSETTINGS fcs = {0};
    CLSID clsid = {0};
    fcs.dwSize = sizeof(fcs);
    fcs.dwMask = FCSM_CLSID;
    fcs.pclsid = &clsid;

    if (SUCCEEDED(SHGetSetFolderCustomSettings(&fcs, pszName, FCS_READ)))
    {
        return IsEqualGUID(&clsid, &CLSID_FolderShortcut);
    }
    return FALSE;
}

void FillFolder(HWND hwndTree, LPTSTR lpszCurDir, LPTSTR lpszExclude,
                    HTREEITEM hParent, DWORD dwFlags)
{
    int     iStrTerm = lstrlen(lpszCurDir);
    WIN32_FIND_DATA fd;
    HANDLE  hfind;
    HTREEITEM hNewItem = NULL;
    #define bAddFiles (dwFlags & FFF_AddFiles)
    #define bAddDirs  (dwFlags & FFF_AddDirs)

    lstrcat(lpszCurDir, TEXT("\\*.*"));

    hfind = FindFirstFile(lpszCurDir, &fd);

    if (hfind != INVALID_HANDLE_VALUE)
    {
        do
        {
            BOOL bIsDir = (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);

            if (((bAddFiles && !bIsDir) ||
                // skip "." and ".." and hidden files
                (bAddDirs && bIsDir && (fd.cFileName[0] != TEXT('.')))) &&
                !(fd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN))
            {
                lpszCurDir[iStrTerm] = TEXT('\\');
                lstrcpy(lpszCurDir + iStrTerm + 1, fd.cFileName);

                // let's fudge it -- if it's a folder shortcut, don't treat it
                // like a real folder, since we can't navigate into it anyway
                // and it's not worth the trouble to try.
                if (bIsDir && IsFolderShortcut(lpszCurDir))
                {
                    bIsDir = FALSE;
                }

                //
                // Don't add this if it's supposed to be excluded
                //

                if (!lpszExclude || !bIsDir ||
                    lstrcmpi(lpszExclude, lpszCurDir) != 0)
                {
                    hNewItem = AddItem(hwndTree, lpszCurDir, hParent, NULL,
                                        FIDFLAG_CANADDNEW | FIDFLAG_CANDEL |
                                        (bIsDir ? FIDFLAG_ISFOLDER : 0));
                    if (bIsDir)
                    {
                        FillFolder(hwndTree, lpszCurDir, NULL,
                                   hNewItem, dwFlags);
                    }
                }
            }
        } while (FindNextFile(hfind, &fd));

        FindClose(hfind);
    }

    lpszCurDir[iStrTerm] = 0;

    //
    //  Non-null if any items added to folder.
    //

    if (hNewItem)
    {
        SortFolder(hwndTree, hParent);
        if (!bAddFiles)
        {
            TreeView_Expand(hwndTree, hParent, TVE_EXPAND);
        }
    }
    #undef  bAddFiles
    #undef  bRecurse
}


//
//  Returns a pointer to the directory string for the currently selected
//  item.
//

LPFILEITEMDATA GetCurSel(HWND hwndTree, HTREEITEM * lphtiSel)
{
    TV_ITEM  tvi;

    tvi.hItem = TreeView_GetSelection(hwndTree);

    if (lphtiSel)
    {
        *lphtiSel = tvi.hItem;
    }

    if (tvi.hItem == NULL)
    {
        return(NULL);
    }

    tvi.mask = TVIF_PARAM;
    TreeView_GetItem(hwndTree, &tvi);

    return((LPFILEITEMDATA)tvi.lParam);
}


//
//  Add the specified special folder..
//

HTREEITEM AddSpecialFolder(HWND hwndTree, HTREEITEM htiParent, int nFolder,
                           LPTSTR pszPath, DWORD dwFlags)
{
    LPITEMIDLIST pidl = NULL;
    HTREEITEM    hti = NULL;

    if (SUCCEEDED(SHGetSpecialFolderLocation(hwndTree, nFolder, &pidl)))
    {
        if (SHGetPathFromIDList(pidl, pszPath))
        {
            //
            //  For the desktop, we want the desktop directory, but the icon
            //  for the magic desktop PIDL.
            //
            if (nFolder == CSIDL_DESKTOPDIRECTORY)
            {
                SHFree(pidl);
                if (FAILED(SHGetSpecialFolderLocation(hwndTree, CSIDL_DESKTOP, &pidl)))
                {
                    pidl = NULL;
                }
            }

            if (NULL != pidl)
            {
                hti = AddItem(hwndTree, pszPath, htiParent, pidl,
                              FIDFLAG_ISFOLDER | dwFlags);
            }
        }
    }
    if (NULL != pidl)
    {
        SHFree(pidl);
    }
    return(hti);
}


BOOL _inline MakePrgIcon0Index(HWND hwndTree, HIMAGELIST himl)
{
    LPITEMIDLIST pidl;

    if (SUCCEEDED(SHGetSpecialFolderLocation(hwndTree, CSIDL_PROGRAMS, &pidl)))
    {
        SHFILEINFO   fi;
        BOOL_PTR fOk = SHGetFileInfo( (LPTSTR) pidl, 0, &fi, sizeof( fi ),
                                      SHGFI_ICON | SHGFI_SMALLICON | SHGFI_PIDL );

        SHFree( pidl );
        
        if (fOk)
        {
            ImageList_AddIcon(himl, fi.hIcon);
            DestroyIcon(fi.hIcon);
            return(TRUE);
        }
    }
    return FALSE;
}


//
//  Initialize the tree
//

void InitFolderTree( HWND hwndTree, BOOL bAddFiles, HIMAGELIST *phiml )
{
    HCURSOR    hcurOld = SetCursor(LoadCursor(NULL, IDC_WAIT));
    HTREEITEM  htiStart = NULL;
    HTREEITEM  htiPrgs = NULL;
    TCHAR      szPathStart[MAX_PATH];
    TCHAR      szPathPrgs[MAX_PATH];
    UINT       flags = ILC_MASK | ILC_COLOR32;
    HIMAGELIST himl;
    
    if(IS_WINDOW_RTL_MIRRORED(hwndTree))
    {
        flags |= ILC_MIRROR;
    }
    himl = ImageList_Create(GetSystemMetrics(SM_CXSMICON),
                                       GetSystemMetrics(SM_CYSMICON),
                                       flags, 10, 1);

    if (phiml)
        *phiml = himl;

    if (!himl)
    {
        return;
    }

    TreeView_SetImageList(hwndTree, himl, TVSIL_NORMAL);

    //
    // Add the programs folder as index 0.  All sub-folders of programs
    // will also have the same icon.  This saves both memory and time.
    //

    if (!MakePrgIcon0Index(hwndTree, himl))
    {
        return;
    }

    if (!bAddFiles)
    {
        AddSpecialFolder(hwndTree, TVI_ROOT, CSIDL_DESKTOPDIRECTORY, szPathStart, 0);
    }

    htiStart = AddSpecialFolder(hwndTree, TVI_ROOT, CSIDL_STARTMENU, szPathStart, FIDFLAG_CANADDNEW);

    if (htiStart)
    {
        htiPrgs = AddSpecialFolder(hwndTree, htiStart, CSIDL_PROGRAMS, szPathPrgs, FIDFLAG_CANADDNEW | FIDFLAG_ISPROGS);
        if (htiPrgs)
        {
            FillFolder(hwndTree, szPathPrgs, NULL, htiPrgs,
                       FFF_AddDirs | (bAddFiles ? FFF_AddFiles : 0));
            //
            // Now fill in the rest of the start menu, excluding programs
            //

            FillFolder(hwndTree, szPathStart, szPathPrgs, htiStart,
                       FFF_AddDirs | (bAddFiles ? FFF_AddFiles : 0));
        }
    }

    //
    // Now select and expand the programs folder.
    //

    if (htiPrgs)
    {
        TreeView_SelectItem(hwndTree, htiPrgs);
        if (bAddFiles)
        {
            TreeView_Expand(hwndTree, htiPrgs, TVE_EXPAND);
        }
    }
    SetCursor(hcurOld);
}


//
//  Delete Selected Item
//

VOID RemoveSelItem(HWND hDlg, HWND hwndTree)
{
    HTREEITEM hCur;
    LPFILEITEMDATA lpfid = GetCurSel(hwndTree, &hCur);

    if (!lpfid)
    {
        ShellMessageBox(g_hinst, hDlg, MAKEINTRESOURCE(IDS_NONESEL),
                        0, MB_OK | MB_ICONEXCLAMATION);
    }
    else
    {
        if (lpfid->dwFlags & FIDFLAG_CANDEL)
        {
            TCHAR szFileDblNull[MAX_PATH+1];

            SHFILEOPSTRUCT sFileOp =
            {
                hDlg,
                FO_DELETE,
                szFileDblNull,
                NULL,
                (lpfid->dwFlags & FIDFLAG_ISFOLDER) ?
                FOF_ALLOWUNDO :
                FOF_SILENT | FOF_ALLOWUNDO,
            };

            lstrcpy(szFileDblNull, lpfid->szPath);

            szFileDblNull[lstrlen(szFileDblNull)+1] = 0;

            if (!SHFileOperation(&sFileOp))
            {
                if (!(sFileOp.fAnyOperationsAborted))
                {
                    TreeView_DeleteItem(hwndTree, hCur);
                }
            }

        }
        else
        {
            ShellMessageBox(g_hinst, hDlg, MAKEINTRESOURCE(IDS_CANTDELETE),
                            0, MB_OK | MB_ICONEXCLAMATION, PathFindFileName(lpfid->szPath));
        }
    }
}

/////////////////////////////////////////////////////////////////////////////
//  END SHARED CODE.  BEGIN WIZARD SPECIFIC CODE.
/////////////////////////////////////////////////////////////////////////////


//
//  Returns -1 if no item is selected, otherwise, sets lpwd->lpszFolder
//  to point to the appropriate string, and returns 0.
//

LPARAM PickFolderNextHit(LPWIZDATA lpwd)
{
    LPFILEITEMDATA lpfid = GetCurSel(GetDlgItem(lpwd->hwnd, IDC_FOLDERTREE), NULL);

    if (lpfid)
    {
        lpwd->lpszFolder = (LPTSTR)&(lpfid->szPath);
        lpwd->szProgDesc[0] = 0;
        return(0);
    }
    else
    {
        return(-1);
    }
}


//
//  Creates a new, empty folder.
//

VOID CreateNewFolder(LPWIZDATA lpwd)
{
    TCHAR          szNewName[MAX_PATH];
    HTREEITEM      hParent;
    LPFILEITEMDATA lpfidParent = GetCurSel(GetDlgItem(lpwd->hwnd, IDC_FOLDERTREE), &hParent);

    if (lpfidParent && (lpfidParent->dwFlags & FIDFLAG_CANADDNEW))
    {
        int   iDirLen = lstrlen(lpfidParent->szPath);
        TCHAR szNewShort[10];
        TCHAR szNewLong[80];

        LoadString(g_hinst, IDS_NEWFOLDERSHORT, szNewShort, ARRAYSIZE(szNewShort));
        LoadString(g_hinst, IDS_NEWFOLDERLONG, szNewLong, ARRAYSIZE(szNewLong));

        PathMakeUniqueName(szNewName, ARRAYSIZE(szNewName),
                           szNewShort, szNewLong, lpfidParent->szPath);
        if (CreateDirectory(szNewName, NULL))
        {
            HWND    hwndTree = GetDlgItem(lpwd->hwnd, IDC_FOLDERTREE);
            HTREEITEM hNewDude = AddItem(hwndTree, szNewName, hParent, NULL,
                              FIDFLAG_ISFOLDER | FIDFLAG_CANADDNEW | FIDFLAG_CANDEL);

            if (hNewDude == NULL)
            {
                TraceMsg(TF_ERROR, "%s", "Unable to add new folder to tree.");
            }

            if (hNewDude)
            {
                SortFolder(hwndTree, hParent);
                TreeView_SelectItem(hwndTree, hNewDude);
                TreeView_EditLabel(hwndTree, hNewDude);
            }
        }
        else
        {
            TraceMsg(TF_ERROR, "%s", "Unable to create new directory");
        }
    }
    else
    {
        TraceMsg(TF_ERROR, "%s", "No group selected.  Can't create directory.");
    }
}


//
//  Begin editing a tree label.  This function returns FALSE for success, and
//  TRUE for failure.
//

BOOL BeginEdit(LPWIZDATA lpwd, TV_DISPINFO * lptvdi)
{
    if (TreeView_GetParent(lptvdi->hdr.hwndFrom, lptvdi->item.hItem))
    {
        lpwd->dwFlags |= WDFLAG_INEDITMODE;
        return FALSE;
    }
    else
    {
        return TRUE;
    }
}


//
//  Return FALSE if rename can't happen.  True if it worked.
//

BOOL EndEdit(LPWIZDATA lpwd, TV_DISPINFO * lptvdi)
{
    BOOL bWorked = FALSE;
    #define lpszNewName (LPTSTR)lptvdi->item.pszText
    #define lpfidOld ((LPFILEITEMDATA)(lptvdi->item.lParam))
    #define hCurItem lptvdi->item.hItem;

    lpwd->dwFlags &= ~WDFLAG_INEDITMODE;

    if (lpszNewName)
    {
        LPFILEITEMDATA lpfidNew = (LPFILEITEMDATA)LocalAlloc(LMEM_FIXED,
                                   sizeof(LPFILEITEMDATA)+MAX_PATH*sizeof(TCHAR));

        if (lpfidNew)
        {
            lpfidNew->dwFlags = lpfidOld->dwFlags;

            lstrcpy(lpfidNew->szPath, lpfidOld->szPath);

            PathRemoveFileSpec(lpfidNew->szPath);

            PathCleanupSpec(lpfidNew->szPath, lpszNewName);

            PathCombine(lpfidNew->szPath, lpfidNew->szPath, lpszNewName);

            if (MoveFile(lpfidOld->szPath, lpfidNew->szPath))
            {
                TV_ITEM tvi;
                tvi.hItem = hCurItem;
                tvi.mask = TVIF_PARAM;
                tvi.lParam = (LPARAM)lpfidNew;
                TreeView_SetItem(lptvdi->hdr.hwndFrom, &tvi);
                bWorked = TRUE;
            }
            else
            {
                TraceMsg(TF_ERROR, "%s", "Unable to rename directory");
            }
            LocalFree(bWorked ? lpfidOld : lpfidNew);
        }
    }

    return(bWorked);

    #undef lpszNewName
    #undef lpfidOld
    #undef hCurItem
}


//
//  Called when Next or Back is hit to force the end of label editing.
//

void ForceEndEdit(LPWIZDATA lpwd)
{
    if (lpwd->dwFlags & WDFLAG_INEDITMODE)
    {
        TreeView_EndEditLabelNow(GetDlgItem(lpwd->hwnd, IDC_FOLDERTREE), FALSE);
    }
}


void FillInItem(TV_DISPINFO * ptvdi)
{
    SHFILEINFO fi;

    #define lpfid ((LPFILEITEMDATA)(ptvdi->item.lParam))
    if (SHGetFileInfo(lpfid->szPath, 0, &fi, sizeof(fi),
                      SHGFI_ICON | SHGFI_DISPLAYNAME | SHGFI_SMALLICON))
    {
        if (ptvdi->item.mask & TVIF_IMAGE)
        {
            ptvdi->item.iImage = ptvdi->item.iSelectedImage =
                  ImageList_AddIcon(TreeView_GetImageList(ptvdi->hdr.hwndFrom, TVSIL_NORMAL),
                                    fi.hIcon);
            ptvdi->item.mask |= TVIF_SELECTEDIMAGE;
        }

        if (ptvdi->item.mask & TVIF_TEXT)
        {
            StrCpyN(ptvdi->item.pszText, fi.szDisplayName, ptvdi->item.cchTextMax);
        }

        DestroyIcon(fi.hIcon);

        ptvdi->item.mask |= TVIF_DI_SETITEM;
    }
}



//
//  Main dialog procedure for tree of folders
//

BOOL_PTR CALLBACK PickFolderDlgProc(HWND hDlg, UINT message , WPARAM wParam, LPARAM lParam)
{
    NMHDR *lpnm = NULL;
    LPPROPSHEETPAGE lpp = (LPPROPSHEETPAGE)(GetWindowLongPtr(hDlg, DWLP_USER));
    LPWIZDATA lpwd = lpp ? (LPWIZDATA)lpp->lParam : NULL;

    switch(message)
    {
        case WM_NOTIFY:
            lpnm = (NMHDR *)lParam;
            if(lpnm)
            {
                switch(lpnm->code)
                {
                    case PSN_SETACTIVE:
                        if(lpwd)
                        {
                            if (lpwd->dwFlags & WDFLAG_LINKHEREWIZ)
                            {
                                SetDlgMsgResult(hDlg, WM_NOTIFY, -1);
                            }
                            else
                            {
                                lpwd->hwnd = hDlg;
                                PropSheet_SetWizButtons(GetParent(hDlg),
                                                        (lpwd->dwFlags & WDFLAG_NOBROWSEPAGE) ?
                                                          PSWIZB_NEXT : PSWIZB_BACK | PSWIZB_NEXT);

                                PostMessage(hDlg, WMPRIV_POKEFOCUS, 0, 0);
                            }
                        }
                        break;

                    case PSN_WIZBACK:
                        if(lpwd)
                        {
                            ForceEndEdit(lpwd);
                            SetDlgMsgResult(hDlg, WM_NOTIFY, 0);
                        }
                        break;

                    case PSN_WIZNEXT:
                        if(lpwd)
                        {
                            ForceEndEdit(lpwd);
                            SetDlgMsgResult(hDlg, WM_NOTIFY, PickFolderNextHit(lpwd));
                        }
                        break;

                    case PSN_RESET:
                        if(lpwd)
                        {
                            CleanUpWizData(lpwd);
                        }
                        break;

                    case NM_DBLCLK:
                        PropSheet_PressButton(GetParent(hDlg), PSBTN_NEXT);
                        break;

                    #define lpfidNew ((LPFILEITEMDATA)(((LPNM_TREEVIEW)lParam)->itemNew.lParam))

                    case TVN_SELCHANGED:
                        Button_Enable(GetDlgItem(hDlg, IDC_NEWFOLDER),
                                      (lpfidNew->dwFlags & FIDFLAG_CANADDNEW));
                        break;
                    #undef lpfidNew

                    #define lptvdi ((TV_DISPINFO *)lParam)

                    case TVN_BEGINLABELEDIT:
                        if(lpwd)
                        {
                            SetDlgMsgResult(hDlg, WM_NOTIFY, BeginEdit(lpwd, lptvdi));
                        }
                        break;

                    case TVN_ENDLABELEDIT:
                        if(lpwd)
                        {
                            SetDlgMsgResult(hDlg, WM_NOTIFY, EndEdit(lpwd, lptvdi));
                        }
                        break;
                    #undef lptvdi

                    #define lptvn ((LPNM_TREEVIEW)lParam)

                    case TVN_ITEMEXPANDING:
                        if (lptvn->action != TVE_EXPAND)
                        {
                            SetDlgMsgResult(hDlg, WM_NOTIFY, -1);
                        }
                        break;

                    case TVN_DELETEITEM:
                        if (lptvn->itemOld.lParam)
                        {
                            LocalFree((LPVOID)lptvn->itemOld.lParam);
                        }
                        break;
                    #undef lptvn

                    case TVN_GETDISPINFO:
                        FillInItem(((TV_DISPINFO *)lParam));
                        break;

                    default:
                        return FALSE;
                }
            }
            break;

        case WM_INITDIALOG:
            lpwd = InitWizSheet(hDlg, lParam, 0);
            if(lpwd)
            {
                lpwd->himl = NULL;

                if( !( lpwd->dwFlags & WDFLAG_LINKHEREWIZ ) )
                {
                    InitFolderTree( GetDlgItem( hDlg, IDC_FOLDERTREE ),
                                    FALSE, &lpwd->himl );
                }
            }
            break;


        case WM_NCDESTROY:
            //
            //  See if we should destroy the himl...
            //

            if(lpwd)
            {
                if (lpwd->himl)
                {
                    ImageList_Destroy(lpwd->himl);
                    lpwd->himl = NULL;  // make sure not twice
                }
            }
            return FALSE;


        case WMPRIV_POKEFOCUS:
            SetFocus(GetDlgItem(hDlg, IDC_FOLDERTREE));
            break;

        case WM_COMMAND:
            switch (GET_WM_COMMAND_ID(wParam, lParam))
            {
                case IDC_NEWFOLDER:
                    if(lpwd)
                    {
                        CreateNewFolder(lpwd);
                    }
                    break;

             ///   case IDC_DELFOLDER:
             ///   {
             ///       HWND    hTree = GetDlgItem(hDlg, IDC_FOLDERTREE);
             ///       RemoveSelItem(hDlg, hTree);
             ///       SetFocus(hTree);
             ///       break;
             ///   }
            }

        default:
            return FALSE;

    }
    return TRUE;
}



/////////////////////////////////////////////////////////////////////////////
//  END WIZARD SPECIFIC CODE.  BEGIN DELETE ITEM DIALOG CODE.
/////////////////////////////////////////////////////////////////////////////

typedef struct _FOLDERTHREADINFO {
    HANDLE     hThread;
    HWND       hwndTree;
    HIMAGELIST himl;
} FOLDERTHREADINFO, * PFOLDERTHREADINFO;


void CALLBACK FolderEnumItems(PFOLDERTHREADINFO pfti, HTREEITEM hParent)
{
    HTREEITEM hitem;

    hitem = hParent;
    while (hitem && pfti->hThread)
    {
        TV_ITEM tvi;
        tvi.mask  = TVIF_IMAGE;
        tvi.hItem = hitem;
        TreeView_GetItem(pfti->hwndTree, &tvi);
        hitem = TreeView_GetNextSibling(pfti->hwndTree, hitem);
    }

    hitem = TreeView_GetChild(pfti->hwndTree, hParent);

    while (hitem && pfti->hThread)
    {
        FolderEnumItems(pfti, hitem);
        hitem = TreeView_GetNextSibling(pfti->hwndTree, hitem);
    }
}


DWORD CALLBACK FolderThread(PFOLDERTHREADINFO pfti)
{
    HANDLE hThread = pfti->hThread;

    FolderEnumItems(pfti, TreeView_GetRoot(pfti->hwndTree));

    CloseHandle(hThread);

    pfti->hThread = 0;

    return 0;
}


VOID CreateFolderThread(PFOLDERTHREADINFO pfti)
{
    //
    // Create background thread to force list view to draw items
    //

    DWORD idThread;

    if (pfti->hThread)
    {
        return;
    }

    pfti->hThread = CreateThread(NULL, 0, FolderThread, pfti, 0, &idThread);

    if(pfti->hThread)
    {
        SetThreadPriority(pfti->hThread, THREAD_PRIORITY_BELOW_NORMAL);
    }
}


//
//  Main dialog procedure for delete items dialog.
//

const static DWORD aDelItemHelpIDs[] = {  // Context Help IDs
    IDC_TEXT,         NO_HELP,
    IDC_FOLDERTREE,   IDH_TRAY_REMOVEDLG_LIST,
    IDC_DELETEITEM,   IDH_TRAY_REMOVEDLG_DEL,

    0, 0
};

void WaitForThreadToFinish(HWND hDlg, FOLDERTHREADINFO *pfti)
{
    if (pfti->hThread)
    {
        SHProcessSentMessagesUntilEvent(hDlg, pfti->hThread, 10000);
        CloseHandle(pfti->hThread);
        pfti->hThread = NULL;
    }
}

BOOL_PTR CALLBACK DelItemDlgProc(HWND hDlg, UINT message , WPARAM wParam, LPARAM lParam)
{
    PFOLDERTHREADINFO pfti = (PFOLDERTHREADINFO)GetWindowLongPtr(hDlg, DWLP_USER);

    switch(message)
    {
        case WM_NOTIFY:
            #define lpnm ((NMHDR *)lParam)

            switch(lpnm->code)
            {
                #define lpfidNew ((LPFILEITEMDATA)(((LPNM_TREEVIEW)lParam)->itemNew.lParam))

                case TVN_SELCHANGED:
                {
                    BOOL fCanDel = (lpfidNew->dwFlags & FIDFLAG_CANDEL);
                    HWND hwndDelItem = GetDlgItem(hDlg, IDC_DELETEITEM);

                    if ((!fCanDel) && (GetFocus() == hwndDelItem))
                    {
                        SetFocus(GetDlgItem(hDlg, IDOK));
                        SendMessage(hDlg, DM_SETDEFID, IDOK, 0);
                    }
                    Button_Enable(hwndDelItem, fCanDel);
                    break;
                }

                #undef lpfidNew

                #define lptvn ((LPNM_TREEVIEW)lParam)

                case TVN_DELETEITEM:
                    if (lptvn->itemOld.lParam)
                    {
                        LocalFree((LPVOID)lptvn->itemOld.lParam);
                    }
                    break;

                #undef lptvn

                #define lptkd ((TV_KEYDOWN *)lParam)

                case TVN_KEYDOWN:
                    if (lptkd->wVKey == VK_DELETE)
                    {
                        WaitForThreadToFinish(hDlg, pfti);
                        RemoveSelItem(hDlg, GetDlgItem(hDlg, IDC_FOLDERTREE));
                        CreateFolderThread(pfti);
                        return TRUE;
                    }
                    break;

                #undef lptkd

                case TVN_GETDISPINFO:
                    FillInItem(((TV_DISPINFO *)lParam));
                    break;

                default:
                    return FALSE;

            #undef lpnm
            }
            break;

        case WM_INITDIALOG:

            SetWindowLongPtr(hDlg, DWLP_USER, lParam);

            pfti = (PFOLDERTHREADINFO)lParam;

            InitFolderTree(GetDlgItem(hDlg, IDC_FOLDERTREE), TRUE, &pfti->himl);

            pfti->hwndTree = GetDlgItem(hDlg, IDC_FOLDERTREE);
            pfti->hThread = 0;

            CreateFolderThread(pfti);
            break;

        case WM_HELP:
            WinHelp((HWND)((LPHELPINFO) lParam)->hItemHandle, NULL,
                HELP_WM_HELP, (DWORD_PTR)(LPTSTR) aDelItemHelpIDs);
            break;

        case WM_CONTEXTMENU:
            WinHelp((HWND) wParam, NULL, HELP_CONTEXTMENU,
                (DWORD_PTR)(LPVOID) aDelItemHelpIDs);
            break;

        case WM_COMMAND:
            switch (GET_WM_COMMAND_ID(wParam, lParam))
            {
                case IDOK:
                case IDCANCEL:
                    WaitForThreadToFinish(hDlg, pfti);                    
                    EndDialog(hDlg, GET_WM_COMMAND_ID(wParam, lParam));
                    break;

                case IDC_DELETEITEM:
                    WaitForThreadToFinish(hDlg, pfti);                    
                    
                    RemoveSelItem(hDlg, GetDlgItem(hDlg, IDC_FOLDERTREE));
                    CreateFolderThread(pfti);
                    break;
            }

        default:
            return FALSE;

    }
    return TRUE;
}


BOOL RemoveItemsDialog( HWND hParent )
{
    BOOL fReturn;

    FOLDERTHREADINFO fti;

    fti.himl = NULL;    // incase we can not create the window

    fReturn = (int)DialogBoxParam( g_hinst, MAKEINTRESOURCE( DLG_DELITEM ),
                                   hParent, DelItemDlgProc, (LPARAM) &fti );

    if( fti.himl )
        ImageList_Destroy( fti.himl );

    return fReturn;
}
