- remove small hack
[reactos.git] / reactos / dll / win32 / shell32 / fprop.c
1 /*
2 * Shell Library Functions
3 *
4 * Copyright 2005 Johannes Anderwald
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21 #include "config.h"
22 #include "wine/port.h"
23 #define YDEBUG
24 #include <string.h>
25 #include <stdarg.h>
26 #include <stdio.h>
27 #include "winerror.h"
28 #include "windef.h"
29 #include "winbase.h"
30 #include "winreg.h"
31 #include "wingdi.h"
32 #include "winuser.h"
33 #include "commdlg.h"
34 #include "wine/debug.h"
35
36 #include "shellapi.h"
37 #include <shlwapi.h>
38 #include "shlobj.h"
39 #include "shell32_main.h"
40 #include "shresdef.h"
41 #include "undocshell.h"
42 #include <prsht.h>
43
44 WINE_DEFAULT_DEBUG_CHANNEL(shell);
45
46 typedef struct _LANGANDCODEPAGE_
47 {
48 WORD lang;
49 WORD code;
50 } LANGANDCODEPAGE, *LPLANGANDCODEPAGE;
51
52 /*************************************************************************
53 *
54 * SH_FileGeneralFileType [Internal]
55 *
56 * retrieves file extension description from registry and sets it in dialog
57 *
58 * TODO: retrieve file extension default icon and load it
59 * find executable name from registry, retrieve description from executable
60 */
61
62 BOOL
63 SH_FileGeneralSetFileType(HWND hwndDlg, WCHAR * filext)
64 {
65 WCHAR name[MAX_PATH];
66 WCHAR value[MAX_PATH];
67 DWORD lname = MAX_PATH;
68 DWORD lvalue = MAX_PATH;
69
70 HKEY hKey;
71 LONG result;
72 HWND hDlgCtrl;
73
74 TRACE("fileext %s\n", debugstr_w(filext));
75
76 if (filext == NULL)
77 return FALSE;
78
79 hDlgCtrl = GetDlgItem(hwndDlg, 14005);
80
81 if (hDlgCtrl == NULL)
82 return FALSE;
83
84 if (RegOpenKeyW(HKEY_CLASSES_ROOT, filext, &hKey) != ERROR_SUCCESS)
85 {
86 /* the fileextension is unknown, so default to string "FileExtension File" */
87 SendMessageW(hDlgCtrl, WM_GETTEXT, (WPARAM)MAX_PATH, (LPARAM)value);
88 sprintfW(name, value, &filext[1]);
89 SendMessageW(hDlgCtrl, WM_SETTEXT, (WPARAM)NULL, (LPARAM)name);
90 return TRUE;
91 }
92 result = RegEnumValueW(hKey, 0, name, &lname, NULL, NULL, (LPBYTE)value, &lvalue);
93 RegCloseKey(hKey);
94
95 if (result != ERROR_SUCCESS)
96 return FALSE;
97 if (RegOpenKeyW(HKEY_CLASSES_ROOT, value, &hKey) == ERROR_SUCCESS)
98 {
99 lvalue = lname = MAX_PATH;
100 result = RegEnumValueW(hKey,0, name, &lname, NULL, NULL, (LPBYTE)value, &lvalue);
101 RegCloseKey(hKey);
102 }
103
104 /* file extension type */
105 SendMessageW(hDlgCtrl, WM_SETTEXT, (WPARAM)NULL, (LPARAM)value);
106 return TRUE;
107 }
108 /*************************************************************************
109 *
110 * SHFileGeneralGetFileTimeString [Internal]
111 *
112 * formats a given LPFILETIME struct into readable user format
113 */
114
115 BOOL
116 SHFileGeneralGetFileTimeString(LPFILETIME lpFileTime, WCHAR * lpResult)
117 {
118 FILETIME ft;
119 SYSTEMTIME dt;
120 WORD wYear;
121 static const WCHAR wFormat[] = {'%','0','2','d','/','%','0','2','d','/','%','0','4','d',' ',' ','%','0','2','d',':','%','0','2','u',0};
122
123 if (lpFileTime == NULL || lpResult == NULL)
124 return FALSE;
125
126 if (!FileTimeToLocalFileTime(lpFileTime, &ft))
127 return FALSE;
128
129 FileTimeToSystemTime(&ft, &dt);
130
131 wYear = dt.wYear;
132 /* ddmmyy */
133 sprintfW (lpResult, wFormat, dt.wDay, dt.wMonth, wYear, dt.wHour, dt.wMinute);
134
135 TRACE("result %s\n",debugstr_w(lpResult));
136 return TRUE;
137 }
138
139 /*************************************************************************
140 *
141 * SH_FileGeneralSetText [Internal]
142 *
143 * sets file path string and filename string
144 *
145 */
146
147 BOOL
148 SH_FileGeneralSetText(HWND hwndDlg, WCHAR * lpstr)
149 {
150 int flength;
151 int plength;
152 WCHAR * lpdir;
153 WCHAR buff[MAX_PATH];
154 HWND hDlgCtrl;
155
156 if (lpstr == NULL)
157 return FALSE;
158
159 lpdir = strrchrW(lpstr, '\\'); /* find the last occurence of '\\' */
160
161 plength = strlenW(lpstr);
162 flength = strlenW(lpdir);
163
164 if (lpdir)
165 {
166 /* location text field */
167 strncpyW(buff, lpstr, plength - flength);
168 buff[plength - flength] = UNICODE_NULL;
169 hDlgCtrl = GetDlgItem(hwndDlg, 14009);
170 SendMessageW(hDlgCtrl, WM_SETTEXT, (WPARAM)NULL, (LPARAM)buff);
171 }
172
173 if(flength > 1)
174 {
175 /* text filename field */
176 strncpyW(buff, &lpdir[1], flength);
177 hDlgCtrl = GetDlgItem(hwndDlg, 14001);
178 SendMessageW(hDlgCtrl, WM_SETTEXT, (WPARAM)NULL, (LPARAM)buff);
179 }
180
181 return TRUE;
182 }
183
184 /*************************************************************************
185 *
186 * SH_FileGeneralSetFileSizeTime [Internal]
187 *
188 * retrieves file information from file and sets in dialog
189 *
190 */
191
192 BOOL
193 SH_FileGeneralSetFileSizeTime(HWND hwndDlg, WCHAR * lpfilename, PULARGE_INTEGER lpfilesize)
194 {
195 BOOL result;
196 HANDLE hFile;
197 FILETIME create_time;
198 FILETIME accessed_time;
199 FILETIME write_time;
200 WCHAR resultstr[MAX_PATH];
201 HWND hDlgCtrl;
202 LARGE_INTEGER file_size;
203
204 if (lpfilename == NULL)
205 return FALSE;
206
207 hFile = CreateFileW(lpfilename,
208 GENERIC_READ,
209 FILE_SHARE_READ,NULL,
210 OPEN_EXISTING,
211 FILE_ATTRIBUTE_NORMAL,
212 NULL);
213
214 if (hFile == INVALID_HANDLE_VALUE)
215 {
216 WARN("failed to open file %s\n", debugstr_w(lpfilename));
217 return FALSE;
218 }
219
220 result = GetFileTime(hFile, &create_time, &accessed_time, &write_time);
221
222 if (!result)
223 {
224 WARN("GetFileTime failed\n");
225 return FALSE;
226 }
227 if (SHFileGeneralGetFileTimeString(&create_time,resultstr))
228 {
229 hDlgCtrl = GetDlgItem(hwndDlg, 14015);
230 SendMessageW(hDlgCtrl, WM_SETTEXT, (WPARAM)NULL, (LPARAM)resultstr);
231 }
232
233 if (SHFileGeneralGetFileTimeString(&accessed_time, resultstr))
234 {
235 hDlgCtrl = GetDlgItem(hwndDlg, 14017);
236 SendMessageW(hDlgCtrl, WM_SETTEXT, (WPARAM)NULL, (LPARAM)resultstr);
237 }
238
239 if (SHFileGeneralGetFileTimeString(&write_time, resultstr))
240 {
241 hDlgCtrl = GetDlgItem(hwndDlg, 14019);
242 SendMessageW(hDlgCtrl, WM_SETTEXT, (WPARAM)NULL, (LPARAM)resultstr);
243 }
244
245 if (!GetFileSizeEx(hFile, &file_size))
246 {
247 WARN("GetFileSize failed\n");
248 CloseHandle(hFile);
249 return FALSE;
250 }
251 CloseHandle(hFile);
252 if (!StrFormatByteSizeW(file_size.QuadPart, resultstr, sizeof(resultstr) / sizeof(WCHAR)))
253 return FALSE;
254 hDlgCtrl = GetDlgItem(hwndDlg, 14011);
255 TRACE("result size %u resultstr %s\n", file_size.QuadPart, debugstr_w(resultstr));
256 SendMessageW(hDlgCtrl, WM_SETTEXT, (WPARAM)NULL, (LPARAM)resultstr);
257
258 if (lpfilesize)
259 lpfilesize->QuadPart = (ULONGLONG)file_size.QuadPart;
260
261 return TRUE;
262 }
263
264 /*************************************************************************
265 *
266 * SH_SetFileVersionText [Internal]
267 *
268 *
269 */
270
271 BOOL
272 SH_FileVersionQuerySetText(HWND hwndDlg, DWORD dlgId, LPVOID pInfo, WCHAR * text, WCHAR ** resptr)
273 {
274 UINT reslen;
275 HWND hDlgCtrl;
276
277 if(hwndDlg == NULL || resptr == NULL || text == NULL)
278 return FALSE;
279
280 if(VerQueryValueW(pInfo, text, (LPVOID *)resptr, &reslen))
281 {
282 /* file description property */
283 hDlgCtrl = GetDlgItem(hwndDlg, dlgId);
284 TRACE("%s :: %s\n",debugstr_w(text), debugstr_w(*resptr));
285 SendMessageW(hDlgCtrl, WM_SETTEXT, (WPARAM)0, (LPARAM)*resptr);
286 return TRUE;
287 }
288 return FALSE;
289 }
290
291 /*************************************************************************
292 *
293 * SH_FileVersionQuerySetListText [Internal]
294 *
295 * retrieves a version string and adds it to listbox
296 *
297 */
298
299
300 BOOL
301 SH_FileVersionQuerySetListText(HWND hwndDlg, LPVOID pInfo, const WCHAR * text, WCHAR **resptr, WORD lang, WORD code)
302 {
303 UINT reslen;
304 HWND hDlgCtrl;
305 UINT index;
306 static const WCHAR wFormat[] = { '\\','S','t','r','i','n','g','F','i','l','e','I','n',
307 'f','o','\\','%','0','4','x','%','0','4','x','\\','%','s',0 };
308 WCHAR buff[256];
309
310 TRACE("text %s, resptr %p hwndDlg %p\n",debugstr_w(text), resptr, hwndDlg);
311
312 if(hwndDlg == NULL || resptr == NULL || text == NULL)
313 return FALSE;
314
315 sprintfW(buff, wFormat, lang, code, text);
316 if(VerQueryValueW(pInfo, buff, (LPVOID *)resptr, &reslen))
317 {
318 /* listbox name property */
319 hDlgCtrl = GetDlgItem(hwndDlg, 14009);
320 TRACE("%s :: %s\n",debugstr_w(text), debugstr_w(*resptr));
321 index = SendMessageW(hDlgCtrl, LB_ADDSTRING, (WPARAM)-1, (LPARAM)text);
322 SendMessageW(hDlgCtrl, LB_SETITEMDATA, (WPARAM)index, (LPARAM)(WCHAR*)*resptr);
323 return TRUE;
324 }
325 return FALSE;
326 }
327
328 /*************************************************************************
329 *
330 * SH_FileVersionInitialize [Internal]
331 *
332 * sets all file version properties in dialog
333 */
334 BOOL
335 SH_FileVersionInitialize(HWND hwndDlg, WCHAR * lpfilename)
336 {
337 LPVOID pBuf;
338 DWORD versize;
339 DWORD handle;
340 LPVOID info = NULL;
341 UINT infolen;
342 WCHAR buff[256];
343 HWND hDlgCtrl;
344 WORD lang = 0;
345 WORD code = 0;
346 LPLANGANDCODEPAGE lplangcode;
347 WCHAR * str;
348 static const WCHAR wVersionFormat[] = { '%','d','.','%','d','.','%','d','.','%','d',0 };
349 static const WCHAR wFileDescriptionFormat[] = { '\\','S','t','r','i','n','g','F','i','l','e','I','n','f','o',
350 '\\','%','0','4','x','%','0','4','x','\\','F','i','l','e','D','e','s','c','r','i','p','t','i','o','n',0 };
351 static const WCHAR wLegalCopyrightFormat[] = { '\\','S','t','r','i','n','g','F','i','l','e','I','n','f','o',
352 '\\','%','0','4','x','%','0','4','x','\\','L','e','g','a','l','C','o','p','y','r','i','g','h','t',0 };
353 static const WCHAR wTranslation[] = { 'V','a','r','F','i','l','e','I','n','f','o','\\','T','r','a','n','s','l','a','t','i','o','n',0 };
354 static const WCHAR wCompanyName[] = { 'C','o','m','p','a','n','y','N','a','m','e',0 };
355 static const WCHAR wFileVersion[] = { 'F','i','l','e','V','e','r','s','i','o','n',0 };
356 static const WCHAR wInternalName[] = { 'I','n','t','e','r','n','a','l','N','a','m','e',0 };
357 static const WCHAR wOriginalFilename[] = { 'O','r','i','g','i','n','a','l','F','i','l','e','n','a','m','e',0 };
358 static const WCHAR wProductName[] = { 'P','r','o','d','u','c','t','N','a','m','e',0 };
359 static const WCHAR wProductVersion[] = { 'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0 };
360 static const WCHAR wSlash[] = { '\\',0 };
361
362
363 if(lpfilename == 0)
364 return FALSE;
365
366 if(!(versize = GetFileVersionInfoSizeW(lpfilename, &handle)))
367 {
368 WARN("GetFileVersionInfoSize failed\n");
369 return FALSE;
370 }
371
372 if(!(pBuf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, versize)))
373 {
374 WARN("HeapAlloc failed bytes %x\n",versize);
375 return FALSE;
376 }
377
378 if(!GetFileVersionInfoW(lpfilename, handle, versize, pBuf))
379 {
380 HeapFree(GetProcessHeap(), 0, pBuf);
381 return FALSE;
382 }
383 if(VerQueryValueW(pBuf, wSlash, &info, &infolen))
384 {
385 VS_FIXEDFILEINFO * inf = (VS_FIXEDFILEINFO *)info;
386 sprintfW(buff, wVersionFormat,inf->dwFileVersionMS & 0xFFFF0000,
387 inf->dwFileVersionMS & 0x0000FFFF,
388 inf->dwFileVersionLS & 0xFFFF0000,
389 inf->dwFileVersionLS & 0x0000FFFF);
390
391 hDlgCtrl = GetDlgItem(hwndDlg, 14001);
392 TRACE("MS %x LS %x res %s \n",inf->dwFileVersionMS, inf->dwFileVersionLS, debugstr_w(buff));
393 SendMessageW(hDlgCtrl, WM_SETTEXT, (WPARAM)NULL, (LPARAM)buff);
394 }
395 if(VerQueryValueW(pBuf, wTranslation, (LPVOID *)&lplangcode, &infolen))
396 {
397 /* FIXME find language from current locale / if not available,
398 * default to english
399 * for now default to first available language
400 */
401 lang = lplangcode->lang;
402 code = lplangcode->code;
403 }
404
405 sprintfW(buff, wFileDescriptionFormat, lang, code);
406 SH_FileVersionQuerySetText(hwndDlg, 14003, pBuf, buff, &str);
407
408 sprintfW(buff, wLegalCopyrightFormat, lang, code);
409 SH_FileVersionQuerySetText(hwndDlg, 14005, pBuf, buff, &str);
410
411 /* listbox properties */
412 SH_FileVersionQuerySetListText(hwndDlg, pBuf, wCompanyName, &str, lang, code);
413 SH_FileVersionQuerySetListText(hwndDlg, pBuf, wFileVersion, &str, lang, code);
414 SH_FileVersionQuerySetListText(hwndDlg, pBuf, wInternalName, &str, lang, code);
415
416 /* FIXME insert language identifier */
417
418 SH_FileVersionQuerySetListText(hwndDlg, pBuf, wOriginalFilename, &str, lang, code);
419 SH_FileVersionQuerySetListText(hwndDlg, pBuf, wProductName, &str, lang, code);
420 SH_FileVersionQuerySetListText(hwndDlg, pBuf, wProductVersion, &str, lang, code);
421 SetWindowLong(hwndDlg, DWL_USER, (LONG)pBuf);
422
423 /* select first item */
424 hDlgCtrl = GetDlgItem(hwndDlg, 14009);
425 SendMessageW(hDlgCtrl, LB_SETCURSEL, 0, 0);
426 str = (WCHAR *)SendMessageW(hDlgCtrl, LB_GETITEMDATA, (WPARAM)0, (LPARAM)NULL);
427 hDlgCtrl = GetDlgItem(hwndDlg, 14010);
428 SendMessageW(hDlgCtrl, WM_SETTEXT, (WPARAM)NULL, (LPARAM)str);
429 return TRUE;
430 }
431
432 /*************************************************************************
433 *
434 * SH_FileVersionDlgProc
435 *
436 * wnd proc of 'Version' property sheet page
437 */
438 INT_PTR
439 CALLBACK
440 SH_FileVersionDlgProc(
441 HWND hwndDlg,
442 UINT uMsg,
443 WPARAM wParam,
444 LPARAM lParam
445 )
446 {
447 LPPROPSHEETPAGE ppsp;
448 WCHAR * lpstr;
449 LPVOID * buf;
450 switch(uMsg)
451 {
452 case WM_INITDIALOG:
453 ppsp = (LPPROPSHEETPAGE)lParam;
454 if(ppsp == NULL)
455 break;
456
457 TRACE("WM_INITDIALOG hwnd %p lParam %p ppsplParam %x\n",hwndDlg, lParam, ppsp->lParam);
458
459 lpstr = (WCHAR *)ppsp->lParam;
460
461 if(lpstr == NULL)
462 break;
463
464 return SH_FileVersionInitialize(hwndDlg, lpstr);
465
466
467 case WM_COMMAND:
468 if(LOWORD(wParam) == 14009 && HIWORD(wParam) == LBN_DBLCLK)
469 {
470 HWND hDlgCtrl;
471 LRESULT lresult;
472 WCHAR * str;
473
474 hDlgCtrl = GetDlgItem(hwndDlg, 14009);
475 lresult = SendMessageW(hDlgCtrl, LB_GETCURSEL, (WPARAM)NULL, (LPARAM)NULL);
476 if(lresult == LB_ERR)
477 {
478 break;
479 }
480 str = (WCHAR *)SendMessageW(hDlgCtrl, LB_GETITEMDATA, (WPARAM)lresult, (LPARAM)NULL);
481
482 if(str == NULL)
483 {
484 break;
485 }
486 hDlgCtrl = GetDlgItem(hwndDlg, 14010);
487 TRACE("hDlgCtrl %x string %s \n",hDlgCtrl, debugstr_w(str));
488 SendMessageW(hDlgCtrl, WM_SETTEXT, (WPARAM)NULL, (LPARAM)str);
489 return TRUE;
490 }
491 case WM_DESTROY:
492 buf = (LPVOID)GetWindowLong(hwndDlg, DWL_USER);
493 HeapFree(GetProcessHeap(), 0, buf);
494 default:
495 break;
496 }
497 return FALSE;
498 }
499
500 /*************************************************************************
501 *
502 * SH_FileGeneralDlgProc
503 *
504 * wnd proc of 'General' property sheet page
505 *
506 */
507
508 INT_PTR
509 CALLBACK
510 SH_FileGeneralDlgProc(
511 HWND hwndDlg,
512 UINT uMsg,
513 WPARAM wParam,
514 LPARAM lParam
515 )
516 {
517 LPPROPSHEETPAGEW ppsp;
518 WCHAR * lpstr;
519 switch(uMsg)
520 {
521 case WM_INITDIALOG:
522 ppsp = (LPPROPSHEETPAGEW)lParam;
523 if (ppsp == NULL)
524 break;
525 TRACE("WM_INITDIALOG hwnd %p lParam %p ppsplParam %S\n",hwndDlg, lParam, ppsp->lParam);
526
527 lpstr = (WCHAR *)ppsp->lParam;
528
529 if ( lpstr == NULL)
530 {
531 ERR("no filename\n");
532 break;
533 }
534 /* set general text properties filename filelocation and icon */
535 SH_FileGeneralSetText(hwndDlg, lpstr);
536 /* enumerate file extension from registry and application which opens it*/
537 SH_FileGeneralSetFileType(hwndDlg, strrchrW(lpstr, '.'));
538 /* set file time create/modfied/accessed */
539 SH_FileGeneralSetFileSizeTime(hwndDlg, lpstr, NULL);
540 return TRUE;
541 default:
542 break;
543 }
544 return FALSE;
545 }
546
547
548 /*************************************************************************
549 *
550 * SH_ShowPropertiesDialog
551 *
552 * called from ShellExecuteExW32
553 *
554 * lpf contains (quoted) path of folder/file
555 *
556 * TODO: provide button change application type if file has registered type
557 * make filename field editable and apply changes to filename on close
558 */
559
560 BOOL
561 SH_ShowPropertiesDialog(WCHAR * lpf)
562 {
563 PROPSHEETHEADERW pinfo;
564 HPROPSHEETPAGE hppages[MAX_PROPERTY_SHEET_PAGE];
565 HPROPSHEETPAGE hpage;
566 WCHAR wFileName[MAX_PATH];
567 UINT num_pages = 0;
568 DWORD dwHandle = 0;
569
570 TRACE("SH_ShowPropertiesDialog entered filename %s\n", debugstr_w(lpf));
571
572 if (lpf== NULL)
573 return FALSE;
574
575 if ( !strlenW(lpf) )
576 return FALSE;
577
578 memset(hppages, 0x0, sizeof(HPROPSHEETPAGE) * MAX_PROPERTY_SHEET_PAGE);
579 if (lpf[0] == '"')
580 {
581 /* remove quotes from lpf */
582 LPWSTR src = lpf + 1;
583 LPWSTR dst = wFileName;
584
585 while(*src && *src!='"')
586 *dst++ = *src++;
587
588 *dst = '\0';
589 }
590 else
591 {
592 strcpyW(wFileName, lpf);
593 }
594
595 if (PathIsDirectoryW(wFileName) || strlenW(wFileName) == 3)
596 {
597 FIXME("directory / drive resources are missing\n");
598 return FALSE;
599 }
600 hpage = SH_CreatePropertySheetPage("SHELL_FILE_GENERAL_DLG", SH_FileGeneralDlgProc, (LPARAM)wFileName);
601
602 if (hpage == NULL)
603 return FALSE;
604
605 hppages[num_pages] = hpage;
606 num_pages++;
607 if ( GetFileVersionInfoSizeW(lpf, &dwHandle) )
608 {
609 if ( (hpage = SH_CreatePropertySheetPage("SHELL_FILE_VERSION_DLG",SH_FileVersionDlgProc, (LPARAM)lpf))!= NULL)
610 {
611 hppages[num_pages] = hpage;
612 num_pages++;
613 }
614 }
615 memset(&pinfo, 0x0, sizeof(PROPSHEETHEADERW));
616 pinfo.dwSize = sizeof(PROPSHEETHEADERW);
617 pinfo.dwFlags = PSH_NOCONTEXTHELP | PSH_PROPTITLE;
618 pinfo.nPages = num_pages;
619 pinfo.phpage = hppages;
620 pinfo.pszCaption = wFileName;
621 return (PropertySheetW(&pinfo) != -1);
622 }
623 /*EOF */