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