| /* |
| * Trash virtual folder support. The trashing engine is implemented in trash.c |
| * |
| * Copyright (C) 2006 Mikolaj Zalewski |
| * |
| * 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 "ntquery.h" |
| #include "shlwapi.h" |
| #include "shlobj.h" |
| #include "shresdef.h" |
| #include "wine/debug.h" |
| |
| #include "shell32_main.h" |
| #include "enumidlist.h" |
| #include "xdg.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); |
| } |
| |
| /* |
| * Recycle Bin folder |
| */ |
| |
| typedef struct tagRecycleBin |
| { |
| const IShellFolder2Vtbl *lpVtbl; |
| const IPersistFolder2Vtbl *lpPersistFolderVtbl; |
| LONG refCount; |
| |
| LPITEMIDLIST pidl; |
| } RecycleBin; |
| |
| static const IShellFolder2Vtbl recycleBinVtbl; |
| static const IPersistFolder2Vtbl recycleBinPersistVtbl; |
| |
| static RecycleBin *impl_from_IPersistFolder(IPersistFolder2 *iface) |
| { |
| return (RecycleBin *)((char *)iface - FIELD_OFFSET(RecycleBin, lpPersistFolderVtbl)); |
| } |
| |
| 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->lpVtbl = &recycleBinVtbl; |
| obj->lpPersistFolderVtbl = &recycleBinPersistVtbl; |
| if (FAILED(ret = IUnknown_QueryInterface((IUnknown *)obj, 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 = (RecycleBin *)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; |
| |
| if (IsEqualGUID(riid, &IID_IPersist) || IsEqualGUID(riid, &IID_IPersistFolder) |
| || IsEqualGUID(riid, &IID_IPersistFolder2)) |
| *ppvObject = &This->lpPersistFolderVtbl; |
| |
| 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 = (RecycleBin *)iface; |
| TRACE("(%p)\n", This); |
| return InterlockedIncrement(&This->refCount); |
| } |
| |
| static ULONG WINAPI RecycleBin_Release(IShellFolder2 *iface) |
| { |
| RecycleBin *This = (RecycleBin *)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 = (RecycleBin *)iface; |
| IEnumIDList *list; |
| LPITEMIDLIST *pidls; |
| HRESULT ret; |
| int pidls_count; |
| int i=0; |
| |
| TRACE("(%p, %p, %x, %p)\n", This, hwnd, grfFlags, ppenumIDList); |
| |
| if (grfFlags & SHCONTF_NONFOLDERS) |
| { |
| *ppenumIDList = NULL; |
| if (FAILED(ret = TRASH_EnumItems(&pidls, &pidls_count))) |
| return ret; |
| |
| list = IEnumIDList_Constructor(); |
| if (list == NULL) |
| goto failed; |
| for (i=0; i<pidls_count; i++) |
| if (!AddToEnumList(list, pidls[i])) |
| goto failed; |
| *ppenumIDList = list; |
| } |
| else |
| { |
| *ppenumIDList = IEnumIDList_Constructor(); |
| if (*ppenumIDList == NULL) |
| return E_OUTOFMEMORY; |
| } |
| |
| return S_OK; |
| |
| failed: |
| if (list) |
| IEnumIDList_Release(list); |
| for (; i<pidls_count; i++) |
| ILFree(pidls[i]); |
| SHFree(pidls); |
| return E_OUTOFMEMORY; |
| } |
| |
| 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 = (RecycleBin *)iface; |
| |
| /* 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); |
| return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (unsigned short)memcmp(pidl1->mkid.abID, pidl2->mkid.abID, pidl1->mkid.cb)); |
| } |
| |
| static HRESULT WINAPI RecycleBin_CreateViewObject(IShellFolder2 *iface, HWND hwndOwner, REFIID riid, void **ppv) |
| { |
| RecycleBin *This = (RecycleBin *)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 *This, HWND hwndOwner, UINT cidl, LPCITEMIDLIST *apidl, |
| REFIID riid, UINT *rgfReserved, void **ppv) |
| { |
| FIXME("(%p, %p, %d, {%p, ...}, %s, %p, %p): stub!\n", This, hwndOwner, cidl, apidl[0], debugstr_guid(riid), rgfReserved, ppv); |
| *ppv = NULL; |
| 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_IPersistFolder(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_IPersistFolder(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 = (RecycleBin *)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 = (RecycleBin *)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 = (RecycleBin *)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 = (RecycleBin *)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 *This, REFIID riid, void **ppvObject) |
| { |
| return RecycleBin_QueryInterface((IShellFolder2 *)impl_from_IPersistFolder(This), riid, ppvObject); |
| } |
| |
| static ULONG WINAPI RecycleBin_IPersistFolder2_AddRef(IPersistFolder2 *This) |
| { |
| return RecycleBin_AddRef((IShellFolder2 *)impl_from_IPersistFolder(This)); |
| } |
| |
| static ULONG WINAPI RecycleBin_IPersistFolder2_Release(IPersistFolder2 *This) |
| { |
| return RecycleBin_Release((IShellFolder2 *)impl_from_IPersistFolder(This)); |
| } |
| |
| static const IPersistFolder2Vtbl recycleBinPersistVtbl = |
| { |
| /* IUnknown */ |
| RecycleBin_IPersistFolder2_QueryInterface, |
| RecycleBin_IPersistFolder2_AddRef, |
| RecycleBin_IPersistFolder2_Release, |
| |
| /* IPersist */ |
| RecycleBin_GetClassID, |
| /* IPersistFolder */ |
| RecycleBin_Initialize, |
| /* IPersistFolder2 */ |
| RecycleBin_GetCurFolder |
| }; |
| |
| /************************************************************************* |
| * SHUpdateRecycleBinIcon [SHELL32.@] |
| * |
| * Undocumented |
| */ |
| HRESULT WINAPI SHUpdateRecycleBinIcon(void) |
| { |
| FIXME("stub\n"); |
| return S_OK; |
| } |