* Sync up to trunk head (r65147).
authorAmine Khaldi <amine.khaldi@reactos.org>
Fri, 31 Oct 2014 15:27:51 +0000 (15:27 +0000)
committerAmine Khaldi <amine.khaldi@reactos.org>
Fri, 31 Oct 2014 15:27:51 +0000 (15:27 +0000)
svn path=/branches/shell-experiments/; revision=65148

31 files changed:
1  2 
base/applications/logoff/logoff.c
base/applications/network/finger/finger.c
base/applications/network/finger/net.c
base/applications/network/ftp/ftp.c
base/applications/network/ipconfig/ipconfig.c
boot/bootdata/hivedef.inf
boot/freeldr/freeldr/include/arch/pc/x86common.h
dll/cpl/appwiz/addons.c
dll/cpl/appwiz/appwiz.c
dll/cpl/appwiz/appwiz.h
dll/cpl/intl/generalp.c
dll/opengl/opengl32/opengl32.h
dll/win32/shell32/CMakeLists.txt
dll/win32/shell32/brsfolder.cpp
dll/win32/shell32/folders/recyclebin.h
dll/win32/shell32/shell32_main.h
dll/win32/shell32/shellstring.c
dll/win32/shell32/shlfileop.cpp
dll/win32/syssetup/classinst.c
dll/win32/syssetup/wizard.c
drivers/filesystems/fastfat/cleanup.c
drivers/filesystems/fastfat/create.c
drivers/filesystems/fastfat/finfo.c
drivers/filesystems/fastfat/fsctl.c
drivers/filesystems/fastfat/vfat.h
drivers/filesystems/ntfs/fsctl.c
lib/fslib/vfatlib/vfatlib.c
ntoskrnl/io/iomgr/file.c
ntoskrnl/io/iomgr/rawfs.c
ntoskrnl/se/token.c
win32ss/gdi/ntgdi/freetype.c

Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
index 47f9575,0000000..5b9fa9a
mode 100644,000000..100644
--- /dev/null
@@@ -1,90 -1,0 +1,90 @@@
-     shellstring.cpp
 +PROJECT(SHELL)
 +
 +set_cpp(WITH_RUNTIME)
 +spec2def(shell32.dll shell32.spec ADD_IMPORTLIB)
 +
 +remove_definitions(-D_WIN32_WINNT=0x502)
 +add_definitions(-D_WIN32_WINNT=0x600)
 +
 +add_definitions(
 +    -D_SHELL32_
 +    -D_WINE)
 +
 +include_directories(
 +    ${REACTOS_SOURCE_DIR}/lib/recyclebin
 +    ${REACTOS_SOURCE_DIR}/lib/atl
 +    ${REACTOS_SOURCE_DIR})
 +
 +list(APPEND SOURCE
 +    #authors.cpp
 +    autocomplete.cpp
 +    brsfolder.cpp
 +    changenotify.cpp
 +    classes.cpp
 +    clipboard.cpp
 +    control.cpp
 +    CMenuBand.cpp
 +    CMenuDeskBar.cpp
 +    dataobject.cpp
 +    dde.cpp
 +    debughlp.cpp
 +    desktop.cpp
 +    dialogs.cpp
 +    dragdrophelper.cpp
 +    enumidlist.cpp
 +    extracticon.cpp
 +    folders.cpp
 +    iconcache.cpp
 +    pidl.cpp
 +    shell32_main.cpp
 +    shellitem.cpp
 +    shelllink.cpp
 +    shellole.cpp
 +    shellord.cpp
 +    shellpath.cpp
 +    shellreg.cpp
 +    folders/desktop.cpp
 +    folders/fs.cpp
 +    folders/mycomp.cpp
 +    folders/mydocuments.cpp
 +    folders/printers.cpp
 +    folders/admintools.cpp
 +    folders/netplaces.cpp
 +    folders/fonts.cpp
 +    folders/cpanel.cpp
 +    folders/recyclebin.cpp
 +    droptargets/CexeDropHandler.cpp
 +    shlexec.cpp
 +    shlfileop.cpp
 +    shlfolder.cpp
 +    shlfsbind.cpp
 +    shlmenu.cpp
 +    shlview.cpp
 +    shpolicy.cpp
 +    stubs.cpp
 +    systray.cpp
 +    fprop.cpp
 +    drive.cpp
 +    defcontextmenu.cpp
 +    openwithmenu.cpp
 +    newmenu.cpp
 +    startmenu.cpp
 +    folder_options.cpp
 +    filedefext.cpp
 +    drvdefext.cpp
 +    precomp.h)
 +
 +add_library(shell32 SHARED
 +    ${SOURCE}
++    shellstring.c
 +    vista.c
 +    shell32.rc
 +    ${CMAKE_CURRENT_BINARY_DIR}/shell32_stubs.c
 +    ${CMAKE_CURRENT_BINARY_DIR}/shell32.def)
 +
 +set_module_type(shell32 win32dll UNICODE HOTPATCHABLE)
 +target_link_libraries(shell32 atlnew wine uuid recyclebin)
 +add_delay_importlibs(shell32 ole32 version fmifs)
 +add_importlibs(shell32 advapi32 browseui gdi32 user32 powrprof comctl32 comdlg32 shdocvw shlwapi devmgr winspool winmm msvcrt kernel32 ntdll)
 +add_pch(shell32 precomp.h SOURCE)
 +add_cd_file(TARGET shell32 DESTINATION reactos/system32 FOR all)
