de50bd1e02fb7b0c11b1d5b13ed85a29b5dec31f
[reactos.git] / dll / win32 / shell32 / folders / recyclebin.cpp
1 /*
2 * Trash virtual folder support. The trashing engine is implemented in trash.c
3 *
4 * Copyright (C) 2006 Mikolaj Zalewski
5 * Copyright (C) 2009 Andrew Hill
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 */
21
22 #include <precomp.h>
23
24 #include <ntquery.h>
25
26 #define MAX_PROPERTY_SHEET_PAGE 32
27
28 WINE_DEFAULT_DEBUG_CHANNEL(CRecycleBin);
29
30 typedef struct
31 {
32 int column_name_id;
33 const GUID *fmtId;
34 DWORD pid;
35 int pcsFlags;
36 int fmt;
37 int cxChars;
38 } columninfo;
39
40 static const columninfo RecycleBinColumns[] =
41 {
42 {IDS_SHV_COLUMN1, &FMTID_Storage, PID_STG_NAME, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 30},
43 {IDS_SHV_COLUMN_DELFROM, &FMTID_Displaced, PID_DISPLACED_FROM, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 30},
44 {IDS_SHV_COLUMN_DELDATE, &FMTID_Displaced, PID_DISPLACED_DATE, SHCOLSTATE_TYPE_DATE | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 20},
45 {IDS_SHV_COLUMN2, &FMTID_Storage, PID_STG_SIZE, SHCOLSTATE_TYPE_INT | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 20},
46 {IDS_SHV_COLUMN3, &FMTID_Storage, PID_STG_STORAGETYPE, SHCOLSTATE_TYPE_INT | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 20},
47 {IDS_SHV_COLUMN4, &FMTID_Storage, PID_STG_WRITETIME, SHCOLSTATE_TYPE_DATE | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 20},
48 /* {"creation time", &FMTID_Storage, PID_STG_CREATETIME, SHCOLSTATE_TYPE_DATE, LVCFMT_LEFT, 20}, */
49 /* {"attribs", &FMTID_Storage, PID_STG_ATTRIBUTES, SHCOLSTATE_TYPE_STR, LVCFMT_LEFT, 20}, */
50 };
51
52 #define COLUMN_NAME 0
53 #define COLUMN_DELFROM 1
54 #define COLUMN_DATEDEL 2
55 #define COLUMN_SIZE 3
56 #define COLUMN_TYPE 4
57 #define COLUMN_MTIME 5
58
59 #define COLUMNS_COUNT 6
60
61 /*
62 * Recycle Bin folder
63 */
64
65 class CRecycleBinEnum :
66 public IEnumIDListImpl
67 {
68 private:
69 public:
70 CRecycleBinEnum();
71 ~CRecycleBinEnum();
72 HRESULT WINAPI Initialize(DWORD dwFlags);
73 static BOOL WINAPI CBEnumRecycleBin(IN PVOID Context, IN HANDLE hDeletedFile);
74 BOOL WINAPI CBEnumRecycleBin(IN HANDLE hDeletedFile);
75
76 BEGIN_COM_MAP(CRecycleBinEnum)
77 COM_INTERFACE_ENTRY_IID(IID_IEnumIDList, IEnumIDList)
78 END_COM_MAP()
79 };
80
81 class CRecycleBinItemContextMenu :
82 public CComObjectRootEx<CComMultiThreadModelNoCS>,
83 public IContextMenu2
84 {
85 private:
86 LPITEMIDLIST apidl;
87 public:
88 CRecycleBinItemContextMenu();
89 ~CRecycleBinItemContextMenu();
90 HRESULT WINAPI Initialize(LPCITEMIDLIST pidl);
91
92 // IContextMenu
93 virtual HRESULT WINAPI QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags);
94 virtual HRESULT WINAPI InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi);
95 virtual HRESULT WINAPI GetCommandString(UINT_PTR idCommand, UINT uFlags, UINT *lpReserved, LPSTR lpszName, UINT uMaxNameLen);
96
97 // IContextMenu2
98 virtual HRESULT WINAPI HandleMenuMsg(UINT uMsg, WPARAM wParam, LPARAM lParam);
99
100 BEGIN_COM_MAP(CRecycleBinItemContextMenu)
101 COM_INTERFACE_ENTRY_IID(IID_IContextMenu, IContextMenu)
102 COM_INTERFACE_ENTRY_IID(IID_IContextMenu2, IContextMenu2)
103 END_COM_MAP()
104 };
105
106 typedef struct
107 {
108 PIDLRecycleStruct *pFileDetails;
109 HANDLE hDeletedFile;
110 BOOL bFound;
111 } SEARCH_CONTEXT, *PSEARCH_CONTEXT;
112
113 typedef struct
114 {
115 DWORD dwNukeOnDelete;
116 DWORD dwSerial;
117 DWORD dwMaxCapacity;
118 } DRIVE_ITEM_CONTEXT, *PDRIVE_ITEM_CONTEXT;
119
120 BOOL WINAPI CBSearchRecycleBin(IN PVOID Context, IN HANDLE hDeletedFile)
121 {
122 PSEARCH_CONTEXT pContext = (PSEARCH_CONTEXT)Context;
123
124 PDELETED_FILE_DETAILS_W pFileDetails;
125 DWORD dwSize;
126 BOOL ret;
127
128 if (!GetDeletedFileDetailsW(hDeletedFile,
129 0,
130 NULL,
131 &dwSize) &&
132 GetLastError() != ERROR_INSUFFICIENT_BUFFER)
133 {
134 ERR("GetDeletedFileDetailsW failed\n");
135 return FALSE;
136 }
137
138 pFileDetails = (DELETED_FILE_DETAILS_W *)SHAlloc(dwSize);
139 if (!pFileDetails)
140 {
141 ERR("No memory\n");
142 return FALSE;
143 }
144
145 if (!GetDeletedFileDetailsW(hDeletedFile,
146 dwSize,
147 pFileDetails,
148 NULL))
149 {
150 ERR("GetDeletedFileDetailsW failed\n");
151 SHFree(pFileDetails);
152 return FALSE;
153 }
154
155 ret = memcmp(pFileDetails, pContext->pFileDetails, dwSize);
156 if (!ret)
157 {
158 pContext->hDeletedFile = hDeletedFile;
159 pContext->bFound = TRUE;
160 }
161 else
162 CloseRecycleBinHandle(hDeletedFile);
163
164 SHFree(pFileDetails);
165 return ret;
166 }
167
168 static PIDLRecycleStruct * _ILGetRecycleStruct(LPCITEMIDLIST pidl)
169 {
170 LPPIDLDATA pdata = _ILGetDataPointer(pidl);
171
172 if (pdata && pdata->type == 0x00)
173 return (PIDLRecycleStruct*) & (pdata->u.crecycle);
174
175 return NULL;
176 }
177
178 CRecycleBinEnum::CRecycleBinEnum()
179 {
180 }
181
182 CRecycleBinEnum::~CRecycleBinEnum()
183 {
184 }
185
186 HRESULT WINAPI CRecycleBinEnum::Initialize(DWORD dwFlags)
187 {
188 static LPCWSTR szDrive = L"C:\\";
189
190 if (dwFlags & SHCONTF_NONFOLDERS)
191 {
192 TRACE("Starting Enumeration\n");
193
194 if (!EnumerateRecycleBinW(szDrive /* FIXME */ , CBEnumRecycleBin, (PVOID)this))
195 {
196 WARN("Error: EnumerateCRecycleBinW failed\n");
197 return E_FAIL;
198 }
199 }
200 else
201 {
202 // do nothing
203 }
204 return S_OK;
205 }
206
207 static LPITEMIDLIST _ILCreateRecycleItem(PDELETED_FILE_DETAILS_W pFileDetails)
208 {
209 PIDLDATA tmp;
210 LPITEMIDLIST pidl;
211 PIDLRecycleStruct * p;
212 int size0 = (char*)&tmp.u.crecycle.szName - (char*)&tmp.u.crecycle;
213 int size = size0;
214
215 tmp.type = 0x00;
216 size += (wcslen(pFileDetails->FileName) + 1) * sizeof(WCHAR);
217
218 pidl = (LPITEMIDLIST)SHAlloc(size + 4);
219 if (!pidl)
220 return pidl;
221
222 pidl->mkid.cb = size + 2;
223 memcpy(pidl->mkid.abID, &tmp, 2 + size0);
224
225 p = &((PIDLDATA*)pidl->mkid.abID)->u.crecycle;
226 RtlCopyMemory(p, pFileDetails, sizeof(DELETED_FILE_DETAILS_W));
227 wcscpy(p->szName, pFileDetails->FileName);
228 *(WORD*)((char*)pidl + (size + 2)) = 0;
229 return pidl;
230 }
231
232 BOOL WINAPI CRecycleBinEnum::CBEnumRecycleBin(IN PVOID Context, IN HANDLE hDeletedFile)
233 {
234 return static_cast<CRecycleBinEnum *>(Context)->CBEnumRecycleBin(hDeletedFile);
235 }
236
237 BOOL WINAPI CRecycleBinEnum::CBEnumRecycleBin(IN HANDLE hDeletedFile)
238 {
239 PDELETED_FILE_DETAILS_W pFileDetails;
240 DWORD dwSize;
241 LPITEMIDLIST pidl = NULL;
242 BOOL ret;
243
244 if (!GetDeletedFileDetailsW(hDeletedFile,
245 0,
246 NULL,
247 &dwSize) &&
248 GetLastError() != ERROR_INSUFFICIENT_BUFFER)
249 {
250 ERR("GetDeletedFileDetailsW failed\n");
251 return FALSE;
252 }
253
254 pFileDetails = (DELETED_FILE_DETAILS_W *)SHAlloc(dwSize);
255 if (!pFileDetails)
256 {
257 ERR("No memory\n");
258 return FALSE;
259 }
260
261 if (!GetDeletedFileDetailsW(hDeletedFile,
262 dwSize,
263 pFileDetails,
264 NULL))
265 {
266 ERR("GetDeletedFileDetailsW failed\n");
267 SHFree(pFileDetails);
268 return FALSE;
269 }
270
271 pidl = _ILCreateRecycleItem(pFileDetails);
272 if (!pidl)
273 {
274 SHFree(pFileDetails);
275 return FALSE;
276 }
277
278 ret = AddToEnumList(pidl);
279
280 if (!ret)
281 SHFree(pidl);
282 SHFree(pFileDetails);
283 TRACE("Returning %d\n", ret);
284 CloseRecycleBinHandle(hDeletedFile);
285 return ret;
286 }
287
288 /**************************************************************************
289 * IContextMenu2 Bitbucket Item Implementation
290 */
291
292 CRecycleBinItemContextMenu::CRecycleBinItemContextMenu()
293 {
294 apidl = NULL;
295 }
296
297 CRecycleBinItemContextMenu::~CRecycleBinItemContextMenu()
298 {
299 ILFree(apidl);
300 }
301
302 HRESULT WINAPI CRecycleBinItemContextMenu::Initialize(LPCITEMIDLIST pidl)
303 {
304 apidl = ILClone(pidl);
305 if (apidl == NULL)
306 return E_OUTOFMEMORY;
307 return S_OK;
308 }
309
310 HRESULT WINAPI CRecycleBinItemContextMenu::QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
311 {
312 WCHAR szBuffer[30] = {0};
313 ULONG Count = 1;
314
315 TRACE("(%p)->(hmenu=%p indexmenu=%x cmdfirst=%x cmdlast=%x flags=%x )\n", this, hMenu, indexMenu, idCmdFirst, idCmdLast, uFlags);
316
317 if (LoadStringW(shell32_hInstance, IDS_RESTORE, szBuffer, sizeof(szBuffer) / sizeof(WCHAR)))
318 {
319 szBuffer[(sizeof(szBuffer)/sizeof(WCHAR))-1] = L'\0';
320 _InsertMenuItemW(hMenu, indexMenu++, TRUE, idCmdFirst + Count, MFT_STRING, szBuffer, MFS_ENABLED);
321 Count++;
322 }
323
324 if (LoadStringW(shell32_hInstance, IDS_CUT, szBuffer, sizeof(szBuffer) / sizeof(WCHAR)))
325 {
326 _InsertMenuItemW(hMenu, indexMenu++, TRUE, idCmdFirst + Count++, MFT_SEPARATOR, NULL, MFS_ENABLED);
327 szBuffer[(sizeof(szBuffer)/sizeof(WCHAR))-1] = L'\0';
328 _InsertMenuItemW(hMenu, indexMenu++, TRUE, idCmdFirst + Count++, MFT_STRING, szBuffer, MFS_ENABLED);
329 }
330
331 if (LoadStringW(shell32_hInstance, IDS_DELETE, szBuffer, sizeof(szBuffer) / sizeof(WCHAR)))
332 {
333 szBuffer[(sizeof(szBuffer)/sizeof(WCHAR))-1] = L'\0';
334 _InsertMenuItemW(hMenu, indexMenu++, TRUE, idCmdFirst + Count++, MFT_SEPARATOR, NULL, MFS_ENABLED);
335 _InsertMenuItemW(hMenu, indexMenu++, TRUE, idCmdFirst + Count++, MFT_STRING, szBuffer, MFS_ENABLED);
336 }
337
338 if (LoadStringW(shell32_hInstance, IDS_PROPERTIES, szBuffer, sizeof(szBuffer) / sizeof(WCHAR)))
339 {
340 szBuffer[(sizeof(szBuffer)/sizeof(WCHAR))-1] = L'\0';
341 _InsertMenuItemW(hMenu, indexMenu++, TRUE, idCmdFirst + Count++, MFT_SEPARATOR, NULL, MFS_ENABLED);
342 _InsertMenuItemW(hMenu, indexMenu++, TRUE, idCmdFirst + Count, MFT_STRING, szBuffer, MFS_DEFAULT);
343 }
344
345 return MAKE_HRESULT(SEVERITY_SUCCESS, 0, Count);
346 }
347
348 HRESULT WINAPI CRecycleBinItemContextMenu::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi)
349 {
350 SEARCH_CONTEXT Context;
351 static LPCWSTR szDrive = L"C:\\";
352
353 TRACE("(%p)->(invcom=%p verb=%p wnd=%p)\n", this, lpcmi, lpcmi->lpVerb, lpcmi->hwnd);
354
355 if (lpcmi->lpVerb == MAKEINTRESOURCEA(1) || lpcmi->lpVerb == MAKEINTRESOURCEA(5))
356 {
357 Context.pFileDetails = _ILGetRecycleStruct(apidl);
358 Context.bFound = FALSE;
359
360 EnumerateRecycleBinW(szDrive, CBSearchRecycleBin, (PVOID)&Context);
361 if (!Context.bFound)
362 return E_FAIL;
363
364 if (lpcmi->lpVerb == MAKEINTRESOURCEA(1))
365 {
366 /* restore file */
367 if (RestoreFile(Context.hDeletedFile))
368 return S_OK;
369 else
370 return E_FAIL;
371 }
372 else
373 {
374 DeleteFileHandleToRecycleBin(Context.hDeletedFile);
375 return E_NOTIMPL;
376 }
377 }
378 else if (lpcmi->lpVerb == MAKEINTRESOURCEA(3))
379 {
380 FIXME("implement cut\n");
381 return E_NOTIMPL;
382 }
383 else if (lpcmi->lpVerb == MAKEINTRESOURCEA(7))
384 {
385 FIXME("implement properties\n");
386 return E_NOTIMPL;
387 }
388
389 return S_OK;
390 }
391
392 HRESULT WINAPI CRecycleBinItemContextMenu::GetCommandString(UINT_PTR idCommand, UINT uFlags, UINT *lpReserved, LPSTR lpszName, UINT uMaxNameLen)
393 {
394 TRACE("(%p)->(idcom=%lx flags=%x %p name=%p len=%x)\n", this, idCommand, uFlags, lpReserved, lpszName, uMaxNameLen);
395
396 return E_FAIL;
397 }
398
399 HRESULT WINAPI CRecycleBinItemContextMenu::HandleMenuMsg(UINT uMsg, WPARAM wParam, LPARAM lParam)
400 {
401 TRACE("CRecycleBin_IContextMenu2Item_HandleMenuMsg (%p)->(msg=%x wp=%lx lp=%lx)\n", this, uMsg, wParam, lParam);
402
403 return E_NOTIMPL;
404 }
405
406 static HRESULT WINAPI CRecycleBinItemContextMenuConstructor(REFIID riid, LPCITEMIDLIST pidl, LPVOID *ppv)
407 {
408 CComObject<CRecycleBinItemContextMenu> *theMenu;
409 CComPtr<IUnknown> result;
410 HRESULT hResult;
411
412 TRACE("%s\n", shdebugstr_guid(&riid));
413
414 if (ppv == NULL)
415 return E_POINTER;
416 *ppv = NULL;
417 ATLTRY(theMenu = new CComObject<CRecycleBinItemContextMenu>);
418 if (theMenu == NULL)
419 return E_OUTOFMEMORY;
420 hResult = theMenu->QueryInterface(riid, (void **)&result);
421 if (FAILED(hResult))
422 {
423 delete theMenu;
424 return hResult;
425 }
426 hResult = theMenu->Initialize(pidl);
427 if (FAILED(hResult))
428 return hResult;
429 *ppv = result.Detach();
430 TRACE ("--(%p)\n", *ppv);
431 return S_OK;
432 }
433
434 /**************************************************************************
435 * registers clipboardformat once
436 */
437 void CRecycleBin::SF_RegisterClipFmt()
438 {
439 TRACE ("(%p)\n", this);
440
441 if (!cfShellIDList)
442 cfShellIDList = RegisterClipboardFormatW(CFSTR_SHELLIDLIST);
443 }
444
445 CRecycleBin::CRecycleBin()
446 {
447 pidl = NULL;
448 iIdEmpty = 0;
449 cfShellIDList = 0;
450 SF_RegisterClipFmt();
451 fAcceptFmt = FALSE;
452 }
453
454 CRecycleBin::~CRecycleBin()
455 {
456 /* InterlockedDecrement(&objCount);*/
457 SHFree(pidl);
458 }
459
460 /*************************************************************************
461 * RecycleBin IPersistFolder2 interface
462 */
463
464 HRESULT WINAPI CRecycleBin::GetClassID(CLSID *pClassID)
465 {
466 TRACE("(%p, %p)\n", this, pClassID);
467 if (pClassID == NULL)
468 return E_INVALIDARG;
469 memcpy(pClassID, &CLSID_RecycleBin, sizeof(CLSID));
470 return S_OK;
471 }
472
473 HRESULT WINAPI CRecycleBin::Initialize(LPCITEMIDLIST pidl)
474 {
475 TRACE("(%p, %p)\n", this, pidl);
476
477 SHFree((LPVOID)this->pidl);
478 this->pidl = ILClone(pidl);
479 if (this->pidl == NULL)
480 return E_OUTOFMEMORY;
481 return S_OK;
482 }
483
484 HRESULT WINAPI CRecycleBin::GetCurFolder(LPITEMIDLIST *ppidl)
485 {
486 TRACE("\n");
487 *ppidl = ILClone(pidl);
488 return S_OK;
489 }
490
491 /*************************************************************************
492 * RecycleBin IShellFolder2 interface
493 */
494
495 HRESULT WINAPI CRecycleBin::ParseDisplayName(HWND hwnd, LPBC pbc,
496 LPOLESTR pszDisplayName, ULONG *pchEaten, LPITEMIDLIST *ppidl,
497 ULONG *pdwAttributes)
498 {
499 FIXME("stub\n");
500 return E_NOTIMPL;
501 }
502
503
504 PDELETED_FILE_DETAILS_W
505 UnpackDetailsFromPidl(LPCITEMIDLIST pidl)
506 {
507 return (PDELETED_FILE_DETAILS_W)&pidl->mkid.abID;
508 }
509
510 HRESULT WINAPI CRecycleBin::EnumObjects(HWND hwndOwner, DWORD dwFlags, LPENUMIDLIST *ppEnumIDList)
511 {
512 CComObject<CRecycleBinEnum> *theEnumerator;
513 CComPtr<IEnumIDList> result;
514 HRESULT hResult;
515
516 TRACE ("(%p)->(HWND=%p flags=0x%08x pplist=%p)\n", this, hwndOwner, dwFlags, ppEnumIDList);
517
518 if (ppEnumIDList == NULL)
519 return E_POINTER;
520 *ppEnumIDList = NULL;
521 ATLTRY (theEnumerator = new CComObject<CRecycleBinEnum>);
522 if (theEnumerator == NULL)
523 return E_OUTOFMEMORY;
524 hResult = theEnumerator->QueryInterface(IID_PPV_ARG(IEnumIDList, &result));
525 if (FAILED (hResult))
526 {
527 delete theEnumerator;
528 return hResult;
529 }
530 hResult = theEnumerator->Initialize(dwFlags);
531 if (FAILED (hResult))
532 return hResult;
533 *ppEnumIDList = result.Detach();
534
535 TRACE ("-- (%p)->(new ID List: %p)\n", this, *ppEnumIDList);
536
537 return S_OK;
538 }
539
540 HRESULT WINAPI CRecycleBin::BindToObject(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv)
541 {
542 FIXME("(%p, %p, %p, %s, %p) - stub\n", this, pidl, pbc, debugstr_guid(&riid), ppv);
543 return E_NOTIMPL;
544 }
545
546 HRESULT WINAPI CRecycleBin::BindToStorage(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv)
547 {
548 FIXME("(%p, %p, %p, %s, %p) - stub\n", this, pidl, pbc, debugstr_guid(&riid), ppv);
549 return E_NOTIMPL;
550 }
551
552 HRESULT WINAPI CRecycleBin::CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
553 {
554 /* TODO */
555 TRACE("(%p, %p, %p, %p)\n", this, (void *)lParam, pidl1, pidl2);
556 if (pidl1->mkid.cb != pidl2->mkid.cb)
557 return MAKE_HRESULT(SEVERITY_SUCCESS, 0, pidl1->mkid.cb - pidl2->mkid.cb);
558 return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (unsigned short)memcmp(pidl1->mkid.abID, pidl2->mkid.abID, pidl1->mkid.cb));
559 }
560
561 HRESULT WINAPI CRecycleBin::CreateViewObject(HWND hwndOwner, REFIID riid, void **ppv)
562 {
563 LPSHELLVIEW pShellView;
564 HRESULT hr = E_NOINTERFACE;
565
566 TRACE("(%p, %p, %s, %p)\n", this, hwndOwner, debugstr_guid(&riid), ppv);
567
568 if (!ppv)
569 return hr;
570
571 *ppv = NULL;
572
573 if (IsEqualIID (riid, IID_IDropTarget))
574 {
575 hr = this->QueryInterface (IID_IDropTarget, ppv);
576 }
577 else if (IsEqualIID (riid, IID_IContextMenu) || IsEqualIID (riid, IID_IContextMenu2))
578 {
579 hr = this->QueryInterface(riid, ppv);
580 }
581 else if (IsEqualIID (riid, IID_IShellView))
582 {
583 hr = IShellView_Constructor ((IShellFolder *)this, &pShellView);
584 if (pShellView)
585 {
586 hr = pShellView->QueryInterface(riid, ppv);
587 pShellView->Release();
588 }
589 }
590 else
591 return hr;
592 TRACE ("-- (%p)->(interface=%p)\n", this, ppv);
593 return hr;
594
595 }
596
597 HRESULT WINAPI CRecycleBin::GetAttributesOf(UINT cidl, LPCITEMIDLIST *apidl,
598 SFGAOF *rgfInOut)
599 {
600 TRACE("(%p, %d, {%p, ...}, {%x})\n", this, cidl, apidl ? apidl[0] : NULL, (unsigned int)*rgfInOut);
601 *rgfInOut &= SFGAO_FOLDER|SFGAO_DROPTARGET|SFGAO_HASPROPSHEET|SFGAO_CANLINK;
602 return S_OK;
603 }
604
605 HRESULT WINAPI CRecycleBin::GetUIObjectOf(HWND hwndOwner, UINT cidl, LPCITEMIDLIST *apidl,
606 REFIID riid, UINT *prgfInOut, void **ppv)
607 {
608 IUnknown *pObj = NULL;
609 HRESULT hr = E_INVALIDARG;
610
611 TRACE ("(%p)->(%p,%u,apidl=%p, %p %p)\n", this,
612 hwndOwner, cidl, apidl, prgfInOut, ppv);
613
614 if (!ppv)
615 return hr;
616
617 *ppv = NULL;
618
619 if ((IsEqualIID (riid, IID_IContextMenu) || IsEqualIID(riid, IID_IContextMenu2)) && (cidl >= 1))
620 {
621 hr = CRecycleBinItemContextMenuConstructor(riid, apidl[0], (void **)&pObj);
622 }
623 else if (IsEqualIID (riid, IID_IDropTarget) && (cidl == 1))
624 {
625 IDropTarget * pDt = NULL;
626 hr = this->QueryInterface(IID_PPV_ARG(IDropTarget, &pDt));
627 pObj = pDt;
628 }
629 else
630 hr = E_NOINTERFACE;
631
632 if (SUCCEEDED(hr) && !pObj)
633 hr = E_OUTOFMEMORY;
634
635 *ppv = pObj;
636 TRACE ("(%p)->hr=0x%08x\n", this, hr);
637 return hr;
638 }
639
640 HRESULT WINAPI CRecycleBin::GetDisplayNameOf(LPCITEMIDLIST pidl, SHGDNF uFlags, STRRET *pName)
641 {
642 PIDLRecycleStruct *pFileDetails;
643 LPWSTR pFileName;
644
645 TRACE("(%p, %p, %x, %p)\n", this, pidl, (unsigned int)uFlags, pName);
646
647
648 if (_ILIsBitBucket (pidl))
649 {
650 WCHAR pszPath[100];
651
652 if (HCR_GetClassNameW(CLSID_RecycleBin, pszPath, MAX_PATH))
653 {
654 pName->uType = STRRET_WSTR;
655 pName->pOleStr = StrDupW(pszPath);
656 return S_OK;
657 }
658 }
659
660 pFileDetails = _ILGetRecycleStruct(pidl);
661 if (!pFileDetails)
662 {
663 pName->cStr[0] = 0;
664 pName->uType = STRRET_CSTR;
665 return E_INVALIDARG;
666 }
667
668 pFileName = wcsrchr(pFileDetails->szName, L'\\');
669 if (!pFileName)
670 {
671 pName->cStr[0] = 0;
672 pName->uType = STRRET_CSTR;
673 return E_UNEXPECTED;
674 }
675
676 pName->pOleStr = StrDupW(pFileName + 1);
677 if (pName->pOleStr == NULL)
678 return E_OUTOFMEMORY;
679
680 pName->uType = STRRET_WSTR;
681 return S_OK;
682 }
683
684 HRESULT WINAPI CRecycleBin::SetNameOf(HWND hwnd, LPCITEMIDLIST pidl, LPCOLESTR pszName,
685 SHGDNF uFlags, LPITEMIDLIST *ppidlOut)
686 {
687 TRACE("\n");
688 return E_FAIL; /* not supported */
689 }
690
691 HRESULT WINAPI CRecycleBin::GetDefaultSearchGUID(GUID *pguid)
692 {
693 FIXME("stub\n");
694 return E_NOTIMPL;
695 }
696
697 HRESULT WINAPI CRecycleBin::EnumSearches(IEnumExtraSearch **ppEnum)
698 {
699 FIXME("stub\n");
700 *ppEnum = NULL;
701 return E_NOTIMPL;
702 }
703
704 HRESULT WINAPI CRecycleBin::GetDefaultColumn(DWORD dwReserved, ULONG *pSort, ULONG *pDisplay)
705 {
706 TRACE("(%p, %x, %p, %p)\n", this, (unsigned int)dwReserved, pSort, pDisplay);
707 *pSort = 0;
708 *pDisplay = 0;
709 return S_OK;
710 }
711
712 HRESULT WINAPI CRecycleBin::GetDefaultColumnState(UINT iColumn, SHCOLSTATEF *pcsFlags)
713 {
714 TRACE("(%p, %d, %p)\n", this, iColumn, pcsFlags);
715 if (iColumn >= COLUMNS_COUNT)
716 return E_INVALIDARG;
717 *pcsFlags = RecycleBinColumns[iColumn].pcsFlags;
718 return S_OK;
719 }
720
721 HRESULT WINAPI CRecycleBin::GetDetailsEx(LPCITEMIDLIST pidl, const SHCOLUMNID *pscid, VARIANT *pv)
722 {
723 FIXME("stub\n");
724 return E_NOTIMPL;
725 }
726
727 static HRESULT FormatDateTime(LPWSTR buffer, int size, FILETIME * ft)
728 {
729 FILETIME lft;
730 SYSTEMTIME time;
731 int ret;
732
733 FileTimeToLocalFileTime(ft, &lft);
734 FileTimeToSystemTime(&lft, &time);
735
736 ret = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &time, NULL, buffer, size);
737 if (ret > 0 && ret < size)
738 {
739 /* Append space + time without seconds */
740 buffer[ret-1] = ' ';
741 GetTimeFormatW(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &time, NULL, &buffer[ret], size - ret);
742 }
743
744 return (ret != 0 ? E_FAIL : S_OK);
745 }
746
747 HRESULT WINAPI CRecycleBin::GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, LPSHELLDETAILS pDetails)
748 {
749 PIDLRecycleStruct * pFileDetails;
750 WCHAR buffer[MAX_PATH];
751 WCHAR szTypeName[100];
752 LPWSTR pszBackslash;
753 UINT Length;
754
755 TRACE("(%p, %p, %d, %p)\n", this, pidl, iColumn, pDetails);
756 if (iColumn >= COLUMNS_COUNT)
757 return E_FAIL;
758 pDetails->fmt = RecycleBinColumns[iColumn].fmt;
759 pDetails->cxChar = RecycleBinColumns[iColumn].cxChars;
760 if (pidl == NULL)
761 {
762 pDetails->str.uType = STRRET_WSTR;
763 LoadStringW(shell32_hInstance, RecycleBinColumns[iColumn].column_name_id, buffer, MAX_PATH);
764 return SHStrDupW(buffer, &pDetails->str.pOleStr);
765 }
766
767 if (iColumn == COLUMN_NAME)
768 return GetDisplayNameOf(pidl, SHGDN_NORMAL, &pDetails->str);
769
770 pFileDetails = _ILGetRecycleStruct(pidl);
771 switch (iColumn)
772 {
773 case COLUMN_DATEDEL:
774 FormatDateTime(buffer, MAX_PATH, &pFileDetails->DeletionTime);
775 break;
776 case COLUMN_DELFROM:
777 pszBackslash = wcsrchr(pFileDetails->szName, L'\\');
778 Length = (pszBackslash - pFileDetails->szName);
779 memcpy((LPVOID)buffer, pFileDetails->szName, Length * sizeof(WCHAR));
780 buffer[Length] = L'\0';
781 break;
782 case COLUMN_SIZE:
783 StrFormatKBSizeW(pFileDetails->FileSize.QuadPart, buffer, MAX_PATH);
784 break;
785 case COLUMN_MTIME:
786 FormatDateTime(buffer, MAX_PATH, &pFileDetails->LastModification);
787 break;
788 case COLUMN_TYPE:
789 szTypeName[0] = L'\0';
790 wcscpy(buffer, PathFindExtensionW(pFileDetails->szName));
791 if (!( HCR_MapTypeToValueW(buffer, buffer, sizeof(buffer) / sizeof(WCHAR), TRUE) &&
792 HCR_MapTypeToValueW(buffer, szTypeName, sizeof(szTypeName) / sizeof(WCHAR), FALSE )))
793 {
794 wcscpy (szTypeName, PathFindExtensionW(pFileDetails->szName));
795 wcscat(szTypeName, L"-");
796 Length = wcslen(szTypeName);
797 if (LoadStringW(shell32_hInstance, IDS_SHV_COLUMN1, &szTypeName[Length], (sizeof(szTypeName) / sizeof(WCHAR)) - Length))
798 szTypeName[(sizeof(szTypeName)/sizeof(WCHAR))-1] = L'\0';
799 }
800 pDetails->str.uType = STRRET_WSTR;
801 return SHStrDupW(szTypeName, &pDetails->str.pOleStr);
802 break;
803 default:
804 return E_FAIL;
805 }
806
807 pDetails->str.uType = STRRET_WSTR;
808 return SHStrDupW(buffer, &pDetails->str.pOleStr);
809 }
810
811 HRESULT WINAPI CRecycleBin::MapColumnToSCID(UINT iColumn, SHCOLUMNID *pscid)
812 {
813 TRACE("(%p, %d, %p)\n", this, iColumn, pscid);
814 if (iColumn >= COLUMNS_COUNT)
815 return E_INVALIDARG;
816 pscid->fmtid = *RecycleBinColumns[iColumn].fmtId;
817 pscid->pid = RecycleBinColumns[iColumn].pid;
818 return S_OK;
819 }
820
821 /*************************************************************************
822 * RecycleBin IContextMenu interface
823 */
824
825 HRESULT WINAPI CRecycleBin::QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
826 {
827 WCHAR szBuffer[100];
828 MENUITEMINFOW mii;
829 int id = 1;
830
831 TRACE("QueryContextMenu %p %p %u %u %u %u\n", this, hMenu, indexMenu, idCmdFirst, idCmdLast, uFlags );
832
833 if (!hMenu)
834 return E_INVALIDARG;
835
836 memset(&mii, 0, sizeof(mii));
837 mii.cbSize = sizeof(mii);
838 mii.fMask = MIIM_TYPE | MIIM_ID | MIIM_STATE;
839 mii.fState = MFS_ENABLED;
840 szBuffer[0] = L'\0';
841 LoadStringW(shell32_hInstance, IDS_EMPTY_BITBUCKET, szBuffer, sizeof(szBuffer) / sizeof(WCHAR));
842 mii.dwTypeData = szBuffer;
843 mii.cch = wcslen(mii.dwTypeData);
844 mii.wID = idCmdFirst + id++;
845 mii.fType = MFT_STRING;
846 iIdEmpty = 1;
847
848 if (!InsertMenuItemW(hMenu, indexMenu, TRUE, &mii))
849 return E_FAIL;
850
851 return MAKE_HRESULT(SEVERITY_SUCCESS, 0, id);
852 }
853
854 HRESULT WINAPI CRecycleBin::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi)
855 {
856 HRESULT hr;
857 LPSHELLBROWSER lpSB;
858 LPSHELLVIEW lpSV = NULL;
859
860 TRACE("%p %p verb %p\n", this, lpcmi, lpcmi->lpVerb);
861
862 if (LOWORD(lpcmi->lpVerb) == iIdEmpty)
863 {
864 // FIXME
865 // path & flags
866 hr = SHEmptyRecycleBinW(lpcmi->hwnd, L"C:\\", 0);
867 TRACE("result %x\n", hr);
868 if (hr != S_OK)
869 return hr;
870
871 lpSB = (LPSHELLBROWSER)SendMessageA(lpcmi->hwnd, CWM_GETISHELLBROWSER, 0, 0);
872 if (lpSB && SUCCEEDED(lpSB->QueryActiveShellView(&lpSV)))
873 lpSV->Refresh();
874 }
875 return S_OK;
876 }
877
878 HRESULT WINAPI CRecycleBin::GetCommandString(UINT_PTR idCommand, UINT uFlags, UINT *lpReserved, LPSTR lpszName, UINT uMaxNameLen)
879 {
880 FIXME("%p %lu %u %p %p %u\n", this, idCommand, uFlags, lpReserved, lpszName, uMaxNameLen);
881
882 return E_NOTIMPL;
883 }
884
885 /*************************************************************************
886 * RecycleBin IShellPropSheetExt interface
887 */
888
889 HRESULT WINAPI CRecycleBin::AddPages(LPFNSVADDPROPSHEETPAGE pfnAddPage, LPARAM lParam)
890 {
891 FIXME("%p %p %lu\n", this, pfnAddPage, lParam);
892
893 return E_NOTIMPL;
894 }
895
896 HRESULT WINAPI CRecycleBin::ReplacePage(EXPPS uPageID, LPFNSVADDPROPSHEETPAGE pfnReplaceWith, LPARAM lParam)
897 {
898 FIXME("%p %lu %p %lu\n", this, uPageID, pfnReplaceWith, lParam);
899
900 return E_NOTIMPL;
901 }
902
903 /*************************************************************************
904 * RecycleBin IShellExtInit interface
905 */
906
907 HRESULT WINAPI CRecycleBin::Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pdtobj, HKEY hkeyProgID)
908 {
909 TRACE("%p %p %p %p\n", this, pidlFolder, pdtobj, hkeyProgID );
910 return S_OK;
911 }
912
913 void toggleNukeOnDeleteOption(HWND hwndDlg, BOOL bEnable)
914 {
915 if (bEnable)
916 {
917 SendDlgItemMessage(hwndDlg, 14001, BM_SETCHECK, BST_UNCHECKED, 0);
918 EnableWindow(GetDlgItem(hwndDlg, 14002), FALSE);
919 SendDlgItemMessage(hwndDlg, 14003, BM_SETCHECK, BST_CHECKED, 0);
920 }
921 else
922 {
923 SendDlgItemMessage(hwndDlg, 14001, BM_SETCHECK, BST_CHECKED, 0);
924 EnableWindow(GetDlgItem(hwndDlg, 14002), TRUE);
925 SendDlgItemMessage(hwndDlg, 14003, BM_SETCHECK, BST_UNCHECKED, 0);
926 }
927 }
928
929
930 static VOID
931 InitializeRecycleBinDlg(HWND hwndDlg, WCHAR DefaultDrive)
932 {
933 WCHAR CurDrive = L'A';
934 WCHAR szDrive[] = L"A:\\";
935 DWORD dwDrives;
936 WCHAR szName[100];
937 WCHAR szVolume[100];
938 DWORD MaxComponent, Flags;
939 DWORD dwSerial;
940 LVCOLUMNW lc;
941 HWND hDlgCtrl;
942 LVITEMW li;
943 INT itemCount;
944 ULARGE_INTEGER TotalNumberOfFreeBytes, TotalNumberOfBytes, FreeBytesAvailable;
945 RECT rect;
946 int columnSize;
947 int defIndex = 0;
948 DWORD dwSize;
949 PDRIVE_ITEM_CONTEXT pItem = NULL, pDefault = NULL, pFirst = NULL;
950
951 hDlgCtrl = GetDlgItem(hwndDlg, 14000);
952
953 if (!LoadStringW(shell32_hInstance, IDS_RECYCLEBIN_LOCATION, szVolume, sizeof(szVolume) / sizeof(WCHAR)))
954 szVolume[0] = 0;
955
956 GetClientRect(hDlgCtrl, &rect);
957
958 memset(&lc, 0, sizeof(LV_COLUMN) );
959 lc.mask = LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM | LVCF_FMT;
960
961 columnSize = 140; //FIXME
962 lc.iSubItem = 0;
963 lc.fmt = LVCFMT_FIXED_WIDTH;
964 lc.cx = columnSize;
965 lc.cchTextMax = wcslen(szVolume);
966 lc.pszText = szVolume;
967 (void)SendMessageW(hDlgCtrl, LVM_INSERTCOLUMNW, 0, (LPARAM)&lc);
968
969 if (!LoadStringW(shell32_hInstance, IDS_RECYCLEBIN_DISKSPACE, szVolume, sizeof(szVolume) / sizeof(WCHAR)))
970 szVolume[0] = 0;
971
972 lc.iSubItem = 1;
973 lc.cx = rect.right - rect.left - columnSize;
974 lc.cchTextMax = wcslen(szVolume);
975 lc.pszText = szVolume;
976 (void)SendMessageW(hDlgCtrl, LVM_INSERTCOLUMNW, 1, (LPARAM)&lc);
977
978 dwDrives = GetLogicalDrives();
979 itemCount = 0;
980 do
981 {
982 if ((dwDrives & 0x1))
983 {
984 UINT Type = GetDriveTypeW(szDrive);
985 if (Type == DRIVE_FIXED) //FIXME
986 {
987 if (!GetVolumeInformationW(szDrive, szName, sizeof(szName) / sizeof(WCHAR), &dwSerial, &MaxComponent, &Flags, NULL, 0))
988 {
989 szName[0] = 0;
990 dwSerial = -1;
991 }
992
993 swprintf(szVolume, L"%s (%c)", szName, szDrive[0]);
994 memset(&li, 0x0, sizeof(LVITEMW));
995 li.mask = LVIF_TEXT | LVIF_PARAM;
996 li.iSubItem = 0;
997 li.pszText = szVolume;
998 li.iItem = itemCount;
999 SendMessageW(hDlgCtrl, LVM_INSERTITEMW, 0, (LPARAM)&li);
1000 if (GetDiskFreeSpaceExW(szDrive, &FreeBytesAvailable , &TotalNumberOfBytes, &TotalNumberOfFreeBytes))
1001 {
1002 if (StrFormatByteSizeW(TotalNumberOfFreeBytes.QuadPart, szVolume, sizeof(szVolume) / sizeof(WCHAR)))
1003 {
1004
1005 pItem = (DRIVE_ITEM_CONTEXT *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(DRIVE_ITEM_CONTEXT));
1006 if (pItem)
1007 {
1008 swprintf(szName, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Bitbucket\\Volume\\%04X-%04X", LOWORD(dwSerial), HIWORD(dwSerial));
1009 dwSize = sizeof(DWORD);
1010 RegGetValueW(HKEY_CURRENT_USER, szName, L"MaxCapacity", RRF_RT_DWORD, NULL, &pItem->dwMaxCapacity, &dwSize);
1011 dwSize = sizeof(DWORD);
1012 RegGetValueW(HKEY_CURRENT_USER, szName, L"NukeOnDelete", RRF_RT_DWORD, NULL, &pItem->dwNukeOnDelete, &dwSize);
1013 pItem->dwSerial = dwSerial;
1014 li.mask = LVIF_PARAM;
1015 li.lParam = (LPARAM)pItem;
1016 (void)SendMessageW(hDlgCtrl, LVM_SETITEMW, 0, (LPARAM)&li);
1017 if (CurDrive == DefaultDrive)
1018 {
1019 defIndex = itemCount;
1020 pDefault = pItem;
1021 }
1022 }
1023 if (!pFirst)
1024 pFirst = pItem;
1025
1026 li.mask = LVIF_TEXT;
1027 li.iSubItem = 1;
1028 li.pszText = szVolume;
1029 li.iItem = itemCount;
1030 (void)SendMessageW(hDlgCtrl, LVM_SETITEMW, 0, (LPARAM)&li);
1031 }
1032 }
1033 itemCount++;
1034 }
1035 }
1036 CurDrive++;
1037 szDrive[0] = CurDrive;
1038 dwDrives = (dwDrives >> 1);
1039 } while(dwDrives);
1040
1041 if (!pDefault)
1042 pDefault = pFirst;
1043 if (pDefault)
1044 {
1045 toggleNukeOnDeleteOption(hwndDlg, pDefault->dwNukeOnDelete);
1046 SetDlgItemInt(hwndDlg, 14002, pDefault->dwMaxCapacity, FALSE);
1047 }
1048 ZeroMemory(&li, sizeof(li));
1049 li.mask = LVIF_STATE;
1050 li.stateMask = (UINT) - 1;
1051 li.state = LVIS_FOCUSED | LVIS_SELECTED;
1052 li.iItem = defIndex;
1053 (void)SendMessageW(hDlgCtrl, LVM_SETITEMW, 0, (LPARAM)&li);
1054
1055 }
1056
1057 static BOOL StoreDriveSettings(HWND hwndDlg)
1058 {
1059 int iCount, iIndex;
1060 HWND hDlgCtrl = GetDlgItem(hwndDlg, 14000);
1061 LVITEMW li;
1062 PDRIVE_ITEM_CONTEXT pItem;
1063 HKEY hKey, hSubKey;
1064 WCHAR szSerial[20];
1065 DWORD dwSize;
1066
1067
1068 if (RegCreateKeyExW(HKEY_CURRENT_USER, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Bitbucket\\Volume", 0, NULL, 0, KEY_WRITE, NULL, &hKey, NULL) != ERROR_SUCCESS)
1069 return FALSE;
1070
1071 iCount = ListView_GetItemCount(hDlgCtrl);
1072
1073 ZeroMemory(&li, sizeof(li));
1074 li.mask = LVIF_PARAM;
1075
1076 for(iIndex = 0; iIndex < iCount; iIndex++)
1077 {
1078 li.iItem = iIndex;
1079 if (SendMessageW(hDlgCtrl, LVM_GETITEMW, 0, (LPARAM)&li))
1080 {
1081 pItem = (PDRIVE_ITEM_CONTEXT)li.lParam;
1082 swprintf(szSerial, L"%04X-%04X", LOWORD(pItem->dwSerial), HIWORD(pItem->dwSerial));
1083 if (RegCreateKeyExW(hKey, szSerial, 0, NULL, 0, KEY_WRITE, NULL, &hSubKey, NULL) == ERROR_SUCCESS)
1084 {
1085 dwSize = sizeof(DWORD);
1086 RegSetValueExW(hSubKey, L"NukeOnDelete", 0, REG_DWORD, (LPBYTE)&pItem->dwNukeOnDelete, dwSize);
1087 dwSize = sizeof(DWORD);
1088 RegSetValueExW(hSubKey, L"MaxCapacity", 0, REG_DWORD, (LPBYTE)&pItem->dwMaxCapacity, dwSize);
1089 RegCloseKey(hSubKey);
1090 }
1091 }
1092 }
1093 RegCloseKey(hKey);
1094 return TRUE;
1095
1096 }
1097
1098 static VOID FreeDriveItemContext(HWND hwndDlg)
1099 {
1100 int iCount, iIndex;
1101 HWND hDlgCtrl = GetDlgItem(hwndDlg, 14000);
1102 LVITEMW li;
1103
1104 iCount = ListView_GetItemCount(hDlgCtrl);
1105
1106 ZeroMemory(&li, sizeof(li));
1107 li.mask = LVIF_PARAM;
1108
1109 for(iIndex = 0; iIndex < iCount; iIndex++)
1110 {
1111 li.iItem = iIndex;
1112 if (SendMessageW(hDlgCtrl, LVM_GETITEMW, 0, (LPARAM)&li))
1113 {
1114 HeapFree(GetProcessHeap(), 0, (LPVOID)li.lParam);
1115 }
1116 }
1117 }
1118
1119 static INT
1120 GetDefaultItem(HWND hwndDlg, LVITEMW * li)
1121 {
1122 HWND hDlgCtrl;
1123 UINT iItemCount, iIndex;
1124
1125 hDlgCtrl = GetDlgItem(hwndDlg, 14000);
1126 if (!hDlgCtrl)
1127 return -1;
1128
1129 iItemCount = ListView_GetItemCount(hDlgCtrl);
1130 if (!iItemCount)
1131 return -1;
1132
1133 ZeroMemory(li, sizeof(LVITEMW));
1134 li->mask = LVIF_PARAM | LVIF_STATE;
1135 li->stateMask = (UINT) - 1;
1136 for (iIndex = 0; iIndex < iItemCount; iIndex++)
1137 {
1138 li->iItem = iIndex;
1139 if (SendMessageW(hDlgCtrl, LVM_GETITEMW, 0, (LPARAM)li))
1140 {
1141 if (li->state & LVIS_SELECTED)
1142 return iIndex;
1143 }
1144 }
1145 return -1;
1146
1147 }
1148
1149 static INT_PTR CALLBACK
1150 RecycleBinDlg(
1151 HWND hwndDlg,
1152 UINT uMsg,
1153 WPARAM wParam,
1154 LPARAM lParam
1155 )
1156 {
1157 LPPSHNOTIFY lppsn;
1158 LPNMLISTVIEW lppl;
1159 LVITEMW li;
1160 PDRIVE_ITEM_CONTEXT pItem;
1161 BOOL bSuccess;
1162 UINT uResult;
1163 PROPSHEETPAGE * page;
1164 DWORD dwStyle;
1165
1166 switch(uMsg)
1167 {
1168 case WM_INITDIALOG:
1169 page = (PROPSHEETPAGE*)lParam;
1170 InitializeRecycleBinDlg(hwndDlg, (WCHAR)page->lParam);
1171 dwStyle = (DWORD) SendDlgItemMessage(hwndDlg, 14000, LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
1172 dwStyle = dwStyle | LVS_EX_FULLROWSELECT;
1173 SendDlgItemMessage(hwndDlg, 14000, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, dwStyle);
1174 if (GetDlgCtrlID((HWND)wParam) != 14000)
1175 {
1176 SetFocus(GetDlgItem(hwndDlg, 14000));
1177 return FALSE;
1178 }
1179 return TRUE;
1180 case WM_COMMAND:
1181 switch(LOWORD(wParam))
1182 {
1183 case 14001:
1184 toggleNukeOnDeleteOption(hwndDlg, FALSE);
1185 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
1186 break;
1187 case 14003:
1188 toggleNukeOnDeleteOption(hwndDlg, TRUE);
1189 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
1190 break;
1191 case 14004:
1192 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
1193 break;
1194 }
1195 break;
1196 case WM_NOTIFY:
1197 lppsn = (LPPSHNOTIFY) lParam;
1198 lppl = (LPNMLISTVIEW) lParam;
1199 if (lppsn->hdr.code == PSN_APPLY)
1200 {
1201 if (GetDefaultItem(hwndDlg, &li) > -1)
1202 {
1203 pItem = (PDRIVE_ITEM_CONTEXT)li.lParam;
1204 if (pItem)
1205 {
1206 uResult = GetDlgItemInt(hwndDlg, 14002, &bSuccess, FALSE);
1207 if (bSuccess)
1208 pItem->dwMaxCapacity = uResult;
1209 if (SendDlgItemMessageW(hwndDlg, 14003, BM_GETCHECK, 0, 0) == BST_CHECKED)
1210 pItem->dwNukeOnDelete = TRUE;
1211 else
1212 pItem->dwNukeOnDelete = FALSE;
1213 }
1214 }
1215 if (StoreDriveSettings(hwndDlg))
1216 {
1217 SetWindowLongPtr( hwndDlg, DWL_MSGRESULT, PSNRET_NOERROR );
1218 return TRUE;
1219 }
1220 }
1221 else if (lppl->hdr.code == LVN_ITEMCHANGING)
1222 {
1223 ZeroMemory(&li, sizeof(li));
1224 li.mask = LVIF_PARAM;
1225 li.iItem = lppl->iItem;
1226 if (!SendMessageW(lppl->hdr.hwndFrom, LVM_GETITEMW, 0, (LPARAM)&li))
1227 return TRUE;
1228
1229 pItem = (PDRIVE_ITEM_CONTEXT)li.lParam;
1230 if (!pItem)
1231 return TRUE;
1232
1233 if (!(lppl->uOldState & LVIS_FOCUSED) && (lppl->uNewState & LVIS_FOCUSED))
1234 {
1235 /* new focused item */
1236 toggleNukeOnDeleteOption(lppl->hdr.hwndFrom, pItem->dwNukeOnDelete);
1237 SetDlgItemInt(hwndDlg, 14002, pItem->dwMaxCapacity, FALSE);
1238 }
1239 else if ((lppl->uOldState & LVIS_FOCUSED) && !(lppl->uNewState & LVIS_FOCUSED))
1240 {
1241 /* kill focus */
1242 uResult = GetDlgItemInt(hwndDlg, 14002, &bSuccess, FALSE);
1243 if (bSuccess)
1244 pItem->dwMaxCapacity = uResult;
1245 if (SendDlgItemMessageW(hwndDlg, 14003, BM_GETCHECK, 0, 0) == BST_CHECKED)
1246 pItem->dwNukeOnDelete = TRUE;
1247 else
1248 pItem->dwNukeOnDelete = FALSE;
1249 }
1250 return TRUE;
1251
1252 }
1253 break;
1254 case WM_DESTROY:
1255 FreeDriveItemContext(hwndDlg);
1256 break;
1257 }
1258 return FALSE;
1259 }
1260
1261 BOOL SH_ShowRecycleBinProperties(WCHAR sDrive)
1262 {
1263 HPROPSHEETPAGE hpsp[1];
1264 PROPSHEETHEADERW psh;
1265 HPROPSHEETPAGE hprop;
1266
1267 BOOL ret;
1268
1269
1270 ZeroMemory(&psh, sizeof(PROPSHEETHEADERW));
1271 psh.dwSize = sizeof(PROPSHEETHEADERW);
1272 psh.dwFlags = PSP_DEFAULT | PSH_PROPTITLE;
1273 psh.pszCaption = MAKEINTRESOURCEW(IDS_RECYCLEBIN_FOLDER_NAME);
1274 psh.hwndParent = NULL;
1275 psh.phpage = hpsp;
1276 psh.hInstance = shell32_hInstance;
1277
1278 hprop = SH_CreatePropertySheetPage(IDD_RECYCLE_BIN_PROPERTIES, RecycleBinDlg, (LPARAM)sDrive, NULL);
1279 if (!hprop)
1280 {
1281 ERR("Failed to create property sheet\n");
1282 return FALSE;
1283 }
1284 hpsp[psh.nPages] = hprop;
1285 psh.nPages++;
1286
1287
1288 ret = PropertySheetW(&psh);
1289 if (ret < 0)
1290 return FALSE;
1291 else
1292 return TRUE;
1293 }
1294
1295 BOOL
1296 TRASH_CanTrashFile(LPCWSTR wszPath)
1297 {
1298 LONG ret;
1299 DWORD dwNukeOnDelete, dwType, VolSerialNumber, MaxComponentLength;
1300 DWORD FileSystemFlags, dwSize, dwDisposition;
1301 HKEY hKey;
1302 WCHAR szBuffer[10];
1303 WCHAR szKey[150] = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Bitbucket\\Volume\\";
1304
1305 if (wszPath[1] != L':')
1306 {
1307 /* path is UNC */
1308 return FALSE;
1309 }
1310
1311 if (GetDriveTypeW(wszPath) != DRIVE_FIXED)
1312 {
1313 /* no bitbucket on removable media */
1314 return FALSE;
1315 }
1316
1317 if (!GetVolumeInformationW(wszPath, NULL, 0, &VolSerialNumber, &MaxComponentLength, &FileSystemFlags, NULL, 0))
1318 {
1319 ERR("GetVolumeInformationW failed with %u\n", GetLastError());
1320 return FALSE;
1321 }
1322
1323 swprintf(szBuffer, L"%04X-%04X", LOWORD(VolSerialNumber), HIWORD(VolSerialNumber));
1324 wcscat(szKey, szBuffer);
1325
1326 if (RegCreateKeyExW(HKEY_CURRENT_USER, szKey, 0, NULL, 0, KEY_WRITE, NULL, &hKey, &dwDisposition) != ERROR_SUCCESS)
1327 {
1328 ERR("RegCreateKeyExW failed\n");
1329 return FALSE;
1330 }
1331
1332 if (dwDisposition & REG_CREATED_NEW_KEY)
1333 {
1334 /* per default move to bitbucket */
1335 dwNukeOnDelete = 0;
1336 RegSetValueExW(hKey, L"NukeOnDelete", 0, REG_DWORD, (LPBYTE)&dwNukeOnDelete, sizeof(DWORD));
1337 /* per default unlimited size */
1338 dwSize = -1;
1339 RegSetValueExW(hKey, L"MaxCapacity", 0, REG_DWORD, (LPBYTE)&dwSize, sizeof(DWORD));
1340 RegCloseKey(hKey);
1341 return TRUE;
1342 }
1343 else
1344 {
1345 dwSize = sizeof(dwNukeOnDelete);
1346 ret = RegQueryValueExW(hKey, L"NukeOnDelete", NULL, &dwType, (LPBYTE)&dwNukeOnDelete, &dwSize);
1347 if (ret != ERROR_SUCCESS)
1348 {
1349 if (ret == ERROR_FILE_NOT_FOUND)
1350 {
1351 /* restore key and enable bitbucket */
1352 dwNukeOnDelete = 0;
1353 RegSetValueExW(hKey, L"NukeOnDelete", 0, REG_DWORD, (LPBYTE)&dwNukeOnDelete, sizeof(DWORD));
1354 }
1355 RegCloseKey(hKey);
1356 return TRUE;
1357 }
1358 else if (dwNukeOnDelete)
1359 {
1360 /* do not delete to bitbucket */
1361 RegCloseKey(hKey);
1362 return FALSE;
1363 }
1364 /* FIXME
1365 * check if bitbucket is full
1366 */
1367 RegCloseKey(hKey);
1368 return TRUE;
1369 }
1370 }
1371
1372 BOOL
1373 TRASH_TrashFile(LPCWSTR wszPath)
1374 {
1375 TRACE("(%s)\n", debugstr_w(wszPath));
1376 return DeleteFileToRecycleBin(wszPath);
1377 }
1378
1379 /*************************************************************************
1380 * SHUpdateCRecycleBinIcon [SHELL32.@]
1381 *
1382 * Undocumented
1383 */
1384 EXTERN_C HRESULT WINAPI SHUpdateRecycleBinIcon(void)
1385 {
1386 FIXME("stub\n");
1387
1388
1389
1390 return S_OK;
1391 }
1392
1393 /****************************************************************************
1394 * IDropTarget implementation
1395 */
1396 BOOL CRecycleBin::QueryDrop(DWORD dwKeyState, LPDWORD pdwEffect)
1397 {
1398 /* TODO on shift we should delete, we should update the cursor manager to show this. */
1399
1400 DWORD dwEffect = DROPEFFECT_COPY;
1401
1402 *pdwEffect = DROPEFFECT_NONE;
1403
1404 if (fAcceptFmt) { /* Does our interpretation of the keystate ... */
1405 *pdwEffect = KeyStateToDropEffect (dwKeyState);
1406
1407 if (*pdwEffect == DROPEFFECT_NONE)
1408 *pdwEffect = dwEffect;
1409
1410 /* ... matches the desired effect ? */
1411 if (dwEffect & *pdwEffect) {
1412 return TRUE;
1413 }
1414 }
1415 return FALSE;
1416 }
1417
1418 HRESULT WINAPI CRecycleBin::DragEnter(IDataObject *pDataObject,
1419 DWORD dwKeyState, POINTL pt, DWORD *pdwEffect)
1420 {
1421 TRACE("Recycle bin drag over (%p)\n", this);
1422 /* The recycle bin accepts pretty much everything, and sets a CSIDL flag. */
1423 fAcceptFmt = TRUE;
1424
1425 QueryDrop(dwKeyState, pdwEffect);
1426 return S_OK;
1427 }
1428
1429 HRESULT WINAPI CRecycleBin::DragOver(DWORD dwKeyState, POINTL pt,
1430 DWORD *pdwEffect)
1431 {
1432 TRACE("(%p)\n", this);
1433
1434 if (!pdwEffect)
1435 return E_INVALIDARG;
1436
1437 QueryDrop(dwKeyState, pdwEffect);
1438
1439 return S_OK;
1440 }
1441
1442 HRESULT WINAPI CRecycleBin::DragLeave()
1443 {
1444 TRACE("(%p)\n", this);
1445
1446 fAcceptFmt = FALSE;
1447
1448 return S_OK;
1449 }
1450
1451 HRESULT WINAPI CRecycleBin::Drop(IDataObject *pDataObject,
1452 DWORD dwKeyState, POINTL pt, DWORD *pdwEffect)
1453 {
1454 TRACE("(%p) object dropped on recycle bin, effect %u\n", this, *pdwEffect);
1455
1456 /* TODO: pdwEffect should be read and make the drop object be permanently deleted in the move case (shift held) */
1457
1458 FORMATETC fmt;
1459 TRACE("(%p)->(DataObject=%p)\n", this, pDataObject);
1460 InitFormatEtc (fmt, cfShellIDList, TYMED_HGLOBAL);
1461
1462 /* Handle cfShellIDList Drop objects here, otherwise send the approriate message to other software */
1463 if (SUCCEEDED(pDataObject->QueryGetData(&fmt))) {
1464 pDataObject->AddRef();
1465 SHCreateThread(DoDeleteThreadProc, pDataObject, NULL, NULL);
1466 }
1467 else
1468 {
1469 /*
1470 * TODO call SetData on the data object with format CFSTR_TARGETCLSID
1471 * set to the Recycle Bin's class identifier CLSID_RecycleBin.
1472 */
1473 }
1474 return S_OK;
1475 }
1476
1477 DWORD WINAPI DoDeleteThreadProc(LPVOID lpParameter)
1478 {
1479 IDataObject *pda = (IDataObject*) lpParameter;
1480 DoDeleteDataObject(pda);
1481 //Release the data object
1482 pda->Release();
1483 return 0;
1484 }
1485
1486 HRESULT WINAPI DoDeleteDataObject(IDataObject *pda)
1487 {
1488 TRACE("performing delete");
1489 HRESULT hr;
1490
1491 STGMEDIUM medium;
1492 FORMATETC formatetc;
1493 InitFormatEtc(formatetc, RegisterClipboardFormatW(CFSTR_SHELLIDLIST), TYMED_HGLOBAL);
1494 hr = pda->GetData(&formatetc, &medium);
1495 if (FAILED(hr))
1496 return hr;
1497
1498 /* lock the handle */
1499 LPIDA lpcida = (LPIDA)GlobalLock(medium.hGlobal);
1500 if (!lpcida)
1501 {
1502 ReleaseStgMedium(&medium);
1503 return E_FAIL;
1504 }
1505
1506 /* convert the data into pidl */
1507 LPITEMIDLIST pidl;
1508 LPITEMIDLIST *apidl = _ILCopyCidaToaPidl(&pidl, lpcida);
1509 if (!apidl)
1510 {
1511 ReleaseStgMedium(&medium);
1512 return E_FAIL;
1513 }
1514
1515 CComPtr<IShellFolder> psfDesktop;
1516 CComPtr<IShellFolder> psfFrom = NULL;
1517
1518 /* Grab the desktop shell folder */
1519 hr = SHGetDesktopFolder(&psfDesktop);
1520 if (FAILED(hr))
1521 {
1522 ERR("SHGetDesktopFolder failed\n");
1523 SHFree(pidl);
1524 _ILFreeaPidl(apidl, lpcida->cidl);
1525 ReleaseStgMedium(&medium);
1526 return E_FAIL;
1527 }
1528
1529 /* Find source folder, this is where the clipboard data was copied from */
1530 if (_ILIsDesktop(pidl))
1531 {
1532 psfFrom = psfDesktop;
1533 }
1534 else
1535 {
1536 hr = psfDesktop->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder, &psfFrom));
1537 if (FAILED(hr))
1538 {
1539 ERR("no IShellFolder\n");
1540 SHFree(pidl);
1541 _ILFreeaPidl(apidl, lpcida->cidl);
1542 ReleaseStgMedium(&medium);
1543 return E_FAIL;
1544 }
1545 }
1546
1547 STRRET strTemp;
1548 hr = psfFrom->GetDisplayNameOf(apidl[0], SHGDN_FORPARSING, &strTemp);
1549 if (FAILED(hr))
1550 {
1551 ERR("IShellFolder_GetDisplayNameOf failed with %x\n", hr);
1552 SHFree(pidl);
1553 _ILFreeaPidl(apidl, lpcida->cidl);
1554 ReleaseStgMedium(&medium);
1555 return hr;
1556 }
1557
1558 WCHAR wszPath[MAX_PATH];
1559 hr = StrRetToBufW(&strTemp, apidl[0], wszPath, _countof(wszPath));
1560 if (FAILED(hr))
1561 {
1562 ERR("StrRetToBufW failed with %x\n", hr);
1563 SHFree(pidl);
1564 _ILFreeaPidl(apidl, lpcida->cidl);
1565 ReleaseStgMedium(&medium);
1566 return hr;
1567 }
1568
1569 /* Only keep the base path */
1570 LPWSTR pwszFilename = PathFindFileNameW(wszPath);
1571 *pwszFilename = L'\0';
1572
1573 /* Build paths list */
1574 LPWSTR pwszPaths = BuildPathsList(wszPath, lpcida->cidl, (LPCITEMIDLIST*) apidl);
1575 if (!pwszPaths)
1576 {
1577 SHFree(pidl);
1578 _ILFreeaPidl(apidl, lpcida->cidl);
1579 ReleaseStgMedium(&medium);
1580 return E_FAIL;
1581 }
1582
1583 /* Delete them */
1584 SHFILEOPSTRUCTW FileOp;
1585 ZeroMemory(&FileOp, sizeof(FileOp));
1586 FileOp.wFunc = FO_DELETE;
1587 FileOp.pFrom = pwszPaths;
1588 FileOp.fFlags = FOF_ALLOWUNDO;
1589
1590 if (SHFileOperationW(&FileOp) != 0)
1591 {
1592 ERR("SHFileOperation failed with 0x%x for %s\n", GetLastError(), debugstr_w(pwszPaths));
1593 hr = E_FAIL;
1594 }
1595
1596 HeapFree(GetProcessHeap(), 0, pwszPaths);
1597 SHFree(pidl);
1598 _ILFreeaPidl(apidl, lpcida->cidl);
1599 ReleaseStgMedium(&medium);
1600
1601 return hr;
1602 }