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 WINE_DEFAULT_DEBUG_CHANNEL(CRecycleBin);
25
26 typedef struct
27 {
28 ULARGE_INTEGER FreeBytesAvailable;
29 DWORD dwSerial;
30 DWORD dwMaxCapacity;
31 DWORD dwNukeOnDelete;
32 } DRIVE_ITEM_CONTEXT, *PDRIVE_ITEM_CONTEXT;
33
toggleNukeOnDeleteOption(HWND hwndDlg,BOOL bEnable)34 static void toggleNukeOnDeleteOption(HWND hwndDlg, BOOL bEnable)
35 {
36 if (bEnable)
37 {
38 SendDlgItemMessage(hwndDlg, 14001, BM_SETCHECK, BST_UNCHECKED, 0);
39 EnableWindow(GetDlgItem(hwndDlg, 14002), FALSE);
40 SendDlgItemMessage(hwndDlg, 14003, BM_SETCHECK, BST_CHECKED, 0);
41 }
42 else
43 {
44 SendDlgItemMessage(hwndDlg, 14001, BM_SETCHECK, BST_CHECKED, 0);
45 EnableWindow(GetDlgItem(hwndDlg, 14002), TRUE);
46 SendDlgItemMessage(hwndDlg, 14003, BM_SETCHECK, BST_UNCHECKED, 0);
47 }
48 }
49
50 static VOID
InitializeRecycleBinDlg(HWND hwndDlg,WCHAR DefaultDrive)51 InitializeRecycleBinDlg(HWND hwndDlg, WCHAR DefaultDrive)
52 {
53 WCHAR szDrive[] = L"A:\\";
54 DWORD dwDrives;
55 WCHAR szName[100];
56 WCHAR szVolume[100];
57 DWORD MaxComponent, Flags;
58 DWORD dwSerial;
59 LVCOLUMNW lc;
60 HWND hDlgCtrl;
61 LVITEMW li;
62 INT itemCount;
63 ULARGE_INTEGER TotalNumberOfFreeBytes, TotalNumberOfBytes, FreeBytesAvailable;
64 RECT rect;
65 int columnSize;
66 int defIndex = 0;
67 DWORD dwSize;
68 PDRIVE_ITEM_CONTEXT pItem = NULL, pDefault = NULL, pFirst = NULL;
69
70 hDlgCtrl = GetDlgItem(hwndDlg, 14000);
71
72 if (!LoadStringW(shell32_hInstance, IDS_RECYCLEBIN_LOCATION, szVolume, _countof(szVolume)))
73 szVolume[0] = 0;
74
75 GetClientRect(hDlgCtrl, &rect);
76
77 ZeroMemory(&lc, sizeof(lc));
78 lc.mask = LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM | LVCF_FMT;
79
80 columnSize = 140; //FIXME
81 lc.iSubItem = 0;
82 lc.fmt = LVCFMT_FIXED_WIDTH;
83 lc.cx = columnSize;
84 lc.cchTextMax = wcslen(szVolume);
85 lc.pszText = szVolume;
86 (void)SendMessageW(hDlgCtrl, LVM_INSERTCOLUMNW, 0, (LPARAM)&lc);
87
88 if (!LoadStringW(shell32_hInstance, IDS_RECYCLEBIN_DISKSPACE, szVolume, _countof(szVolume)))
89 szVolume[0] = 0;
90
91 lc.iSubItem = 1;
92 lc.cx = rect.right - rect.left - columnSize;
93 lc.cchTextMax = wcslen(szVolume);
94 lc.pszText = szVolume;
95 (void)SendMessageW(hDlgCtrl, LVM_INSERTCOLUMNW, 1, (LPARAM)&lc);
96
97 dwDrives = GetLogicalDrives();
98 itemCount = 0;
99 do
100 {
101 if (dwDrives & 0x1)
102 {
103 UINT Type = GetDriveTypeW(szDrive);
104 if (Type == DRIVE_FIXED) //FIXME
105 {
106 if (!GetVolumeInformationW(szDrive, szName, _countof(szName), &dwSerial, &MaxComponent, &Flags, NULL, 0))
107 {
108 szName[0] = 0;
109 dwSerial = -1;
110 }
111
112 swprintf(szVolume, L"%s (%c:)", szName, szDrive[0]);
113 ZeroMemory(&li, sizeof(li));
114 li.mask = LVIF_TEXT | LVIF_PARAM;
115 li.iSubItem = 0;
116 li.pszText = szVolume;
117 li.iItem = itemCount;
118 SendMessageW(hDlgCtrl, LVM_INSERTITEMW, 0, (LPARAM)&li);
119 if (GetDiskFreeSpaceExW(szDrive, &FreeBytesAvailable, &TotalNumberOfBytes, &TotalNumberOfFreeBytes))
120 {
121 if (StrFormatByteSizeW(TotalNumberOfFreeBytes.QuadPart, szVolume, _countof(szVolume)))
122 {
123 pItem = (DRIVE_ITEM_CONTEXT *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(DRIVE_ITEM_CONTEXT));
124 if (pItem)
125 {
126 pItem->FreeBytesAvailable = FreeBytesAvailable;
127 pItem->dwSerial = dwSerial;
128
129 swprintf(szName, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\BitBucket\\Volume\\%04X-%04X", LOWORD(dwSerial), HIWORD(dwSerial));
130
131 dwSize = sizeof(DWORD);
132 RegGetValueW(HKEY_CURRENT_USER, szName, L"MaxCapacity", RRF_RT_DWORD, NULL, &pItem->dwMaxCapacity, &dwSize);
133
134 /* Check if the maximum capacity doesn't exceed the available disk space (in megabytes), and truncate it if needed */
135 FreeBytesAvailable.QuadPart = (FreeBytesAvailable.QuadPart / (1024 * 1024));
136 pItem->dwMaxCapacity = min(pItem->dwMaxCapacity, FreeBytesAvailable.LowPart);
137
138 dwSize = sizeof(DWORD);
139 RegGetValueW(HKEY_CURRENT_USER, szName, L"NukeOnDelete", RRF_RT_DWORD, NULL, &pItem->dwNukeOnDelete, &dwSize);
140
141 li.mask = LVIF_PARAM;
142 li.lParam = (LPARAM)pItem;
143 (void)SendMessageW(hDlgCtrl, LVM_SETITEMW, 0, (LPARAM)&li);
144 if (szDrive[0] == DefaultDrive)
145 {
146 defIndex = itemCount;
147 pDefault = pItem;
148 }
149 }
150 if (!pFirst)
151 pFirst = pItem;
152
153 li.mask = LVIF_TEXT;
154 li.iSubItem = 1;
155 li.pszText = szVolume;
156 li.iItem = itemCount;
157 (void)SendMessageW(hDlgCtrl, LVM_SETITEMW, 0, (LPARAM)&li);
158 }
159 }
160 itemCount++;
161 }
162 }
163 szDrive[0]++;
164 dwDrives = (dwDrives >> 1);
165 } while (dwDrives);
166
167 if (!pDefault)
168 pDefault = pFirst;
169 if (pDefault)
170 {
171 toggleNukeOnDeleteOption(hwndDlg, pDefault->dwNukeOnDelete);
172 SetDlgItemInt(hwndDlg, 14002, pDefault->dwMaxCapacity, FALSE);
173 }
174 ZeroMemory(&li, sizeof(li));
175 li.mask = LVIF_STATE;
176 li.stateMask = (UINT)-1;
177 li.state = LVIS_FOCUSED | LVIS_SELECTED;
178 li.iItem = defIndex;
179 (void)SendMessageW(hDlgCtrl, LVM_SETITEMW, 0, (LPARAM)&li);
180 }
181
StoreDriveSettings(HWND hwndDlg)182 static BOOL StoreDriveSettings(HWND hwndDlg)
183 {
184 int iCount, iIndex;
185 HWND hDlgCtrl = GetDlgItem(hwndDlg, 14000);
186 LVITEMW li;
187 PDRIVE_ITEM_CONTEXT pItem;
188 HKEY hKey, hSubKey;
189 WCHAR szSerial[20];
190 DWORD dwSize;
191
192 if (RegCreateKeyExW(HKEY_CURRENT_USER, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\BitBucket\\Volume", 0, NULL, 0, KEY_WRITE, NULL, &hKey, NULL) != ERROR_SUCCESS)
193 return FALSE;
194
195 iCount = ListView_GetItemCount(hDlgCtrl);
196
197 ZeroMemory(&li, sizeof(li));
198 li.mask = LVIF_PARAM;
199
200 for (iIndex = 0; iIndex < iCount; iIndex++)
201 {
202 li.iItem = iIndex;
203 if (SendMessageW(hDlgCtrl, LVM_GETITEMW, 0, (LPARAM)&li))
204 {
205 pItem = (PDRIVE_ITEM_CONTEXT)li.lParam;
206 swprintf(szSerial, L"%04X-%04X", LOWORD(pItem->dwSerial), HIWORD(pItem->dwSerial));
207 if (RegCreateKeyExW(hKey, szSerial, 0, NULL, 0, KEY_WRITE, NULL, &hSubKey, NULL) == ERROR_SUCCESS)
208 {
209 dwSize = sizeof(DWORD);
210 RegSetValueExW(hSubKey, L"MaxCapacity", 0, REG_DWORD, (LPBYTE)&pItem->dwMaxCapacity, dwSize);
211 dwSize = sizeof(DWORD);
212 RegSetValueExW(hSubKey, L"NukeOnDelete", 0, REG_DWORD, (LPBYTE)&pItem->dwNukeOnDelete, dwSize);
213 RegCloseKey(hSubKey);
214 }
215 }
216 }
217 RegCloseKey(hKey);
218 return TRUE;
219 }
220
FreeDriveItemContext(HWND hwndDlg)221 static VOID FreeDriveItemContext(HWND hwndDlg)
222 {
223 int iCount, iIndex;
224 HWND hDlgCtrl = GetDlgItem(hwndDlg, 14000);
225 LVITEMW li;
226
227 iCount = ListView_GetItemCount(hDlgCtrl);
228
229 ZeroMemory(&li, sizeof(li));
230 li.mask = LVIF_PARAM;
231
232 for (iIndex = 0; iIndex < iCount; iIndex++)
233 {
234 li.iItem = iIndex;
235 if (SendMessageW(hDlgCtrl, LVM_GETITEMW, 0, (LPARAM)&li))
236 {
237 HeapFree(GetProcessHeap(), 0, (PVOID)li.lParam);
238 }
239 }
240 }
241
242 static INT
GetDefaultItem(HWND hwndDlg,LVITEMW * li)243 GetDefaultItem(HWND hwndDlg, LVITEMW* li)
244 {
245 HWND hDlgCtrl;
246 UINT iItemCount, iIndex;
247
248 hDlgCtrl = GetDlgItem(hwndDlg, 14000);
249 if (!hDlgCtrl)
250 return -1;
251
252 iItemCount = ListView_GetItemCount(hDlgCtrl);
253 if (!iItemCount)
254 return -1;
255
256 ZeroMemory(li, sizeof(LVITEMW));
257 li->mask = LVIF_PARAM | LVIF_STATE;
258 li->stateMask = (UINT)-1;
259 for (iIndex = 0; iIndex < iItemCount; iIndex++)
260 {
261 li->iItem = iIndex;
262 if (SendMessageW(hDlgCtrl, LVM_GETITEMW, 0, (LPARAM)li))
263 {
264 if (li->state & LVIS_SELECTED)
265 return iIndex;
266 }
267 }
268 return -1;
269 }
270
271 static INT_PTR CALLBACK
RecycleBinDlg(HWND hwndDlg,UINT uMsg,WPARAM wParam,LPARAM lParam)272 RecycleBinDlg(
273 HWND hwndDlg,
274 UINT uMsg,
275 WPARAM wParam,
276 LPARAM lParam)
277 {
278 LPPSHNOTIFY lppsn;
279 LPNMLISTVIEW lppl;
280 LVITEMW li;
281 PDRIVE_ITEM_CONTEXT pItem;
282 BOOL bSuccess;
283 UINT uResult;
284 PROPSHEETPAGE * page;
285 DWORD dwStyle;
286 ULARGE_INTEGER FreeBytesAvailable;
287
288 switch(uMsg)
289 {
290 case WM_INITDIALOG:
291 page = (PROPSHEETPAGE*)lParam;
292 InitializeRecycleBinDlg(hwndDlg, (WCHAR)page->lParam);
293 SendDlgItemMessage(hwndDlg, 14004, BM_SETCHECK, !SHELL_GetSetting(SSF_NOCONFIRMRECYCLE, fNoConfirmRecycle), 0);
294 dwStyle = (DWORD) SendDlgItemMessage(hwndDlg, 14000, LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
295 dwStyle = dwStyle | LVS_EX_FULLROWSELECT;
296 SendDlgItemMessage(hwndDlg, 14000, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, dwStyle);
297 if (GetDlgCtrlID((HWND)wParam) != 14000)
298 {
299 SetFocus(GetDlgItem(hwndDlg, 14000));
300 return FALSE;
301 }
302 return TRUE;
303 case WM_COMMAND:
304 switch(LOWORD(wParam))
305 {
306 case 14001:
307 toggleNukeOnDeleteOption(hwndDlg, FALSE);
308 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
309 break;
310 case 14002:
311 if (HIWORD(wParam) == EN_CHANGE)
312 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
313 break;
314 case 14003:
315 toggleNukeOnDeleteOption(hwndDlg, TRUE);
316 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
317 break;
318 case 14004:
319 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
320 break;
321 }
322 break;
323 case WM_NOTIFY:
324 lppsn = (LPPSHNOTIFY) lParam;
325 lppl = (LPNMLISTVIEW) lParam;
326 if (lppsn->hdr.code == PSN_APPLY)
327 {
328 SHELLSTATE ss;
329 ss.fNoConfirmRecycle = SendDlgItemMessage(hwndDlg, 14004, BM_GETCHECK, 0, 0) == BST_UNCHECKED;
330 SHGetSetSettings(&ss, SSF_NOCONFIRMRECYCLE, TRUE);
331
332 if (GetDefaultItem(hwndDlg, &li) > -1)
333 {
334 pItem = (PDRIVE_ITEM_CONTEXT)li.lParam;
335 if (pItem)
336 {
337 uResult = GetDlgItemInt(hwndDlg, 14002, &bSuccess, FALSE);
338 if (bSuccess)
339 {
340 /* Check if the maximum capacity doesn't exceed the available disk space (in megabytes), and truncate it if needed */
341 FreeBytesAvailable = pItem->FreeBytesAvailable;
342 FreeBytesAvailable.QuadPart = (FreeBytesAvailable.QuadPart / (1024 * 1024));
343 pItem->dwMaxCapacity = min(uResult, FreeBytesAvailable.LowPart);
344 SetDlgItemInt(hwndDlg, 14002, pItem->dwMaxCapacity, FALSE);
345 }
346 if (SendDlgItemMessageW(hwndDlg, 14003, BM_GETCHECK, 0, 0) == BST_CHECKED)
347 pItem->dwNukeOnDelete = TRUE;
348 else
349 pItem->dwNukeOnDelete = FALSE;
350 }
351 }
352 if (StoreDriveSettings(hwndDlg))
353 {
354 SetWindowLongPtr( hwndDlg, DWLP_MSGRESULT, PSNRET_NOERROR );
355 return TRUE;
356 }
357 }
358 else if (lppl->hdr.code == LVN_ITEMCHANGING)
359 {
360 ZeroMemory(&li, sizeof(li));
361 li.mask = LVIF_PARAM;
362 li.iItem = lppl->iItem;
363 if (!SendMessageW(lppl->hdr.hwndFrom, LVM_GETITEMW, 0, (LPARAM)&li))
364 return TRUE;
365
366 pItem = (PDRIVE_ITEM_CONTEXT)li.lParam;
367 if (!pItem)
368 return TRUE;
369
370 if (!(lppl->uOldState & LVIS_FOCUSED) && (lppl->uNewState & LVIS_FOCUSED))
371 {
372 /* new focused item */
373 toggleNukeOnDeleteOption(lppl->hdr.hwndFrom, pItem->dwNukeOnDelete);
374 SetDlgItemInt(hwndDlg, 14002, pItem->dwMaxCapacity, FALSE);
375 }
376 else if ((lppl->uOldState & LVIS_FOCUSED) && !(lppl->uNewState & LVIS_FOCUSED))
377 {
378 /* kill focus */
379 uResult = GetDlgItemInt(hwndDlg, 14002, &bSuccess, FALSE);
380 if (bSuccess)
381 {
382 /* Check if the maximum capacity doesn't exceed the available disk space (in megabytes), and truncate it if needed */
383 FreeBytesAvailable = pItem->FreeBytesAvailable;
384 FreeBytesAvailable.QuadPart = (FreeBytesAvailable.QuadPart / (1024 * 1024));
385 pItem->dwMaxCapacity = min(uResult, FreeBytesAvailable.LowPart);
386 SetDlgItemInt(hwndDlg, 14002, pItem->dwMaxCapacity, FALSE);
387 }
388 if (SendDlgItemMessageW(hwndDlg, 14003, BM_GETCHECK, 0, 0) == BST_CHECKED)
389 pItem->dwNukeOnDelete = TRUE;
390 else
391 pItem->dwNukeOnDelete = FALSE;
392 }
393 return TRUE;
394
395 }
396 break;
397 case WM_DESTROY:
398 FreeDriveItemContext(hwndDlg);
399 break;
400 }
401 return FALSE;
402 }
403
SH_ShowRecycleBinProperties(WCHAR sDrive)404 BOOL SH_ShowRecycleBinProperties(WCHAR sDrive)
405 {
406 HPROPSHEETPAGE hpsp[1];
407 PROPSHEETHEADERW psh;
408 HPROPSHEETPAGE hprop;
409 INT_PTR ret;
410
411 ZeroMemory(&psh, sizeof(psh));
412 psh.dwSize = sizeof(psh);
413 psh.dwFlags = PSP_DEFAULT | PSH_PROPTITLE;
414 psh.pszCaption = MAKEINTRESOURCEW(IDS_RECYCLEBIN_FOLDER_NAME);
415 psh.hwndParent = NULL;
416 psh.phpage = hpsp;
417 psh.hInstance = shell32_hInstance;
418
419 hprop = SH_CreatePropertySheetPage(IDD_RECYCLE_BIN_PROPERTIES, RecycleBinDlg, (LPARAM)sDrive, NULL);
420 if (!hprop)
421 {
422 ERR("Failed to create property sheet\n");
423 return FALSE;
424 }
425 hpsp[psh.nPages] = hprop;
426 psh.nPages++;
427
428 ret = PropertySheetW(&psh);
429 return (ret >= 0);
430 }
431