index 678d3b4,0000000..2213993
mode 100644,000000..100644
--- /dev/null
@@@ -1,1114 -1,0 +1,1114 @@@
-     lptvid = (TV_ITEMDATA *)SHAlloc( sizeof(TV_ITEMDATA) );
-     if (!lptvid)
 +/*
 + * Copyright 1999 Juergen Schmied
 + *
 + * This library is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public
 + * License as published by the Free Software Foundation; either
 + * version 2.1 of the License, or (at your option) any later version.
 + *
 + * This library is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with this library; if not, write to the Free Software
 + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 + *
 + * FIXME:
 + *  - many memory leaks
 + *  - many flags unimplemented
 + *    - implement new dialog style "make new folder" button
 + *    - implement editbox
 + *    - implement new dialog style resizing
 + */
 +
 +#include "precomp.h"
 +
 +WINE_DEFAULT_DEBUG_CHANNEL(shell);
 +
 +/* original margins and control size */
 +typedef struct tagLAYOUT_DATA
 +{
 +    LONG left, width, right;
 +    LONG top, height, bottom;
 +} LAYOUT_DATA;
 +
 +typedef struct tagbrowse_info
 +{
 +    HWND          hWnd;
 +    HWND          hwndTreeView;
 +    LPBROWSEINFOW lpBrowseInfo;
 +    LPITEMIDLIST  pidlRet;
 +    LAYOUT_DATA  *layout;  /* filled by LayoutInit, used by LayoutUpdate */
 +    SIZE          szMin;
 +} browse_info;
 +
 +typedef struct tagTV_ITEMDATA
 +{
 +   IShellFolder* lpsfParent; /* IShellFolder of the parent */
 +   LPITEMIDLIST  lpi;        /* PIDL relative to parent */
 +   LPITEMIDLIST  lpifq;      /* Fully qualified PIDL */
 +   IEnumIDList*  pEnumIL;    /* Children iterator */
 +} TV_ITEMDATA, *LPTV_ITEMDATA;
 +
 +typedef struct tagLAYOUT_INFO
 +{
 +    int iItemId;          /* control id */
 +    DWORD dwAnchor;       /* BF_* flags specifying which margins should remain constant */
 +} LAYOUT_INFO;
 +
 +static const LAYOUT_INFO g_layout_info[] =
 +{
 +    {IDC_BROWSE_FOR_FOLDER_TITLE,         BF_TOP|BF_LEFT|BF_RIGHT},
 +    {IDC_BROWSE_FOR_FOLDER_STATUS,        BF_TOP|BF_LEFT|BF_RIGHT},
 +    {IDC_BROWSE_FOR_FOLDER_FOLDER,        BF_TOP|BF_LEFT|BF_RIGHT},
 +    {IDC_BROWSE_FOR_FOLDER_TREEVIEW,      BF_TOP|BF_BOTTOM|BF_LEFT|BF_RIGHT},
 +    {IDC_BROWSE_FOR_FOLDER_FOLDER,        BF_BOTTOM|BF_LEFT},
 +    {IDC_BROWSE_FOR_FOLDER_FOLDER_TEXT,    BF_BOTTOM|BF_LEFT|BF_RIGHT},
 +    {IDC_BROWSE_FOR_FOLDER_NEW_FOLDER, BF_BOTTOM|BF_LEFT},
 +    {IDOK,              BF_BOTTOM|BF_RIGHT},
 +    {IDCANCEL,          BF_BOTTOM|BF_RIGHT}
 +};
 +
 +#define LAYOUT_INFO_COUNT (sizeof(g_layout_info)/sizeof(g_layout_info[0]))
 +
 +#define SUPPORTEDFLAGS (BIF_STATUSTEXT | \
 +                        BIF_BROWSEFORCOMPUTER | \
 +                        BIF_RETURNFSANCESTORS | \
 +                        BIF_RETURNONLYFSDIRS | \
 +                        BIF_NONEWFOLDERBUTTON | \
 +                        BIF_NEWDIALOGSTYLE | \
 +                        BIF_BROWSEINCLUDEFILES)
 +
 +static void FillTreeView(browse_info*, IShellFolder*,
 +               LPITEMIDLIST, HTREEITEM, IEnumIDList*);
 +static HTREEITEM InsertTreeViewItem( browse_info*, IShellFolder*,
 +               LPCITEMIDLIST, LPCITEMIDLIST, IEnumIDList*, HTREEITEM);
 +
 +static const WCHAR szBrowseFolderInfo[] = L"__WINE_BRSFOLDERDLG_INFO";
 +
 +static DWORD __inline BrowseFlagsToSHCONTF(UINT ulFlags)
 +{
 +    return SHCONTF_FOLDERS | (ulFlags & BIF_BROWSEINCLUDEFILES ? SHCONTF_NONFOLDERS : 0);
 +}
 +
 +static void browsefolder_callback( LPBROWSEINFOW lpBrowseInfo, HWND hWnd,
 +                                   UINT msg, LPARAM param )
 +{
 +    if (!lpBrowseInfo->lpfn)
 +        return;
 +    lpBrowseInfo->lpfn( hWnd, msg, param, lpBrowseInfo->lParam );
 +}
 +
 +static LAYOUT_DATA *LayoutInit(HWND hwnd, const LAYOUT_INFO *layout_info, int layout_count)
 +{
 +    LAYOUT_DATA *data;
 +    RECT rcWnd;
 +    int i;
 +
 +    GetClientRect(hwnd, &rcWnd);
 +    data = (LAYOUT_DATA *)SHAlloc(sizeof(LAYOUT_DATA)*layout_count);
 +    for (i = 0; i < layout_count; i++)
 +    {
 +        RECT r;
 +        HWND hItem = GetDlgItem(hwnd, layout_info[i].iItemId);
 +
 +        if (hItem == NULL)
 +            ERR("Item %d not found\n", i);
 +        GetWindowRect(hItem, &r);
 +        MapWindowPoints(HWND_DESKTOP, hwnd, (LPPOINT)&r, 2);
 +
 +        data[i].left = r.left;
 +        data[i].right = rcWnd.right - r.right;
 +        data[i].width = r.right - r.left;
 +
 +        data[i].top = r.top;
 +        data[i].bottom = rcWnd.bottom - r.bottom;
 +        data[i].height = r.bottom - r.top;
 +    }
 +    return data;
 +}
 +
 +static void LayoutUpdate(HWND hwnd, LAYOUT_DATA *data, const LAYOUT_INFO *layout_info, int layout_count)
 +{
 +    RECT rcWnd;
 +    int i;
 +
 +    GetClientRect(hwnd, &rcWnd);
 +    for (i = 0; i < layout_count; i++)
 +    {
 +        RECT r;
 +        HWND hItem = GetDlgItem(hwnd, layout_info[i].iItemId);
 +
 +        GetWindowRect(hItem, &r);
 +        MapWindowPoints(HWND_DESKTOP, hwnd, (LPPOINT)&r, 2);
 +
 +        if (layout_info[i].dwAnchor & BF_RIGHT)
 +        {
 +            r.right = rcWnd.right - data[i].right;
 +            if (!(layout_info[i].dwAnchor & BF_LEFT))
 +                r.left = r.right - data[i].width;
 +        }
 +
 +        if (layout_info[i].dwAnchor & BF_BOTTOM)
 +        {
 +            r.bottom = rcWnd.bottom - data[i].bottom;
 +            if (!(layout_info[i].dwAnchor & BF_TOP))
 +                r.top = r.bottom - data[i].height;
 +        }
 +
 +        SetWindowPos(hItem, NULL, r.left, r.top, r.right - r.left, r.bottom - r.top, SWP_NOZORDER);
 +    }
 +}
 +
 +
 +/******************************************************************************
 + * InitializeTreeView [Internal]
 + *
 + * Called from WM_INITDIALOG handler.
 + *
 + * PARAMS
 + *  hwndParent [I] The BrowseForFolder dialog
 + *  root       [I] ITEMIDLIST of the root shell folder
 + */
 +static void InitializeTreeView( browse_info *info )
 +{
 +    LPITEMIDLIST pidlParent, pidlChild;
 +    HIMAGELIST hImageList;
 +    HRESULT hr;
 +    CComPtr<IShellFolder> lpsfParent;
 +    CComPtr<IShellFolder> lpsfRoot;
 +    CComPtr<IEnumIDList> pEnumChildren;
 +    HTREEITEM item;
 +    DWORD flags;
 +    LPCITEMIDLIST root = info->lpBrowseInfo->pidlRoot;
 +
 +    TRACE("%p\n", info );
 +
 +    Shell_GetImageLists(NULL, &hImageList);
 +
 +    if (hImageList)
 +        SendMessageW( info->hwndTreeView, TVM_SETIMAGELIST, 0, (LPARAM)hImageList );
 +
 +    /* We want to call InsertTreeViewItem down the code, in order to insert
 +     * the root item of the treeview. Due to InsertTreeViewItem's signature,
 +     * we need the following to do this:
 +     *
 +     * + An ITEMIDLIST corresponding to _the parent_ of root.
 +     * + An ITEMIDLIST, which is a relative path from root's parent to root
 +     *   (containing a single SHITEMID).
 +     * + An IShellFolder interface pointer of root's parent folder.
 +     *
 +     * If root is 'Desktop', then root's parent is also 'Desktop'.
 +     */
 +
 +    pidlParent = ILClone(root);
 +    ILRemoveLastID(pidlParent);
 +    pidlChild = ILClone(ILFindLastID(root));
 +
 +    if (_ILIsDesktop(pidlParent))
 +    {
 +        hr = SHGetDesktopFolder(&lpsfParent);
 +    } else {
 +        CComPtr<IShellFolder> lpsfDesktop;
 +        hr = SHGetDesktopFolder(&lpsfDesktop);
 +        if (FAILED(hr)) {
 +            WARN("SHGetDesktopFolder failed! hr = %08x\n", hr);
 +            ILFree(pidlChild);
 +            ILFree(pidlParent);
 +            return;
 +        }
 +        hr = lpsfDesktop->BindToObject(pidlParent, 0, IID_PPV_ARG(IShellFolder, &lpsfParent));
 +    }
 +
 +    if (FAILED(hr)) {
 +        WARN("Could not bind to parent shell folder! hr = %08x\n", hr);
 +        ILFree(pidlChild);
 +        ILFree(pidlParent);
 +        return;
 +    }
 +
 +    if (pidlChild && pidlChild->mkid.cb) {
 +        hr = lpsfParent->BindToObject(pidlChild, 0, IID_PPV_ARG(IShellFolder, &lpsfRoot));
 +    } else {
 +        lpsfRoot = lpsfParent;
 +    }
 +
 +    if (FAILED(hr)) {
 +        WARN("Could not bind to root shell folder! hr = %08x\n", hr);
 +        ILFree(pidlChild);
 +        ILFree(pidlParent);
 +        return;
 +    }
 +
 +    flags = BrowseFlagsToSHCONTF( info->lpBrowseInfo->ulFlags );
 +    hr = lpsfRoot->EnumObjects(info->hWnd, flags, &pEnumChildren );
 +    if (FAILED(hr)) {
 +        WARN("Could not get child iterator! hr = %08x\n", hr);
 +        ILFree(pidlChild);
 +        ILFree(pidlParent);
 +        return;
 +    }
 +
 +    SendMessageW( info->hwndTreeView, TVM_DELETEITEM, 0, (LPARAM)TVI_ROOT );
 +    item = InsertTreeViewItem( info, lpsfParent, pidlChild,
 +                               pidlParent, pEnumChildren, TVI_ROOT );
 +    SendMessageW( info->hwndTreeView, TVM_EXPAND, TVE_EXPAND, (LPARAM)item );
 +
 +    ILFree(pidlChild);
 +    ILFree(pidlParent);
 +}
 +
 +static int GetIcon(LPCITEMIDLIST lpi, UINT uFlags)
 +{
 +    SHFILEINFOW sfi;
 +    SHGetFileInfoW((LPCWSTR)lpi, 0 ,&sfi, sizeof(SHFILEINFOW), uFlags);
 +    return sfi.iIcon;
 +}
 +
 +static void GetNormalAndSelectedIcons(LPITEMIDLIST lpifq, LPTVITEMW lpTV_ITEM)
 +{
 +    LPITEMIDLIST pidlDesktop = NULL;
 +    DWORD flags;
 +
 +    TRACE("%p %p\n",lpifq, lpTV_ITEM);
 +
 +    if (!lpifq)
 +    {
 +        pidlDesktop = _ILCreateDesktop();
 +        lpifq = pidlDesktop;
 +    }
 +
 +    flags = SHGFI_PIDL | SHGFI_SYSICONINDEX | SHGFI_SMALLICON;
 +    lpTV_ITEM->iImage = GetIcon( lpifq, flags );
 +
 +    flags = SHGFI_PIDL | SHGFI_SYSICONINDEX | SHGFI_SMALLICON | SHGFI_OPENICON;
 +    lpTV_ITEM->iSelectedImage = GetIcon( lpifq, flags );
 +
 +    if (pidlDesktop)
 +        ILFree( pidlDesktop );
 +}
 +
 +/******************************************************************************
 + * GetName [Internal]
 + *
 + * Query a shell folder for the display name of one of it's children
 + *
 + * PARAMS
 + *  lpsf           [I] IShellFolder interface of the folder to be queried.
 + *  lpi            [I] ITEMIDLIST of the child, relative to parent
 + *  dwFlags        [I] as in IShellFolder::GetDisplayNameOf
 + *  lpFriendlyName [O] The desired display name in unicode
 + *
 + * RETURNS
 + *  Success: TRUE
 + *  Failure: FALSE
 + */
 +static BOOL GetName(IShellFolder * lpsf, LPCITEMIDLIST lpi, DWORD dwFlags, LPWSTR lpFriendlyName)
 +{
 +    BOOL   bSuccess=TRUE;
 +    STRRET str;
 +
 +    TRACE("%p %p %x %p\n", lpsf, lpi, dwFlags, lpFriendlyName);
 +    if (SUCCEEDED(lpsf->GetDisplayNameOf(lpi, dwFlags, &str)))
 +          bSuccess = StrRetToStrNW(lpFriendlyName, MAX_PATH, &str, lpi);
 +    else
 +      bSuccess = FALSE;
 +
 +    TRACE("-- %s\n", debugstr_w(lpFriendlyName));
 +    return bSuccess;
 +}
 +
 +/******************************************************************************
 + * InsertTreeViewItem [Internal]
 + *
 + * PARAMS
 + *  info       [I] data for the dialog
 + *  lpsf       [I] IShellFolder interface of the item's parent shell folder
 + *  pidl       [I] ITEMIDLIST of the child to insert, relative to parent
 + *  pidlParent [I] ITEMIDLIST of the parent shell folder
 + *  pEnumIL    [I] Iterator for the children of the item to be inserted
 + *  hParent    [I] The treeview-item that represents the parent shell folder
 + *
 + * RETURNS
 + *  Success: Handle to the created and inserted treeview-item
 + *  Failure: NULL
 + */
 +static HTREEITEM InsertTreeViewItem( browse_info *info, IShellFolder * lpsf,
 +    LPCITEMIDLIST pidl, LPCITEMIDLIST pidlParent, IEnumIDList* pEnumIL,
 +    HTREEITEM hParent)
 +{
 +    TVITEMW     tvi;
 +    TVINSERTSTRUCTW    tvins;
 +    WCHAR        szBuff[MAX_PATH];
 +    LPTV_ITEMDATA    lptvid=0;
 +
 +    tvi.mask  = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM;
 +
 +    tvi.cChildren= pEnumIL ? 1 : 0;
 +    tvi.mask |= TVIF_CHILDREN;
 +
-     if (!GetName(lpsf, pidl, SHGDN_NORMAL, szBuff))
++    if (!GetName(lpsf, pidl, SHGDN_NORMAL, szBuff))
 +        return NULL;
 +
++    lptvid = (TV_ITEMDATA *)SHAlloc( sizeof(TV_ITEMDATA) );
++    if (!lptvid)
 +        return NULL;
 +
 +    tvi.pszText    = szBuff;
 +    tvi.cchTextMax = MAX_PATH;
 +    tvi.lParam = (LPARAM)lptvid;
 +
 +    lpsf->AddRef();
 +    pEnumIL->AddRef();
 +    lptvid->lpsfParent = lpsf;
 +    lptvid->lpi    = ILClone(pidl);
 +    lptvid->lpifq    = pidlParent ? ILCombine(pidlParent, pidl) : ILClone(pidl);
 +    lptvid->pEnumIL = pEnumIL;
 +    GetNormalAndSelectedIcons(lptvid->lpifq, &tvi);
 +
 +    tvins.item         = tvi;
 +    tvins.hInsertAfter = NULL;
 +    tvins.hParent      = hParent;
 +
 +    return TreeView_InsertItem( info->hwndTreeView, &tvins );
 +}
 +
 +/******************************************************************************
 + * FillTreeView [Internal]
 + *
 + * For each child (given by lpe) of the parent shell folder, which is given by
 + * lpsf and whose PIDL is pidl, insert a treeview-item right under hParent
 + *
 + * PARAMS
 + *  info    [I] data for the dialog
 + *  lpsf    [I] IShellFolder interface of the parent shell folder
 + *  pidl    [I] ITEMIDLIST of the parent shell folder
 + *  hParent [I] The treeview item that represents the parent shell folder
 + *  lpe     [I] An iterator for the children of the parent shell folder
 + */
 +static void FillTreeView( browse_info *info, IShellFolder * lpsf,
 +                 LPITEMIDLIST  pidl, HTREEITEM hParent, IEnumIDList* lpe)
 +{
 +    LPITEMIDLIST    pidlTemp = 0;
 +    ULONG        ulFetched;
 +    HRESULT        hr;
 +    HWND        hwnd = GetParent( info->hwndTreeView );
 +
 +    TRACE("%p %p %p %p\n",lpsf, pidl, hParent, lpe);
 +
 +    /* No IEnumIDList -> No children */
 +    if (!lpe) return;
 +
 +    SetCapture( hwnd );
 +    SetCursor( LoadCursorA( 0, (LPSTR)IDC_WAIT ) );
 +
 +    while (S_OK == lpe->Next(1,&pidlTemp,&ulFetched))
 +    {
 +        ULONG ulAttrs = SFGAO_HASSUBFOLDER | SFGAO_FOLDER;
 +        CComPtr<IEnumIDList> pEnumIL;
 +        CComPtr<IShellFolder> pSFChild;
 +        lpsf->GetAttributesOf(1, (LPCITEMIDLIST*)&pidlTemp, &ulAttrs);
 +        if (ulAttrs & SFGAO_FOLDER)
 +        {
 +            hr = lpsf->BindToObject(pidlTemp, NULL, IID_PPV_ARG(IShellFolder, &pSFChild));
 +            if (SUCCEEDED(hr))
 +                {
 +                DWORD flags = BrowseFlagsToSHCONTF(info->lpBrowseInfo->ulFlags);
 +                hr = pSFChild->EnumObjects(hwnd, flags, &pEnumIL);
 +                    if (hr == S_OK)
 +                    {
 +                        if ((pEnumIL->Skip(1) != S_OK) ||
 +                             FAILED(pEnumIL->Reset()))
 +                        {
 +                            pEnumIL = NULL;
 +                        }
 +                    }
 +                }
 +        }
 +
 +        if (!InsertTreeViewItem(info, lpsf, pidlTemp, pidl, pEnumIL, hParent))
 +            goto done;
 +        SHFree(pidlTemp);  /* Finally, free the pidl that the shell gave us... */
 +        pidlTemp=NULL;
 +    }
 +
 +done:
 +    ReleaseCapture();
 +    SetCursor(LoadCursorW(0, (LPWSTR)IDC_ARROW));
 +    SHFree(pidlTemp);
 +}
 +
 +static BOOL __inline PIDLIsType(LPCITEMIDLIST pidl, PIDLTYPE type)
 +{
 +    LPPIDLDATA data = _ILGetDataPointer(pidl);
 +    if (!data)
 +        return FALSE;
 +    return (data->type == type);
 +}
 +
 +static void BrsFolder_CheckValidSelection( browse_info *info, LPTV_ITEMDATA lptvid )
 +{
 +    LPBROWSEINFOW lpBrowseInfo = info->lpBrowseInfo;
 +    LPCITEMIDLIST pidl = lptvid->lpi;
 +    BOOL bEnabled = TRUE;
 +    DWORD dwAttributes;
 +    HRESULT r;
 +
 +    if ((lpBrowseInfo->ulFlags & BIF_BROWSEFORCOMPUTER) &&
 +        !PIDLIsType(pidl, PT_COMP))
 +        bEnabled = FALSE;
 +    if (lpBrowseInfo->ulFlags & BIF_RETURNFSANCESTORS)
 +    {
 +        dwAttributes = SFGAO_FILESYSANCESTOR | SFGAO_FILESYSTEM;
 +        r = lptvid->lpsfParent->GetAttributesOf(1,
 +                                (LPCITEMIDLIST*)&lptvid->lpi, &dwAttributes);
 +        if (FAILED(r) || !(dwAttributes & (SFGAO_FILESYSANCESTOR|SFGAO_FILESYSTEM)))
 +            bEnabled = FALSE;
 +    }
 +
 +    dwAttributes = SFGAO_FOLDER | SFGAO_FILESYSTEM;
 +    r = lptvid->lpsfParent->GetAttributesOf(1,
 +                                            (LPCITEMIDLIST*)&lptvid->lpi,
 +                                            &dwAttributes);
 +    if (FAILED(r) ||
 +        ((dwAttributes & (SFGAO_FOLDER|SFGAO_FILESYSTEM)) != (SFGAO_FOLDER|SFGAO_FILESYSTEM)))
 +    {
 +        if (lpBrowseInfo->ulFlags & BIF_RETURNONLYFSDIRS)
 +            bEnabled = FALSE;
 +        EnableWindow(GetDlgItem(info->hWnd, IDC_BROWSE_FOR_FOLDER_NEW_FOLDER), FALSE);
 +    }
 +    else
 +        EnableWindow(GetDlgItem(info->hWnd, IDC_BROWSE_FOR_FOLDER_NEW_FOLDER), TRUE);
 +
 +    SendMessageW(info->hWnd, BFFM_ENABLEOK, 0, bEnabled);
 +}
 +
 +static LRESULT BrsFolder_Treeview_Delete( browse_info *info, NMTREEVIEWW *pnmtv )
 +{
 +    LPTV_ITEMDATA lptvid = (LPTV_ITEMDATA)pnmtv->itemOld.lParam;
 +
 +    TRACE("TVN_DELETEITEMA/W %p\n", lptvid);
 +
 +    lptvid->lpsfParent->Release();
 +    if (lptvid->pEnumIL)
 +        lptvid->pEnumIL->Release();
 +    SHFree(lptvid->lpi);
 +    SHFree(lptvid->lpifq);
 +    SHFree(lptvid);
 +    return 0;
 +}
 +
 +static LRESULT BrsFolder_Treeview_Expand( browse_info *info, NMTREEVIEWW *pnmtv )
 +{
 +    CComPtr<IShellFolder> lpsf2;
 +    LPTV_ITEMDATA lptvid = (LPTV_ITEMDATA) pnmtv->itemNew.lParam;
 +    HRESULT r;
 +
 +    TRACE("TVN_ITEMEXPANDINGA/W\n");
 +
 +    if ((pnmtv->itemNew.state & TVIS_EXPANDEDONCE))
 +        return 0;
 +
 +    if (lptvid->lpi && lptvid->lpi->mkid.cb) {
 +        r = lptvid->lpsfParent->BindToObject(lptvid->lpi, 0, IID_PPV_ARG(IShellFolder, &lpsf2));
 +    } else {
 +        lpsf2 = lptvid->lpsfParent;
 +        r = lpsf2->AddRef();
 +    }
 +
 +    if (SUCCEEDED(r))
 +    {
 +        FillTreeView( info, lpsf2, lptvid->lpifq, pnmtv->itemNew.hItem, lptvid->pEnumIL);
 +        lpsf2->Release();
 +    }
 +
 +    /* My Computer is already sorted and trying to do a simple text
 +     * sort will only mess things up */
 +    if (!_ILIsMyComputer(lptvid->lpi))
 +        SendMessageW( info->hwndTreeView, TVM_SORTCHILDREN,
 +                      FALSE, (LPARAM)pnmtv->itemNew.hItem );
 +
 +    return 0;
 +}
 +
 +static HRESULT BrsFolder_Treeview_Changed( browse_info *info, NMTREEVIEWW *pnmtv )
 +{
 +    LPTV_ITEMDATA lptvid = (LPTV_ITEMDATA) pnmtv->itemNew.lParam;
 +    WCHAR name[MAX_PATH];
 +
 +    ILFree(info->pidlRet);
 +    info->pidlRet = ILClone(lptvid->lpifq);
 +
 +    if (GetName(lptvid->lpsfParent, lptvid->lpi, SHGDN_NORMAL, name))
 +            SetWindowTextW( GetDlgItem(info->hWnd, IDC_BROWSE_FOR_FOLDER_FOLDER_TEXT), name );
 +
 +    browsefolder_callback( info->lpBrowseInfo, info->hWnd, BFFM_SELCHANGED,
 +                           (LPARAM)info->pidlRet );
 +    BrsFolder_CheckValidSelection( info, lptvid );
 +    return 0;
 +}
 +
 +static LRESULT BrsFolder_Treeview_Rename(browse_info *info, NMTVDISPINFOW *pnmtv)
 +{
 +    LPTV_ITEMDATA item_data;
 +    WCHAR old_path[MAX_PATH], new_path[MAX_PATH], *p;
 +    NMTREEVIEWW nmtv;
 +    TVITEMW item;
 +
 +    if(!pnmtv->item.pszText)
 +        return 0;
 +
 +    item.mask = TVIF_HANDLE|TVIF_PARAM;
 +    item.hItem = (HTREEITEM)SendMessageW(info->hwndTreeView, TVM_GETNEXTITEM, TVGN_CARET, 0);
 +    SendMessageW(info->hwndTreeView, TVM_GETITEMW, 0, (LPARAM)&item);
 +    item_data = (LPTV_ITEMDATA)item.lParam;
 +
 +    SHGetPathFromIDListW(item_data->lpifq, old_path);
 +    if(!(p = strrchrW(old_path, '\\')))
 +        return 0;
 +    p = new_path+(p-old_path+1);
 +    memcpy(new_path, old_path, (p-new_path)*sizeof(WCHAR));
 +    strcpyW(p, pnmtv->item.pszText);
 +
 +    if(!MoveFileW(old_path, new_path))
 +        return 0;
 +
 +    SHFree(item_data->lpifq);
 +    SHFree(item_data->lpi);
 +    item_data->lpifq = SHSimpleIDListFromPathW(new_path);
 +    item_data->lpsfParent->ParseDisplayName(NULL, NULL, pnmtv->item.pszText,
 +                                            NULL, &item_data->lpi, NULL);
 +
 +    item.mask = TVIF_HANDLE|TVIF_TEXT;
 +    item.pszText = pnmtv->item.pszText;
 +    SendMessageW(info->hwndTreeView, TVM_SETITEMW, 0, (LPARAM)&item);
 +
 +    nmtv.itemNew.lParam = item.lParam;
 +    BrsFolder_Treeview_Changed(info, &nmtv);
 +    return 0;
 +}
 +
 +static LRESULT BrsFolder_OnNotify( browse_info *info, UINT CtlID, LPNMHDR lpnmh )
 +{
 +    NMTREEVIEWW *pnmtv = (NMTREEVIEWW *)lpnmh;
 +
 +    TRACE("%p %x %p msg=%x\n", info, CtlID, lpnmh, pnmtv->hdr.code);
 +
 +    if (pnmtv->hdr.idFrom != IDC_BROWSE_FOR_FOLDER_TREEVIEW)
 +        return 0;
 +
 +    switch (pnmtv->hdr.code)
 +    {
 +    case TVN_DELETEITEMA:
 +    case TVN_DELETEITEMW:
 +        return BrsFolder_Treeview_Delete( info, pnmtv );
 +
 +    case TVN_ITEMEXPANDINGA:
 +    case TVN_ITEMEXPANDINGW:
 +        return BrsFolder_Treeview_Expand( info, pnmtv );
 +
 +    case TVN_SELCHANGEDA:
 +    case TVN_SELCHANGEDW:
 +        return BrsFolder_Treeview_Changed( info, pnmtv );
 +
 +    case TVN_ENDLABELEDITA:
 +    case TVN_ENDLABELEDITW:
 +        return BrsFolder_Treeview_Rename( info, (LPNMTVDISPINFOW)pnmtv );
 +
 +    default:
 +        WARN("unhandled (%d)\n", pnmtv->hdr.code);
 +        break;
 +    }
 +
 +    return 0;
 +}
 +
 +
 +static BOOL BrsFolder_OnCreate( HWND hWnd, browse_info *info )
 +{
 +    LPBROWSEINFOW lpBrowseInfo = info->lpBrowseInfo;
 +
 +    info->hWnd = hWnd;
 +    SetPropW( hWnd, szBrowseFolderInfo, info );
 +
 +    if (lpBrowseInfo->ulFlags & BIF_NEWDIALOGSTYLE)
 +        FIXME("flags BIF_NEWDIALOGSTYLE partially implemented\n");
 +    if (lpBrowseInfo->ulFlags & ~SUPPORTEDFLAGS)
 +    FIXME("flags %x not implemented\n", lpBrowseInfo->ulFlags & ~SUPPORTEDFLAGS);
 +
 +    if (lpBrowseInfo->ulFlags & BIF_NEWDIALOGSTYLE)
 +    {
 +        RECT rcWnd;
 +
 +        info->layout = LayoutInit(hWnd, g_layout_info, LAYOUT_INFO_COUNT);
 +
 +        /* TODO: Windows allows shrinking the windows a bit */
 +        GetWindowRect(hWnd, &rcWnd);
 +        info->szMin.cx = rcWnd.right - rcWnd.left;
 +        info->szMin.cy = rcWnd.bottom - rcWnd.top;
 +    }
 +    else
 +    {
 +        info->layout = NULL;
 +    }
 +
 +    if (lpBrowseInfo->lpszTitle)
 +        SetWindowTextW( GetDlgItem(hWnd, IDC_BROWSE_FOR_FOLDER_TITLE), lpBrowseInfo->lpszTitle );
 +    else
 +        ShowWindow( GetDlgItem(hWnd, IDC_BROWSE_FOR_FOLDER_TITLE), SW_HIDE );
 +
 +    if (!(lpBrowseInfo->ulFlags & BIF_STATUSTEXT)
 +        || (lpBrowseInfo->ulFlags & BIF_NEWDIALOGSTYLE))
 +        ShowWindow( GetDlgItem(hWnd, IDC_BROWSE_FOR_FOLDER_STATUS), SW_HIDE );
 +
 +    /* Hide "Make New Folder" Button? */
 +    if ((lpBrowseInfo->ulFlags & BIF_NONEWFOLDERBUTTON)
 +        || !(lpBrowseInfo->ulFlags & BIF_NEWDIALOGSTYLE))
 +        ShowWindow( GetDlgItem(hWnd, IDC_BROWSE_FOR_FOLDER_NEW_FOLDER), SW_HIDE );
 +
 +    /* Hide the editbox? */
 +    if (!(lpBrowseInfo->ulFlags & BIF_EDITBOX))
 +    {
 +        ShowWindow( GetDlgItem(hWnd, IDC_BROWSE_FOR_FOLDER_FOLDER), SW_HIDE );
 +        ShowWindow( GetDlgItem(hWnd, IDC_BROWSE_FOR_FOLDER_FOLDER_TEXT), SW_HIDE );
 +    }
 +
 +    info->hwndTreeView = GetDlgItem( hWnd, IDC_BROWSE_FOR_FOLDER_TREEVIEW );
 +    if (info->hwndTreeView)
 +    {
 +        InitializeTreeView( info );
 +
 +        /* Resize the treeview if there's not editbox */
 +        if ((lpBrowseInfo->ulFlags & BIF_NEWDIALOGSTYLE)
 +            && !(lpBrowseInfo->ulFlags & BIF_EDITBOX))
 +        {
 +            RECT rc;
 +            GetClientRect(info->hwndTreeView, &rc);
 +            SetWindowPos(info->hwndTreeView, HWND_TOP, 0, 0,
 +                         rc.right, rc.bottom + 40, SWP_NOMOVE);
 +        }
 +    }
 +    else
 +        ERR("treeview control missing!\n");
 +
 +    browsefolder_callback( info->lpBrowseInfo, hWnd, BFFM_INITIALIZED, 0 );
 +
 +    return TRUE;
 +}
 +
 +static HRESULT BrsFolder_Rename(browse_info *info, HTREEITEM rename)
 +{
 +    SendMessageW(info->hwndTreeView, TVM_SELECTITEM, TVGN_CARET, (LPARAM)rename);
 +    SendMessageW(info->hwndTreeView, TVM_EDITLABELW, 0, (LPARAM)rename);
 +    return S_OK;
 +}
 +
 +static HRESULT BrsFolder_NewFolder(browse_info *info)
 +{
 +    DWORD flags = BrowseFlagsToSHCONTF(info->lpBrowseInfo->ulFlags);
 +    CComPtr<IShellFolder> desktop;
 +    CComPtr<IShellFolder> cur;
 +    CComPtr<ISFHelper> sfhelper;
 +    WCHAR name[MAX_PATH];
 +    HTREEITEM parent, added;
 +    LPTV_ITEMDATA item_data;
 +    LPITEMIDLIST new_item;
 +    TVITEMW item;
 +    HRESULT hr;
 +    int len;
 +
 +    if(!info->pidlRet) {
 +        ERR("Make new folder button should be disabled\n");
 +        return E_FAIL;
 +    }
 +
 +    /* Create new directory */
 +    hr = SHGetDesktopFolder(&desktop);
 +    if(FAILED(hr))
 +        return hr;
 +    hr = desktop->BindToObject(info->pidlRet, 0, IID_PPV_ARG(IShellFolder, &cur));
 +    if(FAILED(hr))
 +        return hr;
 +
 +    hr = cur->QueryInterface(IID_PPV_ARG(ISFHelper, &sfhelper));
 +    if(FAILED(hr))
 +        return hr;
 +
 +    hr = SHGetPathFromIDListW(info->pidlRet, name);
 +    if(FAILED(hr))
 +        goto cleanup;
 +
 +    len = strlenW(name);
 +    if(len<MAX_PATH)
 +        name[len++] = '\\';
 +    hr = sfhelper->GetUniqueName(&name[len], MAX_PATH-len);
 +    if(FAILED(hr))
 +        goto cleanup;
 +
 +    hr = E_FAIL;
 +    if(!CreateDirectoryW(name, NULL))
 +        goto cleanup;
 +
 +    /* Update parent of newly created directory */
 +    parent = (HTREEITEM)SendMessageW(info->hwndTreeView, TVM_GETNEXTITEM, TVGN_CARET, 0);
 +    if(!parent)
 +        goto cleanup;
 +
 +    SendMessageW(info->hwndTreeView, TVM_EXPAND, TVE_EXPAND, (LPARAM)parent);
 +
 +    memset(&item, 0, sizeof(TVITEMW));
 +    item.mask = TVIF_PARAM|TVIF_STATE;
 +    item.hItem = parent;
 +    SendMessageW(info->hwndTreeView, TVM_GETITEMW, 0, (LPARAM)&item);
 +    item_data = (LPTV_ITEMDATA)item.lParam;
 +    if(!item_data)
 +        goto cleanup;
 +
 +    if(item_data->pEnumIL)
 +        item_data->pEnumIL->Release();
 +    hr = cur->EnumObjects(info->hwndTreeView, flags, &item_data->pEnumIL);
 +    if(FAILED(hr))
 +        goto cleanup;
 +
 +    /* Update treeview */
 +    if(!(item.state&TVIS_EXPANDEDONCE)) {
 +        item.mask = TVIF_STATE;
 +        item.state = TVIS_EXPANDEDONCE;
 +        item.stateMask = TVIS_EXPANDEDONCE;
 +        SendMessageW(info->hwndTreeView, TVM_SETITEMW, 0, (LPARAM)&item);
 +    }
 +
 +    hr = cur->ParseDisplayName(NULL, NULL, name+len, NULL, &new_item, NULL);
 +    if(FAILED(hr))
 +        goto cleanup;
 +
 +    added = InsertTreeViewItem(info, cur, new_item, item_data->lpifq, NULL, parent);
 +    SHFree(new_item);
 +
 +    SendMessageW(info->hwndTreeView, TVM_SORTCHILDREN, FALSE, (LPARAM)parent);
 +    return BrsFolder_Rename(info, added);
 +
 +cleanup:
 +    return hr;
 +}
 +
 +static BOOL BrsFolder_OnCommand( browse_info *info, UINT id )
 +{
 +    LPBROWSEINFOW lpBrowseInfo = info->lpBrowseInfo;
 +
 +    switch (id)
 +    {
 +    case IDOK:
 +        /* The original pidl is owned by the treeview and will be free'd. */
 +        info->pidlRet = ILClone(info->pidlRet);
 +        if (info->pidlRet == NULL) /* A null pidl would mean a cancel */
 +            info->pidlRet = _ILCreateDesktop();
 +        pdump( info->pidlRet );
 +        if (lpBrowseInfo->pszDisplayName)
 +            SHGetPathFromIDListW( info->pidlRet, lpBrowseInfo->pszDisplayName );
 +        EndDialog( info->hWnd, 1 );
 +        return TRUE;
 +
 +    case IDCANCEL:
 +        EndDialog( info->hWnd, 0 );
 +        return TRUE;
 +
 +    case IDC_BROWSE_FOR_FOLDER_NEW_FOLDER:
 +        BrsFolder_NewFolder(info);
 +        return TRUE;
 +    }
 +    return FALSE;
 +}
 +
 +static BOOL BrsFolder_OnSetExpanded(browse_info *info, LPVOID selection,
 +    BOOL is_str, HTREEITEM *pItem)
 +{
 +    LPITEMIDLIST pidlSelection = (LPITEMIDLIST)selection;
 +    LPCITEMIDLIST pidlCurrent, pidlRoot;
 +    TVITEMEXW item;
 +    BOOL bResult = FALSE;
 +
 +    memset(&item, 0, sizeof(item));
 +
 +    /* If 'selection' is a string, convert to a Shell ID List. */ 
 +    if (is_str) {
 +        CComPtr<IShellFolder> psfDesktop;
 +        HRESULT hr;
 +
 +        hr = SHGetDesktopFolder(&psfDesktop);
 +        if (FAILED(hr))
 +            goto done;
 +
 +        hr = psfDesktop->ParseDisplayName(NULL, NULL, (LPOLESTR)selection,
 +                                          NULL, &pidlSelection, NULL);
 +        if (FAILED(hr))
 +            goto done;
 +    }
 +
 +    /* Move pidlCurrent behind the SHITEMIDs in pidlSelection, which are the root of
 +     * the sub-tree currently displayed. */
 +    pidlRoot = info->lpBrowseInfo->pidlRoot;
 +    pidlCurrent = pidlSelection;
 +    while (!_ILIsEmpty(pidlRoot) && _ILIsEqualSimple(pidlRoot, pidlCurrent)) {
 +        pidlRoot = ILGetNext(pidlRoot);
 +        pidlCurrent = ILGetNext(pidlCurrent);
 +    }
 +
 +    /* The given ID List is not part of the SHBrowseForFolder's current sub-tree. */
 +    if (!_ILIsEmpty(pidlRoot))
 +        goto done;
 +
 +    /* Initialize item to point to the first child of the root folder. */
 +    item.mask = TVIF_PARAM;
 +    item.hItem = (HTREEITEM)SendMessageW(info->hwndTreeView, TVM_GETNEXTITEM, TVGN_ROOT, 0);
 +
 +    if (item.hItem)
 +        item.hItem = (HTREEITEM)SendMessageW(info->hwndTreeView, TVM_GETNEXTITEM, TVGN_CHILD,
 +                                             (LPARAM)item.hItem);
 +
 +    /* Walk the tree along the nodes corresponding to the remaining ITEMIDLIST */
 +    while (item.hItem && !_ILIsEmpty(pidlCurrent)) {
 +        LPTV_ITEMDATA pItemData;
 +
 +        SendMessageW(info->hwndTreeView, TVM_GETITEMW, 0, (LPARAM)&item);
 +        pItemData = (LPTV_ITEMDATA)item.lParam;
 +
 +        if (_ILIsEqualSimple(pItemData->lpi, pidlCurrent)) {
 +            pidlCurrent = ILGetNext(pidlCurrent);
 +            if (!_ILIsEmpty(pidlCurrent)) {
 +                /* Only expand current node and move on to it's first child,
 +                 * if we didn't already reach the last SHITEMID */
 +                SendMessageW(info->hwndTreeView, TVM_EXPAND, TVE_EXPAND, (LPARAM)item.hItem);
 +                item.hItem = (HTREEITEM)SendMessageW(info->hwndTreeView, TVM_GETNEXTITEM, TVGN_CHILD,
 +                                                     (LPARAM)item.hItem);
 +            }
 +        } else {
 +            item.hItem = (HTREEITEM)SendMessageW(info->hwndTreeView, TVM_GETNEXTITEM, TVGN_NEXT,
 +                                                 (LPARAM)item.hItem);
 +        }
 +    }
 +
 +    if (_ILIsEmpty(pidlCurrent) && item.hItem)
 +        bResult = TRUE;
 +
 +done:
 +    if (pidlSelection && pidlSelection != (LPITEMIDLIST)selection)
 +        ILFree(pidlSelection);
 +
 +    if (pItem)
 +        *pItem = item.hItem;
 +
 +    return bResult;
 +}
 +
 +static BOOL BrsFolder_OnSetSelectionW(browse_info *info, LPVOID selection, BOOL is_str) {
 +    HTREEITEM hItem;
 +    BOOL bResult;
 +
 +    if (!selection) return FALSE;
 +
 +    bResult = BrsFolder_OnSetExpanded(info, selection, is_str, &hItem);
 +    if (bResult)
 +        SendMessageW(info->hwndTreeView, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hItem );
 +    return bResult;
 +}
 +
 +static BOOL BrsFolder_OnSetSelectionA(browse_info *info, LPVOID selection, BOOL is_str) {
 +    LPWSTR selectionW = NULL;
 +    BOOL result = FALSE;
 +    int length;
 +
 +    if (!is_str)
 +        return BrsFolder_OnSetSelectionW(info, selection, is_str);
 +
 +    if ((length = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)selection, -1, NULL, 0)) &&
 +        (selectionW = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, length * sizeof(WCHAR))) &&
 +        MultiByteToWideChar(CP_ACP, 0, (LPCSTR)selection, -1, selectionW, length))
 +    {
 +        result = BrsFolder_OnSetSelectionW(info, selectionW, is_str);
 +    }
 +
 +    HeapFree(GetProcessHeap(), 0, selectionW);
 +    return result;
 +}
 +
 +static BOOL BrsFolder_OnWindowPosChanging(browse_info *info, WINDOWPOS *pos)
 +{
 +    if ((info->lpBrowseInfo->ulFlags & BIF_NEWDIALOGSTYLE) && !(pos->flags & SWP_NOSIZE))
 +    {
 +        if (pos->cx < info->szMin.cx)
 +            pos->cx = info->szMin.cx;
 +        if (pos->cy < info->szMin.cy)
 +            pos->cy = info->szMin.cy;
 +    }
 +    return 0;
 +}
 +
 +static INT BrsFolder_OnDestroy(browse_info *info)
 +{
 +    if (info->layout)
 +    {
 +        SHFree(info->layout);
 +        info->layout = NULL;
 +    }
 +
 +    return 0;
 +}
 +
 +/*************************************************************************
 + *             BrsFolderDlgProc32  (not an exported API function)
 + */
 +static INT_PTR CALLBACK BrsFolderDlgProc( HWND hWnd, UINT msg, WPARAM wParam,
 +                                          LPARAM lParam )
 +{
 +    browse_info *info;
 +
 +    TRACE("hwnd=%p msg=%04x 0x%08lx 0x%08lx\n", hWnd, msg, wParam, lParam );
 +
 +    if (msg == WM_INITDIALOG)
 +        return BrsFolder_OnCreate( hWnd, (browse_info*) lParam );
 +
 +    info = (browse_info*) GetPropW( hWnd, szBrowseFolderInfo );
 +
 +    switch (msg)
 +    {
 +    case WM_NOTIFY:
 +        return BrsFolder_OnNotify( info, (UINT)wParam, (LPNMHDR)lParam);
 +
 +    case WM_COMMAND:
 +        return BrsFolder_OnCommand( info, wParam );
 +
 +    case WM_WINDOWPOSCHANGING:
 +        return BrsFolder_OnWindowPosChanging( info, (WINDOWPOS *)lParam);
 +
 +    case WM_SIZE:
 +        if (info->layout)  /* new style dialogs */
 +            LayoutUpdate(hWnd, info->layout, g_layout_info, LAYOUT_INFO_COUNT);
 +        return 0;
 +
 +    case BFFM_SETSTATUSTEXTA:
 +        TRACE("Set status %s\n", debugstr_a((LPSTR)lParam));
 +        SetWindowTextA(GetDlgItem(hWnd, IDC_BROWSE_FOR_FOLDER_STATUS), (LPSTR)lParam);
 +        break;
 +
 +    case BFFM_SETSTATUSTEXTW:
 +        TRACE("Set status %s\n", debugstr_w((LPWSTR)lParam));
 +        SetWindowTextW(GetDlgItem(hWnd, IDC_BROWSE_FOR_FOLDER_STATUS), (LPWSTR)lParam);
 +        break;
 +
 +    case BFFM_ENABLEOK:
 +        TRACE("Enable %ld\n", lParam);
 +        EnableWindow(GetDlgItem(hWnd, 1), (lParam)?TRUE:FALSE);
 +        break;
 +
 +    case BFFM_SETOKTEXT: /* unicode only */
 +        TRACE("Set OK text %s\n", debugstr_w((LPWSTR)wParam));
 +        SetWindowTextW(GetDlgItem(hWnd, 1), (LPWSTR)wParam);
 +        break;
 +
 +    case BFFM_SETSELECTIONA:
 +        return BrsFolder_OnSetSelectionA(info, (LPVOID)lParam, (BOOL)wParam);
 +
 +    case BFFM_SETSELECTIONW:
 +        return BrsFolder_OnSetSelectionW(info, (LPVOID)lParam, (BOOL)wParam);
 +
 +    case BFFM_SETEXPANDED: /* unicode only */
 +        return BrsFolder_OnSetExpanded(info, (LPVOID)lParam, (BOOL)wParam, NULL);
 +
 +    case WM_DESTROY:
 +        return BrsFolder_OnDestroy(info);
 +    }
 +    return FALSE;
 +}
 +
 +
 +
 +
 +/*************************************************************************
 + * SHBrowseForFolderA [SHELL32.@]
 + * SHBrowseForFolder  [SHELL32.@]
 + */
 +LPITEMIDLIST WINAPI SHBrowseForFolderA (LPBROWSEINFOA lpbi)
 +{
 +    BROWSEINFOW bi;
 +    LPITEMIDLIST lpid;
 +    INT len;
 +    LPWSTR title;
 +
 +    TRACE("%p\n", lpbi);
 +
 +    bi.hwndOwner = lpbi->hwndOwner;
 +    bi.pidlRoot = lpbi->pidlRoot;
 +    if (lpbi->pszDisplayName)
 +    {
 +        bi.pszDisplayName = (WCHAR *)HeapAlloc( GetProcessHeap(), 0, MAX_PATH * sizeof(WCHAR) );
 +        MultiByteToWideChar( CP_ACP, 0, lpbi->pszDisplayName, -1, bi.pszDisplayName, MAX_PATH );
 +    }
 +    else
 +        bi.pszDisplayName = NULL;
 +
 +    if (lpbi->lpszTitle)
 +    {
 +        len = MultiByteToWideChar( CP_ACP, 0, lpbi->lpszTitle, -1, NULL, 0 );
 +        title = (WCHAR *)HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
 +        MultiByteToWideChar( CP_ACP, 0, lpbi->lpszTitle, -1, title, len );
 +    }
 +    else
 +        title = NULL;
 +
 +    bi.lpszTitle = title;
 +    bi.ulFlags = lpbi->ulFlags;
 +    bi.lpfn = lpbi->lpfn;
 +    bi.lParam = lpbi->lParam;
 +    bi.iImage = lpbi->iImage;
 +    lpid = SHBrowseForFolderW( &bi );
 +    if (bi.pszDisplayName)
 +    {
 +        WideCharToMultiByte( CP_ACP, 0, bi.pszDisplayName, -1,
 +                             lpbi->pszDisplayName, MAX_PATH, 0, NULL);
 +        HeapFree( GetProcessHeap(), 0, bi.pszDisplayName );
 +    }
 +    HeapFree(GetProcessHeap(), 0, title);
 +    lpbi->iImage = bi.iImage;
 +    return lpid;
 +}
 +
 +
 +/*************************************************************************
 + * SHBrowseForFolderW [SHELL32.@]
 + *
 + * NOTES
 + *  crashes when passed a null pointer
 + */
 +LPITEMIDLIST WINAPI SHBrowseForFolderW (LPBROWSEINFOW lpbi)
 +{
 +    browse_info info;
 +    DWORD r;
 +    HRESULT hr;
 +    WORD wDlgId;
 +
 +    info.hWnd = 0;
 +    info.pidlRet = NULL;
 +    info.lpBrowseInfo = lpbi;
 +    info.hwndTreeView = NULL;
 +
 +    hr = OleInitialize(NULL);
 +
 +    if (lpbi->ulFlags & BIF_NEWDIALOGSTYLE)
 +        wDlgId = IDD_BROWSE_FOR_FOLDER_NEW;
 +    else
 +        wDlgId = IDD_BROWSE_FOR_FOLDER;
 +    r = DialogBoxParamW( shell32_hInstance, MAKEINTRESOURCEW(wDlgId), lpbi->hwndOwner,
 +                         BrsFolderDlgProc, (LPARAM)&info );
 +    if (SUCCEEDED(hr))
 +        OleUninitialize();
 +    if (!r)
 +    {
 +        ILFree(info.pidlRet);
 +        return NULL;
 +    }
 +
 +    return info.pidlRet;
 +}
