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