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