[SHELL32]
[reactos.git] / reactos / dll / win32 / shell32 / dialogs / filedefext.cpp
1 /*
2 * Provides default file shell extension
3 *
4 * Copyright 2005 Johannes Anderwald
5 * Copyright 2012 Rafal Harabien
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(shell);
25
26 EXTERN_C BOOL PathIsExeW(LPCWSTR lpszPath);
27
28 BOOL CFileVersionInfo::Load(LPCWSTR pwszPath)
29 {
30 ULONG cbInfo = GetFileVersionInfoSizeW(pwszPath, NULL);
31 if (!cbInfo)
32 {
33 WARN("GetFileVersionInfoSize %ls failed\n", pwszPath);
34 return FALSE;
35 }
36
37 m_pInfo = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbInfo);
38 if (!m_pInfo)
39 {
40 ERR("HeapAlloc failed bytes %x\n", cbInfo);
41 return FALSE;
42 }
43
44 if (!GetFileVersionInfoW(pwszPath, 0, cbInfo, m_pInfo))
45 {
46 ERR("GetFileVersionInfoW failed\n");
47 return FALSE;
48 }
49
50 LPLANGANDCODEPAGE lpLangCode;
51 UINT cBytes;
52 if (!VerQueryValueW(m_pInfo, L"\\VarFileInfo\\Translation", (LPVOID *)&lpLangCode, &cBytes) || cBytes < sizeof(LANGANDCODEPAGE))
53 {
54 ERR("VerQueryValueW failed\n");
55 return FALSE;
56 }
57
58 /* FIXME: find language from current locale / if not available,
59 * default to english
60 * for now default to first available language
61 */
62 m_wLang = lpLangCode->wLang;
63 m_wCode = lpLangCode->wCode;
64 TRACE("Lang %hx Code %hu\n", m_wLang, m_wCode);
65
66 return TRUE;
67 }
68
69 LPCWSTR CFileVersionInfo::GetString(LPCWSTR pwszName)
70 {
71 if (!m_pInfo)
72 return NULL;
73
74 WCHAR wszBuf[256];
75 swprintf(wszBuf, L"\\StringFileInfo\\%04x%04x\\%s", m_wLang, m_wCode, pwszName);
76
77 /* Query string in version block */
78 LPCWSTR pwszResult = NULL;
79 UINT cBytes = 0;
80 if (!VerQueryValueW(m_pInfo, wszBuf, (LPVOID *)&pwszResult, &cBytes))
81 pwszResult = NULL;
82
83 if (!m_wLang && !m_wCode)
84 {
85 /* Try US English */
86 swprintf(wszBuf, L"\\StringFileInfo\\%04x%04x\\%s", MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), 1252, pwszName);
87 if (!VerQueryValueW(m_pInfo, wszBuf, (LPVOID *)&pwszResult, &cBytes))
88 pwszResult = NULL;
89 }
90
91 if (!pwszResult)
92 ERR("VerQueryValueW %ls failed\n", pwszName);
93 else
94 TRACE("%ls: %ls\n", pwszName, pwszResult);
95
96 return pwszResult;
97 }
98
99 VS_FIXEDFILEINFO *CFileVersionInfo::GetFixedInfo()
100 {
101 if (!m_pInfo)
102 return NULL;
103
104 VS_FIXEDFILEINFO *pInfo;
105 UINT cBytes;
106 if (!VerQueryValueW(m_pInfo, L"\\", (PVOID*)&pInfo, &cBytes))
107 return NULL;
108 return pInfo;
109 }
110
111 LPCWSTR CFileVersionInfo::GetLangName()
112 {
113 if (!m_pInfo)
114 return NULL;
115
116 if (!m_wszLang[0])
117 {
118 if (!VerLanguageNameW(m_wLang, m_wszLang, _countof(m_wszLang)))
119 ERR("VerLanguageNameW failed\n");
120 }
121
122 return m_wszLang;
123 }
124
125 UINT
126 SH_FormatInteger(LONGLONG Num, LPWSTR pwszResult, UINT cchResultMax)
127 {
128 // Print the number in uniform mode
129 WCHAR wszNumber[24];
130 swprintf(wszNumber, L"%I64u", Num);
131
132 // Get system strings for decimal and thousand separators.
133 WCHAR wszDecimalSep[8], wszThousandSep[8];
134 GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, wszDecimalSep, _countof(wszDecimalSep));
135 GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, wszThousandSep, _countof(wszThousandSep));
136
137 // Initialize format for printing the number in bytes
138 NUMBERFMTW nf;
139 ZeroMemory(&nf, sizeof(nf));
140 nf.lpDecimalSep = wszDecimalSep;
141 nf.lpThousandSep = wszThousandSep;
142
143 // Get system string for groups separator
144 WCHAR wszGrouping[12];
145 INT cchGrouping = GetLocaleInfoW(LOCALE_USER_DEFAULT,
146 LOCALE_SGROUPING,
147 wszGrouping,
148 _countof(wszGrouping));
149
150 // Convert grouping specs from string to integer
151 for (INT i = 0; i < cchGrouping; i++)
152 {
153 WCHAR wch = wszGrouping[i];
154
155 if (wch >= L'0' && wch <= L'9')
156 nf.Grouping = nf.Grouping * 10 + (wch - L'0');
157 else if (wch != L';')
158 break;
159 }
160
161 if ((nf.Grouping % 10) == 0)
162 nf.Grouping /= 10;
163 else
164 nf.Grouping *= 10;
165
166 // Format the number
167 INT cchResult = GetNumberFormatW(LOCALE_USER_DEFAULT,
168 0,
169 wszNumber,
170 &nf,
171 pwszResult,
172 cchResultMax);
173
174 if (!cchResult)
175 return 0;
176
177 // GetNumberFormatW returns number of characters including UNICODE_NULL
178 return cchResult - 1;
179 }
180
181 UINT
182 SH_FormatByteSize(LONGLONG cbSize, LPWSTR pwszResult, UINT cchResultMax)
183 {
184 /* Write formated bytes count */
185 INT cchWritten = SH_FormatInteger(cbSize, pwszResult, cchResultMax);
186 if (!cchWritten)
187 return 0;
188
189 /* Copy " bytes" to buffer */
190 LPWSTR pwszEnd = pwszResult + cchWritten;
191 size_t cchRemaining = cchResultMax - cchWritten;
192 StringCchCopyExW(pwszEnd, cchRemaining, L" ", &pwszEnd, &cchRemaining, NULL);
193 cchWritten = LoadStringW(shell32_hInstance, IDS_BYTES_FORMAT, pwszEnd, cchRemaining);
194 cchRemaining -= cchWritten;
195
196 return cchResultMax - cchRemaining;
197 }
198
199 /*************************************************************************
200 *
201 * SH_FormatFileSizeWithBytes
202 *
203 * Format a size in bytes to string.
204 *
205 * lpQwSize = Pointer to 64bit large integer to format
206 * pszBuf = Buffer to fill with output string
207 * cchBuf = size of pszBuf in characters
208 *
209 */
210
211 LPWSTR
212 SH_FormatFileSizeWithBytes(const PULARGE_INTEGER lpQwSize, LPWSTR pwszResult, UINT cchResultMax)
213 {
214 /* Format bytes in KBs, MBs etc */
215 if (StrFormatByteSizeW(lpQwSize->QuadPart, pwszResult, cchResultMax) == NULL)
216 return NULL;
217
218 /* If there is less bytes than 1KB, we have nothing to do */
219 if (lpQwSize->QuadPart < 1024)
220 return pwszResult;
221
222 /* Concate " (" */
223 UINT cchWritten = wcslen(pwszResult);
224 LPWSTR pwszEnd = pwszResult + cchWritten;
225 size_t cchRemaining = cchResultMax - cchWritten;
226 StringCchCopyExW(pwszEnd, cchRemaining, L" (", &pwszEnd, &cchRemaining, 0);
227
228 /* Write formated bytes count */
229 cchWritten = SH_FormatByteSize(lpQwSize->QuadPart, pwszEnd, cchRemaining);
230 pwszEnd += cchWritten;
231 cchRemaining -= cchWritten;
232
233 /* Copy ")" to the buffer */
234 StringCchCopyW(pwszEnd, cchRemaining, L")");
235
236 return pwszResult;
237 }
238
239 /*************************************************************************
240 *
241 * SH_CreatePropertySheetPage [Internal]
242 *
243 * creates a property sheet page from an resource name
244 *
245 */
246
247 HPROPSHEETPAGE
248 SH_CreatePropertySheetPage(WORD wDialogId, DLGPROC pfnDlgProc, LPARAM lParam, LPCWSTR pwszTitle)
249 {
250 HRSRC hRes = FindResourceW(shell32_hInstance, MAKEINTRESOURCEW(wDialogId), (LPWSTR)RT_DIALOG);
251 if (hRes == NULL)
252 {
253 ERR("failed to find resource id\n");
254 return NULL;
255 }
256
257 LPVOID pTemplate = LoadResource(shell32_hInstance, hRes);
258 if (pTemplate == NULL)
259 {
260 ERR("failed to load resource\n");
261 return NULL;
262 }
263
264 PROPSHEETPAGEW Page;
265 memset(&Page, 0x0, sizeof(PROPSHEETPAGEW));
266 Page.dwSize = sizeof(PROPSHEETPAGEW);
267 Page.dwFlags = PSP_DLGINDIRECT;
268 Page.pResource = (DLGTEMPLATE*)pTemplate;
269 Page.pfnDlgProc = pfnDlgProc;
270 Page.lParam = lParam;
271 Page.pszTitle = pwszTitle;
272
273 if (pwszTitle)
274 Page.dwFlags |= PSP_USETITLE;
275
276 return CreatePropertySheetPageW(&Page);
277 }
278
279 VOID
280 CFileDefExt::InitOpensWithField(HWND hwndDlg)
281 {
282 WCHAR wszBuf[MAX_PATH] = L"";
283 WCHAR wszPath[MAX_PATH] = L"";
284 DWORD dwSize = sizeof(wszBuf);
285 BOOL bUnknownApp = TRUE;
286 LPCWSTR pwszExt = PathFindExtensionW(m_wszPath);
287
288 if (RegGetValueW(HKEY_CLASSES_ROOT, pwszExt, L"", RRF_RT_REG_SZ, NULL, wszBuf, &dwSize) == ERROR_SUCCESS)
289 {
290 bUnknownApp = FALSE;
291 StringCbCatW(wszBuf, sizeof(wszBuf), L"\\shell\\open\\command");
292 dwSize = sizeof(wszPath);
293 if (RegGetValueW(HKEY_CLASSES_ROOT, wszBuf, L"", RRF_RT_REG_SZ, NULL, wszPath, &dwSize) == ERROR_SUCCESS)
294 {
295 /* Get path from command line */
296 ExpandEnvironmentStringsW(wszPath, wszBuf, _countof(wszBuf));
297 PathRemoveArgs(wszBuf);
298 PathUnquoteSpacesW(wszBuf);
299 PathSearchAndQualify(wszBuf, wszPath, _countof(wszPath));
300
301 HICON hIcon;
302 if (ExtractIconExW(wszPath, 0, NULL, &hIcon, 1))
303 {
304 HWND hIconCtrl = GetDlgItem(hwndDlg, 14025);
305 HWND hDescrCtrl = GetDlgItem(hwndDlg, 14007);
306 ShowWindow(hIconCtrl, SW_SHOW);
307 RECT rcIcon, rcDescr;
308 GetWindowRect(hIconCtrl, &rcIcon);
309
310 rcIcon.right = rcIcon.left + GetSystemMetrics(SM_CXSMICON);
311 rcIcon.bottom = rcIcon.top + GetSystemMetrics(SM_CYSMICON);
312
313 MapWindowPoints(NULL, hwndDlg, (LPPOINT)&rcIcon, 2);
314 GetWindowRect(hDescrCtrl, &rcDescr);
315 MapWindowPoints(NULL, hwndDlg, (LPPOINT)&rcDescr, 2);
316 INT cxOffset = rcIcon.right + 2 - rcDescr.left;
317 SetWindowPos(hDescrCtrl, NULL,
318 rcDescr.left + cxOffset, rcDescr.top,
319 rcDescr.right - rcDescr.left - cxOffset, rcDescr.bottom - rcDescr.top,
320 SWP_NOZORDER);
321 SendMessageW(hIconCtrl, STM_SETICON, (WPARAM)hIcon, 0);
322 } else
323 ERR("Failed to extract icon\n");
324
325 if (PathFileExistsW(wszPath))
326 {
327 /* Get file description */
328 CFileVersionInfo VerInfo;
329 VerInfo.Load(wszPath);
330 LPCWSTR pwszDescr = VerInfo.GetString(L"FileDescription");
331 if (pwszDescr)
332 SetDlgItemTextW(hwndDlg, 14007, pwszDescr);
333 else
334 {
335 /* File has no description - display filename */
336 LPWSTR pwszFilename = PathFindFileNameW(wszPath);
337 PathRemoveExtension(pwszFilename);
338 pwszFilename[0] = towupper(pwszFilename[0]);
339 SetDlgItemTextW(hwndDlg, 14007, pwszFilename);
340 }
341 }
342 else
343 bUnknownApp = TRUE;
344 } else
345 WARN("RegGetValueW %ls failed\n", wszBuf);
346 } else
347 WARN("RegGetValueW %ls failed\n", pwszExt);
348
349 if (bUnknownApp)
350 {
351 /* Unknown application */
352 LoadStringW(shell32_hInstance, IDS_UNKNOWN_APP, wszBuf, _countof(wszBuf));
353 SetDlgItemTextW(hwndDlg, 14007, wszBuf);
354 }
355 }
356
357 /*************************************************************************
358 *
359 * SH_FileGeneralFileType [Internal]
360 *
361 * retrieves file extension description from registry and sets it in dialog
362 *
363 * TODO: retrieve file extension default icon and load it
364 * find executable name from registry, retrieve description from executable
365 */
366
367 BOOL
368 CFileDefExt::InitFileType(HWND hwndDlg)
369 {
370 TRACE("path %s\n", debugstr_w(m_wszPath));
371
372 HWND hDlgCtrl = GetDlgItem(hwndDlg, 14005);
373 if (hDlgCtrl == NULL)
374 return FALSE;
375
376 /* Get file information */
377 SHFILEINFO fi;
378 if (!SHGetFileInfoW(m_wszPath, 0, &fi, sizeof(fi), SHGFI_TYPENAME|SHGFI_ICON))
379 {
380 ERR("SHGetFileInfoW failed for %ls (%lu)\n", m_wszPath, GetLastError());
381 fi.szTypeName[0] = L'\0';
382 fi.hIcon = NULL;
383 }
384
385 LPCWSTR pwszExt = PathFindExtensionW(m_wszPath);
386 if (pwszExt[0])
387 {
388 WCHAR wszBuf[256];
389
390 if (!fi.szTypeName[0])
391 {
392 /* The file type is unknown, so default to string "FileExtension File" */
393 size_t cchRemaining = 0;
394 LPWSTR pwszEnd = NULL;
395
396 StringCchPrintfExW(wszBuf, _countof(wszBuf), &pwszEnd, &cchRemaining, 0, L"%s ", pwszExt + 1);
397 SendMessageW(hDlgCtrl, WM_GETTEXT, (WPARAM)cchRemaining, (LPARAM)pwszEnd);
398
399 SendMessageW(hDlgCtrl, WM_SETTEXT, (WPARAM)NULL, (LPARAM)wszBuf);
400 }
401 else
402 {
403 /* Update file type */
404 StringCbPrintfW(wszBuf, sizeof(wszBuf), L"%s (%s)", fi.szTypeName, pwszExt);
405 SendMessageW(hDlgCtrl, WM_SETTEXT, (WPARAM)NULL, (LPARAM)wszBuf);
406 }
407 }
408
409 /* Update file icon */
410 if (fi.hIcon)
411 SendDlgItemMessageW(hwndDlg, 14000, STM_SETICON, (WPARAM)fi.hIcon, 0);
412 else
413 ERR("No icon %ls\n", m_wszPath);
414
415 return TRUE;
416 }
417
418 /*************************************************************************
419 *
420 * CFileDefExt::InitFilePath [Internal]
421 *
422 * sets file path string and filename string
423 *
424 */
425
426 BOOL
427 CFileDefExt::InitFilePath(HWND hwndDlg)
428 {
429 /* Find the filename */
430 WCHAR *pwszFilename = PathFindFileNameW(m_wszPath);
431
432 if (pwszFilename > m_wszPath)
433 {
434 /* Location field */
435 WCHAR wszLocation[MAX_PATH];
436 StringCchCopyNW(wszLocation, _countof(wszLocation), m_wszPath, pwszFilename - m_wszPath);
437 PathRemoveBackslashW(wszLocation);
438
439 SetDlgItemTextW(hwndDlg, 14009, wszLocation);
440 }
441
442 /* Filename field */
443 SetDlgItemTextW(hwndDlg, 14001, pwszFilename);
444
445 return TRUE;
446 }
447
448 /*************************************************************************
449 *
450 * CFileDefExt::GetFileTimeString [Internal]
451 *
452 * formats a given LPFILETIME struct into readable user format
453 */
454
455 BOOL
456 CFileDefExt::GetFileTimeString(LPFILETIME lpFileTime, LPWSTR pwszResult, UINT cchResult)
457 {
458 FILETIME ft;
459 SYSTEMTIME st;
460
461 if (!FileTimeToLocalFileTime(lpFileTime, &ft) || !FileTimeToSystemTime(&ft, &st))
462 return FALSE;
463
464 size_t cchRemaining = cchResult;
465 LPWSTR pwszEnd = pwszResult;
466 int cchWritten = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_LONGDATE, &st, NULL, pwszEnd, cchRemaining);
467 if (cchWritten)
468 --cchWritten; // GetDateFormatW returns count with terminating zero
469 else
470 ERR("GetDateFormatW failed\n");
471 cchRemaining -= cchWritten;
472 pwszEnd += cchWritten;
473
474 StringCchCopyExW(pwszEnd, cchRemaining, L", ", &pwszEnd, &cchRemaining, 0);
475
476 cchWritten = GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &st, NULL, pwszEnd, cchRemaining);
477 if (cchWritten)
478 --cchWritten; // GetTimeFormatW returns count with terminating zero
479 else
480 ERR("GetTimeFormatW failed\n");
481 TRACE("result %s\n", debugstr_w(pwszResult));
482 return TRUE;
483 }
484
485 /*************************************************************************
486 *
487 * CFileDefExt::InitFileAttr [Internal]
488 *
489 * retrieves file information from file and sets in dialog
490 *
491 */
492
493 BOOL
494 CFileDefExt::InitFileAttr(HWND hwndDlg)
495 {
496 WCHAR wszBuf[MAX_PATH];
497
498 TRACE("InitFileAttr %ls\n", m_wszPath);
499
500 WIN32_FILE_ATTRIBUTE_DATA FileInfo;
501 if (GetFileAttributesExW(m_wszPath, GetFileExInfoStandard, &FileInfo))
502 {
503 /* Update attribute checkboxes */
504 if (FileInfo.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
505 SendDlgItemMessage(hwndDlg, 14021, BM_SETCHECK, BST_CHECKED, 0);
506 if (FileInfo.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
507 SendDlgItemMessage(hwndDlg, 14022, BM_SETCHECK, BST_CHECKED, 0);
508 if (FileInfo.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE)
509 SendDlgItemMessage(hwndDlg, 14023, BM_SETCHECK, BST_CHECKED, 0);
510
511 /* Update creation time */
512 if (GetFileTimeString(&FileInfo.ftCreationTime, wszBuf, _countof(wszBuf)))
513 SetDlgItemTextW(hwndDlg, 14015, wszBuf);
514
515 /* For files display last access and last write time */
516 if (!m_bDir)
517 {
518 if (GetFileTimeString(&FileInfo.ftLastAccessTime, wszBuf, _countof(wszBuf)))
519 SetDlgItemTextW(hwndDlg, 14019, wszBuf);
520
521 if (GetFileTimeString(&FileInfo.ftLastWriteTime, wszBuf, _countof(wszBuf)))
522 SetDlgItemTextW(hwndDlg, 14017, wszBuf);
523
524 /* Update size of file */
525 ULARGE_INTEGER FileSize;
526 FileSize.u.LowPart = FileInfo.nFileSizeLow;
527 FileSize.u.HighPart = FileInfo.nFileSizeHigh;
528 if (SH_FormatFileSizeWithBytes(&FileSize, wszBuf, _countof(wszBuf)))
529 SetDlgItemTextW(hwndDlg, 14011, wszBuf);
530 }
531 }
532
533 if (m_bDir)
534 {
535 /* For directories files have to be counted */
536
537 _CountFolderAndFilesData *data = static_cast<_CountFolderAndFilesData*>(HeapAlloc(GetProcessHeap(), 0, sizeof(_CountFolderAndFilesData)));
538 data->This = this;
539 data->pwszBuf = static_cast<LPWSTR>(HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) * MAX_PATH));
540 data->cchBufMax = MAX_PATH;
541 data->hwndDlg = hwndDlg;
542 this->AddRef();
543 StringCchCopyW(data->pwszBuf, MAX_PATH, m_wszPath);
544
545 SHCreateThread(CFileDefExt::_CountFolderAndFilesThreadProc, data, NULL, NULL);
546
547 /* Update size field */
548 if (SH_FormatFileSizeWithBytes(&m_DirSize, wszBuf, _countof(wszBuf)))
549 SetDlgItemTextW(hwndDlg, 14011, wszBuf);
550
551 /* Display files and folders count */
552 WCHAR wszFormat[256];
553 LoadStringW(shell32_hInstance, IDS_FILE_FOLDER, wszFormat, _countof(wszFormat));
554 StringCchPrintfW(wszBuf, _countof(wszBuf), wszFormat, m_cFiles, m_cFolders);
555 SetDlgItemTextW(hwndDlg, 14027, wszBuf);
556 }
557
558 /* Hide Advanced button. TODO: Implement advanced dialog and enable this button if filesystem supports compression or encryption */
559 ShowWindow(GetDlgItem(hwndDlg, 14028), SW_HIDE);
560
561 return TRUE;
562 }
563
564 /*************************************************************************
565 *
566 * CFileDefExt::InitGeneralPage [Internal]
567 *
568 * sets all file general properties in dialog
569 */
570
571 BOOL
572 CFileDefExt::InitGeneralPage(HWND hwndDlg)
573 {
574 /* Set general text properties filename filelocation and icon */
575 InitFilePath(hwndDlg);
576
577 /* Set file type and icon */
578 InitFileType(hwndDlg);
579
580 /* Set open with application */
581 if (!m_bDir)
582 {
583 if (!PathIsExeW(m_wszPath))
584 InitOpensWithField(hwndDlg);
585 else
586 {
587 WCHAR wszBuf[MAX_PATH];
588 LoadStringW(shell32_hInstance, IDS_EXE_DESCRIPTION, wszBuf, _countof(wszBuf));
589 SetDlgItemTextW(hwndDlg, 14006, wszBuf);
590 ShowWindow(GetDlgItem(hwndDlg, 14024), SW_HIDE);
591 LPCWSTR pwszDescr = m_VerInfo.GetString(L"FileDescription");
592 if (pwszDescr)
593 SetDlgItemTextW(hwndDlg, 14007, pwszDescr);
594 else
595 {
596 StringCbCopyW(wszBuf, sizeof(wszBuf), PathFindFileNameW(m_wszPath));
597 PathRemoveExtension(wszBuf);
598 SetDlgItemTextW(hwndDlg, 14007, wszBuf);
599 }
600 }
601 }
602
603 /* Set file created/modfied/accessed time, size and attributes */
604 InitFileAttr(hwndDlg);
605
606 return TRUE;
607 }
608
609 /*************************************************************************
610 *
611 * CFileDefExt::GeneralPageProc
612 *
613 * wnd proc of 'General' property sheet page
614 *
615 */
616
617 INT_PTR CALLBACK
618 CFileDefExt::GeneralPageProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
619 {
620 switch (uMsg)
621 {
622 case WM_INITDIALOG:
623 {
624 LPPROPSHEETPAGEW ppsp = (LPPROPSHEETPAGEW)lParam;
625
626 if (ppsp == NULL || !ppsp->lParam)
627 break;
628
629 TRACE("WM_INITDIALOG hwnd %p lParam %p ppsplParam %S\n", hwndDlg, lParam, ppsp->lParam);
630
631 CFileDefExt *pFileDefExt = reinterpret_cast<CFileDefExt *>(ppsp->lParam);
632 SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)pFileDefExt);
633 pFileDefExt->InitGeneralPage(hwndDlg);
634 break;
635 }
636 case WM_COMMAND:
637 if (LOWORD(wParam) == 14024) /* Opens With - Change */
638 {
639 CFileDefExt *pFileDefExt = reinterpret_cast<CFileDefExt *>(GetWindowLongPtr(hwndDlg, DWLP_USER));
640 OPENASINFO oainfo;
641 oainfo.pcszFile = pFileDefExt->m_wszPath;
642 oainfo.pcszClass = NULL;
643 oainfo.oaifInFlags = OAIF_REGISTER_EXT|OAIF_FORCE_REGISTRATION;
644 return SUCCEEDED(SHOpenWithDialog(hwndDlg, &oainfo));
645 }
646 else if (LOWORD(wParam) == 14021 || LOWORD(wParam) == 14022 || LOWORD(wParam) == 14023) /* checkboxes */
647 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
648 else if (LOWORD(wParam) == 14001) /* Name */
649 {
650 if (HIWORD(wParam) == EN_CHANGE)
651 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
652 }
653 break;
654 case WM_NOTIFY:
655 {
656 LPPSHNOTIFY lppsn = (LPPSHNOTIFY)lParam;
657 if (lppsn->hdr.code == PSN_APPLY)
658 {
659 CFileDefExt *pFileDefExt = reinterpret_cast<CFileDefExt *>(GetWindowLongPtr(hwndDlg, DWLP_USER));
660
661 /* Update attributes first */
662 DWORD dwAttr = GetFileAttributesW(pFileDefExt->m_wszPath);
663 if (dwAttr)
664 {
665 dwAttr &= ~(FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_ARCHIVE);
666
667 if (BST_CHECKED == SendDlgItemMessageW(hwndDlg, 14021, BM_GETCHECK, 0, 0))
668 dwAttr |= FILE_ATTRIBUTE_READONLY;
669 if (BST_CHECKED == SendDlgItemMessageW(hwndDlg, 14022, BM_GETCHECK, 0, 0))
670 dwAttr |= FILE_ATTRIBUTE_HIDDEN;
671 if (BST_CHECKED == SendDlgItemMessageW(hwndDlg, 14023, BM_GETCHECK, 0, 0))
672 dwAttr |= FILE_ATTRIBUTE_ARCHIVE;
673
674 if (!SetFileAttributesW(pFileDefExt->m_wszPath, dwAttr))
675 ERR("SetFileAttributesW failed\n");
676 }
677
678 /* Update filename now */
679 WCHAR wszBuf[MAX_PATH];
680 StringCchCopyW(wszBuf, _countof(wszBuf), pFileDefExt->m_wszPath);
681 LPWSTR pwszFilename = PathFindFileNameW(wszBuf);
682 UINT cchFilenameMax = _countof(wszBuf) - (pwszFilename - wszBuf);
683 if (GetDlgItemTextW(hwndDlg, 14001, pwszFilename, cchFilenameMax))
684 {
685 if (!MoveFileW(pFileDefExt->m_wszPath, wszBuf))
686 ERR("MoveFileW failed\n");
687 }
688
689 SetWindowLongPtr(hwndDlg, DWL_MSGRESULT, PSNRET_NOERROR);
690 return TRUE;
691 }
692 break;
693 }
694 default:
695 break;
696 }
697
698 return FALSE;
699 }
700
701 /*************************************************************************
702 *
703 * CFileDefExt::InitVersionPage [Internal]
704 *
705 * sets all file version properties in dialog
706 */
707
708 BOOL
709 CFileDefExt::InitVersionPage(HWND hwndDlg)
710 {
711 /* Get fixed info */
712 VS_FIXEDFILEINFO *pInfo = m_VerInfo.GetFixedInfo();
713 if (pInfo)
714 {
715 WCHAR wszVersion[256];
716 swprintf(wszVersion, L"%u.%u.%u.%u", HIWORD(pInfo->dwFileVersionMS),
717 LOWORD(pInfo->dwFileVersionMS),
718 HIWORD(pInfo->dwFileVersionLS),
719 LOWORD(pInfo->dwFileVersionLS));
720 TRACE("MS %x LS %x ver %s \n", pInfo->dwFileVersionMS, pInfo->dwFileVersionLS, debugstr_w(wszVersion));
721 SetDlgItemTextW(hwndDlg, 14001, wszVersion);
722 }
723
724 /* Update labels */
725 SetVersionLabel(hwndDlg, 14003, L"FileDescription");
726 SetVersionLabel(hwndDlg, 14005, L"LegalCopyright");
727
728 /* Add items to listbox */
729 AddVersionString(hwndDlg, L"CompanyName");
730 LPCWSTR pwszLang = m_VerInfo.GetLangName();
731 if (pwszLang)
732 {
733 HWND hDlgCtrl = GetDlgItem(hwndDlg, 14009);
734 UINT Index = SendMessageW(hDlgCtrl, LB_ADDSTRING, (WPARAM)-1, (LPARAM)L"Language");
735 SendMessageW(hDlgCtrl, LB_SETITEMDATA, (WPARAM)Index, (LPARAM)(WCHAR *)pwszLang);
736 }
737 AddVersionString(hwndDlg, L"ProductName");
738 AddVersionString(hwndDlg, L"InternalName");
739 AddVersionString(hwndDlg, L"OriginalFilename");
740 AddVersionString(hwndDlg, L"FileVersion");
741 AddVersionString(hwndDlg, L"ProductVersion");
742
743 /* Attach file version to dialog window */
744 SetWindowLongPtr(hwndDlg, DWL_USER, (LONG_PTR)this);
745
746 /* Select first item */
747 HWND hDlgCtrl = GetDlgItem(hwndDlg, 14009);
748 SendMessageW(hDlgCtrl, LB_SETCURSEL, 0, 0);
749 LPCWSTR pwszText = (LPCWSTR)SendMessageW(hDlgCtrl, LB_GETITEMDATA, (WPARAM)0, (LPARAM)NULL);
750 if (pwszText && pwszText != (LPCWSTR)LB_ERR)
751 SetDlgItemTextW(hwndDlg, 14010, pwszText);
752
753 return TRUE;
754 }
755
756 /*************************************************************************
757 *
758 * CFileDefExt::SetVersionLabel [Internal]
759 *
760 * retrieves a version string and uses it to set label text
761 */
762
763 BOOL
764 CFileDefExt::SetVersionLabel(HWND hwndDlg, DWORD idCtrl, LPCWSTR pwszName)
765 {
766 if (hwndDlg == NULL || pwszName == NULL)
767 return FALSE;
768
769 LPCWSTR pwszValue = m_VerInfo.GetString(pwszName);
770 if (pwszValue)
771 {
772 /* file description property */
773 TRACE("%s :: %s\n", debugstr_w(pwszName), debugstr_w(pwszValue));
774 SetDlgItemTextW(hwndDlg, idCtrl, pwszValue);
775 return TRUE;
776 }
777
778 return FALSE;
779 }
780
781 /*************************************************************************
782 *
783 * CFileDefExt::AddVersionString [Internal]
784 *
785 * retrieves a version string and adds it to listbox
786 */
787
788 BOOL
789 CFileDefExt::AddVersionString(HWND hwndDlg, LPCWSTR pwszName)
790 {
791 TRACE("pwszName %s, hwndDlg %p\n", debugstr_w(pwszName), hwndDlg);
792
793 if (hwndDlg == NULL || pwszName == NULL)
794 return FALSE;
795
796 LPCWSTR pwszValue = m_VerInfo.GetString(pwszName);
797 if (pwszValue)
798 {
799 /* listbox name property */
800 HWND hDlgCtrl = GetDlgItem(hwndDlg, 14009);
801 TRACE("%s :: %s\n", debugstr_w(pwszName), debugstr_w(pwszValue));
802 UINT Index = SendMessageW(hDlgCtrl, LB_ADDSTRING, (WPARAM) -1, (LPARAM)pwszName);
803 SendMessageW(hDlgCtrl, LB_SETITEMDATA, (WPARAM)Index, (LPARAM)(WCHAR *)pwszValue);
804 return TRUE;
805 }
806
807 return FALSE;
808 }
809
810 /*************************************************************************
811 *
812 * CFileDefExt::VersionPageProc
813 *
814 * wnd proc of 'Version' property sheet page
815 */
816
817 INT_PTR CALLBACK
818 CFileDefExt::VersionPageProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
819 {
820 switch (uMsg)
821 {
822 case WM_INITDIALOG:
823 {
824 LPPROPSHEETPAGE ppsp = (LPPROPSHEETPAGE)lParam;
825
826 if (ppsp == NULL || !ppsp->lParam)
827 break;
828
829 TRACE("WM_INITDIALOG hwnd %p lParam %p ppsplParam %x\n", hwndDlg, lParam, ppsp->lParam);
830
831 CFileDefExt *pFileDefExt = reinterpret_cast<CFileDefExt *>(ppsp->lParam);
832 return pFileDefExt->InitVersionPage(hwndDlg);
833 }
834 case WM_COMMAND:
835 if (LOWORD(wParam) == 14009 && HIWORD(wParam) == LBN_SELCHANGE)
836 {
837 HWND hDlgCtrl = (HWND)lParam;
838
839 LRESULT Index = SendMessageW(hDlgCtrl, LB_GETCURSEL, (WPARAM)NULL, (LPARAM)NULL);
840 if (Index == LB_ERR)
841 break;
842
843 LPCWSTR pwszData = (LPCWSTR)SendMessageW(hDlgCtrl, LB_GETITEMDATA, (WPARAM)Index, (LPARAM)NULL);
844 if (pwszData == NULL)
845 break;
846
847 TRACE("hDlgCtrl %x string %s\n", hDlgCtrl, debugstr_w(pwszData));
848 SetDlgItemTextW(hwndDlg, 14010, pwszData);
849
850 return TRUE;
851 }
852 break;
853 case WM_DESTROY:
854 break;
855 default:
856 break;
857 }
858
859 return FALSE;
860 }
861
862 CFileDefExt::CFileDefExt():
863 m_bDir(FALSE), m_cFiles(0), m_cFolders(0)
864 {
865 m_wszPath[0] = L'\0';
866 m_DirSize.QuadPart = 0ull;
867 }
868
869 CFileDefExt::~CFileDefExt()
870 {
871
872 }
873
874 HRESULT WINAPI
875 CFileDefExt::Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pDataObj, HKEY hkeyProgID)
876 {
877 FORMATETC format;
878 STGMEDIUM stgm;
879 HRESULT hr;
880
881 TRACE("%p %p %p %p\n", this, pidlFolder, pDataObj, hkeyProgID);
882
883 if (!pDataObj)
884 return E_FAIL;
885
886 format.cfFormat = CF_HDROP;
887 format.ptd = NULL;
888 format.dwAspect = DVASPECT_CONTENT;
889 format.lindex = -1;
890 format.tymed = TYMED_HGLOBAL;
891
892 hr = pDataObj->GetData(&format, &stgm);
893 if (FAILED(hr))
894 return hr;
895
896 if (!DragQueryFileW((HDROP)stgm.hGlobal, 0, m_wszPath, _countof(m_wszPath)))
897 {
898 ERR("DragQueryFileW failed\n");
899 ReleaseStgMedium(&stgm);
900 return E_FAIL;
901 }
902
903 ReleaseStgMedium(&stgm);
904
905 TRACE("File properties %ls\n", m_wszPath);
906 m_bDir = PathIsDirectoryW(m_wszPath) ? TRUE : FALSE;
907 if (!m_bDir)
908 m_VerInfo.Load(m_wszPath);
909
910 return S_OK;
911 }
912
913 HRESULT WINAPI
914 CFileDefExt::QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
915 {
916 UNIMPLEMENTED;
917 return E_NOTIMPL;
918 }
919
920 HRESULT WINAPI
921 CFileDefExt::InvokeCommand(LPCMINVOKECOMMANDINFO lpici)
922 {
923 UNIMPLEMENTED;
924 return E_NOTIMPL;
925 }
926
927 HRESULT WINAPI
928 CFileDefExt::GetCommandString(UINT_PTR idCmd, UINT uType, UINT *pwReserved, LPSTR pszName, UINT cchMax)
929 {
930 UNIMPLEMENTED;
931 return E_NOTIMPL;
932 }
933
934 HRESULT WINAPI
935 CFileDefExt::AddPages(LPFNADDPROPSHEETPAGE pfnAddPage, LPARAM lParam)
936 {
937 HPROPSHEETPAGE hPage;
938 WORD wResId = m_bDir ? IDD_FOLDER_PROPERTIES : IDD_FILE_PROPERTIES;
939
940 hPage = SH_CreatePropertySheetPage(wResId,
941 GeneralPageProc,
942 (LPARAM)this,
943 NULL);
944 if (hPage)
945 pfnAddPage(hPage, lParam);
946
947 if (!m_bDir && GetFileVersionInfoSizeW(m_wszPath, NULL))
948 {
949 hPage = SH_CreatePropertySheetPage(IDD_FILE_VERSION,
950 VersionPageProc,
951 (LPARAM)this,
952 NULL);
953 if (hPage)
954 pfnAddPage(hPage, lParam);
955 }
956
957 return S_OK;
958 }
959
960 HRESULT WINAPI
961 CFileDefExt::ReplacePage(UINT uPageID, LPFNADDPROPSHEETPAGE pfnReplacePage, LPARAM lParam)
962 {
963 UNIMPLEMENTED;
964 return E_NOTIMPL;
965 }
966
967 HRESULT WINAPI
968 CFileDefExt::SetSite(IUnknown *punk)
969 {
970 UNIMPLEMENTED;
971 return E_NOTIMPL;
972 }
973
974 HRESULT WINAPI
975 CFileDefExt::GetSite(REFIID iid, void **ppvSite)
976 {
977 UNIMPLEMENTED;
978 return E_NOTIMPL;
979 }
980
981 DWORD WINAPI
982 CFileDefExt::_CountFolderAndFilesThreadProc(LPVOID lpParameter)
983 {
984 _CountFolderAndFilesData *data = static_cast<_CountFolderAndFilesData*>(lpParameter);
985 DWORD ticks = 0;
986 data->This->CountFolderAndFiles(data->hwndDlg, data->pwszBuf, data->cchBufMax, &ticks);
987
988 //Release the CFileDefExt and data object holds in the copying thread.
989 data->This->Release();
990 HeapFree(GetProcessHeap(), 0, data->pwszBuf);
991 HeapFree(GetProcessHeap(), 0, data);
992
993 return 0;
994 }
995
996 BOOL
997 CFileDefExt::CountFolderAndFiles(HWND hwndDlg, LPWSTR pwszBuf, UINT cchBufMax, DWORD *ticks)
998 {
999 /* Find filename position */
1000 UINT cchBuf = wcslen(pwszBuf);
1001 WCHAR *pwszFilename = pwszBuf + cchBuf;
1002 size_t cchFilenameMax = cchBufMax - cchBuf;
1003 if (!cchFilenameMax)
1004 return FALSE;
1005 *(pwszFilename++) = '\\';
1006 --cchFilenameMax;
1007
1008 /* Find all files, FIXME: shouldn't be "*"? */
1009 StringCchCopyW(pwszFilename, cchFilenameMax, L"*");
1010
1011 WIN32_FIND_DATAW wfd;
1012 HANDLE hFind = FindFirstFileW(pwszBuf, &wfd);
1013 if (hFind == INVALID_HANDLE_VALUE)
1014 {
1015 ERR("FindFirstFileW %ls failed\n", pwszBuf);
1016 return FALSE;
1017 }
1018
1019 BOOL root = FALSE;
1020 if (*ticks == 0) {
1021 *ticks = GetTickCount();
1022 root = TRUE;
1023 }
1024
1025 do
1026 {
1027 if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1028 {
1029 /* Don't process "." and ".." items */
1030 if (!wcscmp(wfd.cFileName, L".") || !wcscmp(wfd.cFileName, L".."))
1031 continue;
1032
1033 ++m_cFolders;
1034
1035 StringCchCopyW(pwszFilename, cchFilenameMax, wfd.cFileName);
1036 CountFolderAndFiles(hwndDlg, pwszBuf, cchBufMax, ticks);
1037 }
1038 else
1039 {
1040 m_cFiles++;
1041
1042 ULARGE_INTEGER FileSize;
1043 FileSize.u.LowPart = wfd.nFileSizeLow;
1044 FileSize.u.HighPart = wfd.nFileSizeHigh;
1045 m_DirSize.QuadPart += FileSize.QuadPart;
1046 }
1047 if (GetTickCount() - *ticks > (DWORD) 300)
1048 {
1049 /* FIXME Using IsWindow is generally ill advised */
1050 if (IsWindow(hwndDlg))
1051 {
1052 WCHAR wszBuf[MAX_PATH];
1053
1054 if (SH_FormatFileSizeWithBytes(&m_DirSize, wszBuf, _countof(wszBuf)))
1055 SetDlgItemTextW(hwndDlg, 14011, wszBuf);
1056
1057 /* Display files and folders count */
1058 WCHAR wszFormat[256];
1059 LoadStringW(shell32_hInstance, IDS_FILE_FOLDER, wszFormat, _countof(wszFormat));
1060 StringCchPrintfW(wszBuf, _countof(wszBuf), wszFormat, m_cFiles, m_cFolders);
1061 SetDlgItemTextW(hwndDlg, 14027, wszBuf);
1062 *ticks = GetTickCount();
1063 }
1064 else
1065 break;
1066 }
1067 } while(FindNextFileW(hFind, &wfd));
1068
1069 if (root && IsWindow(hwndDlg))
1070 {
1071 WCHAR wszBuf[MAX_PATH];
1072
1073 if (SH_FormatFileSizeWithBytes(&m_DirSize, wszBuf, _countof(wszBuf)))
1074 SetDlgItemTextW(hwndDlg, 14011, wszBuf);
1075
1076 /* Display files and folders count */
1077 WCHAR wszFormat[256];
1078 LoadStringW(shell32_hInstance, IDS_FILE_FOLDER, wszFormat, _countof(wszFormat));
1079 StringCchPrintfW(wszBuf, _countof(wszBuf), wszFormat, m_cFiles, m_cFolders);
1080 SetDlgItemTextW(hwndDlg, 14027, wszBuf);
1081 }
1082
1083 FindClose(hFind);
1084 return TRUE;
1085 }