Sync with trunk.
[reactos.git] / dll / win32 / shell32 / 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 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 if (rcIcon.left == rcIcon.right)
310 ERR("Icon control has invalid width: %d-%d\n", rcIcon.left, rcIcon.right);
311 MapWindowPoints(NULL, hwndDlg, (LPPOINT)&rcIcon, 2);
312 GetWindowRect(hDescrCtrl, &rcDescr);
313 MapWindowPoints(NULL, hwndDlg, (LPPOINT)&rcDescr, 2);
314 INT cxOffset = rcIcon.right + 2 - rcDescr.left;
315 SetWindowPos(hDescrCtrl, NULL,
316 rcDescr.left + cxOffset, rcDescr.top,
317 rcDescr.right - rcDescr.left - cxOffset, rcDescr.bottom - rcDescr.top,
318 SWP_NOZORDER);
319 SendMessageW(hIconCtrl, STM_SETICON, (WPARAM)hIcon, 0);
320 } else
321 ERR("Failed to extract icon\n");
322
323 if (PathFileExistsW(wszPath))
324 {
325 /* Get file description */
326 CFileVersionInfo VerInfo;
327 VerInfo.Load(wszPath);
328 LPCWSTR pwszDescr = VerInfo.GetString(L"FileDescription");
329 if (pwszDescr)
330 SetDlgItemTextW(hwndDlg, 14007, pwszDescr);
331 else
332 {
333 /* File has no description - display filename */
334 LPWSTR pwszFilename = PathFindFileNameW(wszPath);
335 PathRemoveExtension(pwszFilename);
336 pwszFilename[0] = towupper(pwszFilename[0]);
337 SetDlgItemTextW(hwndDlg, 14007, pwszFilename);
338 }
339 }
340 else
341 bUnknownApp = TRUE;
342 } else
343 WARN("RegGetValueW %ls failed\n", wszBuf);
344 } else
345 WARN("RegGetValueW %ls failed\n", pwszExt);
346
347 if (bUnknownApp)
348 {
349 /* Unknown application */
350 LoadStringW(shell32_hInstance, IDS_UNKNOWN_APP, wszBuf, _countof(wszBuf));
351 SetDlgItemTextW(hwndDlg, 14007, wszBuf);
352 }
353 }
354
355 /*************************************************************************
356 *
357 * SH_FileGeneralFileType [Internal]
358 *
359 * retrieves file extension description from registry and sets it in dialog
360 *
361 * TODO: retrieve file extension default icon and load it
362 * find executable name from registry, retrieve description from executable
363 */
364
365 BOOL
366 CFileDefExt::InitFileType(HWND hwndDlg)
367 {
368 TRACE("path %s\n", debugstr_w(m_wszPath));
369
370 HWND hDlgCtrl = GetDlgItem(hwndDlg, 14005);
371 if (hDlgCtrl == NULL)
372 return FALSE;
373
374 /* Get file information */
375 SHFILEINFO fi;
376 if (!SHGetFileInfoW(m_wszPath, 0, &fi, sizeof(fi), SHGFI_TYPENAME|SHGFI_ICON))
377 {
378 ERR("SHGetFileInfoW failed for %ls (%lu)\n", m_wszPath, GetLastError());
379 fi.szTypeName[0] = L'\0';
380 fi.hIcon = NULL;
381 }
382
383 LPCWSTR pwszExt = PathFindExtensionW(m_wszPath);
384 if (pwszExt[0])
385 {
386 WCHAR wszBuf[256];
387
388 if (!fi.szTypeName[0])
389 {
390 /* The file type is unknown, so default to string "FileExtension File" */
391 size_t cchRemaining = 0;
392 LPWSTR pwszEnd = NULL;
393
394 StringCchPrintfExW(wszBuf, _countof(wszBuf), &pwszEnd, &cchRemaining, 0, L"%s ", pwszExt + 1);
395 SendMessageW(hDlgCtrl, WM_GETTEXT, (WPARAM)cchRemaining, (LPARAM)pwszEnd);
396
397 SendMessageW(hDlgCtrl, WM_SETTEXT, (WPARAM)NULL, (LPARAM)wszBuf);
398 }
399 else
400 {
401 /* Update file type */
402 StringCbPrintfW(wszBuf, sizeof(wszBuf), L"%s (%s)", fi.szTypeName, pwszExt);
403 SendMessageW(hDlgCtrl, WM_SETTEXT, (WPARAM)NULL, (LPARAM)wszBuf);
404 }
405 }
406
407 /* Update file icon */
408 if (fi.hIcon)
409 SendDlgItemMessageW(hwndDlg, 14000, STM_SETICON, (WPARAM)fi.hIcon, 0);
410 else
411 ERR("No icon %ls\n", m_wszPath);
412
413 return TRUE;
414 }
415
416 /*************************************************************************
417 *
418 * CFileDefExt::InitFilePath [Internal]
419 *
420 * sets file path string and filename string
421 *
422 */
423
424 BOOL
425 CFileDefExt::InitFilePath(HWND hwndDlg)
426 {
427 /* Find the filename */
428 WCHAR *pwszFilename = PathFindFileNameW(m_wszPath);
429
430 if (pwszFilename > m_wszPath)
431 {
432 /* Location field */
433 WCHAR wszLocation[MAX_PATH];
434 StringCchCopyNW(wszLocation, _countof(wszLocation), m_wszPath, pwszFilename - m_wszPath);
435 PathRemoveBackslashW(wszLocation);
436
437 SetDlgItemTextW(hwndDlg, 14009, wszLocation);
438 }
439
440 /* Filename field */
441 SetDlgItemTextW(hwndDlg, 14001, pwszFilename);
442
443 return TRUE;
444 }
445
446 /*************************************************************************
447 *
448 * CFileDefExt::GetFileTimeString [Internal]
449 *
450 * formats a given LPFILETIME struct into readable user format
451 */
452
453 BOOL
454 CFileDefExt::GetFileTimeString(LPFILETIME lpFileTime, LPWSTR pwszResult, UINT cchResult)
455 {
456 FILETIME ft;
457 SYSTEMTIME st;
458
459 if (!FileTimeToLocalFileTime(lpFileTime, &ft) || !FileTimeToSystemTime(&ft, &st))
460 return FALSE;
461
462 size_t cchRemaining = cchResult;
463 LPWSTR pwszEnd = pwszResult;
464 int cchWritten = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_LONGDATE, &st, NULL, pwszEnd, cchRemaining);
465 if (cchWritten)
466 --cchWritten; // GetDateFormatW returns count with terminating zero
467 else
468 ERR("GetDateFormatW failed\n");
469 cchRemaining -= cchWritten;
470 pwszEnd += cchWritten;
471
472 StringCchCopyExW(pwszEnd, cchRemaining, L", ", &pwszEnd, &cchRemaining, 0);
473
474 cchWritten = GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &st, NULL, pwszEnd, cchRemaining);
475 if (cchWritten)
476 --cchWritten; // GetTimeFormatW returns count with terminating zero
477 else
478 ERR("GetTimeFormatW failed\n");
479 TRACE("result %s\n", debugstr_w(pwszResult));
480 return TRUE;
481 }
482
483 /*************************************************************************
484 *
485 * CFileDefExt::InitFileAttr [Internal]
486 *
487 * retrieves file information from file and sets in dialog
488 *
489 */
490
491 BOOL
492 CFileDefExt::InitFileAttr(HWND hwndDlg)
493 {
494 WCHAR wszBuf[MAX_PATH];
495
496 TRACE("InitFileAttr %ls\n", m_wszPath);
497
498 WIN32_FILE_ATTRIBUTE_DATA FileInfo;
499 if (GetFileAttributesExW(m_wszPath, GetFileExInfoStandard, &FileInfo))
500 {
501 /* Update attribute checkboxes */
502 if (FileInfo.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
503 SendDlgItemMessage(hwndDlg, 14021, BM_SETCHECK, BST_CHECKED, 0);
504 if (FileInfo.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
505 SendDlgItemMessage(hwndDlg, 14022, BM_SETCHECK, BST_CHECKED, 0);
506 if (FileInfo.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE)
507 SendDlgItemMessage(hwndDlg, 14023, BM_SETCHECK, BST_CHECKED, 0);
508
509 /* Update creation time */
510 if (GetFileTimeString(&FileInfo.ftCreationTime, wszBuf, _countof(wszBuf)))
511 SetDlgItemTextW(hwndDlg, 14015, wszBuf);
512
513 /* For files display last access and last write time */
514 if (!m_bDir)
515 {
516 if (GetFileTimeString(&FileInfo.ftLastAccessTime, wszBuf, _countof(wszBuf)))
517 SetDlgItemTextW(hwndDlg, 14019, wszBuf);
518
519 if (GetFileTimeString(&FileInfo.ftLastWriteTime, wszBuf, _countof(wszBuf)))
520 SetDlgItemTextW(hwndDlg, 14017, wszBuf);
521
522 /* Update size of file */
523 ULARGE_INTEGER FileSize;
524 FileSize.u.LowPart = FileInfo.nFileSizeLow;
525 FileSize.u.HighPart = FileInfo.nFileSizeHigh;
526 if (SH_FormatFileSizeWithBytes(&FileSize, wszBuf, _countof(wszBuf)))
527 SetDlgItemTextW(hwndDlg, 14011, wszBuf);
528 }
529 }
530
531 if (m_bDir)
532 {
533 /* For directories files have to be counted */
534
535 _CountFolderAndFilesData *data = static_cast<_CountFolderAndFilesData*>(HeapAlloc(GetProcessHeap(), 0, sizeof(_CountFolderAndFilesData)));
536 data->This = this;
537 data->pwszBuf = static_cast<LPWSTR>(HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) * MAX_PATH));
538 data->cchBufMax = MAX_PATH;
539 data->hwndDlg = hwndDlg;
540 this->AddRef();
541 StringCchCopyW(data->pwszBuf, MAX_PATH, m_wszPath);
542
543 SHCreateThread(CFileDefExt::_CountFolderAndFilesThreadProc, data, NULL, NULL);
544
545 /* Update size field */
546 if (SH_FormatFileSizeWithBytes(&m_DirSize, wszBuf, _countof(wszBuf)))
547 SetDlgItemTextW(hwndDlg, 14011, wszBuf);
548
549 /* Display files and folders count */
550 WCHAR wszFormat[256];
551 LoadStringW(shell32_hInstance, IDS_FILE_FOLDER, wszFormat, _countof(wszFormat));
552 StringCchPrintfW(wszBuf, _countof(wszBuf), wszFormat, m_cFiles, m_cFolders);
553 SetDlgItemTextW(hwndDlg, 14027, wszBuf);
554 }
555
556 /* Hide Advanced button. TODO: Implement advanced dialog and enable this button if filesystem supports compression or encryption */
557 ShowWindow(GetDlgItem(hwndDlg, 14028), SW_HIDE);
558
559 return TRUE;
560 }
561
562 /*************************************************************************
563 *
564 * CFileDefExt::InitGeneralPage [Internal]
565 *
566 * sets all file general properties in dialog
567 */
568
569 BOOL
570 CFileDefExt::InitGeneralPage(HWND hwndDlg)
571 {
572 /* Set general text properties filename filelocation and icon */
573 InitFilePath(hwndDlg);
574
575 /* Set file type and icon */
576 InitFileType(hwndDlg);
577
578 /* Set open with application */
579 if (!m_bDir)
580 {
581 if (!PathIsExeW(m_wszPath))
582 InitOpensWithField(hwndDlg);
583 else
584 {
585 WCHAR wszBuf[MAX_PATH];
586 LoadStringW(shell32_hInstance, IDS_EXE_DESCRIPTION, wszBuf, _countof(wszBuf));
587 SetDlgItemTextW(hwndDlg, 14006, wszBuf);
588 ShowWindow(GetDlgItem(hwndDlg, 14024), SW_HIDE);
589 LPCWSTR pwszDescr = m_VerInfo.GetString(L"FileDescription");
590 if (pwszDescr)
591 SetDlgItemTextW(hwndDlg, 14007, pwszDescr);
592 else
593 {
594 StringCbCopyW(wszBuf, sizeof(wszBuf), PathFindFileNameW(m_wszPath));
595 PathRemoveExtension(wszBuf);
596 SetDlgItemTextW(hwndDlg, 14007, wszBuf);
597 }
598 }
599 }
600
601 /* Set file created/modfied/accessed time, size and attributes */
602 InitFileAttr(hwndDlg);
603
604 return TRUE;
605 }
606
607 /*************************************************************************
608 *
609 * CFileDefExt::GeneralPageProc
610 *
611 * wnd proc of 'General' property sheet page
612 *
613 */
614
615 INT_PTR CALLBACK
616 CFileDefExt::GeneralPageProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
617 {
618 switch (uMsg)
619 {
620 case WM_INITDIALOG:
621 {
622 LPPROPSHEETPAGEW ppsp = (LPPROPSHEETPAGEW)lParam;
623
624 if (ppsp == NULL || !ppsp->lParam)
625 break;
626
627 TRACE("WM_INITDIALOG hwnd %p lParam %p ppsplParam %S\n", hwndDlg, lParam, ppsp->lParam);
628
629 CFileDefExt *pFileDefExt = reinterpret_cast<CFileDefExt *>(ppsp->lParam);
630 SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)pFileDefExt);
631 pFileDefExt->InitGeneralPage(hwndDlg);
632 break;
633 }
634 case WM_COMMAND:
635 if (LOWORD(wParam) == 14024) /* Opens With - Change */
636 {
637 CFileDefExt *pFileDefExt = reinterpret_cast<CFileDefExt *>(GetWindowLongPtr(hwndDlg, DWLP_USER));
638 OPENASINFO oainfo;
639 oainfo.pcszFile = pFileDefExt->m_wszPath;
640 oainfo.pcszClass = NULL;
641 oainfo.oaifInFlags = OAIF_REGISTER_EXT|OAIF_FORCE_REGISTRATION;
642 return SUCCEEDED(SHOpenWithDialog(hwndDlg, &oainfo));
643 }
644 else if (LOWORD(wParam) == 14021 || LOWORD(wParam) == 14022 || LOWORD(wParam) == 14023) /* checkboxes */
645 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
646 else if (LOWORD(wParam) == 14001) /* Name */
647 {
648 if (HIWORD(wParam) == EN_CHANGE)
649 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
650 }
651 break;
652 case WM_NOTIFY:
653 {
654 LPPSHNOTIFY lppsn = (LPPSHNOTIFY)lParam;
655 if (lppsn->hdr.code == PSN_APPLY)
656 {
657 CFileDefExt *pFileDefExt = reinterpret_cast<CFileDefExt *>(GetWindowLongPtr(hwndDlg, DWLP_USER));
658
659 /* Update attributes first */
660 DWORD dwAttr = GetFileAttributesW(pFileDefExt->m_wszPath);
661 if (dwAttr)
662 {
663 dwAttr &= ~(FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_ARCHIVE);
664
665 if (BST_CHECKED == SendDlgItemMessageW(hwndDlg, 14021, BM_GETCHECK, 0, 0))
666 dwAttr |= FILE_ATTRIBUTE_READONLY;
667 if (BST_CHECKED == SendDlgItemMessageW(hwndDlg, 14022, BM_GETCHECK, 0, 0))
668 dwAttr |= FILE_ATTRIBUTE_HIDDEN;
669 if (BST_CHECKED == SendDlgItemMessageW(hwndDlg, 14023, BM_GETCHECK, 0, 0))
670 dwAttr |= FILE_ATTRIBUTE_ARCHIVE;
671
672 if (!SetFileAttributesW(pFileDefExt->m_wszPath, dwAttr))
673 ERR("SetFileAttributesW failed\n");
674 }
675
676 /* Update filename now */
677 WCHAR wszBuf[MAX_PATH];
678 StringCchCopyW(wszBuf, _countof(wszBuf), pFileDefExt->m_wszPath);
679 LPWSTR pwszFilename = PathFindFileNameW(wszBuf);
680 UINT cchFilenameMax = _countof(wszBuf) - (pwszFilename - wszBuf);
681 if (GetDlgItemTextW(hwndDlg, 14001, pwszFilename, cchFilenameMax))
682 {
683 if (!MoveFileW(pFileDefExt->m_wszPath, wszBuf))
684 ERR("MoveFileW failed\n");
685 }
686
687 SetWindowLongPtr(hwndDlg, DWL_MSGRESULT, PSNRET_NOERROR);
688 return TRUE;
689 }
690 break;
691 }
692 default:
693 break;
694 }
695
696 return FALSE;
697 }
698
699 /*************************************************************************
700 *
701 * CFileDefExt::InitVersionPage [Internal]
702 *
703 * sets all file version properties in dialog
704 */
705
706 BOOL
707 CFileDefExt::InitVersionPage(HWND hwndDlg)
708 {
709 /* Get fixed info */
710 VS_FIXEDFILEINFO *pInfo = m_VerInfo.GetFixedInfo();
711 if (pInfo)
712 {
713 WCHAR wszVersion[256];
714 swprintf(wszVersion, L"%u.%u.%u.%u", HIWORD(pInfo->dwFileVersionMS),
715 LOWORD(pInfo->dwFileVersionMS),
716 HIWORD(pInfo->dwFileVersionLS),
717 LOWORD(pInfo->dwFileVersionLS));
718 TRACE("MS %x LS %x ver %s \n", pInfo->dwFileVersionMS, pInfo->dwFileVersionLS, debugstr_w(wszVersion));
719 SetDlgItemTextW(hwndDlg, 14001, wszVersion);
720 }
721
722 /* Update labels */
723 SetVersionLabel(hwndDlg, 14003, L"FileDescription");
724 SetVersionLabel(hwndDlg, 14005, L"LegalCopyright");
725
726 /* Add items to listbox */
727 AddVersionString(hwndDlg, L"CompanyName");
728 LPCWSTR pwszLang = m_VerInfo.GetLangName();
729 if (pwszLang)
730 {
731 HWND hDlgCtrl = GetDlgItem(hwndDlg, 14009);
732 UINT Index = SendMessageW(hDlgCtrl, LB_ADDSTRING, (WPARAM)-1, (LPARAM)L"Language");
733 SendMessageW(hDlgCtrl, LB_SETITEMDATA, (WPARAM)Index, (LPARAM)(WCHAR *)pwszLang);
734 }
735 AddVersionString(hwndDlg, L"ProductName");
736 AddVersionString(hwndDlg, L"InternalName");
737 AddVersionString(hwndDlg, L"OriginalFilename");
738 AddVersionString(hwndDlg, L"FileVersion");
739 AddVersionString(hwndDlg, L"ProductVersion");
740
741 /* Attach file version to dialog window */
742 SetWindowLongPtr(hwndDlg, DWL_USER, (LONG_PTR)this);
743
744 /* Select first item */
745 HWND hDlgCtrl = GetDlgItem(hwndDlg, 14009);
746 SendMessageW(hDlgCtrl, LB_SETCURSEL, 0, 0);
747 LPCWSTR pwszText = (LPCWSTR)SendMessageW(hDlgCtrl, LB_GETITEMDATA, (WPARAM)0, (LPARAM)NULL);
748 if (pwszText && pwszText != (LPCWSTR)LB_ERR)
749 SetDlgItemTextW(hwndDlg, 14010, pwszText);
750
751 return TRUE;
752 }
753
754 /*************************************************************************
755 *
756 * CFileDefExt::SetVersionLabel [Internal]
757 *
758 * retrieves a version string and uses it to set label text
759 */
760
761 BOOL
762 CFileDefExt::SetVersionLabel(HWND hwndDlg, DWORD idCtrl, LPCWSTR pwszName)
763 {
764 if (hwndDlg == NULL || pwszName == NULL)
765 return FALSE;
766
767 LPCWSTR pwszValue = m_VerInfo.GetString(pwszName);
768 if (pwszValue)
769 {
770 /* file description property */
771 TRACE("%s :: %s\n", debugstr_w(pwszName), debugstr_w(pwszValue));
772 SetDlgItemTextW(hwndDlg, idCtrl, pwszValue);
773 return TRUE;
774 }
775
776 return FALSE;
777 }
778
779 /*************************************************************************
780 *
781 * CFileDefExt::AddVersionString [Internal]
782 *
783 * retrieves a version string and adds it to listbox
784 */
785
786 BOOL
787 CFileDefExt::AddVersionString(HWND hwndDlg, LPCWSTR pwszName)
788 {
789 TRACE("pwszName %s, hwndDlg %p\n", debugstr_w(pwszName), hwndDlg);
790
791 if (hwndDlg == NULL || pwszName == NULL)
792 return FALSE;
793
794 LPCWSTR pwszValue = m_VerInfo.GetString(pwszName);
795 if (pwszValue)
796 {
797 /* listbox name property */
798 HWND hDlgCtrl = GetDlgItem(hwndDlg, 14009);
799 TRACE("%s :: %s\n", debugstr_w(pwszName), debugstr_w(pwszValue));
800 UINT Index = SendMessageW(hDlgCtrl, LB_ADDSTRING, (WPARAM) -1, (LPARAM)pwszName);
801 SendMessageW(hDlgCtrl, LB_SETITEMDATA, (WPARAM)Index, (LPARAM)(WCHAR *)pwszValue);
802 return TRUE;
803 }
804
805 return FALSE;
806 }
807
808 /*************************************************************************
809 *
810 * CFileDefExt::VersionPageProc
811 *
812 * wnd proc of 'Version' property sheet page
813 */
814
815 INT_PTR CALLBACK
816 CFileDefExt::VersionPageProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
817 {
818 switch (uMsg)
819 {
820 case WM_INITDIALOG:
821 {
822 LPPROPSHEETPAGE ppsp = (LPPROPSHEETPAGE)lParam;
823
824 if (ppsp == NULL || !ppsp->lParam)
825 break;
826
827 TRACE("WM_INITDIALOG hwnd %p lParam %p ppsplParam %x\n", hwndDlg, lParam, ppsp->lParam);
828
829 CFileDefExt *pFileDefExt = reinterpret_cast<CFileDefExt *>(ppsp->lParam);
830 return pFileDefExt->InitVersionPage(hwndDlg);
831 }
832 case WM_COMMAND:
833 if (LOWORD(wParam) == 14009 && HIWORD(wParam) == LBN_SELCHANGE)
834 {
835 HWND hDlgCtrl = (HWND)lParam;
836
837 LRESULT Index = SendMessageW(hDlgCtrl, LB_GETCURSEL, (WPARAM)NULL, (LPARAM)NULL);
838 if (Index == LB_ERR)
839 break;
840
841 LPCWSTR pwszData = (LPCWSTR)SendMessageW(hDlgCtrl, LB_GETITEMDATA, (WPARAM)Index, (LPARAM)NULL);
842 if (pwszData == NULL)
843 break;
844
845 TRACE("hDlgCtrl %x string %s\n", hDlgCtrl, debugstr_w(pwszData));
846 SetDlgItemTextW(hwndDlg, 14010, pwszData);
847
848 return TRUE;
849 }
850 break;
851 case WM_DESTROY:
852 break;
853 default:
854 break;
855 }
856
857 return FALSE;
858 }
859
860 CFileDefExt::CFileDefExt():
861 m_bDir(FALSE), m_cFiles(0), m_cFolders(0)
862 {
863 m_wszPath[0] = L'\0';
864 m_DirSize.QuadPart = 0ull;
865 }
866
867 CFileDefExt::~CFileDefExt()
868 {
869
870 }
871
872 HRESULT WINAPI
873 CFileDefExt::Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pDataObj, HKEY hkeyProgID)
874 {
875 FORMATETC format;
876 STGMEDIUM stgm;
877 HRESULT hr;
878
879 TRACE("%p %p %p %p\n", this, pidlFolder, pDataObj, hkeyProgID);
880
881 if (!pDataObj)
882 return E_FAIL;
883
884 format.cfFormat = CF_HDROP;
885 format.ptd = NULL;
886 format.dwAspect = DVASPECT_CONTENT;
887 format.lindex = -1;
888 format.tymed = TYMED_HGLOBAL;
889
890 hr = pDataObj->GetData(&format, &stgm);
891 if (FAILED(hr))
892 return hr;
893
894 if (!DragQueryFileW((HDROP)stgm.hGlobal, 0, m_wszPath, _countof(m_wszPath)))
895 {
896 ERR("DragQueryFileW failed\n");
897 ReleaseStgMedium(&stgm);
898 return E_FAIL;
899 }
900
901 ReleaseStgMedium(&stgm);
902
903 TRACE("File properties %ls\n", m_wszPath);
904 m_bDir = PathIsDirectoryW(m_wszPath) ? TRUE : FALSE;
905 if (!m_bDir)
906 m_VerInfo.Load(m_wszPath);
907
908 return S_OK;
909 }
910
911 HRESULT WINAPI
912 CFileDefExt::QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
913 {
914 UNIMPLEMENTED;
915 return E_NOTIMPL;
916 }
917
918 HRESULT WINAPI
919 CFileDefExt::InvokeCommand(LPCMINVOKECOMMANDINFO lpici)
920 {
921 UNIMPLEMENTED;
922 return E_NOTIMPL;
923 }
924
925 HRESULT WINAPI
926 CFileDefExt::GetCommandString(UINT_PTR idCmd, UINT uType, UINT *pwReserved, LPSTR pszName, UINT cchMax)
927 {
928 UNIMPLEMENTED;
929 return E_NOTIMPL;
930 }
931
932 HRESULT WINAPI
933 CFileDefExt::AddPages(LPFNADDPROPSHEETPAGE pfnAddPage, LPARAM lParam)
934 {
935 HPROPSHEETPAGE hPage;
936 WORD wResId = m_bDir ? IDD_FOLDER_PROPERTIES : IDD_FILE_PROPERTIES;
937
938 hPage = SH_CreatePropertySheetPage(wResId,
939 GeneralPageProc,
940 (LPARAM)this,
941 NULL);
942 if (hPage)
943 pfnAddPage(hPage, lParam);
944
945 if (!m_bDir && GetFileVersionInfoSizeW(m_wszPath, NULL))
946 {
947 hPage = SH_CreatePropertySheetPage(IDD_FILE_VERSION,
948 VersionPageProc,
949 (LPARAM)this,
950 NULL);
951 if (hPage)
952 pfnAddPage(hPage, lParam);
953 }
954
955 return S_OK;
956 }
957
958 HRESULT WINAPI
959 CFileDefExt::ReplacePage(UINT uPageID, LPFNADDPROPSHEETPAGE pfnReplacePage, LPARAM lParam)
960 {
961 UNIMPLEMENTED;
962 return E_NOTIMPL;
963 }
964
965 HRESULT WINAPI
966 CFileDefExt::SetSite(IUnknown *punk)
967 {
968 UNIMPLEMENTED;
969 return E_NOTIMPL;
970 }
971
972 HRESULT WINAPI
973 CFileDefExt::GetSite(REFIID iid, void **ppvSite)
974 {
975 UNIMPLEMENTED;
976 return E_NOTIMPL;
977 }
978
979 DWORD WINAPI
980 CFileDefExt::_CountFolderAndFilesThreadProc(LPVOID lpParameter)
981 {
982 _CountFolderAndFilesData *data = static_cast<_CountFolderAndFilesData*>(lpParameter);
983 DWORD ticks = 0;
984 data->This->CountFolderAndFiles(data->hwndDlg, data->pwszBuf, data->cchBufMax, &ticks);
985
986 //Release the CFileDefExt and data object holds in the copying thread.
987 data->This->Release();
988 HeapFree(GetProcessHeap(), 0, data->pwszBuf);
989 HeapFree(GetProcessHeap(), 0, data);
990
991 return 0;
992 }
993
994 BOOL
995 CFileDefExt::CountFolderAndFiles(HWND hwndDlg, LPWSTR pwszBuf, UINT cchBufMax, DWORD *ticks)
996 {
997 /* Find filename position */
998 UINT cchBuf = wcslen(pwszBuf);
999 WCHAR *pwszFilename = pwszBuf + cchBuf;
1000 size_t cchFilenameMax = cchBufMax - cchBuf;
1001 if (!cchFilenameMax)
1002 return FALSE;
1003 *(pwszFilename++) = '\\';
1004 --cchFilenameMax;
1005
1006 /* Find all files, FIXME: shouldn't be "*"? */
1007 StringCchCopyW(pwszFilename, cchFilenameMax, L"*");
1008
1009 WIN32_FIND_DATAW wfd;
1010 HANDLE hFind = FindFirstFileW(pwszBuf, &wfd);
1011 if (hFind == INVALID_HANDLE_VALUE)
1012 {
1013 ERR("FindFirstFileW %ls failed\n", pwszBuf);
1014 return FALSE;
1015 }
1016
1017 BOOL root = FALSE;
1018 if (*ticks == 0) {
1019 *ticks = GetTickCount();
1020 root = TRUE;
1021 }
1022
1023 do
1024 {
1025 if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1026 {
1027 /* Don't process "." and ".." items */
1028 if (!wcscmp(wfd.cFileName, L".") || !wcscmp(wfd.cFileName, L".."))
1029 continue;
1030
1031 ++m_cFolders;
1032
1033 StringCchCopyW(pwszFilename, cchFilenameMax, wfd.cFileName);
1034 CountFolderAndFiles(hwndDlg, pwszBuf, cchBufMax, ticks);
1035 }
1036 else
1037 {
1038 m_cFiles++;
1039
1040 ULARGE_INTEGER FileSize;
1041 FileSize.u.LowPart = wfd.nFileSizeLow;
1042 FileSize.u.HighPart = wfd.nFileSizeHigh;
1043 m_DirSize.QuadPart += FileSize.QuadPart;
1044 }
1045 if (GetTickCount() - *ticks > (DWORD) 300)
1046 {
1047 /* FIXME Using IsWindow is generally ill advised */
1048 if (IsWindow(hwndDlg))
1049 {
1050 WCHAR wszBuf[MAX_PATH];
1051
1052 if (SH_FormatFileSizeWithBytes(&m_DirSize, wszBuf, _countof(wszBuf)))
1053 SetDlgItemTextW(hwndDlg, 14011, wszBuf);
1054
1055 /* Display files and folders count */
1056 WCHAR wszFormat[256];
1057 LoadStringW(shell32_hInstance, IDS_FILE_FOLDER, wszFormat, _countof(wszFormat));
1058 StringCchPrintfW(wszBuf, _countof(wszBuf), wszFormat, m_cFiles, m_cFolders);
1059 SetDlgItemTextW(hwndDlg, 14027, wszBuf);
1060 *ticks = GetTickCount();
1061 }
1062 else
1063 break;
1064 }
1065 } while(FindNextFileW(hFind, &wfd));
1066
1067 if (root && IsWindow(hwndDlg))
1068 {
1069 WCHAR wszBuf[MAX_PATH];
1070
1071 if (SH_FormatFileSizeWithBytes(&m_DirSize, wszBuf, _countof(wszBuf)))
1072 SetDlgItemTextW(hwndDlg, 14011, wszBuf);
1073
1074 /* Display files and folders count */
1075 WCHAR wszFormat[256];
1076 LoadStringW(shell32_hInstance, IDS_FILE_FOLDER, wszFormat, _countof(wszFormat));
1077 StringCchPrintfW(wszBuf, _countof(wszBuf), wszFormat, m_cFiles, m_cFolders);
1078 SetDlgItemTextW(hwndDlg, 14027, wszBuf);
1079 }
1080
1081 FindClose(hFind);
1082 return TRUE;
1083 }