wine/dlls/shell32/recyclebin.c

917 lines
29 KiB
C

/*
* Trash virtual folder support. The trashing engine is implemented in trash.c
*
* Copyright (C) 2006 Mikolaj Zalewski
* Copyright 2011 Jay Yang
*
* 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 "config.h"
#define COBJMACROS
#define NONAMELESSUNION
#include <stdarg.h>
#include "winerror.h"
#include "windef.h"
#include "winbase.h"
#include "winreg.h"
#include "winuser.h"
#include "shlwapi.h"
#include "ntquery.h"
#include "shlobj.h"
#include "shresdef.h"
#include "shellfolder.h"
#include "shellapi.h"
#include "knownfolders.h"
#include "wine/debug.h"
#include "shell32_main.h"
#include "xdg.h"
#include "pidl.h"
WINE_DEFAULT_DEBUG_CHANNEL(recyclebin);
typedef struct
{
int column_name_id;
const GUID *fmtId;
DWORD pid;
int pcsFlags;
int fmt;
int cxChars;
} columninfo;
static const columninfo RecycleBinColumns[] =
{
{IDS_SHV_COLUMN1, &FMTID_Storage, PID_STG_NAME, SHCOLSTATE_TYPE_STR|SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 30},
{IDS_SHV_COLUMN_DELFROM, &FMTID_Displaced, PID_DISPLACED_FROM, SHCOLSTATE_TYPE_STR|SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 30},
{IDS_SHV_COLUMN_DELDATE, &FMTID_Displaced, PID_DISPLACED_DATE, SHCOLSTATE_TYPE_DATE|SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 20},
{IDS_SHV_COLUMN2, &FMTID_Storage, PID_STG_SIZE, SHCOLSTATE_TYPE_INT|SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 20},
{IDS_SHV_COLUMN3, &FMTID_Storage, PID_STG_STORAGETYPE,SHCOLSTATE_TYPE_INT|SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 20},
{IDS_SHV_COLUMN4, &FMTID_Storage, PID_STG_WRITETIME, SHCOLSTATE_TYPE_DATE|SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 20},
/* {"creation time", &FMTID_Storage, PID_STG_CREATETIME, SHCOLSTATE_TYPE_DATE, LVCFMT_LEFT, 20}, */
/* {"attribs", &FMTID_Storage, PID_STG_ATTRIBUTES, SHCOLSTATE_TYPE_STR, LVCFMT_LEFT, 20}, */
};
#define COLUMN_NAME 0
#define COLUMN_DELFROM 1
#define COLUMN_DATEDEL 2
#define COLUMN_SIZE 3
#define COLUMN_TYPE 4
#define COLUMN_MTIME 5
#define COLUMNS_COUNT 6
static HRESULT FormatDateTime(LPWSTR buffer, int size, FILETIME ft)
{
FILETIME lft;
SYSTEMTIME time;
int ret;
FileTimeToLocalFileTime(&ft, &lft);
FileTimeToSystemTime(&lft, &time);
ret = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &time, NULL, buffer, size);
if (ret>0 && ret<size)
{
/* Append space + time without seconds */
buffer[ret-1] = ' ';
GetTimeFormatW(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &time, NULL, &buffer[ret], size - ret);
}
return (ret!=0 ? E_FAIL : S_OK);
}
typedef struct tagRecycleBinMenu
{
IContextMenu2 IContextMenu2_iface;
LONG refCount;
UINT cidl;
LPITEMIDLIST *apidl;
IShellFolder2 *folder;
} RecycleBinMenu;
static const IContextMenu2Vtbl recycleBinMenuVtbl;
static RecycleBinMenu *impl_from_IContextMenu2(IContextMenu2 *iface)
{
return CONTAINING_RECORD(iface, RecycleBinMenu, IContextMenu2_iface);
}
static IContextMenu2* RecycleBinMenu_Constructor(UINT cidl, LPCITEMIDLIST *apidl, IShellFolder2 *folder)
{
RecycleBinMenu *This = SHAlloc(sizeof(RecycleBinMenu));
TRACE("(%u,%p)\n",cidl,apidl);
This->IContextMenu2_iface.lpVtbl = &recycleBinMenuVtbl;
This->cidl = cidl;
This->apidl = _ILCopyaPidl(apidl,cidl);
IShellFolder2_AddRef(folder);
This->folder = folder;
This->refCount = 1;
return &This->IContextMenu2_iface;
}
static HRESULT WINAPI RecycleBinMenu_QueryInterface(IContextMenu2 *iface,
REFIID riid,
void **ppvObject)
{
RecycleBinMenu *This = impl_from_IContextMenu2(iface);
TRACE("(%p, %s, %p) - stub\n", This, debugstr_guid(riid), ppvObject);
return E_NOTIMPL;
}
static ULONG WINAPI RecycleBinMenu_AddRef(IContextMenu2 *iface)
{
RecycleBinMenu *This = impl_from_IContextMenu2(iface);
TRACE("(%p)\n", This);
return InterlockedIncrement(&This->refCount);
}
static ULONG WINAPI RecycleBinMenu_Release(IContextMenu2 *iface)
{
RecycleBinMenu *This = impl_from_IContextMenu2(iface);
UINT result;
TRACE("(%p)\n", This);
result = InterlockedDecrement(&This->refCount);
if (result == 0)
{
TRACE("Destroying object\n");
_ILFreeaPidl(This->apidl,This->cidl);
IShellFolder2_Release(This->folder);
SHFree(This);
}
return result;
}
static HRESULT WINAPI RecycleBinMenu_QueryContextMenu(IContextMenu2 *iface,
HMENU hmenu,
UINT indexMenu,
UINT idCmdFirst,
UINT idCmdLast,
UINT uFlags)
{
HMENU menures = LoadMenuW(shell32_hInstance,MAKEINTRESOURCEW(MENU_RECYCLEBIN));
if(uFlags & CMF_DEFAULTONLY)
return E_NOTIMPL;
else{
UINT idMax = Shell_MergeMenus(hmenu,GetSubMenu(menures,0),indexMenu,idCmdFirst,idCmdLast,MM_SUBMENUSHAVEIDS);
TRACE("Added %d id(s)\n",idMax-idCmdFirst);
return MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, idMax-idCmdFirst+1);
}
}
static void DoErase(RecycleBinMenu *This)
{
ISFHelper *helper;
IShellFolder2_QueryInterface(This->folder,&IID_ISFHelper,(void**)&helper);
if(helper)
ISFHelper_DeleteItems(helper,This->cidl,(LPCITEMIDLIST*)This->apidl);
}
static void DoRestore(RecycleBinMenu *This)
{
/*TODO add prompts*/
UINT i;
for(i=0;i<This->cidl;i++)
{
WIN32_FIND_DATAW data;
TRASH_UnpackItemID(&((This->apidl[i])->mkid),&data);
if(PathFileExistsW(data.cFileName))
{
PIDLIST_ABSOLUTE dest_pidl = ILCreateFromPathW(data.cFileName);
WCHAR message[100];
WCHAR caption[50];
if(_ILIsFolder(ILFindLastID(dest_pidl)))
LoadStringW(shell32_hInstance,IDS_RECYCLEBIN_OVERWRITEFOLDER,
message,sizeof(message)/sizeof(WCHAR));
else
LoadStringW(shell32_hInstance,IDS_RECYCLEBIN_OVERWRITEFILE,
message,sizeof(message)/sizeof(WCHAR));
LoadStringW(shell32_hInstance,IDS_RECYCLEBIN_OVERWRITE_CAPTION,
caption,sizeof(caption)/sizeof(WCHAR));
if(ShellMessageBoxW(shell32_hInstance,GetActiveWindow(),message,
caption,MB_YESNO|MB_ICONEXCLAMATION,
data.cFileName)!=IDYES)
continue;
}
if(SUCCEEDED(TRASH_RestoreItem(This->apidl[i])))
{
IPersistFolder2 *persist;
LPITEMIDLIST root_pidl;
PIDLIST_ABSOLUTE dest_pidl = ILCreateFromPathW(data.cFileName);
BOOL is_folder = _ILIsFolder(ILFindLastID(dest_pidl));
IShellFolder2_QueryInterface(This->folder,&IID_IPersistFolder2,
(void**)&persist);
IPersistFolder2_GetCurFolder(persist,&root_pidl);
SHChangeNotify(is_folder ? SHCNE_RMDIR : SHCNE_DELETE,
SHCNF_IDLIST,ILCombine(root_pidl,This->apidl[i]),0);
SHChangeNotify(is_folder ? SHCNE_MKDIR : SHCNE_CREATE,
SHCNF_IDLIST,dest_pidl,0);
ILFree(dest_pidl);
ILFree(root_pidl);
}
}
}
static HRESULT WINAPI RecycleBinMenu_InvokeCommand(IContextMenu2 *iface,
LPCMINVOKECOMMANDINFO pici)
{
RecycleBinMenu *This = impl_from_IContextMenu2(iface);
LPCSTR verb = pici->lpVerb;
if(IS_INTRESOURCE(verb))
{
switch(LOWORD(verb))
{
case IDM_RECYCLEBIN_ERASE:
DoErase(This);
break;
case IDM_RECYCLEBIN_RESTORE:
DoRestore(This);
break;
default:
return E_NOTIMPL;
}
}
return S_OK;
}
static HRESULT WINAPI RecycleBinMenu_GetCommandString(IContextMenu2 *iface,
UINT_PTR idCmd,
UINT uType,
UINT *pwReserved,
LPSTR pszName,
UINT cchMax)
{
TRACE("(%p, %lu, %u, %p, %s, %u) - stub\n",iface,idCmd,uType,pwReserved,debugstr_a(pszName),cchMax);
return E_NOTIMPL;
}
static HRESULT WINAPI RecycleBinMenu_HandleMenuMsg(IContextMenu2 *iface,
UINT uMsg, WPARAM wParam,
LPARAM lParam)
{
TRACE("(%p, %u, 0x%lx, 0x%lx) - stub\n",iface,uMsg,wParam,lParam);
return E_NOTIMPL;
}
static const IContextMenu2Vtbl recycleBinMenuVtbl =
{
RecycleBinMenu_QueryInterface,
RecycleBinMenu_AddRef,
RecycleBinMenu_Release,
RecycleBinMenu_QueryContextMenu,
RecycleBinMenu_InvokeCommand,
RecycleBinMenu_GetCommandString,
RecycleBinMenu_HandleMenuMsg,
};
/*
* Recycle Bin folder
*/
typedef struct tagRecycleBin
{
IShellFolder2 IShellFolder2_iface;
IPersistFolder2 IPersistFolder2_iface;
ISFHelper ISFHelper_iface;
LONG refCount;
LPITEMIDLIST pidl;
} RecycleBin;
static const IShellFolder2Vtbl recycleBinVtbl;
static const IPersistFolder2Vtbl recycleBinPersistVtbl;
static const ISFHelperVtbl sfhelperVtbl;
static inline RecycleBin *impl_from_IShellFolder2(IShellFolder2 *iface)
{
return CONTAINING_RECORD(iface, RecycleBin, IShellFolder2_iface);
}
static RecycleBin *impl_from_IPersistFolder2(IPersistFolder2 *iface)
{
return CONTAINING_RECORD(iface, RecycleBin, IPersistFolder2_iface);
}
static RecycleBin *impl_from_ISFHelper(ISFHelper *iface)
{
return CONTAINING_RECORD(iface, RecycleBin, ISFHelper_iface);
}
static void RecycleBin_Destructor(RecycleBin *This);
HRESULT WINAPI RecycleBin_Constructor(IUnknown *pUnkOuter, REFIID riid, LPVOID *ppOutput)
{
RecycleBin *obj;
HRESULT ret;
if (pUnkOuter)
return CLASS_E_NOAGGREGATION;
obj = SHAlloc(sizeof(RecycleBin));
if (obj == NULL)
return E_OUTOFMEMORY;
ZeroMemory(obj, sizeof(RecycleBin));
obj->IShellFolder2_iface.lpVtbl = &recycleBinVtbl;
obj->IPersistFolder2_iface.lpVtbl = &recycleBinPersistVtbl;
obj->ISFHelper_iface.lpVtbl = &sfhelperVtbl;
if (FAILED(ret = IPersistFolder2_QueryInterface(&obj->IPersistFolder2_iface, riid, ppOutput)))
{
RecycleBin_Destructor(obj);
return ret;
}
/* InterlockedIncrement(&objCount);*/
return S_OK;
}
static void RecycleBin_Destructor(RecycleBin *This)
{
/* InterlockedDecrement(&objCount);*/
SHFree(This->pidl);
SHFree(This);
}
static HRESULT WINAPI RecycleBin_QueryInterface(IShellFolder2 *iface, REFIID riid, void **ppvObject)
{
RecycleBin *This = impl_from_IShellFolder2(iface);
TRACE("(%p, %s, %p)\n", This, debugstr_guid(riid), ppvObject);
*ppvObject = NULL;
if (IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_IShellFolder)
|| IsEqualGUID(riid, &IID_IShellFolder2))
*ppvObject = &This->IShellFolder2_iface;
if (IsEqualGUID(riid, &IID_IPersist) || IsEqualGUID(riid, &IID_IPersistFolder)
|| IsEqualGUID(riid, &IID_IPersistFolder2))
*ppvObject = &This->IPersistFolder2_iface;
if (IsEqualGUID(riid, &IID_ISFHelper))
*ppvObject = &This->ISFHelper_iface;
if (*ppvObject != NULL)
{
IUnknown_AddRef((IUnknown *)*ppvObject);
return S_OK;
}
WARN("no interface %s\n", debugstr_guid(riid));
return E_NOINTERFACE;
}
static ULONG WINAPI RecycleBin_AddRef(IShellFolder2 *iface)
{
RecycleBin *This = impl_from_IShellFolder2(iface);
TRACE("(%p)\n", This);
return InterlockedIncrement(&This->refCount);
}
static ULONG WINAPI RecycleBin_Release(IShellFolder2 *iface)
{
RecycleBin *This = impl_from_IShellFolder2(iface);
LONG result;
TRACE("(%p)\n", This);
result = InterlockedDecrement(&This->refCount);
if (result == 0)
{
TRACE("Destroy object\n");
RecycleBin_Destructor(This);
}
return result;
}
static HRESULT WINAPI RecycleBin_ParseDisplayName(IShellFolder2 *This, HWND hwnd, LPBC pbc,
LPOLESTR pszDisplayName, ULONG *pchEaten, LPITEMIDLIST *ppidl,
ULONG *pdwAttributes)
{
FIXME("stub\n");
return E_NOTIMPL;
}
static HRESULT WINAPI RecycleBin_EnumObjects(IShellFolder2 *iface, HWND hwnd, SHCONTF grfFlags, IEnumIDList **ppenumIDList)
{
RecycleBin *This = impl_from_IShellFolder2(iface);
IEnumIDListImpl *list;
LPITEMIDLIST *pidls;
HRESULT ret = E_OUTOFMEMORY;
int pidls_count;
int i=0;
TRACE("(%p, %p, %x, %p)\n", This, hwnd, grfFlags, ppenumIDList);
*ppenumIDList = NULL;
list = IEnumIDList_Constructor();
if (!list)
return E_OUTOFMEMORY;
if (grfFlags & SHCONTF_NONFOLDERS)
{
if (FAILED(ret = TRASH_EnumItems(&pidls, &pidls_count)))
goto failed;
for (i=0; i<pidls_count; i++)
if (!AddToEnumList(list, pidls[i]))
goto failed;
}
*ppenumIDList = &list->IEnumIDList_iface;
return S_OK;
failed:
if (list)
IEnumIDList_Release(&list->IEnumIDList_iface);
for (; i<pidls_count; i++)
ILFree(pidls[i]);
SHFree(pidls);
return ret;
}
static HRESULT WINAPI RecycleBin_BindToObject(IShellFolder2 *This, LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv)
{
FIXME("(%p, %p, %p, %s, %p) - stub\n", This, pidl, pbc, debugstr_guid(riid), ppv);
return E_NOTIMPL;
}
static HRESULT WINAPI RecycleBin_BindToStorage(IShellFolder2 *This, LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv)
{
FIXME("(%p, %p, %p, %s, %p) - stub\n", This, pidl, pbc, debugstr_guid(riid), ppv);
return E_NOTIMPL;
}
static HRESULT WINAPI RecycleBin_CompareIDs(IShellFolder2 *iface, LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
{
RecycleBin *This = impl_from_IShellFolder2(iface);
int ret;
/* TODO */
TRACE("(%p, %p, %p, %p)\n", This, (void *)lParam, pidl1, pidl2);
if (pidl1->mkid.cb != pidl2->mkid.cb)
return MAKE_HRESULT(SEVERITY_SUCCESS, 0, pidl1->mkid.cb - pidl2->mkid.cb);
/* Looks too complicated, but in optimized memcpy we might get
* a 32bit wide difference and would truncate it to 16 bit, so
* erroneously returning equality. */
ret = memcmp(pidl1->mkid.abID, pidl2->mkid.abID, pidl1->mkid.cb);
if (ret < 0) ret = -1;
if (ret > 0) ret = 1;
return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (unsigned short)ret);
}
static HRESULT WINAPI RecycleBin_CreateViewObject(IShellFolder2 *iface, HWND hwndOwner, REFIID riid, void **ppv)
{
RecycleBin *This = impl_from_IShellFolder2(iface);
HRESULT ret;
TRACE("(%p, %p, %s, %p)\n", This, hwndOwner, debugstr_guid(riid), ppv);
*ppv = NULL;
if (IsEqualGUID(riid, &IID_IShellView))
{
IShellView *tmp;
CSFV sfv;
ZeroMemory(&sfv, sizeof(sfv));
sfv.cbSize = sizeof(sfv);
sfv.pshf = (IShellFolder *)This;
TRACE("Calling SHCreateShellFolderViewEx\n");
ret = SHCreateShellFolderViewEx(&sfv, &tmp);
TRACE("Result: %08x, output: %p\n", (unsigned int)ret, tmp);
*ppv = tmp;
return ret;
}
return E_NOINTERFACE;
}
static HRESULT WINAPI RecycleBin_GetAttributesOf(IShellFolder2 *This, UINT cidl, LPCITEMIDLIST *apidl,
SFGAOF *rgfInOut)
{
TRACE("(%p, %d, {%p, ...}, {%x})\n", This, cidl, apidl[0], *rgfInOut);
*rgfInOut &= SFGAO_CANMOVE|SFGAO_CANDELETE|SFGAO_HASPROPSHEET|SFGAO_FILESYSTEM;
return S_OK;
}
static HRESULT WINAPI RecycleBin_GetUIObjectOf(IShellFolder2 *iface, HWND hwndOwner, UINT cidl, LPCITEMIDLIST *apidl,
REFIID riid, UINT *rgfReserved, void **ppv)
{
RecycleBin *This = impl_from_IShellFolder2(iface);
*ppv = NULL;
if(IsEqualGUID(riid, &IID_IContextMenu) || IsEqualGUID(riid, &IID_IContextMenu2))
{
TRACE("(%p, %p, %d, {%p, ...}, %s, %p, %p)\n", This, hwndOwner, cidl, apidl[0], debugstr_guid(riid), rgfReserved, ppv);
*ppv = RecycleBinMenu_Constructor(cidl,apidl,&(This->IShellFolder2_iface));
return S_OK;
}
FIXME("(%p, %p, %d, {%p, ...}, %s, %p, %p): stub!\n", iface, hwndOwner, cidl, apidl[0], debugstr_guid(riid), rgfReserved, ppv);
return E_NOTIMPL;
}
static HRESULT WINAPI RecycleBin_GetDisplayNameOf(IShellFolder2 *This, LPCITEMIDLIST pidl, SHGDNF uFlags, STRRET *pName)
{
WIN32_FIND_DATAW data;
TRACE("(%p, %p, %x, %p)\n", This, pidl, uFlags, pName);
TRASH_UnpackItemID(&pidl->mkid, &data);
pName->uType = STRRET_WSTR;
pName->u.pOleStr = StrDupW(PathFindFileNameW(data.cFileName));
if (pName->u.pOleStr == NULL)
return E_OUTOFMEMORY;
return S_OK;
}
static HRESULT WINAPI RecycleBin_SetNameOf(IShellFolder2 *This, HWND hwnd, LPCITEMIDLIST pidl, LPCOLESTR pszName,
SHGDNF uFlags, LPITEMIDLIST *ppidlOut)
{
TRACE("\n");
return E_FAIL; /* not supported */
}
static HRESULT WINAPI RecycleBin_GetClassID(IPersistFolder2 *This, CLSID *pClassID)
{
TRACE("(%p, %p)\n", This, pClassID);
if (This == NULL || pClassID == NULL)
return E_INVALIDARG;
*pClassID = CLSID_RecycleBin;
return S_OK;
}
static HRESULT WINAPI RecycleBin_Initialize(IPersistFolder2 *iface, LPCITEMIDLIST pidl)
{
RecycleBin *This = impl_from_IPersistFolder2(iface);
TRACE("(%p, %p)\n", This, pidl);
This->pidl = ILClone(pidl);
if (This->pidl == NULL)
return E_OUTOFMEMORY;
return S_OK;
}
static HRESULT WINAPI RecycleBin_GetCurFolder(IPersistFolder2 *iface, LPITEMIDLIST *ppidl)
{
RecycleBin *This = impl_from_IPersistFolder2(iface);
TRACE("\n");
*ppidl = ILClone(This->pidl);
return S_OK;
}
static HRESULT WINAPI RecycleBin_GetDefaultSearchGUID(IShellFolder2 *iface, GUID *pguid)
{
FIXME("stub\n");
return E_NOTIMPL;
}
static HRESULT WINAPI RecycleBin_EnumSearches(IShellFolder2 *iface, IEnumExtraSearch **ppEnum)
{
FIXME("stub\n");
*ppEnum = NULL;
return E_NOTIMPL;
}
static HRESULT WINAPI RecycleBin_GetDefaultColumn(IShellFolder2 *iface, DWORD dwReserved, ULONG *pSort, ULONG *pDisplay)
{
RecycleBin *This = impl_from_IShellFolder2(iface);
TRACE("(%p, %x, %p, %p)\n", This, dwReserved, pSort, pDisplay);
*pSort = 0;
*pDisplay = 0;
return S_OK;
}
static HRESULT WINAPI RecycleBin_GetDefaultColumnState(IShellFolder2 *iface, UINT iColumn, SHCOLSTATEF *pcsFlags)
{
RecycleBin *This = impl_from_IShellFolder2(iface);
TRACE("(%p, %d, %p)\n", This, iColumn, pcsFlags);
if (iColumn >= COLUMNS_COUNT)
return E_INVALIDARG;
*pcsFlags = RecycleBinColumns[iColumn].pcsFlags;
return S_OK;
}
static HRESULT WINAPI RecycleBin_GetDetailsEx(IShellFolder2 *iface, LPCITEMIDLIST pidl, const SHCOLUMNID *pscid, VARIANT *pv)
{
FIXME("stub\n");
return E_NOTIMPL;
}
static HRESULT WINAPI RecycleBin_GetDetailsOf(IShellFolder2 *iface, LPCITEMIDLIST pidl, UINT iColumn, LPSHELLDETAILS pDetails)
{
RecycleBin *This = impl_from_IShellFolder2(iface);
WIN32_FIND_DATAW data;
WCHAR buffer[MAX_PATH];
TRACE("(%p, %p, %d, %p)\n", This, pidl, iColumn, pDetails);
if (iColumn >= COLUMNS_COUNT)
return E_FAIL;
pDetails->fmt = RecycleBinColumns[iColumn].fmt;
pDetails->cxChar = RecycleBinColumns[iColumn].cxChars;
if (pidl == NULL)
{
pDetails->str.uType = STRRET_WSTR;
LoadStringW(shell32_hInstance, RecycleBinColumns[iColumn].column_name_id, buffer, MAX_PATH);
return SHStrDupW(buffer, &pDetails->str.u.pOleStr);
}
if (iColumn == COLUMN_NAME)
return RecycleBin_GetDisplayNameOf(iface, pidl, SHGDN_NORMAL, &pDetails->str);
TRASH_UnpackItemID(&pidl->mkid, &data);
switch (iColumn)
{
case COLUMN_DATEDEL:
FormatDateTime(buffer, MAX_PATH, data.ftLastAccessTime);
break;
case COLUMN_DELFROM:
lstrcpyW(buffer, data.cFileName);
PathRemoveFileSpecW(buffer);
break;
case COLUMN_SIZE:
StrFormatKBSizeW(((LONGLONG)data.nFileSizeHigh<<32)|data.nFileSizeLow, buffer, MAX_PATH);
break;
case COLUMN_MTIME:
FormatDateTime(buffer, MAX_PATH, data.ftLastWriteTime);
break;
case COLUMN_TYPE:
/* TODO */
buffer[0] = 0;
break;
default:
return E_FAIL;
}
pDetails->str.uType = STRRET_WSTR;
return SHStrDupW(buffer, &pDetails->str.u.pOleStr);
}
static HRESULT WINAPI RecycleBin_MapColumnToSCID(IShellFolder2 *iface, UINT iColumn, SHCOLUMNID *pscid)
{
RecycleBin *This = impl_from_IShellFolder2(iface);
TRACE("(%p, %d, %p)\n", This, iColumn, pscid);
if (iColumn>=COLUMNS_COUNT)
return E_INVALIDARG;
pscid->fmtid = *RecycleBinColumns[iColumn].fmtId;
pscid->pid = RecycleBinColumns[iColumn].pid;
return S_OK;
}
static const IShellFolder2Vtbl recycleBinVtbl =
{
/* IUnknown */
RecycleBin_QueryInterface,
RecycleBin_AddRef,
RecycleBin_Release,
/* IShellFolder */
RecycleBin_ParseDisplayName,
RecycleBin_EnumObjects,
RecycleBin_BindToObject,
RecycleBin_BindToStorage,
RecycleBin_CompareIDs,
RecycleBin_CreateViewObject,
RecycleBin_GetAttributesOf,
RecycleBin_GetUIObjectOf,
RecycleBin_GetDisplayNameOf,
RecycleBin_SetNameOf,
/* IShellFolder2 */
RecycleBin_GetDefaultSearchGUID,
RecycleBin_EnumSearches,
RecycleBin_GetDefaultColumn,
RecycleBin_GetDefaultColumnState,
RecycleBin_GetDetailsEx,
RecycleBin_GetDetailsOf,
RecycleBin_MapColumnToSCID
};
static HRESULT WINAPI RecycleBin_IPersistFolder2_QueryInterface(IPersistFolder2 *iface, REFIID riid,
void **ppvObject)
{
RecycleBin *This = impl_from_IPersistFolder2(iface);
return IShellFolder2_QueryInterface(&This->IShellFolder2_iface, riid, ppvObject);
}
static ULONG WINAPI RecycleBin_IPersistFolder2_AddRef(IPersistFolder2 *iface)
{
RecycleBin *This = impl_from_IPersistFolder2(iface);
return IShellFolder2_AddRef(&This->IShellFolder2_iface);
}
static ULONG WINAPI RecycleBin_IPersistFolder2_Release(IPersistFolder2 *iface)
{
RecycleBin *This = impl_from_IPersistFolder2(iface);
return IShellFolder2_Release(&This->IShellFolder2_iface);
}
static const IPersistFolder2Vtbl recycleBinPersistVtbl =
{
/* IUnknown */
RecycleBin_IPersistFolder2_QueryInterface,
RecycleBin_IPersistFolder2_AddRef,
RecycleBin_IPersistFolder2_Release,
/* IPersist */
RecycleBin_GetClassID,
/* IPersistFolder */
RecycleBin_Initialize,
/* IPersistFolder2 */
RecycleBin_GetCurFolder
};
static HRESULT WINAPI RecycleBin_ISFHelper_QueryInterface(ISFHelper *iface, REFIID riid,
void **ppvObject)
{
RecycleBin *This = impl_from_ISFHelper(iface);
return IShellFolder2_QueryInterface(&This->IShellFolder2_iface, riid, ppvObject);
}
static ULONG WINAPI RecycleBin_ISFHelper_AddRef(ISFHelper *iface)
{
RecycleBin *This = impl_from_ISFHelper(iface);
return IShellFolder2_AddRef(&This->IShellFolder2_iface);
}
static ULONG WINAPI RecycleBin_ISFHelper_Release(ISFHelper *iface)
{
RecycleBin *This = impl_from_ISFHelper(iface);
return IShellFolder2_Release(&This->IShellFolder2_iface);
}
static HRESULT WINAPI RecycleBin_GetUniqueName(ISFHelper *iface,LPWSTR lpName,
UINT uLen)
{
return E_NOTIMPL;
}
static HRESULT WINAPI RecycleBin_AddFolder(ISFHelper * iface, HWND hwnd,
LPCWSTR pwszName,
LPITEMIDLIST * ppidlOut)
{
/*Adding folders doesn't make sense in the recycle bin*/
return E_NOTIMPL;
}
static HRESULT erase_items(HWND parent,const LPCITEMIDLIST * apidl, UINT cidl, BOOL confirm)
{
UINT i=0;
HRESULT ret = S_OK;
LPITEMIDLIST recyclebin;
if(confirm)
{
WCHAR arg[MAX_PATH];
WCHAR message[100];
WCHAR caption[50];
switch(cidl)
{
case 0:
return S_OK;
case 1:
{
WIN32_FIND_DATAW data;
TRASH_UnpackItemID(&((*apidl)->mkid),&data);
lstrcpynW(arg,data.cFileName,MAX_PATH);
LoadStringW(shell32_hInstance,IDS_RECYCLEBIN_ERASEITEM,message,
sizeof(message)/sizeof(WCHAR));
break;
}
default:
{
static const WCHAR format[]={'%','u','\0'};
LoadStringW(shell32_hInstance,IDS_RECYCLEBIN_ERASEMULTIPLE,
message,sizeof(message)/sizeof(WCHAR));
sprintfW(arg,format,cidl);
break;
}
}
LoadStringW(shell32_hInstance,IDS_RECYCLEBIN_ERASE_CAPTION,caption,
sizeof(caption)/sizeof(WCHAR));
if(ShellMessageBoxW(shell32_hInstance,parent,message,caption,
MB_YESNO|MB_ICONEXCLAMATION,arg)!=IDYES)
return ret;
}
SHGetFolderLocation(parent,CSIDL_BITBUCKET,0,0,&recyclebin);
for (; i<cidl; i++)
{
if(SUCCEEDED(TRASH_EraseItem(apidl[i])))
SHChangeNotify(SHCNE_DELETE,SHCNF_IDLIST,
ILCombine(recyclebin,apidl[i]),0);
}
ILFree(recyclebin);
return S_OK;
}
static HRESULT WINAPI RecycleBin_DeleteItems(ISFHelper * iface, UINT cidl,
LPCITEMIDLIST * apidl)
{
TRACE("(%p,%u,%p)\n",iface,cidl,apidl);
return erase_items(GetActiveWindow(),apidl,cidl,TRUE);
}
static HRESULT WINAPI RecycleBin_CopyItems(ISFHelper * iface,
IShellFolder * pSFFrom,
UINT cidl, LPCITEMIDLIST * apidl)
{
return E_NOTIMPL;
}
static const ISFHelperVtbl sfhelperVtbl =
{
RecycleBin_ISFHelper_QueryInterface,
RecycleBin_ISFHelper_AddRef,
RecycleBin_ISFHelper_Release,
RecycleBin_GetUniqueName,
RecycleBin_AddFolder,
RecycleBin_DeleteItems,
RecycleBin_CopyItems
};
HRESULT WINAPI SHQueryRecycleBinA(LPCSTR pszRootPath, LPSHQUERYRBINFO pSHQueryRBInfo)
{
WCHAR wszRootPath[MAX_PATH];
MultiByteToWideChar(CP_ACP, 0, pszRootPath, -1, wszRootPath, MAX_PATH);
return SHQueryRecycleBinW(wszRootPath, pSHQueryRBInfo);
}
HRESULT WINAPI SHQueryRecycleBinW(LPCWSTR pszRootPath, LPSHQUERYRBINFO pSHQueryRBInfo)
{
LPITEMIDLIST *apidl;
INT cidl;
INT i=0;
TRACE("(%s, %p)\n", debugstr_w(pszRootPath), pSHQueryRBInfo);
FIXME("Ignoring pszRootPath=%s\n",debugstr_w(pszRootPath));
TRASH_EnumItems(&apidl,&cidl);
pSHQueryRBInfo->i64NumItems = cidl;
pSHQueryRBInfo->i64Size = 0;
for (; i<cidl; i++)
{
WIN32_FIND_DATAW data;
TRASH_UnpackItemID(&((apidl[i])->mkid),&data);
pSHQueryRBInfo->i64Size += ((DWORDLONG)data.nFileSizeHigh << 32) + data.nFileSizeLow;
ILFree(apidl[i]);
}
SHFree(apidl);
return S_OK;
}
HRESULT WINAPI SHEmptyRecycleBinA(HWND hwnd, LPCSTR pszRootPath, DWORD dwFlags)
{
WCHAR wszRootPath[MAX_PATH];
MultiByteToWideChar(CP_ACP, 0, pszRootPath, -1, wszRootPath, MAX_PATH);
return SHEmptyRecycleBinW(hwnd, wszRootPath, dwFlags);
}
#define SHERB_NOCONFIRMATION 1
#define SHERB_NOPROGRESSUI 2
#define SHERB_NOSOUND 4
HRESULT WINAPI SHEmptyRecycleBinW(HWND hwnd, LPCWSTR pszRootPath, DWORD dwFlags)
{
LPITEMIDLIST *apidl;
INT cidl;
INT i=0;
HRESULT ret;
TRACE("(%p, %s, 0x%08x)\n", hwnd, debugstr_w(pszRootPath) , dwFlags);
FIXME("Ignoring pszRootPath=%s\n",debugstr_w(pszRootPath));
TRASH_EnumItems(&apidl,&cidl);
ret = erase_items(hwnd,(const LPCITEMIDLIST*)apidl,cidl,!(dwFlags & SHERB_NOCONFIRMATION));
for (;i<cidl;i++)
ILFree(apidl[i]);
SHFree(apidl);
return ret;
}
/*************************************************************************
* SHUpdateRecycleBinIcon [SHELL32.@]
*
* Undocumented
*/
HRESULT WINAPI SHUpdateRecycleBinIcon(void)
{
FIXME("stub\n");
return S_OK;
}