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