Simple merge
index 0000000,8bccd1c..8bccd1c
mode 000000,100644..100644
--- /dev/null
index 22a03b1,0000000..e8dddf1
mode 100644,000000..100644
--- /dev/null
@@@ -1,1969 -1,0 +1,1967 @@@
- #include "xdg.h"
 +/*
 + * SHFileOperation
 + *
 + * Copyright 2000 Juergen Schmied
 + * Copyright 2002 Andriy Palamarchuk
 + * Copyright 2004 Dietrich Teickner (from Odin)
 + * Copyright 2004 Rolf Kalbermatter
 + *
 + * This library is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public
 + * License as published by the Free Software Foundation; either
 + * version 2.1 of the License, or (at your option) any later version.
 + *
 + * This library is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with this library; if not, write to the Free Software
 + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 + */
 +
 +#include "precomp.h"
 +
 +WINE_DEFAULT_DEBUG_CHANNEL(shell);
 +
 +#define IsAttrib(x, y)  ((INVALID_FILE_ATTRIBUTES != (x)) && ((x) & (y)))
 +#define IsAttribFile(x) (!((x) & FILE_ATTRIBUTE_DIRECTORY))
 +#define IsAttribDir(x)  IsAttrib(x, FILE_ATTRIBUTE_DIRECTORY)
 +#define IsDotDir(x)     ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
 +
 +#define FO_MASK         0xF
 +#define WM_FILE       (WM_USER + 1)
 +#define TIMER_ID      (100)
 +
 +static const WCHAR wWildcardFile[] = {'*',0};
 +static const WCHAR wWildcardChars[] = {'*','?',0};
 +
 +static DWORD SHNotifyCreateDirectoryW(LPCWSTR path, LPSECURITY_ATTRIBUTES sec);
 +static DWORD SHNotifyRemoveDirectoryW(LPCWSTR path);
 +static DWORD SHNotifyDeleteFileW(LPCWSTR path);
 +static DWORD SHNotifyMoveFileW(LPCWSTR src, LPCWSTR dest, BOOL isdir);
 +static DWORD SHNotifyCopyFileW(LPCWSTR src, LPCWSTR dest, BOOL bFailIfExists);
 +static DWORD SHFindAttrW(LPCWSTR pName, BOOL fileOnly);
 +
 +typedef struct
 +{
 +    SHFILEOPSTRUCTW *req;
 +    DWORD dwYesToAllMask;
 +    BOOL bManyItems;
 +    BOOL bCancelled;
 +} FILE_OPERATION;
 +
 +#define ERROR_SHELL_INTERNAL_FILE_NOT_FOUND 1026
 +
 +typedef struct
 +{
 +    DWORD attributes;
 +    LPWSTR szDirectory;
 +    LPWSTR szFilename;
 +    LPWSTR szFullPath;
 +    BOOL bFromWildcard;
 +    BOOL bFromRelative;
 +    BOOL bExists;
 +} FILE_ENTRY;
 +
 +typedef struct
 +{
 +    FILE_ENTRY *feFiles;
 +    DWORD num_alloc;
 +    DWORD dwNumFiles;
 +    BOOL bAnyFromWildcard;
 +    BOOL bAnyDirectories;
 +    BOOL bAnyDontExist;
 +} FILE_LIST;
 +
 +typedef struct
 +{
 +    FILE_LIST * from;
 +    FILE_LIST * to;
 +    FILE_OPERATION * op;
 +    DWORD Index;
 +    HWND hDlgCtrl;
 +    HWND hwndDlg;
 +}FILE_OPERATION_CONTEXT;
 +
 +
 +/* Confirm dialogs with an optional "Yes To All" as used in file operations confirmations
 + */
 +static const WCHAR CONFIRM_MSG_PROP[] = {'W','I','N','E','_','C','O','N','F','I','R','M',0};
 +
 +struct confirm_msg_info
 +{
 +    LPWSTR lpszText;
 +    LPWSTR lpszCaption;
 +    HICON hIcon;
 +    BOOL bYesToAll;
 +};
 +
 +/* as some buttons may be hidden and the dialog height may change we may need
 + * to move the controls */
 +static void confirm_msg_move_button(HWND hDlg, INT iId, INT *xPos, INT yOffset, BOOL bShow)
 +{
 +    HWND hButton = GetDlgItem(hDlg, iId);
 +    RECT r;
 +
 +    if (bShow)
 +    {
 +        POINT pt;
 +        int width;
 +
 +        GetWindowRect(hButton, &r);
 +        width = r.right - r.left;
 +        pt.x = r.left;
 +        pt.y = r.top;
 +        ScreenToClient(hDlg, &pt);
 +        MoveWindow(hButton, *xPos - width, pt.y - yOffset, width, r.bottom - r.top, FALSE);
 +        *xPos -= width + 5;
 +    }
 +    else
 +        ShowWindow(hButton, SW_HIDE);
 +}
 +
 +/* Note: we paint the text manually and don't use the static control to make
 + * sure the text has the same height as the one computed in WM_INITDIALOG
 + */
 +static INT_PTR ConfirmMsgBox_Paint(HWND hDlg)
 +{
 +    PAINTSTRUCT ps;
 +    HFONT hOldFont;
 +    RECT r;
 +    HDC hdc;
 +
 +    BeginPaint(hDlg, &ps);
 +    hdc = ps.hdc;
 +
 +    GetClientRect(GetDlgItem(hDlg, IDC_YESTOALL_MESSAGE), &r);
 +    /* this will remap the rect to dialog coords */
 +    MapWindowPoints(GetDlgItem(hDlg, IDC_YESTOALL_MESSAGE), hDlg, (LPPOINT)&r, 2);
 +    hOldFont = (HFONT)SelectObject(hdc, (HFONT)SendDlgItemMessageW(hDlg, IDC_YESTOALL_MESSAGE, WM_GETFONT, 0, 0));
 +    DrawTextW(hdc, (LPWSTR)GetPropW(hDlg, CONFIRM_MSG_PROP), -1, &r, DT_NOPREFIX | DT_PATH_ELLIPSIS | DT_WORDBREAK);
 +    SelectObject(hdc, hOldFont);
 +    EndPaint(hDlg, &ps);
 +
 +    return TRUE;
 +}
 +
 +static INT_PTR ConfirmMsgBox_Init(HWND hDlg, LPARAM lParam)
 +{
 +    struct confirm_msg_info *info = (struct confirm_msg_info *)lParam;
 +    INT xPos, yOffset;
 +    int width, height;
 +    HFONT hOldFont;
 +    HDC hdc;
 +    RECT r;
 +
 +    SetWindowTextW(hDlg, info->lpszCaption);
 +    ShowWindow(GetDlgItem(hDlg, IDC_YESTOALL_MESSAGE), SW_HIDE);
 +    SetPropW(hDlg, CONFIRM_MSG_PROP, info->lpszText);
 +    SendDlgItemMessageW(hDlg, IDC_YESTOALL_ICON, STM_SETICON, (WPARAM)info->hIcon, 0);
 +
 +    /* compute the text height and resize the dialog */
 +    GetClientRect(GetDlgItem(hDlg, IDC_YESTOALL_MESSAGE), &r);
 +    hdc = GetDC(hDlg);
 +    yOffset = r.bottom;
 +    hOldFont = (HFONT)SelectObject(hdc, (HFONT)SendDlgItemMessageW(hDlg, IDC_YESTOALL_MESSAGE, WM_GETFONT, 0, 0));
 +    DrawTextW(hdc, info->lpszText, -1, &r, DT_NOPREFIX | DT_PATH_ELLIPSIS | DT_WORDBREAK | DT_CALCRECT);
 +    SelectObject(hdc, hOldFont);
 +    yOffset -= r.bottom;
 +    yOffset = min(yOffset, 35);  /* don't make the dialog too small */
 +    ReleaseDC(hDlg, hdc);
 +
 +    GetClientRect(hDlg, &r);
 +    xPos = r.right - 7;
 +    GetWindowRect(hDlg, &r);
 +    width = r.right - r.left;
 +    height = r.bottom - r.top - yOffset;
 +    MoveWindow(hDlg, (GetSystemMetrics(SM_CXSCREEN) - width)/2,
 +        (GetSystemMetrics(SM_CYSCREEN) - height)/2, width, height, FALSE);
 +
 +    confirm_msg_move_button(hDlg, IDCANCEL,     &xPos, yOffset, info->bYesToAll);
 +    confirm_msg_move_button(hDlg, IDNO,         &xPos, yOffset, TRUE);
 +    confirm_msg_move_button(hDlg, IDC_YESTOALL, &xPos, yOffset, info->bYesToAll);
 +    confirm_msg_move_button(hDlg, IDYES,        &xPos, yOffset, TRUE);
 +
 +    return TRUE;
 +}
 +
 +static INT_PTR CALLBACK ConfirmMsgBoxProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
 +{
 +    switch (uMsg)
 +    {
 +        case WM_INITDIALOG:
 +            return ConfirmMsgBox_Init(hDlg, lParam);
 +        case WM_PAINT:
 +            return ConfirmMsgBox_Paint(hDlg);
 +        case WM_COMMAND:
 +            EndDialog(hDlg, wParam);
 +            break;
 +        case WM_CLOSE:
 +            EndDialog(hDlg, IDCANCEL);
 +            break;
 +    }
 +    return FALSE;
 +}
 +
 +int SHELL_ConfirmMsgBox(HWND hWnd, LPWSTR lpszText, LPWSTR lpszCaption, HICON hIcon, BOOL bYesToAll)
 +{
 +    struct confirm_msg_info info;
 +
 +    info.lpszText = lpszText;
 +    info.lpszCaption = lpszCaption;
 +    info.hIcon = hIcon;
 +    info.bYesToAll = bYesToAll;
 +    return DialogBoxParamW(shell32_hInstance, MAKEINTRESOURCEW(IDD_YESTOALL_MSGBOX), hWnd, ConfirmMsgBoxProc, (LPARAM)&info);
 +}
 +
 +/* confirmation dialogs content */
 +typedef struct
 +{
 +    HINSTANCE hIconInstance;
 +    UINT icon_resource_id;
 +    UINT caption_resource_id, text_resource_id;
 +} SHELL_ConfirmIDstruc;
 +
 +static BOOL SHELL_ConfirmIDs(int nKindOfDialog, SHELL_ConfirmIDstruc *ids)
 +{
 +    ids->hIconInstance = shell32_hInstance;
 +    switch (nKindOfDialog)
 +    {
 +        case ASK_DELETE_FILE:
 +            ids->icon_resource_id = IDI_SHELL_CONFIRM_DELETE;
 +            ids->caption_resource_id  = IDS_DELETEITEM_CAPTION;
 +            ids->text_resource_id  = IDS_DELETEITEM_TEXT;
 +            return TRUE;
 +
 +        case ASK_DELETE_FOLDER:
 +            ids->icon_resource_id = IDI_SHELL_CONFIRM_DELETE;
 +            ids->caption_resource_id  = IDS_DELETEFOLDER_CAPTION;
 +            ids->text_resource_id  = IDS_DELETEITEM_TEXT;
 +            return TRUE;
 +
 +        case ASK_DELETE_MULTIPLE_ITEM:
 +            ids->icon_resource_id = IDI_SHELL_CONFIRM_DELETE;
 +            ids->caption_resource_id  = IDS_DELETEITEM_CAPTION;
 +            ids->text_resource_id  = IDS_DELETEMULTIPLE_TEXT;
 +            return TRUE;
 +
 +        case ASK_TRASH_FILE:
 +            ids->icon_resource_id = IDI_SHELL_TRASH_FILE;
 +            ids->caption_resource_id = IDS_DELETEITEM_CAPTION;
 +            ids->text_resource_id = IDS_TRASHITEM_TEXT;
 +            return TRUE;
 +
 +        case ASK_TRASH_FOLDER:
 +            ids->icon_resource_id = IDI_SHELL_TRASH_FILE;
 +            ids->caption_resource_id = IDS_DELETEFOLDER_CAPTION;
 +            ids->text_resource_id = IDS_TRASHFOLDER_TEXT;
 +            return TRUE;
 +
 +        case ASK_TRASH_MULTIPLE_ITEM:
 +            ids->icon_resource_id = IDI_SHELL_TRASH_FILE;
 +            ids->caption_resource_id = IDS_DELETEITEM_CAPTION;
 +            ids->text_resource_id = IDS_TRASHMULTIPLE_TEXT;
 +            return TRUE;
 +
 +        case ASK_CANT_TRASH_ITEM:
 +            ids->icon_resource_id = IDI_SHELL_CONFIRM_DELETE;
 +            ids->caption_resource_id  = IDS_DELETEITEM_CAPTION;
 +            ids->text_resource_id  = IDS_CANTTRASH_TEXT;
 +            return TRUE;
 +
 +        case ASK_DELETE_SELECTED:
 +            ids->icon_resource_id = IDI_SHELL_CONFIRM_DELETE;
 +            ids->caption_resource_id  = IDS_DELETEITEM_CAPTION;
 +            ids->text_resource_id  = IDS_DELETESELECTED_TEXT;
 +            return TRUE;
 +
 +      case ASK_OVERWRITE_FILE:
 +            ids->hIconInstance = NULL;
 +            ids->icon_resource_id = IDI_SHELL_CONFIRM_DELETE;
 +            ids->caption_resource_id  = IDS_OVERWRITEFILE_CAPTION;
 +            ids->text_resource_id  = IDS_OVERWRITEFILE_TEXT;
 +            return TRUE;
 +
 +      case ASK_OVERWRITE_FOLDER:
 +            ids->hIconInstance = NULL;
 +            ids->icon_resource_id = IDI_SHELL_CONFIRM_DELETE;
 +            ids->caption_resource_id  = IDS_OVERWRITEFILE_CAPTION;
 +            ids->text_resource_id  = IDS_OVERWRITEFOLDER_TEXT;
 +            return TRUE;
 +
 +        default:
 +            FIXME(" Unhandled nKindOfDialog %d stub\n", nKindOfDialog);
 +    }
 +    return FALSE;
 +}
 +
 +static BOOL SHELL_ConfirmDialogW(HWND hWnd, int nKindOfDialog, LPCWSTR szDir, FILE_OPERATION *op)
 +{
 +    WCHAR szCaption[255], szText[255], szBuffer[MAX_PATH + 256];
 +    SHELL_ConfirmIDstruc ids;
 +    DWORD_PTR args[1];
 +    HICON hIcon;
 +    int ret;
 +
 +    assert(nKindOfDialog >= 0 && nKindOfDialog < 32);
 +    if (op && (op->dwYesToAllMask & (1 << nKindOfDialog)))
 +        return TRUE;
 +
 +    if (!SHELL_ConfirmIDs(nKindOfDialog, &ids)) return FALSE;
 +
 +    LoadStringW(shell32_hInstance, ids.caption_resource_id, szCaption, sizeof(szCaption)/sizeof(WCHAR));
 +    LoadStringW(shell32_hInstance, ids.text_resource_id, szText, sizeof(szText)/sizeof(WCHAR));
 +
 +    args[0] = (DWORD_PTR)szDir;
 +    FormatMessageW(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ARGUMENT_ARRAY,
 +                   szText, 0, 0, szBuffer, sizeof(szBuffer), (va_list*)args);
 +    hIcon = LoadIconW(ids.hIconInstance, (LPWSTR)MAKEINTRESOURCE(ids.icon_resource_id));
 +
 +    ret = SHELL_ConfirmMsgBox(hWnd, szBuffer, szCaption, hIcon, op && op->bManyItems);
 +    if (op)
 +    {
 +        if (ret == IDC_YESTOALL)
 +        {
 +            op->dwYesToAllMask |= (1 << nKindOfDialog);
 +            ret = IDYES;
 +        }
 +        if (ret == IDCANCEL)
 +            op->bCancelled = TRUE;
 +        if (ret != IDYES)
 +            op->req->fAnyOperationsAborted = TRUE;
 +    }
 +    return ret == IDYES;
 +}
 +
 +BOOL SHELL_ConfirmYesNoW(HWND hWnd, int nKindOfDialog, LPCWSTR szDir)
 +{
 +    return SHELL_ConfirmDialogW(hWnd, nKindOfDialog, szDir, NULL);
 +}
 +
 +static DWORD SHELL32_AnsiToUnicodeBuf(LPCSTR aPath, LPWSTR *wPath, DWORD minChars)
 +{
 +    DWORD len = MultiByteToWideChar(CP_ACP, 0, aPath, -1, NULL, 0);
 +
 +    if (len < minChars)
 +      len = minChars;
 +
 +    *wPath = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
 +    if (*wPath)
 +    {
 +      MultiByteToWideChar(CP_ACP, 0, aPath, -1, *wPath, len);
 +      return NO_ERROR;
 +    }
 +    return E_OUTOFMEMORY;
 +}
 +
 +static void SHELL32_FreeUnicodeBuf(LPWSTR wPath)
 +{
 +    HeapFree(GetProcessHeap(), 0, wPath);
 +}
 +
 +EXTERN_C HRESULT WINAPI SHIsFileAvailableOffline(LPCWSTR path, LPDWORD status)
 +{
 +    FIXME("(%s, %p) stub\n", debugstr_w(path), status);
 +    return E_FAIL;
 +}
 +
 +/**************************************************************************
 + * SHELL_DeleteDirectory()  [internal]
 + *
 + * Asks for confirmation when bShowUI is true and deletes the directory and
 + * all its subdirectories and files if necessary.
 + */
 +BOOL SHELL_DeleteDirectoryW(HWND hwnd, LPCWSTR pszDir, BOOL bShowUI)
 +{
 +    BOOL    ret = TRUE;
 +    HANDLE  hFind;
 +    WIN32_FIND_DATAW wfd;
 +    WCHAR   szTemp[MAX_PATH];
 +
 +    /* Make sure the directory exists before eventually prompting the user */
 +    PathCombineW(szTemp, pszDir, wWildcardFile);
 +    hFind = FindFirstFileW(szTemp, &wfd);
 +    if (hFind == INVALID_HANDLE_VALUE)
 +      return FALSE;
 +
 +    if (!bShowUI || (ret = SHELL_ConfirmDialogW(hwnd, ASK_DELETE_FOLDER, pszDir, NULL)))
 +    {
 +        do
 +        {
 +            if (IsDotDir(wfd.cFileName))
 +                continue;
 +            PathCombineW(szTemp, pszDir, wfd.cFileName);
 +            if (FILE_ATTRIBUTE_DIRECTORY & wfd.dwFileAttributes)
 +                ret = SHELL_DeleteDirectoryW(hwnd, szTemp, FALSE);
 +            else
 +                ret = (SHNotifyDeleteFileW(szTemp) == ERROR_SUCCESS);
 +        } while (ret && FindNextFileW(hFind, &wfd));
 +    }
 +    FindClose(hFind);
 +    if (ret)
 +        ret = (SHNotifyRemoveDirectoryW(pszDir) == ERROR_SUCCESS);
 +    return ret;
 +}
 +
 +/**************************************************************************
 + * Win32CreateDirectory      [SHELL32.93]
 + *
 + * Creates a directory. Also triggers a change notify if one exists.
 + *
 + * PARAMS
 + *  path       [I]   path to directory to create
 + *
 + * RETURNS
 + *  TRUE if successful, FALSE otherwise
 + */
 +
 +static DWORD SHNotifyCreateDirectoryW(LPCWSTR path, LPSECURITY_ATTRIBUTES sec)
 +{
 +    TRACE("(%s, %p)\n", debugstr_w(path), sec);
 +
 +    if (CreateDirectoryW(path, sec))
 +    {
 +      SHChangeNotify(SHCNE_MKDIR, SHCNF_PATHW, path, NULL);
 +      return ERROR_SUCCESS;
 +    }
 +    return GetLastError();
 +}
 +
 +/**********************************************************************/
 +
 +EXTERN_C BOOL WINAPI Win32CreateDirectoryW(LPCWSTR path, LPSECURITY_ATTRIBUTES sec)
 +{
 +    return (SHNotifyCreateDirectoryW(path, sec) == ERROR_SUCCESS);
 +}
 +
 +/************************************************************************
 + * Win32RemoveDirectory      [SHELL32.94]
 + *
 + * Deletes a directory. Also triggers a change notify if one exists.
 + *
 + * PARAMS
 + *  path       [I]   path to directory to delete
 + *
 + * RETURNS
 + *  TRUE if successful, FALSE otherwise
 + */
 +static DWORD SHNotifyRemoveDirectoryW(LPCWSTR path)
 +{
 +    BOOL ret;
 +    TRACE("(%s)\n", debugstr_w(path));
 +
 +    ret = RemoveDirectoryW(path);
 +    if (!ret)
 +    {
 +      /* Directory may be write protected */
 +      DWORD dwAttr = GetFileAttributesW(path);
 +      if (IsAttrib(dwAttr, FILE_ATTRIBUTE_READONLY))
 +        if (SetFileAttributesW(path, dwAttr & ~FILE_ATTRIBUTE_READONLY))
 +          ret = RemoveDirectoryW(path);
 +    }
 +    if (ret)
 +    {
 +      SHChangeNotify(SHCNE_RMDIR, SHCNF_PATHW, path, NULL);
 +      return ERROR_SUCCESS;
 +    }
 +    return GetLastError();
 +}
 +
 +/***********************************************************************/
 +
 +EXTERN_C BOOL WINAPI Win32RemoveDirectoryW(LPCWSTR path)
 +{
 +    return (SHNotifyRemoveDirectoryW(path) == ERROR_SUCCESS);
 +}
 +
 +/************************************************************************
 + * Win32DeleteFile           [SHELL32.164]
 + *
 + * Deletes a file. Also triggers a change notify if one exists.
 + *
 + * PARAMS
 + *  path       [I]   path to file to delete
 + *
 + * RETURNS
 + *  TRUE if successful, FALSE otherwise
 + */
 +static DWORD SHNotifyDeleteFileW(LPCWSTR path)
 +{
 +    BOOL ret;
 +
 +    TRACE("(%s)\n", debugstr_w(path));
 +
 +    ret = DeleteFileW(path);
 +    if (!ret)
 +    {
 +      /* File may be write protected or a system file */
 +      DWORD dwAttr = GetFileAttributesW(path);
 +      if (IsAttrib(dwAttr, FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM))
 +        if (SetFileAttributesW(path, dwAttr & ~(FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM)))
 +            ret = DeleteFileW(path);
 +    }
 +    if (ret)
 +    {
 +        SHChangeNotify(SHCNE_DELETE, SHCNF_PATHW, path, NULL);
 +        return ERROR_SUCCESS;
 +    }
 +    return GetLastError();
 +}
 +
 +/***********************************************************************/
 +
 +EXTERN_C DWORD WINAPI Win32DeleteFileW(LPCWSTR path)
 +{
 +    return (SHNotifyDeleteFileW(path) == ERROR_SUCCESS);
 +}
 +
 +/************************************************************************
 + * SHNotifyMoveFile          [internal]
 + *
 + * Moves a file. Also triggers a change notify if one exists.
 + *
 + * PARAMS
 + *  src        [I]   path to source file to move
 + *  dest       [I]   path to target file to move to
 + *
 + * RETURNS
 + *  ERORR_SUCCESS if successful
 + */
 +static DWORD SHNotifyMoveFileW(LPCWSTR src, LPCWSTR dest, BOOL isdir)
 +{
 +    BOOL ret;
 +
 +    TRACE("(%s %s)\n", debugstr_w(src), debugstr_w(dest));
 +
 +        ret = MoveFileExW(src, dest, MOVEFILE_REPLACE_EXISTING);
 +
 +        /* MOVEFILE_REPLACE_EXISTING fails with dirs, so try MoveFile */
 +        if (!ret)
 +            ret = MoveFileW(src, dest);
 +
 +    if (!ret)
 +    {
 +      DWORD dwAttr;
 +
 +      dwAttr = SHFindAttrW(dest, FALSE);
 +      if (INVALID_FILE_ATTRIBUTES == dwAttr)
 +      {
 +        /* Source file may be write protected or a system file */
 +        dwAttr = GetFileAttributesW(src);
 +        if (IsAttrib(dwAttr, FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM))
 +            if (SetFileAttributesW(src, dwAttr & ~(FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM)))
 +                ret = MoveFileW(src, dest);
 +      }
 +    }
 +    if (ret)
 +    {
 +        SHChangeNotify(isdir ? SHCNE_MKDIR : SHCNE_CREATE, SHCNF_PATHW, dest, NULL);
 +        SHChangeNotify(isdir ? SHCNE_RMDIR : SHCNE_DELETE, SHCNF_PATHW, src, NULL);
 +        return ERROR_SUCCESS;
 +    }
 +    return GetLastError();
 +}
 +
 +static DWORD WINAPI SHOperationProgressRoutine(LARGE_INTEGER TotalFileSize, LARGE_INTEGER TotalBytesTransferred, LARGE_INTEGER StreamSize, LARGE_INTEGER StreamBytesTransferred, DWORD dwStreamNumber, DWORD dwCallbackReason, HANDLE hSourceFile, HANDLE hDestinationFile, LPVOID lpData)
 +{
 +    FILE_OPERATION_CONTEXT * Context;
 +    LARGE_INTEGER Progress;
 +
 +    /* get context */
 +    Context = (FILE_OPERATION_CONTEXT*)lpData;
 +
 +    if (TotalBytesTransferred.QuadPart)
 +    {
 +        Progress.QuadPart = (TotalBytesTransferred.QuadPart * 100) / TotalFileSize.QuadPart;
 +    }
 +    else
 +    {
 +        Progress.QuadPart = 1;
 +    }
 +
 +    /* update progress bar */
 +    SendMessageW(Context->hDlgCtrl, PBM_SETPOS, (WPARAM)Progress.u.LowPart, 0);
 +
 +    if (TotalBytesTransferred.QuadPart == TotalFileSize.QuadPart)
 +    {
 +        /* file was copied */
 +        Context->Index++;
 +        PostMessageW(Context->hwndDlg, WM_FILE, 0, 0);
 +    }
 +
 +    return PROGRESS_CONTINUE;
 +}
 +
 +BOOL
 +QueueFile(
 +    FILE_OPERATION_CONTEXT * Context)
 +{
 +    FILE_ENTRY * from, *to = NULL;
 +    BOOL bRet = FALSE;
 +
 +    if (Context->Index >= Context->from->dwNumFiles)
 +        return FALSE;
 +
 +    /* get current file */
 +    from = &Context->from->feFiles[Context->Index];
 +
 +    if (Context->op->req->wFunc != FO_DELETE)
 +        to = &Context->to->feFiles[Context->Index];
 +
 +    /* update status */
 +    SetDlgItemTextW(Context->hwndDlg, 14001, from->szFullPath);
 +
 +    if (Context->op->req->wFunc == FO_COPY)
 +    {
 +        bRet = CopyFileExW(from->szFullPath, to->szFullPath, SHOperationProgressRoutine, (LPVOID)Context, &Context->op->bCancelled, 0);
 +    }
 +    else if (Context->op->req->wFunc == FO_MOVE)
 +    {
 +        //bRet = MoveFileWithProgressW(from->szFullPath, to->szFullPath, SHOperationProgressRoutine, (LPVOID)Context, MOVEFILE_COPY_ALLOWED);
 +    }
 +    else if (Context->op->req->wFunc == FO_DELETE)
 +    {
 +        bRet = DeleteFile(from->szFullPath);
 +    }
 +
 +    return bRet;
 +}
 +
 +static INT_PTR CALLBACK SHOperationDialog(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
 +{
 +    FILE_OPERATION_CONTEXT * Context;
 +
 +    Context = (FILE_OPERATION_CONTEXT*) GetWindowLongPtr(hwndDlg, DWLP_USER);
 +
 +    switch(uMsg)
 +    {
 +        case WM_INITDIALOG:
 +            SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG)lParam);
 +
 +            /* get context */
 +            Context = (FILE_OPERATION_CONTEXT*)lParam;
 +
 +            /* store progress bar handle */
 +            Context->hDlgCtrl = GetDlgItem(hwndDlg, 14000);
 +
 +            /* store window handle */
 +            Context->hwndDlg = hwndDlg;
 +
 +            /* set progress bar range */
 +            (void)SendMessageW(Context->hDlgCtrl, (UINT) PBM_SETRANGE, 0, MAKELPARAM(0, 100));
 +
 +            /* start file queueing */
 +            SetTimer(hwndDlg, TIMER_ID, 1000, NULL);
 +            //QueueFile(Context);
 +
 +            return TRUE;
 +
 +        case WM_CLOSE:
 +            Context->op->bCancelled = TRUE;
 +            EndDialog(hwndDlg, Context->op->bCancelled);
 +            return TRUE;
 +
 +        case WM_COMMAND:
 +            if (LOWORD(wParam) == 14002)
 +            {
 +                Context->op->bCancelled = TRUE;
 +                EndDialog(hwndDlg, Context->op->bCancelled);
 +                return TRUE;
 +            }; break;
 +
 +        case WM_TIMER:
 +            if (wParam == TIMER_ID)
 +            {
 +                QueueFile(Context);
 +                KillTimer(hwndDlg, TIMER_ID);
 +            }; break;
 +
 +        case WM_FILE:
 +            if (!QueueFile(Context))
 +                EndDialog(hwndDlg, Context->op->bCancelled);
 +        default:
 +            break;
 +    }
 +    return FALSE;
 +}
 +
 +HRESULT
 +SHShowFileOperationDialog(FILE_OPERATION *op, FILE_LIST *flFrom, FILE_LIST *flTo)
 +{
 +    HWND hwnd;
 +    BOOL bRet;
 +    MSG msg;
 +    FILE_OPERATION_CONTEXT Context;
 +
 +    Context.from = flFrom;
 +    Context.to = flTo;
 +    Context.op = op;
 +    Context.Index = 0;
 +    Context.op->bCancelled = FALSE;
 +
 +    hwnd = CreateDialogParam(shell32_hInstance, MAKEINTRESOURCE(IDD_FILE_COPY), NULL, SHOperationDialog, (LPARAM)&Context);
 +    if (hwnd == NULL)
 +    {
 +        ERR("Failed to create dialog\n");
 +        return E_FAIL;
 +    }
 +    ShowWindow(hwnd, SW_SHOWNORMAL);
 +
 +    while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0)
 +    {
 +        if (!IsWindow(hwnd) || !IsDialogMessage(hwnd, &msg))
 +        {
 +            TranslateMessage(&msg);
 +            DispatchMessage(&msg);
 +        }
 +    }
 +
 +    return NOERROR;
 +}
 +
 +
 +/************************************************************************
 + * SHNotifyCopyFile          [internal]
 + *
 + * Copies a file. Also triggers a change notify if one exists.
 + *
 + * PARAMS
 + *  src           [I]   path to source file to move
 + *  dest          [I]   path to target file to move to
 + *  bFailIfExists [I]   if TRUE, the target file will not be overwritten if
 + *                      a file with this name already exists
 + *
 + * RETURNS
 + *  ERROR_SUCCESS if successful
 + */
 +static DWORD SHNotifyCopyFileW(LPCWSTR src, LPCWSTR dest, BOOL bFailIfExists)
 +{
 +    BOOL ret;
 +    DWORD attribs;
 +
 +    TRACE("(%s %s %s)\n", debugstr_w(src), debugstr_w(dest), bFailIfExists ? "failIfExists" : "");
 +
 +    /* Destination file may already exist with read only attribute */
 +    attribs = GetFileAttributesW(dest);
 +    if (IsAttrib(attribs, FILE_ATTRIBUTE_READONLY))
 +        SetFileAttributesW(dest, attribs & ~FILE_ATTRIBUTE_READONLY);
 +
 +    if (GetFileAttributesW(dest) & FILE_ATTRIBUTE_READONLY)
 +    {
 +        SetFileAttributesW(dest, attribs & ~FILE_ATTRIBUTE_READONLY);
 +        if (GetFileAttributesW(dest) & FILE_ATTRIBUTE_READONLY)
 +        {
 +            TRACE("[shell32, SHNotifyCopyFileW] STILL SHIT\n");
 +        }
 +    }
 +
 +    ret = CopyFileW(src, dest, bFailIfExists);
 +    if (ret)
 +    {
 +        SHChangeNotify(SHCNE_CREATE, SHCNF_PATHW, dest, NULL);
 +        return ERROR_SUCCESS;
 +    }
 +
 +    return GetLastError();
 +}
 +
 +/*************************************************************************
 + * SHCreateDirectory         [SHELL32.165]
 + *
 + * This function creates a file system folder whose fully qualified path is
 + * given by path. If one or more of the intermediate folders do not exist,
 + * they will be created as well.
 + *
 + * PARAMS
 + *  hWnd       [I]
 + *  path       [I]   path of directory to create
 + *
 + * RETURNS
 + *  ERROR_SUCCESS or one of the following values:
 + *  ERROR_BAD_PATHNAME if the path is relative
 + *  ERROR_FILE_EXISTS when a file with that name exists
 + *  ERROR_PATH_NOT_FOUND can't find the path, probably invalid
 + *  ERROR_INVALID_NAME if the path contains invalid chars
 + *  ERROR_ALREADY_EXISTS when the directory already exists
 + *  ERROR_FILENAME_EXCED_RANGE if the filename was to long to process
 + *
 + * NOTES
 + *  exported by ordinal
 + *  Win9x exports ANSI
 + *  WinNT/2000 exports Unicode
 + */
 +int WINAPI SHCreateDirectory(HWND hWnd, LPCWSTR path)
 +{
 +     return SHCreateDirectoryExW(hWnd, path, NULL);
 +}
 +
 +/*************************************************************************
 + * SHCreateDirectoryExA      [SHELL32.@]
 + *
 + * This function creates a file system folder whose fully qualified path is
 + * given by path. If one or more of the intermediate folders do not exist,
 + * they will be created as well.
 + *
 + * PARAMS
 + *  hWnd       [I]
 + *  path       [I]   path of directory to create
 + *  sec        [I]   security attributes to use or NULL
 + *
 + * RETURNS
 + *  ERROR_SUCCESS or one of the following values:
 + *  ERROR_BAD_PATHNAME or ERROR_PATH_NOT_FOUND if the path is relative
 + *  ERROR_INVALID_NAME if the path contains invalid chars
 + *  ERROR_FILE_EXISTS when a file with that name exists
 + *  ERROR_ALREADY_EXISTS when the directory already exists
 + *  ERROR_FILENAME_EXCED_RANGE if the filename was to long to process
 + *
 + *  FIXME: Not implemented yet;
 + *  SHCreateDirectoryEx also verifies that the files in the directory will be visible
 + *  if the path is a network path to deal with network drivers which might have a limited
 + *  but unknown maximum path length. If not:
 + *
 + *  If hWnd is set to a valid window handle, a message box is displayed warning
 + *  the user that the files may not be accessible. If the user chooses not to
 + *  proceed, the function returns ERROR_CANCELLED.
 + *
 + *  If hWnd is set to NULL, no user interface is displayed and the function
 + *  returns ERROR_CANCELLED.
 + */
 +int WINAPI SHCreateDirectoryExA(HWND hWnd, LPCSTR path, LPSECURITY_ATTRIBUTES sec)
 +{
 +    LPWSTR wPath;
 +    DWORD retCode;
 +
 +    TRACE("(%s, %p)\n", debugstr_a(path), sec);
 +
 +    retCode = SHELL32_AnsiToUnicodeBuf(path, &wPath, 0);
 +    if (!retCode)
 +    {
 +        retCode = SHCreateDirectoryExW(hWnd, wPath, sec);
 +        SHELL32_FreeUnicodeBuf(wPath);
 +    }
 +    return retCode;
 +}
 +
 +/*************************************************************************
 + * SHCreateDirectoryExW      [SHELL32.@]
 + *
 + * See SHCreateDirectoryExA.
 + */
 +int WINAPI SHCreateDirectoryExW(HWND hWnd, LPCWSTR path, LPSECURITY_ATTRIBUTES sec)
 +{
 +    int ret = ERROR_BAD_PATHNAME;
 +    TRACE("(%p, %s, %p)\n", hWnd, debugstr_w(path), sec);
 +
 +    if (PathIsRelativeW(path))
 +    {
 +      SetLastError(ret);
 +    }
 +    else
 +    {
 +        ret = SHNotifyCreateDirectoryW(path, sec);
 +        /* Refuse to work on certain error codes before trying to create directories recursively */
 +        if (ret != ERROR_SUCCESS &&
 +          ret != ERROR_FILE_EXISTS &&
 +          ret != ERROR_ALREADY_EXISTS &&
 +          ret != ERROR_FILENAME_EXCED_RANGE)
 +        {
 +            WCHAR *pEnd, *pSlash, szTemp[MAX_PATH + 1];  /* extra for PathAddBackslash() */
 +
 +            lstrcpynW(szTemp, path, MAX_PATH);
 +            pEnd = PathAddBackslashW(szTemp);
 +            pSlash = szTemp + 3;
 +
 +            while (*pSlash)
 +            {
 +                while (*pSlash && *pSlash != '\\') pSlash++;
 +                if (*pSlash)
 +                {
 +                    *pSlash = 0;    /* terminate path at separator */
 +
 +                    ret = SHNotifyCreateDirectoryW(szTemp, pSlash + 1 == pEnd ? sec : NULL);
 +                }
 +                *pSlash++ = '\\'; /* put the separator back */
 +            }
 +        }
 +
 +        if (ret && hWnd && (ERROR_CANCELLED != ret))
 +        {
 +            /* We failed and should show a dialog box */
 +            FIXME("Show system error message, creating path %s, failed with error %d\n", debugstr_w(path), ret);
 +            ret = ERROR_CANCELLED; /* Error has been already presented to user (not really yet!) */
 +        }
 +    }
 +
 +    return ret;
 +}
 +
 +/*************************************************************************
 + * SHFindAttrW      [internal]
 + *
 + * Get the Attributes for a file or directory. The difference to GetAttributes()
 + * is that this function will also work for paths containing wildcard characters
 + * in its filename.
 +
 + * PARAMS
 + *  path       [I]   path of directory or file to check
 + *  fileOnly   [I]   TRUE if only files should be found
 + *
 + * RETURNS
 + *  INVALID_FILE_ATTRIBUTES if the path does not exist, the actual attributes of
 + *  the first file or directory found otherwise
 + */
 +static DWORD SHFindAttrW(LPCWSTR pName, BOOL fileOnly)
 +{
 +    WIN32_FIND_DATAW wfd;
 +    BOOL b_FileMask = fileOnly && (NULL != StrPBrkW(pName, wWildcardChars));
 +    DWORD dwAttr = INVALID_FILE_ATTRIBUTES;
 +    HANDLE hFind = FindFirstFileW(pName, &wfd);
 +
 +    TRACE("%s %d\n", debugstr_w(pName), fileOnly);
 +    if (INVALID_HANDLE_VALUE != hFind)
 +    {
 +        do
 +        {
 +            if (b_FileMask && IsAttribDir(wfd.dwFileAttributes))
 +               continue;
 +            dwAttr = wfd.dwFileAttributes;
 +            break;
 +        } while (FindNextFileW(hFind, &wfd));
 +
 +      FindClose(hFind);
 +    }
 +    return dwAttr;
 +}
 +
 +/*************************************************************************
 + *
 + * _ConvertAtoW  helper function for SHFileOperationA
 + *
 + * Converts a string or string-list to unicode.
 + */
 +static DWORD _ConvertAtoW(PCSTR strSrc, PCWSTR* pStrDest, BOOL isList)
 +{
 +    *pStrDest = NULL;
 +
 +    // If the input is null, nothing to convert.
 +    if (!strSrc)
 +        return 0;
 +
 +    // Measure the total size, depending on if it's a zero-terminated list.
 +    int sizeA = 0;
 +    if (isList)
 +    {
 +        PCSTR tmpSrc = strSrc;
 +        int size;
 +        do
 +        {
 +            size = lstrlenA(tmpSrc) + 1;
 +            sizeA += size;
 +            tmpSrc += size;
 +        } while (size != 1);
 +    }
 +    else
 +    {
 +        sizeA = lstrlenA(strSrc) + 1;
 +    }
 +
 +    // Measure the needed allocation size.
 +    int sizeW = MultiByteToWideChar(CP_ACP, 0, strSrc, sizeA, NULL, 0);
 +    if (!sizeW)
 +        return GetLastError();
 +
 +    PWSTR strDest = (PWSTR) HeapAlloc(GetProcessHeap(), 0, sizeW * sizeof(WCHAR));
 +    if (!strDest)
 +        return ERROR_OUTOFMEMORY;
 +
 +    int err = MultiByteToWideChar(CP_ACP, 0, strSrc, sizeA, strDest, sizeW);
 +    if (!err)
 +    {
 +        HeapFree(GetProcessHeap(), 0, strDest);
 +        return GetLastError();
 +    }
 +
 +    *pStrDest = strDest;
 +    return 0;
 +}
 +
 +/*************************************************************************
 + * SHFileOperationA          [SHELL32.@]
 + *
 + * Function to copy, move, delete and create one or more files with optional
 + * user prompts.
 + *
 + * PARAMS
 + *  lpFileOp   [I/O] pointer to a structure containing all the necessary information
 + *
 + * RETURNS
 + *  Success: ERROR_SUCCESS.
 + *  Failure: ERROR_CANCELLED.
 + *
 + * NOTES
 + *  exported by name
 + */
 +int WINAPI SHFileOperationA(LPSHFILEOPSTRUCTA lpFileOp)
 +{
 +    int errCode, retCode;
 +    SHFILEOPSTRUCTW nFileOp = { 0 };
 +
 +    // Convert A information to W
 +    nFileOp.hwnd = lpFileOp->hwnd;
 +    nFileOp.wFunc = lpFileOp->wFunc;
 +    nFileOp.fFlags = lpFileOp->fFlags;
 +
 +    errCode = _ConvertAtoW(lpFileOp->pFrom, &nFileOp.pFrom, TRUE);
 +    if (errCode != 0)
 +        goto cleanup;
 +
 +    if (FO_DELETE != (nFileOp.wFunc & FO_MASK))
 +    {
 +        errCode = _ConvertAtoW(lpFileOp->pTo, &nFileOp.pTo, TRUE);
 +        if (errCode != 0)
 +            goto cleanup;
 +    }
 +
 +    if (nFileOp.fFlags & FOF_SIMPLEPROGRESS)
 +    {
 +        errCode = _ConvertAtoW(lpFileOp->lpszProgressTitle, &nFileOp.lpszProgressTitle, FALSE);
 +        if (errCode != 0)
 +            goto cleanup;
 +    }
 +
 +    // Call the actual function
 +    retCode = SHFileOperationW(&nFileOp);
 +
 +    // Cleanup
 +cleanup:
 +    if (nFileOp.pFrom)
 +        HeapFree(GetProcessHeap(), 0, (PVOID) nFileOp.pFrom);
 +    if (nFileOp.pTo)
 +        HeapFree(GetProcessHeap(), 0, (PVOID) nFileOp.pTo);
 +    if (nFileOp.lpszProgressTitle)
 +        HeapFree(GetProcessHeap(), 0, (PVOID) nFileOp.lpszProgressTitle);
 +
 +    if (errCode != 0)
 +    {
 +        lpFileOp->fAnyOperationsAborted = TRUE;
 +        SetLastError(errCode);
 +
 +        return errCode;
 +    }
 +
 +    // Thankfully, starting with NT4 the name mappings are always unicode, so no need to convert.
 +    lpFileOp->hNameMappings = nFileOp.hNameMappings;
 +    lpFileOp->fAnyOperationsAborted = nFileOp.fAnyOperationsAborted;
 +    return retCode;
 +}
 +
 +static void __inline grow_list(FILE_LIST *list)
 +{
 +    FILE_ENTRY *newx = (FILE_ENTRY *)HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, list->feFiles,
 +                                  list->num_alloc * 2 * sizeof(*newx) );
 +    list->feFiles = newx;
 +    list->num_alloc *= 2;
 +}
 +
 +/* adds a file to the FILE_ENTRY struct
 + */
 +static void add_file_to_entry(FILE_ENTRY *feFile, LPCWSTR szFile)
 +{
 +    DWORD dwLen = lstrlenW(szFile) + 1;
 +    LPCWSTR ptr;
 +
 +    feFile->szFullPath = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, dwLen * sizeof(WCHAR));
 +    lstrcpyW(feFile->szFullPath, szFile);
 +
 +    ptr = StrRChrW(szFile, NULL, '\\');
 +    if (ptr)
 +    {
 +        dwLen = ptr - szFile + 1;
 +        feFile->szDirectory = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, dwLen * sizeof(WCHAR));
 +        lstrcpynW(feFile->szDirectory, szFile, dwLen);
 +
 +        dwLen = lstrlenW(feFile->szFullPath) - dwLen + 1;
 +        feFile->szFilename = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, dwLen * sizeof(WCHAR));
 +        lstrcpyW(feFile->szFilename, ptr + 1); /* skip over backslash */
 +    }
 +    feFile->bFromWildcard = FALSE;
 +}
 +
 +static LPWSTR wildcard_to_file(LPCWSTR szWildCard, LPCWSTR szFileName)
 +{
 +    LPCWSTR ptr;
 +    LPWSTR szFullPath;
 +    DWORD dwDirLen, dwFullLen;
 +
 +    ptr = StrRChrW(szWildCard, NULL, '\\');
 +    dwDirLen = ptr - szWildCard + 1;
 +
 +    dwFullLen = dwDirLen + lstrlenW(szFileName) + 1;
 +    szFullPath = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, dwFullLen * sizeof(WCHAR));
 +
 +    lstrcpynW(szFullPath, szWildCard, dwDirLen + 1);
 +    lstrcatW(szFullPath, szFileName);
 +
 +    return szFullPath;
 +}
 +
 +static void parse_wildcard_files(FILE_LIST *flList, LPCWSTR szFile, LPDWORD pdwListIndex)
 +{
 +    WIN32_FIND_DATAW wfd;
 +    HANDLE hFile = FindFirstFileW(szFile, &wfd);
 +    FILE_ENTRY *file;
 +    LPWSTR szFullPath;
 +    BOOL res;
 +
 +    if (hFile == INVALID_HANDLE_VALUE) return;
 +
 +    for (res = TRUE; res; res = FindNextFileW(hFile, &wfd))
 +    {
 +        if (IsDotDir(wfd.cFileName))
 +            continue;
 +
 +        if (*pdwListIndex >= flList->num_alloc)
 +            grow_list( flList );
 +
 +        szFullPath = wildcard_to_file(szFile, wfd.cFileName);
 +        file = &flList->feFiles[(*pdwListIndex)++];
 +        add_file_to_entry(file, szFullPath);
 +        file->bFromWildcard = TRUE;
 +        file->attributes = wfd.dwFileAttributes;
 +
 +        if (IsAttribDir(file->attributes))
 +            flList->bAnyDirectories = TRUE;
 +
 +        HeapFree(GetProcessHeap(), 0, szFullPath);
 +    }
 +
 +    FindClose(hFile);
 +}
 +
 +/* takes the null-separated file list and fills out the FILE_LIST */
 +static HRESULT parse_file_list(FILE_LIST *flList, LPCWSTR szFiles)
 +{
 +    LPCWSTR ptr = szFiles;
 +    WCHAR szCurFile[MAX_PATH];
 +    DWORD i = 0;
 +
 +    if (!szFiles)
 +        return ERROR_INVALID_PARAMETER;
 +
 +    flList->bAnyFromWildcard = FALSE;
 +    flList->bAnyDirectories = FALSE;
 +    flList->bAnyDontExist = FALSE;
 +    flList->num_alloc = 32;
 +    flList->dwNumFiles = 0;
 +
 +    /* empty list */
 +    if (!szFiles[0])
 +        return ERROR_ACCESS_DENIED;
 +
 +    flList->feFiles = (FILE_ENTRY *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
 +                                flList->num_alloc * sizeof(FILE_ENTRY));
 +
 +    while (*ptr)
 +    {
 +        if (i >= flList->num_alloc) grow_list( flList );
 +
 +        /* change relative to absolute path */
 +        if (PathIsRelativeW(ptr))
 +        {
 +            GetCurrentDirectoryW(MAX_PATH, szCurFile);
 +            PathCombineW(szCurFile, szCurFile, ptr);
 +            flList->feFiles[i].bFromRelative = TRUE;
 +        }
 +        else
 +        {
 +            lstrcpyW(szCurFile, ptr);
 +            flList->feFiles[i].bFromRelative = FALSE;
 +        }
 +
 +        /* parse wildcard files if they are in the filename */
 +        if (StrPBrkW(szCurFile, wWildcardChars))
 +        {
 +            parse_wildcard_files(flList, szCurFile, &i);
 +            flList->bAnyFromWildcard = TRUE;
 +            i--;
 +        }
 +        else
 +        {
 +            FILE_ENTRY *file = &flList->feFiles[i];
 +            add_file_to_entry(file, szCurFile);
 +            file->attributes = GetFileAttributesW( file->szFullPath );
 +            file->bExists = (file->attributes != INVALID_FILE_ATTRIBUTES);
 +
 +            if (!file->bExists)
 +                flList->bAnyDontExist = TRUE;
 +
 +            if (IsAttribDir(file->attributes))
 +                flList->bAnyDirectories = TRUE;
 +        }
 +
 +        /* advance to the next string */
 +        ptr += lstrlenW(ptr) + 1;
 +        i++;
 +    }
 +    flList->dwNumFiles = i;
 +
 +    return S_OK;
 +}
 +
 +/* free the FILE_LIST */
 +static void destroy_file_list(FILE_LIST *flList)
 +{
 +    DWORD i;
 +
 +    if (!flList || !flList->feFiles)
 +        return;
 +
 +    for (i = 0; i < flList->dwNumFiles; i++)
 +    {
 +        HeapFree(GetProcessHeap(), 0, flList->feFiles[i].szDirectory);
 +        HeapFree(GetProcessHeap(), 0, flList->feFiles[i].szFilename);
 +        HeapFree(GetProcessHeap(), 0, flList->feFiles[i].szFullPath);
 +    }
 +
 +    HeapFree(GetProcessHeap(), 0, flList->feFiles);
 +}
 +
 +static void copy_dir_to_dir(FILE_OPERATION *op, const FILE_ENTRY *feFrom, LPCWSTR szDestPath)
 +{
 +    WCHAR szFrom[MAX_PATH], szTo[MAX_PATH];
 +    SHFILEOPSTRUCTW fileOp;
 +
 +    static const WCHAR wildCardFiles[] = {'*','.','*',0};
 +
 +    if (IsDotDir(feFrom->szFilename))
 +        return;
 +
 +    if (PathFileExistsW(szDestPath))
 +        PathCombineW(szTo, szDestPath, feFrom->szFilename);
 +    else
 +        lstrcpyW(szTo, szDestPath);
 +
 +    if (!(op->req->fFlags & FOF_NOCONFIRMATION) && PathFileExistsW(szTo))
 +    {
 +        if (!SHELL_ConfirmDialogW(op->req->hwnd, ASK_OVERWRITE_FOLDER, feFrom->szFilename, op))
 +        {
 +            /* Vista returns an ERROR_CANCELLED even if user pressed "No" */
 +            if (!op->bManyItems)
 +                op->bCancelled = TRUE;
 +            return;
 +        }
 +    }
 +
 +    szTo[lstrlenW(szTo) + 1] = '\0';
 +    SHNotifyCreateDirectoryW(szTo, NULL);
 +
 +    PathCombineW(szFrom, feFrom->szFullPath, wildCardFiles);
 +    szFrom[lstrlenW(szFrom) + 1] = '\0';
 +
 +    fileOp = *op->req;
 +    fileOp.pFrom = szFrom;
 +    fileOp.pTo = szTo;
 +    fileOp.fFlags &= ~FOF_MULTIDESTFILES; /* we know we're copying to one dir */
 +
 +    /* Don't ask the user about overwriting files when he accepted to overwrite the
 +       folder. FIXME: this is not exactly what Windows does - e.g. there would be
 +       an additional confirmation for a nested folder */
 +    fileOp.fFlags |= FOF_NOCONFIRMATION;
 +
 +    SHFileOperationW(&fileOp);
 +}
 +
 +static BOOL copy_file_to_file(FILE_OPERATION *op, const WCHAR *szFrom, const WCHAR *szTo)
 +{
 +    if (!(op->req->fFlags & FOF_NOCONFIRMATION) && PathFileExistsW(szTo))
 +    {
 +        if (!SHELL_ConfirmDialogW(op->req->hwnd, ASK_OVERWRITE_FILE, PathFindFileNameW(szTo), op))
 +            return 0;
 +    }
 +
 +    return SHNotifyCopyFileW(szFrom, szTo, FALSE) == 0;
 +}
 +
 +/* copy a file or directory to another directory */
 +static void copy_to_dir(FILE_OPERATION *op, const FILE_ENTRY *feFrom, const FILE_ENTRY *feTo)
 +{
 +    if (!PathFileExistsW(feTo->szFullPath))
 +        SHNotifyCreateDirectoryW(feTo->szFullPath, NULL);
 +
 +    if (IsAttribFile(feFrom->attributes))
 +    {
 +        WCHAR szDestPath[MAX_PATH];
 +
 +        PathCombineW(szDestPath, feTo->szFullPath, feFrom->szFilename);
 +        copy_file_to_file(op, feFrom->szFullPath, szDestPath);
 +    }
 +    else if (!(op->req->fFlags & FOF_FILESONLY && feFrom->bFromWildcard))
 +        copy_dir_to_dir(op, feFrom, feTo->szFullPath);
 +}
 +
 +static void create_dest_dirs(LPCWSTR szDestDir)
 +{
 +    WCHAR dir[MAX_PATH];
 +    LPCWSTR ptr = StrChrW(szDestDir, '\\');
 +
 +    /* make sure all directories up to last one are created */
 +    while (ptr && (ptr = StrChrW(ptr + 1, '\\')))
 +    {
 +        lstrcpynW(dir, szDestDir, ptr - szDestDir + 1);
 +
 +        if (!PathFileExistsW(dir))
 +            SHNotifyCreateDirectoryW(dir, NULL);
 +    }
 +
 +    /* create last directory */
 +    if (!PathFileExistsW(szDestDir))
 +        SHNotifyCreateDirectoryW(szDestDir, NULL);
 +}
 +
 +/* the FO_COPY operation */
 +static HRESULT copy_files(FILE_OPERATION *op, const FILE_LIST *flFrom, FILE_LIST *flTo)
 +{
 +    DWORD i;
 +    const FILE_ENTRY *entryToCopy;
 +    const FILE_ENTRY *fileDest = &flTo->feFiles[0];
 +
 +    if (flFrom->bAnyDontExist)
 +        return ERROR_SHELL_INTERNAL_FILE_NOT_FOUND;
 +
 +    if (flTo->dwNumFiles == 0)
 +    {
 +        /* If the destination is empty, SHFileOperation should use the current directory */
 +        WCHAR curdir[MAX_PATH+1];
 +
 +        GetCurrentDirectoryW(MAX_PATH, curdir);
 +        curdir[lstrlenW(curdir)+1] = 0;
 +
 +        destroy_file_list(flTo);
 +        ZeroMemory(flTo, sizeof(FILE_LIST));
 +        parse_file_list(flTo, curdir);
 +        fileDest = &flTo->feFiles[0];
 +    }
 +
 +    if (op->req->fFlags & FOF_MULTIDESTFILES)
 +    {
 +        if (flFrom->bAnyFromWildcard)
 +            return ERROR_CANCELLED;
 +
 +        if (flFrom->dwNumFiles != flTo->dwNumFiles)
 +        {
 +            if (flFrom->dwNumFiles != 1 && !IsAttribDir(fileDest->attributes))
 +                return ERROR_CANCELLED;
 +
 +            /* Free all but the first entry. */
 +            for (i = 1; i < flTo->dwNumFiles; i++)
 +            {
 +                HeapFree(GetProcessHeap(), 0, flTo->feFiles[i].szDirectory);
 +                HeapFree(GetProcessHeap(), 0, flTo->feFiles[i].szFilename);
 +                HeapFree(GetProcessHeap(), 0, flTo->feFiles[i].szFullPath);
 +            }
 +
 +            flTo->dwNumFiles = 1;
 +        }
 +        else if (IsAttribDir(fileDest->attributes))
 +        {
 +            for (i = 1; i < flTo->dwNumFiles; i++)
 +                if (!IsAttribDir(flTo->feFiles[i].attributes) ||
 +                    !IsAttribDir(flFrom->feFiles[i].attributes))
 +                {
 +                    return ERROR_CANCELLED;
 +                }
 +        }
 +    }
 +    else if (flFrom->dwNumFiles != 1)
 +    {
 +        if (flTo->dwNumFiles != 1 && !IsAttribDir(fileDest->attributes))
 +            return ERROR_CANCELLED;
 +
 +        if (PathFileExistsW(fileDest->szFullPath) &&
 +            IsAttribFile(fileDest->attributes))
 +        {
 +            return ERROR_CANCELLED;
 +        }
 +
 +        if (flTo->dwNumFiles == 1 && fileDest->bFromRelative &&
 +            !PathFileExistsW(fileDest->szFullPath))
 +        {
 +            return ERROR_CANCELLED;
 +        }
 +    }
 +
 +    for (i = 0; i < flFrom->dwNumFiles; i++)
 +    {
 +        entryToCopy = &flFrom->feFiles[i];
 +
 +        if ((op->req->fFlags & FOF_MULTIDESTFILES) &&
 +            flTo->dwNumFiles > 1)
 +        {
 +            fileDest = &flTo->feFiles[i];
 +        }
 +
 +        if (IsAttribDir(entryToCopy->attributes) &&
 +            !lstrcmpiW(entryToCopy->szFullPath, fileDest->szDirectory))
 +        {
 +            return ERROR_SUCCESS;
 +        }
 +
 +        create_dest_dirs(fileDest->szDirectory);
 +
 +        if (!lstrcmpiW(entryToCopy->szFullPath, fileDest->szFullPath))
 +        {
 +            if (IsAttribFile(entryToCopy->attributes))
 +                return ERROR_NO_MORE_SEARCH_HANDLES;
 +            else
 +                return ERROR_SUCCESS;
 +        }
 +
 +        if ((flFrom->dwNumFiles > 1 && flTo->dwNumFiles == 1) ||
 +            IsAttribDir(fileDest->attributes))
 +        {
 +            copy_to_dir(op, entryToCopy, fileDest);
 +        }
 +        else if (IsAttribDir(entryToCopy->attributes))
 +        {
 +            copy_dir_to_dir(op, entryToCopy, fileDest->szFullPath);
 +        }
 +        else
 +        {
 +            if (!copy_file_to_file(op, entryToCopy->szFullPath, fileDest->szFullPath))
 +            {
 +                op->req->fAnyOperationsAborted = TRUE;
 +                return ERROR_CANCELLED;
 +            }
 +        }
 +
 +        /* Vista return code. XP would return e.g. ERROR_FILE_NOT_FOUND, ERROR_ALREADY_EXISTS */
 +        if (op->bCancelled)
 +            return ERROR_CANCELLED;
 +    }
 +
 +    /* Vista return code. On XP if the used pressed "No" for the last item,
 +     * ERROR_ARENA_TRASHED would be returned */
 +    return ERROR_SUCCESS;
 +}
 +
 +static BOOL confirm_delete_list(HWND hWnd, DWORD fFlags, BOOL fTrash, const FILE_LIST *flFrom)
 +{
 +    if (flFrom->dwNumFiles > 1)
 +    {
 +        WCHAR tmp[8];
 +        const WCHAR format[] = {'%','d',0};
 +
 +        wnsprintfW(tmp, sizeof(tmp)/sizeof(tmp[0]), format, flFrom->dwNumFiles);
 +        return SHELL_ConfirmDialogW(hWnd, (fTrash?ASK_TRASH_MULTIPLE_ITEM:ASK_DELETE_MULTIPLE_ITEM), tmp, NULL);
 +    }
 +    else
 +    {
 +        const FILE_ENTRY *fileEntry = &flFrom->feFiles[0];
 +
 +        if (IsAttribFile(fileEntry->attributes))
 +            return SHELL_ConfirmDialogW(hWnd, (fTrash?ASK_TRASH_FILE:ASK_DELETE_FILE), fileEntry->szFullPath, NULL);
 +        else if (!(fFlags & FOF_FILESONLY && fileEntry->bFromWildcard))
 +            return SHELL_ConfirmDialogW(hWnd, (fTrash?ASK_TRASH_FOLDER:ASK_DELETE_FOLDER), fileEntry->szFullPath, NULL);
 +    }
 +    return TRUE;
 +}
 +
 +/* the FO_DELETE operation */
 +static HRESULT delete_files(LPSHFILEOPSTRUCTW lpFileOp, const FILE_LIST *flFrom)
 +{
 +    const FILE_ENTRY *fileEntry;
 +    DWORD i;
 +    BOOL bPathExists;
 +    BOOL bTrash;
 +
 +    if (!flFrom->dwNumFiles)
 +        return ERROR_SUCCESS;
 +
 +    /* Windows also checks only the first item */
 +    bTrash = (lpFileOp->fFlags & FOF_ALLOWUNDO)
 +        && TRASH_CanTrashFile(flFrom->feFiles[0].szFullPath);
 +
 +    if (!(lpFileOp->fFlags & FOF_NOCONFIRMATION) || (!bTrash && lpFileOp->fFlags & FOF_WANTNUKEWARNING))
 +        if (!confirm_delete_list(lpFileOp->hwnd, lpFileOp->fFlags, bTrash, flFrom))
 +        {
 +            lpFileOp->fAnyOperationsAborted = TRUE;
 +            return 0;
 +        }
 +
 +    for (i = 0; i < flFrom->dwNumFiles; i++)
 +    {
 +        bPathExists = TRUE;
 +        fileEntry = &flFrom->feFiles[i];
 +
 +        if (!IsAttribFile(fileEntry->attributes) &&
 +            (lpFileOp->fFlags & FOF_FILESONLY && fileEntry->bFromWildcard))
 +            continue;
 +
 +        if (bTrash)
 +        {
 +            BOOL bDelete;
 +            if (TRASH_TrashFile(fileEntry->szFullPath))
 +            {
 +                SHChangeNotify(SHCNE_DELETE, SHCNF_PATHW, fileEntry->szFullPath, NULL);
 +                continue;
 +            }
 +
 +            /* Note: Windows silently deletes the file in such a situation, we show a dialog */
 +            if (!(lpFileOp->fFlags & FOF_NOCONFIRMATION) || (lpFileOp->fFlags & FOF_WANTNUKEWARNING))
 +                bDelete = SHELL_ConfirmDialogW(lpFileOp->hwnd, ASK_CANT_TRASH_ITEM, fileEntry->szFullPath, NULL);
 +            else
 +                bDelete = TRUE;
 +
 +            if (!bDelete)
 +            {
 +                lpFileOp->fAnyOperationsAborted = TRUE;
 +                break;
 +            }
 +        }
 +
 +        /* delete the file or directory */
 +        if (IsAttribFile(fileEntry->attributes))
 +        {
 +            bPathExists = DeleteFileW(fileEntry->szFullPath);
 +            SHChangeNotify(SHCNE_DELETE, SHCNF_PATHW, fileEntry->szFullPath, NULL);
 +        }
 +        else
 +            bPathExists = SHELL_DeleteDirectoryW(lpFileOp->hwnd, fileEntry->szFullPath, FALSE);
 +
 +        if (!bPathExists)
 +        {
 +            DWORD err = GetLastError();
 +
 +            if (ERROR_FILE_NOT_FOUND == err)
 +            {
 +                // This is a windows 2003 server specific value which ahs been removed.
 +                // Later versions of windows return ERROR_FILE_NOT_FOUND.
 +                return 1026;
 +            }
 +            else
 +            {
 +                return err;
 +            }
 +        }
 +    }
 +
 +    return ERROR_SUCCESS;
 +}
 +
 +static void move_dir_to_dir(LPSHFILEOPSTRUCTW lpFileOp, const FILE_ENTRY *feFrom, LPCWSTR szDestPath)
 +{
 +    WCHAR szFrom[MAX_PATH], szTo[MAX_PATH];
 +    SHFILEOPSTRUCTW fileOp;
 +
 +    static const WCHAR wildCardFiles[] = {'*','.','*',0};
 +
 +    if (IsDotDir(feFrom->szFilename))
 +        return;
 +
 +    SHNotifyCreateDirectoryW(szDestPath, NULL);
 +
 +    PathCombineW(szFrom, feFrom->szFullPath, wildCardFiles);
 +    szFrom[lstrlenW(szFrom) + 1] = '\0';
 +
 +    lstrcpyW(szTo, szDestPath);
 +    szTo[lstrlenW(szDestPath) + 1] = '\0';
 +
 +    fileOp = *lpFileOp;
 +    fileOp.pFrom = szFrom;
 +    fileOp.pTo = szTo;
 +
 +    SHFileOperationW(&fileOp);
 +}
 +
 +/* moves a file or directory to another directory */
 +static void move_to_dir(LPSHFILEOPSTRUCTW lpFileOp, const FILE_ENTRY *feFrom, const FILE_ENTRY *feTo)
 +{
 +    WCHAR szDestPath[MAX_PATH];
 +
 +    PathCombineW(szDestPath, feTo->szFullPath, feFrom->szFilename);
 +
 +    if (IsAttribFile(feFrom->attributes))
 +        SHNotifyMoveFileW(feFrom->szFullPath, szDestPath, FALSE);
 +    else if (!(lpFileOp->fFlags & FOF_FILESONLY && feFrom->bFromWildcard))
 +        move_dir_to_dir(lpFileOp, feFrom, szDestPath);
 +}
 +
 +/* the FO_MOVE operation */
 +static HRESULT move_files(LPSHFILEOPSTRUCTW lpFileOp, const FILE_LIST *flFrom, const FILE_LIST *flTo)
 +{
 +    DWORD i;
 +    const FILE_ENTRY *entryToMove;
 +    const FILE_ENTRY *fileDest;
 +
 +    if (!flFrom->dwNumFiles || !flTo->dwNumFiles)
 +        return ERROR_CANCELLED;
 +
 +    if (!(lpFileOp->fFlags & FOF_MULTIDESTFILES) &&
 +        flTo->dwNumFiles > 1 && flFrom->dwNumFiles > 1)
 +    {
 +        return ERROR_CANCELLED;
 +    }
 +
 +    if (!(lpFileOp->fFlags & FOF_MULTIDESTFILES) &&
 +        !flFrom->bAnyDirectories &&
 +        flFrom->dwNumFiles > flTo->dwNumFiles)
 +    {
 +        return ERROR_CANCELLED;
 +    }
 +
 +    if (!PathFileExistsW(flTo->feFiles[0].szDirectory))
 +        return ERROR_CANCELLED;
 +
 +    if ((lpFileOp->fFlags & FOF_MULTIDESTFILES) &&
 +        flFrom->dwNumFiles != flTo->dwNumFiles)
 +    {
 +        return ERROR_CANCELLED;
 +    }
 +
 +    fileDest = &flTo->feFiles[0];
 +    for (i = 0; i < flFrom->dwNumFiles; i++)
 +    {
 +        entryToMove = &flFrom->feFiles[i];
 +
 +        if (lpFileOp->fFlags & FOF_MULTIDESTFILES)
 +            fileDest = &flTo->feFiles[i];
 +
 +        if (!PathFileExistsW(fileDest->szDirectory))
 +            return ERROR_CANCELLED;
 +
 +        if (fileDest->bExists && IsAttribDir(fileDest->attributes))
 +            move_to_dir(lpFileOp, entryToMove, fileDest);
 +        else
 +            SHNotifyMoveFileW(entryToMove->szFullPath, fileDest->szFullPath, IsAttribDir(entryToMove->attributes));
 +    }
 +
 +    return ERROR_SUCCESS;
 +}
 +
 +/* the FO_RENAME files */
 +static HRESULT rename_files(LPSHFILEOPSTRUCTW lpFileOp, const FILE_LIST *flFrom, const FILE_LIST *flTo)
 +{
 +    const FILE_ENTRY *feFrom;
 +    const FILE_ENTRY *feTo;
 +
 +    if (flFrom->dwNumFiles != 1)
 +        return ERROR_GEN_FAILURE;
 +
 +    if (flTo->dwNumFiles != 1)
 +        return ERROR_CANCELLED;
 +
 +    feFrom = &flFrom->feFiles[0];
 +    feTo= &flTo->feFiles[0];
 +
 +    /* fail if destination doesn't exist */
 +    if (!feFrom->bExists)
 +        return ERROR_SHELL_INTERNAL_FILE_NOT_FOUND;
 +
 +    /* fail if destination already exists */
 +    if (feTo->bExists)
 +        return ERROR_ALREADY_EXISTS;
 +
 +    return SHNotifyMoveFileW(feFrom->szFullPath, feTo->szFullPath, IsAttribDir(feFrom->attributes));
 +}
 +
 +/* alert the user if an unsupported flag is used */
 +static void check_flags(FILEOP_FLAGS fFlags)
 +{
 +    WORD wUnsupportedFlags = FOF_NO_CONNECTED_ELEMENTS |
 +        FOF_NOCOPYSECURITYATTRIBS | FOF_NORECURSEREPARSE |
 +        FOF_RENAMEONCOLLISION | FOF_WANTMAPPINGHANDLE;
 +
 +    if (fFlags & wUnsupportedFlags)
 +        FIXME("Unsupported flags: %04x\n", fFlags);
 +}
 +
 +/*************************************************************************
 + * SHFileOperationW          [SHELL32.@]
 + *
 + * See SHFileOperationA
 + */
 +int WINAPI SHFileOperationW(LPSHFILEOPSTRUCTW lpFileOp)
 +{
 +    FILE_OPERATION op;
 +    FILE_LIST flFrom, flTo;
 +    int ret = 0;
 +
 +    if (!lpFileOp)
 +        return ERROR_INVALID_PARAMETER;
 +
 +    check_flags(lpFileOp->fFlags);
 +
 +    ZeroMemory(&flFrom, sizeof(FILE_LIST));
 +    ZeroMemory(&flTo, sizeof(FILE_LIST));
 +
 +    if ((ret = parse_file_list(&flFrom, lpFileOp->pFrom)))
 +        return ret;
 +
 +    if (lpFileOp->wFunc != FO_DELETE)
 +        parse_file_list(&flTo, lpFileOp->pTo);
 +
 +    ZeroMemory(&op, sizeof(op));
 +    op.req = lpFileOp;
 +    op.bManyItems = (flFrom.dwNumFiles > 1);
 +
 +    switch (lpFileOp->wFunc)
 +    {
 +        case FO_COPY:
 +            ret = copy_files(&op, &flFrom, &flTo);
 +            break;
 +        case FO_DELETE:
 +            ret = delete_files(lpFileOp, &flFrom);
 +            break;
 +        case FO_MOVE:
 +            ret = move_files(lpFileOp, &flFrom, &flTo);
 +            break;
 +        case FO_RENAME:
 +            ret = rename_files(lpFileOp, &flFrom, &flTo);
 +            break;
 +        default:
 +            ret = ERROR_INVALID_PARAMETER;
 +            break;
 +    }
 +
 +    destroy_file_list(&flFrom);
 +
 +    if (lpFileOp->wFunc != FO_DELETE)
 +        destroy_file_list(&flTo);
 +
 +    if (ret == ERROR_CANCELLED)
 +        lpFileOp->fAnyOperationsAborted = TRUE;
 +
 +    return ret;
 +}
 +
 +// Used by SHFreeNameMappings
 +static int CALLBACK _DestroyCallback(void *p, void *pData)
 +{
 +    LPSHNAMEMAPPINGW lp = (SHNAMEMAPPINGW *)p;
 +
 +    SHFree(lp->pszOldPath);
 +    SHFree(lp->pszNewPath);
 +
 +    return TRUE;
 +}
 +
 +/*************************************************************************
 + * SHFreeNameMappings      [shell32.246]
 + *
 + * Free the mapping handle returned by SHFileOperation if FOF_WANTSMAPPINGHANDLE
 + * was specified.
 + *
 + * PARAMS
 + *  hNameMapping [I] handle to the name mappings used during renaming of files
 + *
 + * RETURNS
 + *  Nothing
 + */
 +void WINAPI SHFreeNameMappings(HANDLE hNameMapping)
 +{
 +    if (hNameMapping)
 +    {
 +        DSA_DestroyCallback((HDSA) hNameMapping, _DestroyCallback, NULL);
 +    }
 +}
 +
 +/*************************************************************************
 + * SheGetDirA [SHELL32.@]
 + *
 + * drive = 0: returns the current directory path
 + * drive > 0: returns the current directory path of the specified drive
 + *            drive=1 -> A:  drive=2 -> B:  ...
 + * returns 0 if successful
 +*/
 +EXTERN_C DWORD WINAPI SheGetDirA(DWORD drive, LPSTR buffer)
 +{
 +    WCHAR org_path[MAX_PATH];
 +    DWORD ret;
 +    char drv_path[3];
 +
 +    /* change current directory to the specified drive */
 +    if (drive) {
 +        strcpy(drv_path, "A:");
 +        drv_path[0] += (char)drive-1;
 +
 +        GetCurrentDirectoryW(MAX_PATH, org_path);
 +
 +        SetCurrentDirectoryA(drv_path);
 +    }
 +
 +    /* query current directory path of the specified drive */
 +    ret = GetCurrentDirectoryA(MAX_PATH, buffer);
 +
 +    /* back to the original drive */
 +    if (drive)
 +        SetCurrentDirectoryW(org_path);
 +
 +    if (!ret)
 +        return GetLastError();
 +
 +    return 0;
 +}
 +
 +/*************************************************************************
 + * SheGetDirW [SHELL32.@]
 + *
 + * drive = 0: returns the current directory path
 + * drive > 0: returns the current directory path of the specified drive
 + *            drive=1 -> A:  drive=2 -> B:  ...
 + * returns 0 if successful
 + */
 +EXTERN_C DWORD WINAPI SheGetDirW(DWORD drive, LPWSTR buffer)
 +{
 +    WCHAR org_path[MAX_PATH];
 +    DWORD ret;
 +    char drv_path[3];
 +
 +    /* change current directory to the specified drive */
 +    if (drive)
 +    {
 +        strcpy(drv_path, "A:");
 +        drv_path[0] += (char)drive-1;
 +
 +        GetCurrentDirectoryW(MAX_PATH, org_path);
 +
 +        SetCurrentDirectoryA(drv_path);
 +    }
 +
 +    /* query current directory path of the specified drive */
 +    ret = GetCurrentDirectoryW(MAX_PATH, buffer);
 +
 +    /* back to the original drive */
 +    if (drive)
 +        SetCurrentDirectoryW(org_path);
 +
 +    if (!ret)
 +        return GetLastError();
 +
 +    return 0;
 +}
 +
 +/*************************************************************************
 + * SheChangeDirA [SHELL32.@]
 + *
 + * changes the current directory to the specified path
 + * and returns 0 if successful
 + */
 +EXTERN_C DWORD WINAPI SheChangeDirA(LPSTR path)
 +{
 +    if (SetCurrentDirectoryA(path))
 +        return 0;
 +    else
 +        return GetLastError();
 +}
 +
 +/*************************************************************************
 + * SheChangeDirW [SHELL32.@]
 + *
 + * changes the current directory to the specified path
 + * and returns 0 if successful
 + */
 +EXTERN_C DWORD WINAPI SheChangeDirW(LPWSTR path)
 +{
 +    if (SetCurrentDirectoryW(path))
 +        return 0;
 +    else
 +        return GetLastError();
 +}
 +
 +/*************************************************************************
 + * IsNetDrive            [SHELL32.66]
 + */
 +EXTERN_C int WINAPI IsNetDrive(int drive)
 +{
 +    char root[4];
 +    strcpy(root, "A:\\");
 +    root[0] += (char)drive;
 +    return (GetDriveTypeA(root) == DRIVE_REMOTE);
 +}
 +
 +
 +/*************************************************************************
 + * RealDriveType                [SHELL32.524]
 + */
 +EXTERN_C INT WINAPI RealDriveType(INT drive, BOOL bQueryNet)
 +{
 +    char root[] = "A:\\";
 +    root[0] += (char)drive;
 +    return GetDriveTypeA(root);
 +}
 +
 +/***********************************************************************
 + *              SHPathPrepareForWriteW (SHELL32.@)
 + */
 +EXTERN_C HRESULT WINAPI SHPathPrepareForWriteW(HWND hwnd, IUnknown *modless, LPCWSTR path, DWORD flags)
 +{
 +    DWORD res;
 +    DWORD err;
 +    LPCWSTR realpath;
 +    int len;
 +    WCHAR* last_slash;
 +    WCHAR* temppath=NULL;
 +
 +    TRACE("%p %p %s 0x%80x\n", hwnd, modless, debugstr_w(path), flags);
 +
 +    if (flags & ~(SHPPFW_DIRCREATE|SHPPFW_ASKDIRCREATE|SHPPFW_IGNOREFILENAME))
 +        FIXME("unimplemented flags 0x%08x\n", flags);
 +
 +    /* cut off filename if necessary */
 +    if (flags & SHPPFW_IGNOREFILENAME)
 +    {
 +        last_slash = StrRChrW(path, NULL, '\\');
 +        if (last_slash == NULL)
 +            len = 1;
 +        else
 +            len = last_slash - path + 1;
 +        temppath = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
 +        if (!temppath)
 +            return E_OUTOFMEMORY;
 +        StrCpyNW(temppath, path, len);
 +        realpath = temppath;
 +    }
 +    else
 +    {
 +        realpath = path;
 +    }
 +
 +    /* try to create the directory if asked to */
 +    if (flags & (SHPPFW_DIRCREATE|SHPPFW_ASKDIRCREATE))
 +    {
 +        if (flags & SHPPFW_ASKDIRCREATE)
 +            FIXME("treating SHPPFW_ASKDIRCREATE as SHPPFW_DIRCREATE\n");
 +
 +        SHCreateDirectoryExW(0, realpath, NULL);
 +    }
 +
 +    /* check if we can access the directory */
 +    res = GetFileAttributesW(realpath);
 +
 +    HeapFree(GetProcessHeap(), 0, temppath);
 +
 +    if (res == INVALID_FILE_ATTRIBUTES)
 +    {
 +        err = GetLastError();
 +        if (err == ERROR_FILE_NOT_FOUND)
 +            return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
 +        return HRESULT_FROM_WIN32(err);
 +    }
 +    else if (res & FILE_ATTRIBUTE_DIRECTORY)
 +        return S_OK;
 +    else
 +        return HRESULT_FROM_WIN32(ERROR_DIRECTORY);
 +}
 +
 +/***********************************************************************
 + *              SHPathPrepareForWriteA (SHELL32.@)
 + */
 +EXTERN_C HRESULT WINAPI SHPathPrepareForWriteA(HWND hwnd, IUnknown *modless, LPCSTR path, DWORD flags)
 +{
 +    WCHAR wpath[MAX_PATH];
 +    MultiByteToWideChar( CP_ACP, 0, path, -1, wpath, MAX_PATH);
 +    return SHPathPrepareForWriteW(hwnd, modless, wpath, flags);
 +}
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge