[SHELL32] Add icons to the 'File Types' listview (#557)
[reactos.git] / dll / win32 / shell32 / dialogs / folder_options.cpp
1 /*
2 * Open With Context Menu extension
3 *
4 * Copyright 2007 Johannes Anderwald <johannes.anderwald@reactos.org>
5 * Copyright 2016-2018 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
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 (fprop);
25
26 /// Folder Options:
27 /// CLASSKEY = HKEY_CLASSES_ROOT\CLSID\{6DFD7C5C-2451-11d3-A299-00C04F8EF6AF}
28 /// DefaultIcon = %SystemRoot%\system32\SHELL32.dll,-210
29 /// Verbs: Open / RunAs
30 /// Cmd: rundll32.exe shell32.dll,Options_RunDLL 0
31
32 /// ShellFolder Attributes: 0x0
33
34 typedef struct
35 {
36 WCHAR FileExtension[30];
37 WCHAR FileDescription[100];
38 WCHAR ClassKey[MAX_PATH];
39 DWORD EditFlags;
40 WCHAR AppName[64];
41 HICON hIconLarge;
42 HICON hIconSmall;
43 WCHAR ProgramPath[MAX_PATH];
44 } FOLDER_FILE_TYPE_ENTRY, *PFOLDER_FILE_TYPE_ENTRY;
45
46 // uniquely-defined icon entry for Advanced Settings
47 typedef struct ADVANCED_ICON
48 {
49 WCHAR szPath[MAX_PATH];
50 UINT nIconIndex;
51 } ADVANCED_ICON;
52
53 // predefined icon IDs (See CreateTreeImageList function below)
54 #define I_CHECKED 0
55 #define I_UNCHECKED 1
56 #define I_CHECKED_DISABLED 2
57 #define I_UNCHECKED_DISABLED 3
58 #define I_RADIO_CHECKED 4
59 #define I_RADIO_UNCHECKED 5
60 #define I_RADIO_CHECKED_DISABLED 6
61 #define I_RADIO_UNCHECKED_DISABLED 7
62
63 #define PREDEFINED_ICON_COUNT 8
64
65 // definition of icon stock
66 static ADVANCED_ICON * s_AdvancedIcons = NULL;
67 static INT s_AdvancedIconCount = 0;
68 static HIMAGELIST s_hImageList = NULL;
69
70 static INT
71 Advanced_FindIcon(LPCWSTR pszPath, UINT nIconIndex)
72 {
73 for (INT i = PREDEFINED_ICON_COUNT; i < s_AdvancedIconCount; ++i)
74 {
75 ADVANCED_ICON *pIcon = &s_AdvancedIcons[i];
76 if (pIcon->nIconIndex == nIconIndex &&
77 lstrcmpiW(pIcon->szPath, pszPath) == 0)
78 {
79 return i; // icon ID
80 }
81 }
82 return -1; // not found
83 }
84
85 static INT
86 Advanced_AddIcon(LPCWSTR pszPath, UINT nIconIndex)
87 {
88 ADVANCED_ICON *pAllocated;
89
90 // return the ID if already existed
91 INT nIconID = Advanced_FindIcon(pszPath, nIconIndex);
92 if (nIconID != -1)
93 return nIconID; // already exists
94
95 // extract a small icon
96 HICON hIconSmall = NULL;
97 ExtractIconExW(pszPath, nIconIndex, NULL, &hIconSmall, 1);
98 if (hIconSmall == NULL)
99 return -1; // failure
100
101 // resize s_AdvancedIcons
102 size_t Size = (s_AdvancedIconCount + 1) * sizeof(ADVANCED_ICON);
103 pAllocated = (ADVANCED_ICON *)realloc(s_AdvancedIcons, Size);
104 if (pAllocated == NULL)
105 return -1; // failure
106 else
107 s_AdvancedIcons = pAllocated;
108
109 // save icon information
110 ADVANCED_ICON *pIcon = &s_AdvancedIcons[s_AdvancedIconCount];
111 lstrcpynW(pIcon->szPath, pszPath, _countof(pIcon->szPath));
112 pIcon->nIconIndex = nIconIndex;
113
114 // add the icon to the image list
115 ImageList_AddIcon(s_hImageList, hIconSmall);
116
117 // increment the counter
118 nIconID = s_AdvancedIconCount;
119 ++s_AdvancedIconCount;
120
121 DestroyIcon(hIconSmall);
122
123 return nIconID; // newly-added icon ID
124 }
125
126 // types of Advanced Setting entry
127 typedef enum ADVANCED_ENTRY_TYPE
128 {
129 AETYPE_GROUP,
130 AETYPE_CHECKBOX,
131 AETYPE_RADIO,
132 } ADVANCED_ENTRY_TYPE;
133
134 // an entry info of Advanced Settings
135 typedef struct ADVANCED_ENTRY
136 {
137 DWORD dwID; // entry ID
138 DWORD dwParentID; // parent entry ID
139 DWORD dwResourceID; // resource ID
140 WCHAR szKeyName[64]; // entry key name
141 DWORD dwType; // ADVANCED_ENTRY_TYPE
142 WCHAR szText[MAX_PATH]; // text
143 INT nIconID; // icon ID (See ADVANCED_ICON)
144
145 HKEY hkeyRoot; // registry root key
146 WCHAR szRegPath[MAX_PATH]; // registry path
147 WCHAR szValueName[64]; // registry value name
148
149 DWORD dwCheckedValue; // checked value
150 DWORD dwUncheckedValue; // unchecked value
151 DWORD dwDefaultValue; // defalut value
152 BOOL bHasUncheckedValue; // If FALSE, UncheckedValue is invalid
153
154 HTREEITEM hItem; // for TreeView
155 BOOL bGrayed; // disabled?
156 BOOL bChecked; // checked?
157 } ADVANCED_ENTRY, *PADVANCED_ENTRY;
158
159 // definition of advanced entries
160 static ADVANCED_ENTRY * s_Advanced = NULL;
161 static INT s_AdvancedCount = 0;
162
163 static HBITMAP
164 Create24BppBitmap(HDC hDC, INT cx, INT cy)
165 {
166 BITMAPINFO bi;
167 LPVOID pvBits;
168
169 ZeroMemory(&bi, sizeof(bi));
170 bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
171 bi.bmiHeader.biWidth = cx;
172 bi.bmiHeader.biHeight = cy;
173 bi.bmiHeader.biPlanes = 1;
174 bi.bmiHeader.biBitCount = 24;
175 bi.bmiHeader.biCompression = BI_RGB;
176
177 HBITMAP hbm = CreateDIBSection(hDC, &bi, DIB_RGB_COLORS, &pvBits, NULL, 0);
178 return hbm;
179 }
180
181 static HBITMAP BitmapFromIcon(HICON hIcon, INT cx, INT cy)
182 {
183 HDC hDC = CreateCompatibleDC(NULL);
184 if (!hDC)
185 return NULL;
186
187 HBITMAP hbm = Create24BppBitmap(hDC, cx, cy);
188 if (!hbm)
189 {
190 DeleteDC(hDC);
191 return NULL;
192 }
193
194 HGDIOBJ hbmOld = SelectObject(hDC, hbm);
195 {
196 RECT rc = { 0, 0, cx, cy };
197 FillRect(hDC, &rc, HBRUSH(COLOR_3DFACE + 1));
198 if (hIcon)
199 {
200 DrawIconEx(hDC, 0, 0, hIcon, cx, cy, 0, NULL, DI_NORMAL);
201 }
202 }
203 SelectObject(hDC, hbmOld);
204 DeleteDC(hDC);
205
206 return hbm;
207 }
208
209 static HBITMAP
210 CreateCheckImage(HDC hDC, BOOL bCheck, BOOL bEnabled = TRUE)
211 {
212 INT cxSmallIcon = GetSystemMetrics(SM_CXSMICON);
213 INT cySmallIcon = GetSystemMetrics(SM_CYSMICON);
214
215 HBITMAP hbm = Create24BppBitmap(hDC, cxSmallIcon, cySmallIcon);
216 if (hbm == NULL)
217 return NULL; // failure
218
219 RECT Rect, BoxRect;
220 SetRect(&Rect, 0, 0, cxSmallIcon, cySmallIcon);
221 BoxRect = Rect;
222 InflateRect(&BoxRect, -1, -1);
223
224 HGDIOBJ hbmOld = SelectObject(hDC, hbm);
225 {
226 UINT uState = DFCS_BUTTONCHECK | DFCS_FLAT | DFCS_MONO;
227 if (bCheck)
228 uState |= DFCS_CHECKED;
229 if (!bEnabled)
230 uState |= DFCS_INACTIVE;
231 DrawFrameControl(hDC, &BoxRect, DFC_BUTTON, uState);
232 }
233 SelectObject(hDC, hbmOld);
234
235 return hbm; // success
236 }
237
238 static HBITMAP
239 CreateCheckMask(HDC hDC)
240 {
241 INT cxSmallIcon = GetSystemMetrics(SM_CXSMICON);
242 INT cySmallIcon = GetSystemMetrics(SM_CYSMICON);
243
244 HBITMAP hbm = CreateBitmap(cxSmallIcon, cySmallIcon, 1, 1, NULL);
245 if (hbm == NULL)
246 return NULL; // failure
247
248 RECT Rect, BoxRect;
249 SetRect(&Rect, 0, 0, cxSmallIcon, cySmallIcon);
250 BoxRect = Rect;
251 InflateRect(&BoxRect, -1, -1);
252
253 HGDIOBJ hbmOld = SelectObject(hDC, hbm);
254 {
255 FillRect(hDC, &Rect, HBRUSH(GetStockObject(WHITE_BRUSH)));
256 FillRect(hDC, &BoxRect, HBRUSH(GetStockObject(BLACK_BRUSH)));
257 }
258 SelectObject(hDC, hbmOld);
259
260 return hbm; // success
261 }
262
263 static HBITMAP
264 CreateRadioImage(HDC hDC, BOOL bCheck, BOOL bEnabled = TRUE)
265 {
266 INT cxSmallIcon = GetSystemMetrics(SM_CXSMICON);
267 INT cySmallIcon = GetSystemMetrics(SM_CYSMICON);
268
269 HBITMAP hbm = Create24BppBitmap(hDC, cxSmallIcon, cySmallIcon);
270 if (hbm == NULL)
271 return NULL; // failure
272
273 RECT Rect, BoxRect;
274 SetRect(&Rect, 0, 0, cxSmallIcon, cySmallIcon);
275 BoxRect = Rect;
276 InflateRect(&BoxRect, -1, -1);
277
278 HGDIOBJ hbmOld = SelectObject(hDC, hbm);
279 {
280 UINT uState = DFCS_BUTTONRADIOIMAGE | DFCS_FLAT | DFCS_MONO;
281 if (bCheck)
282 uState |= DFCS_CHECKED;
283 if (!bEnabled)
284 uState |= DFCS_INACTIVE;
285 DrawFrameControl(hDC, &BoxRect, DFC_BUTTON, uState);
286 }
287 SelectObject(hDC, hbmOld);
288
289 return hbm; // success
290 }
291
292 static HBITMAP
293 CreateRadioMask(HDC hDC)
294 {
295 INT cxSmallIcon = GetSystemMetrics(SM_CXSMICON);
296 INT cySmallIcon = GetSystemMetrics(SM_CYSMICON);
297
298 HBITMAP hbm = CreateBitmap(cxSmallIcon, cySmallIcon, 1, 1, NULL);
299 if (hbm == NULL)
300 return NULL; // failure
301
302 RECT Rect, BoxRect;
303 SetRect(&Rect, 0, 0, cxSmallIcon, cySmallIcon);
304 BoxRect = Rect;
305 InflateRect(&BoxRect, -1, -1);
306
307 HGDIOBJ hbmOld = SelectObject(hDC, hbm);
308 {
309 FillRect(hDC, &Rect, HBRUSH(GetStockObject(WHITE_BRUSH)));
310 UINT uState = DFCS_BUTTONRADIOMASK | DFCS_FLAT | DFCS_MONO;
311 DrawFrameControl(hDC, &BoxRect, DFC_BUTTON, uState);
312 }
313 SelectObject(hDC, hbmOld);
314
315 return hbm; // success
316 }
317
318 static HIMAGELIST
319 CreateTreeImageList(VOID)
320 {
321 HIMAGELIST hImageList;
322 hImageList = ImageList_Create(16, 16, ILC_COLOR24 | ILC_MASK, 9, 1);
323 if (hImageList == NULL)
324 return NULL; // failure
325
326 // free if existed
327 if (s_AdvancedIcons)
328 {
329 free(s_AdvancedIcons);
330 s_AdvancedIcons = NULL;
331 }
332 s_AdvancedIconCount = 0;
333
334 // allocate now
335 ADVANCED_ICON *pAllocated;
336 size_t Size = PREDEFINED_ICON_COUNT * sizeof(ADVANCED_ICON);
337 pAllocated = (ADVANCED_ICON *)calloc(1, Size);
338 if (pAllocated == NULL)
339 return NULL; // failure
340
341 s_AdvancedIconCount = PREDEFINED_ICON_COUNT;
342 s_AdvancedIcons = pAllocated;
343
344 // add the predefined icons
345
346 HDC hDC = CreateCompatibleDC(NULL);
347 HBITMAP hbmMask = CreateCheckMask(hDC);
348
349 HBITMAP hbmChecked, hbmUnchecked;
350
351 hbmChecked = CreateCheckImage(hDC, TRUE);
352 ImageList_Add(hImageList, hbmChecked, hbmMask);
353 DeleteObject(hbmChecked);
354
355 hbmUnchecked = CreateCheckImage(hDC, FALSE);
356 ImageList_Add(hImageList, hbmUnchecked, hbmMask);
357 DeleteObject(hbmUnchecked);
358
359 hbmChecked = CreateCheckImage(hDC, TRUE, FALSE);
360 ImageList_Add(hImageList, hbmChecked, hbmMask);
361 DeleteObject(hbmChecked);
362
363 hbmUnchecked = CreateCheckImage(hDC, FALSE, FALSE);
364 ImageList_Add(hImageList, hbmUnchecked, hbmMask);
365 DeleteObject(hbmUnchecked);
366
367 DeleteObject(hbmMask);
368 hbmMask = CreateRadioMask(hDC);
369
370 hbmChecked = CreateRadioImage(hDC, TRUE);
371 ImageList_Add(hImageList, hbmChecked, hbmMask);
372 DeleteObject(hbmChecked);
373
374 hbmUnchecked = CreateRadioImage(hDC, FALSE);
375 ImageList_Add(hImageList, hbmUnchecked, hbmMask);
376 DeleteObject(hbmUnchecked);
377
378 hbmChecked = CreateRadioImage(hDC, TRUE, FALSE);
379 ImageList_Add(hImageList, hbmChecked, hbmMask);
380 DeleteObject(hbmChecked);
381
382 hbmUnchecked = CreateRadioImage(hDC, FALSE, FALSE);
383 ImageList_Add(hImageList, hbmUnchecked, hbmMask);
384 DeleteObject(hbmUnchecked);
385
386 DeleteObject(hbmMask);
387
388 return hImageList;
389 }
390
391 static ADVANCED_ENTRY *
392 Advanced_GetItem(DWORD dwID)
393 {
394 if (dwID == DWORD(-1))
395 return NULL;
396
397 for (INT i = 0; i < s_AdvancedCount; ++i)
398 {
399 ADVANCED_ENTRY *pEntry = &s_Advanced[i];
400 if (pEntry->dwID == dwID)
401 return pEntry;
402 }
403 return NULL; // failure
404 }
405
406 static INT
407 Advanced_GetImage(ADVANCED_ENTRY *pEntry)
408 {
409 switch (pEntry->dwType)
410 {
411 case AETYPE_GROUP:
412 return pEntry->nIconID;
413
414 case AETYPE_CHECKBOX:
415 if (pEntry->bGrayed)
416 {
417 if (pEntry->bChecked)
418 return I_CHECKED_DISABLED;
419 else
420 return I_UNCHECKED_DISABLED;
421 }
422 else
423 {
424 if (pEntry->bChecked)
425 return I_CHECKED;
426 else
427 return I_UNCHECKED;
428 }
429
430 case AETYPE_RADIO:
431 if (pEntry->bGrayed)
432 {
433 if (pEntry->bChecked)
434 return I_RADIO_CHECKED_DISABLED;
435 else
436 return I_RADIO_UNCHECKED_DISABLED;
437 }
438 else
439 {
440 if (pEntry->bChecked)
441 return I_RADIO_CHECKED;
442 else
443 return I_RADIO_UNCHECKED;
444 }
445 }
446 return -1; // failure
447 }
448
449 static VOID
450 Advanced_InsertEntry(HWND hwndTreeView, ADVANCED_ENTRY *pEntry)
451 {
452 ADVANCED_ENTRY *pParent = Advanced_GetItem(pEntry->dwParentID);
453 HTREEITEM hParent = TVI_ROOT;
454 if (pParent)
455 hParent = pParent->hItem;
456
457 TV_INSERTSTRUCT Insertion;
458 ZeroMemory(&Insertion, sizeof(Insertion));
459 Insertion.hParent = hParent;
460 Insertion.hInsertAfter = TVI_LAST;
461 Insertion.item.mask =
462 TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM;
463 Insertion.item.pszText = pEntry->szText;
464
465 INT iImage = Advanced_GetImage(pEntry);
466 Insertion.item.iImage = Insertion.item.iSelectedImage = iImage;
467 Insertion.item.lParam = pEntry->dwID;
468 pEntry->hItem = TreeView_InsertItem(hwndTreeView, &Insertion);
469 }
470
471 static VOID
472 Advanced_InsertAll(HWND hwndTreeView)
473 {
474 TreeView_DeleteAllItems(hwndTreeView);
475
476 // insert the entries
477 ADVANCED_ENTRY *pEntry;
478 for (INT i = 0; i < s_AdvancedCount; ++i)
479 {
480 pEntry = &s_Advanced[i];
481 Advanced_InsertEntry(hwndTreeView, pEntry);
482 }
483
484 // expand all
485 for (INT i = 0; i < s_AdvancedCount; ++i)
486 {
487 pEntry = &s_Advanced[i];
488 if (pEntry->dwType == AETYPE_GROUP)
489 {
490 TreeView_Expand(hwndTreeView, pEntry->hItem, TVE_EXPAND);
491 }
492 }
493 }
494
495 static BOOL
496 Advanced_LoadTree(HKEY hKey, LPCWSTR pszKeyName, DWORD dwParentID)
497 {
498 DWORD dwIndex;
499 WCHAR szKeyName[64], szText[MAX_PATH], *pch;
500 DWORD Size, Value;
501 ADVANCED_ENTRY *pAllocated;
502
503 // resize s_Advanced
504 Size = (s_AdvancedCount + 1) * sizeof(ADVANCED_ENTRY);
505 pAllocated = (ADVANCED_ENTRY *)realloc(s_Advanced, Size);
506 if (pAllocated == NULL)
507 return FALSE; // failure
508 else
509 s_Advanced = pAllocated;
510
511 ADVANCED_ENTRY *pEntry = &s_Advanced[s_AdvancedCount];
512
513 // dwID, dwParentID, szKeyName
514 pEntry->dwID = s_AdvancedCount;
515 pEntry->dwParentID = dwParentID;
516 lstrcpynW(pEntry->szKeyName, pszKeyName, _countof(pEntry->szKeyName));
517
518 // Text, ResourceID
519 pEntry->szText[0] = 0;
520 pEntry->dwResourceID = 0;
521 szText[0] = 0;
522 Size = sizeof(szText);
523 RegQueryValueExW(hKey, L"Text", NULL, NULL, LPBYTE(szText), &Size);
524 if (szText[0] == L'@')
525 {
526 pch = wcsrchr(szText, L',');
527 if (pch)
528 {
529 *pch = 0;
530 dwIndex = abs(_wtoi(pch + 1));
531 pEntry->dwResourceID = dwIndex;
532 }
533 HINSTANCE hInst = LoadLibraryW(&szText[1]);
534 LoadStringW(hInst, dwIndex, szText, _countof(szText));
535 FreeLibrary(hInst);
536 }
537 else
538 {
539 pEntry->dwResourceID = DWORD(-1);
540 }
541 lstrcpynW(pEntry->szText, szText, _countof(pEntry->szText));
542
543 // Type
544 szText[0] = 0;
545 RegQueryValueExW(hKey, L"Type", NULL, NULL, LPBYTE(szText), &Size);
546 if (lstrcmpiW(szText, L"checkbox") == 0)
547 pEntry->dwType = AETYPE_CHECKBOX;
548 else if (lstrcmpiW(szText, L"radio") == 0)
549 pEntry->dwType = AETYPE_RADIO;
550 else if (lstrcmpiW(szText, L"group") == 0)
551 pEntry->dwType = AETYPE_GROUP;
552 else
553 return FALSE; // failure
554
555 pEntry->nIconID = -1;
556 if (pEntry->dwType == AETYPE_GROUP)
557 {
558 // Bitmap (Icon)
559 UINT nIconIndex = 0;
560 Size = sizeof(szText);
561 szText[0] = 0;
562 RegQueryValueExW(hKey, L"Bitmap", NULL, NULL, LPBYTE(szText), &Size);
563
564 WCHAR szExpanded[MAX_PATH];
565 ExpandEnvironmentStringsW(szText, szExpanded, _countof(szExpanded));
566 pch = wcsrchr(szExpanded, L',');
567 if (pch)
568 {
569 *pch = 0;
570 nIconIndex = abs(_wtoi(pch + 1));
571 }
572 pEntry->nIconID = Advanced_AddIcon(szExpanded, nIconIndex);
573 }
574
575 if (pEntry->dwType == AETYPE_GROUP)
576 {
577 pEntry->hkeyRoot = NULL;
578 pEntry->szRegPath[0] = 0;
579 pEntry->szValueName[0] = 0;
580 pEntry->dwCheckedValue = 0;
581 pEntry->bHasUncheckedValue = FALSE;
582 pEntry->dwUncheckedValue = 0;
583 pEntry->dwDefaultValue = 0;
584 pEntry->hItem = NULL;
585 pEntry->bGrayed = FALSE;
586 pEntry->bChecked = FALSE;
587 }
588 else
589 {
590 // HKeyRoot
591 Value = DWORD(HKEY_CURRENT_USER);
592 Size = sizeof(Value);
593 RegQueryValueExW(hKey, L"HKeyRoot", NULL, NULL, LPBYTE(&Value), &Size);
594 pEntry->hkeyRoot = HKEY(Value);
595
596 // RegPath
597 pEntry->szRegPath[0] = 0;
598 Size = sizeof(szText);
599 RegQueryValueExW(hKey, L"RegPath", NULL, NULL, LPBYTE(szText), &Size);
600 lstrcpynW(pEntry->szRegPath, szText, _countof(pEntry->szRegPath));
601
602 // ValueName
603 pEntry->szValueName[0] = 0;
604 Size = sizeof(szText);
605 RegQueryValueExW(hKey, L"ValueName", NULL, NULL, LPBYTE(szText), &Size);
606 lstrcpynW(pEntry->szValueName, szText, _countof(pEntry->szValueName));
607
608 // CheckedValue
609 Size = sizeof(Value);
610 Value = 0x00000001;
611 RegQueryValueExW(hKey, L"CheckedValue", NULL, NULL, LPBYTE(&Value), &Size);
612 pEntry->dwCheckedValue = Value;
613
614 // UncheckedValue
615 Size = sizeof(Value);
616 Value = 0x00000000;
617 pEntry->bHasUncheckedValue = TRUE;
618 if (RegQueryValueExW(hKey, L"UncheckedValue", NULL,
619 NULL, LPBYTE(&Value), &Size) != ERROR_SUCCESS)
620 {
621 pEntry->bHasUncheckedValue = FALSE;
622 }
623 pEntry->dwUncheckedValue = Value;
624
625 // DefaultValue
626 Size = sizeof(Value);
627 Value = 0x00000001;
628 RegQueryValueExW(hKey, L"DefaultValue", NULL, NULL, LPBYTE(&Value), &Size);
629 pEntry->dwDefaultValue = Value;
630
631 // hItem
632 pEntry->hItem = NULL;
633
634 // bGrayed, bChecked
635 HKEY hkeyTarget;
636 Value = pEntry->dwDefaultValue;
637 pEntry->bGrayed = TRUE;
638 if (RegOpenKeyExW(HKEY(pEntry->hkeyRoot), pEntry->szRegPath, 0,
639 KEY_READ, &hkeyTarget) == ERROR_SUCCESS)
640 {
641 Size = sizeof(Value);
642 if (RegQueryValueExW(hkeyTarget, pEntry->szValueName, NULL, NULL,
643 LPBYTE(&Value), &Size) == ERROR_SUCCESS)
644 {
645 pEntry->bGrayed = FALSE;
646 }
647 RegCloseKey(hkeyTarget);
648 }
649 pEntry->bChecked = (Value == pEntry->dwCheckedValue);
650 }
651
652 // Grayed (ReactOS extension)
653 Size = sizeof(Value);
654 Value = FALSE;
655 RegQueryValueExW(hKey, L"Grayed", NULL, NULL, LPBYTE(&Value), &Size);
656 if (!pEntry->bGrayed)
657 pEntry->bGrayed = Value;
658
659 BOOL bIsGroup = (pEntry->dwType == AETYPE_GROUP);
660 dwParentID = pEntry->dwID;
661 ++s_AdvancedCount;
662
663 if (!bIsGroup)
664 return TRUE; // success
665
666 // load the children
667 dwIndex = 0;
668 while (RegEnumKeyW(hKey, dwIndex, szKeyName,
669 _countof(szKeyName)) == ERROR_SUCCESS)
670 {
671 HKEY hkeyChild;
672 if (RegOpenKeyExW(hKey, szKeyName, 0, KEY_READ,
673 &hkeyChild) != ERROR_SUCCESS)
674 {
675 ++dwIndex;
676 continue; // failure
677 }
678
679 Advanced_LoadTree(hkeyChild, szKeyName, dwParentID);
680 RegCloseKey(hkeyChild);
681
682 ++dwIndex;
683 }
684
685 return TRUE; // success
686 }
687
688 static BOOL
689 Advanced_LoadAll(VOID)
690 {
691 static const WCHAR s_szAdvanced[] =
692 L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced";
693
694 // free if already existed
695 if (s_Advanced)
696 {
697 free(s_Advanced);
698 s_Advanced = NULL;
699 }
700 s_AdvancedCount = 0;
701
702 HKEY hKey;
703 if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, s_szAdvanced, 0,
704 KEY_READ, &hKey) != ERROR_SUCCESS)
705 {
706 return FALSE; // failure
707 }
708
709 // load the children
710 WCHAR szKeyName[64];
711 DWORD dwIndex = 0;
712 while (RegEnumKeyW(hKey, dwIndex, szKeyName,
713 _countof(szKeyName)) == ERROR_SUCCESS)
714 {
715 HKEY hkeyChild;
716 if (RegOpenKeyExW(hKey, szKeyName, 0, KEY_READ,
717 &hkeyChild) != ERROR_SUCCESS)
718 {
719 ++dwIndex;
720 continue; // failure
721 }
722
723 Advanced_LoadTree(hkeyChild, szKeyName, DWORD(-1));
724 RegCloseKey(hkeyChild);
725
726 ++dwIndex;
727 }
728
729 RegCloseKey(hKey);
730
731 return TRUE; // success
732 }
733
734 static int
735 Advanced_Compare(const void *x, const void *y)
736 {
737 ADVANCED_ENTRY *pEntry1 = (ADVANCED_ENTRY *)x;
738 ADVANCED_ENTRY *pEntry2 = (ADVANCED_ENTRY *)y;
739
740 DWORD dwParentID1 = pEntry1->dwParentID;
741 DWORD dwParentID2 = pEntry2->dwParentID;
742
743 if (dwParentID1 == dwParentID2)
744 return lstrcmpi(pEntry1->szText, pEntry2->szText);
745
746 DWORD i, m, n;
747 const UINT MAX_DEPTH = 32;
748 ADVANCED_ENTRY *pArray1[MAX_DEPTH];
749 ADVANCED_ENTRY *pArray2[MAX_DEPTH];
750
751 // Make ancestor lists
752 for (i = m = n = 0; i < MAX_DEPTH; ++i)
753 {
754 ADVANCED_ENTRY *pParent1 = Advanced_GetItem(dwParentID1);
755 ADVANCED_ENTRY *pParent2 = Advanced_GetItem(dwParentID2);
756 if (!pParent1 && !pParent2)
757 break;
758
759 if (pParent1)
760 {
761 pArray1[m++] = pParent1;
762 dwParentID1 = pParent1->dwParentID;
763 }
764 if (pParent2)
765 {
766 pArray2[n++] = pParent2;
767 dwParentID2 = pParent2->dwParentID;
768 }
769 }
770
771 UINT k = min(m, n);
772 for (i = 0; i < k; ++i)
773 {
774 INT nCompare = lstrcmpi(pArray1[m - i - 1]->szText, pArray2[n - i - 1]->szText);
775 if (nCompare < 0)
776 return -1;
777 if (nCompare > 0)
778 return 1;
779 }
780
781 if (m < n)
782 return -1;
783 if (m > n)
784 return 1;
785 return lstrcmpi(pEntry1->szText, pEntry2->szText);
786 }
787
788 static VOID
789 Advanced_SortAll(VOID)
790 {
791 qsort(s_Advanced, s_AdvancedCount, sizeof(ADVANCED_ENTRY), Advanced_Compare);
792 }
793
794 EXTERN_C HPSXA WINAPI SHCreatePropSheetExtArrayEx(HKEY hKey, LPCWSTR pszSubKey, UINT max_iface, IDataObject *pDataObj);
795
796 static VOID
797 UpdateGeneralIcons(HWND hDlg)
798 {
799 HWND hwndTaskIcon, hwndFolderIcon, hwndClickIcon;
800 HICON hTaskIcon = NULL, hFolderIcon = NULL, hClickIcon = NULL;
801 LPTSTR lpTaskIconName = NULL, lpFolderIconName = NULL, lpClickIconName = NULL;
802
803 // show task setting icon
804 if(IsDlgButtonChecked(hDlg, IDC_FOLDER_OPTIONS_COMMONTASKS) == BST_CHECKED)
805 lpTaskIconName = MAKEINTRESOURCE(IDI_SHELL_SHOW_COMMON_TASKS);
806 else if(IsDlgButtonChecked(hDlg, IDC_FOLDER_OPTIONS_CLASSICFOLDERS) == BST_CHECKED)
807 lpTaskIconName = MAKEINTRESOURCE(IDI_SHELL_CLASSIC_FOLDERS);
808
809 if (lpTaskIconName)
810 {
811 hTaskIcon = (HICON)LoadImage(shell32_hInstance,
812 lpTaskIconName,
813 IMAGE_ICON,
814 0,
815 0,
816 LR_DEFAULTCOLOR);
817 if (hTaskIcon)
818 {
819 hwndTaskIcon = GetDlgItem(hDlg,
820 IDC_FOLDER_OPTIONS_TASKICON);
821 if (hwndTaskIcon)
822 {
823 SendMessage(hwndTaskIcon,
824 STM_SETIMAGE,
825 IMAGE_ICON,
826 (LPARAM)hTaskIcon);
827 }
828 }
829 }
830
831 // show Folder setting icons
832 if(IsDlgButtonChecked(hDlg, IDC_FOLDER_OPTIONS_SAMEWINDOW) == BST_CHECKED)
833 lpFolderIconName = MAKEINTRESOURCE(IDI_SHELL_OPEN_IN_SOME_WINDOW);
834 else if(IsDlgButtonChecked(hDlg, IDC_FOLDER_OPTIONS_OWNWINDOW) == BST_CHECKED)
835 lpFolderIconName = MAKEINTRESOURCE(IDI_SHELL_OPEN_IN_NEW_WINDOW);
836
837 if (lpFolderIconName)
838 {
839 hFolderIcon = (HICON)LoadImage(shell32_hInstance,
840 lpFolderIconName,
841 IMAGE_ICON,
842 0,
843 0,
844 LR_DEFAULTCOLOR);
845 if (hFolderIcon)
846 {
847 hwndFolderIcon = GetDlgItem(hDlg,
848 IDC_FOLDER_OPTIONS_FOLDERICON);
849 if (hwndFolderIcon)
850 {
851 SendMessage(hwndFolderIcon,
852 STM_SETIMAGE,
853 IMAGE_ICON,
854 (LPARAM)hFolderIcon);
855 }
856 }
857 }
858
859 // Show click setting icon
860 if(IsDlgButtonChecked(hDlg, IDC_FOLDER_OPTIONS_SINGLECLICK) == BST_CHECKED)
861 lpClickIconName = MAKEINTRESOURCE(IDI_SHELL_SINGLE_CLICK_TO_OPEN);
862 else if(IsDlgButtonChecked(hDlg, IDC_FOLDER_OPTIONS_DOUBLECLICK) == BST_CHECKED)
863 lpClickIconName = MAKEINTRESOURCE(IDI_SHELL_DOUBLE_CLICK_TO_OPEN);
864
865 if (lpClickIconName)
866 {
867 hClickIcon = (HICON)LoadImage(shell32_hInstance,
868 lpClickIconName,
869 IMAGE_ICON,
870 0,
871 0,
872 LR_DEFAULTCOLOR);
873 if (hClickIcon)
874 {
875 hwndClickIcon = GetDlgItem(hDlg,
876 IDC_FOLDER_OPTIONS_CLICKICON);
877 if (hwndClickIcon)
878 {
879 SendMessage(hwndClickIcon,
880 STM_SETIMAGE,
881 IMAGE_ICON,
882 (LPARAM)hClickIcon);
883 }
884 }
885 }
886
887 // Clean up
888 if(hTaskIcon)
889 DeleteObject(hTaskIcon);
890 if(hFolderIcon)
891 DeleteObject(hFolderIcon);
892 if(hClickIcon)
893 DeleteObject(hClickIcon);
894
895 return;
896 }
897
898 INT_PTR
899 CALLBACK
900 FolderOptionsGeneralDlg(
901 HWND hwndDlg,
902 UINT uMsg,
903 WPARAM wParam,
904 LPARAM lParam
905 )
906 {
907 switch(uMsg)
908 {
909 case WM_INITDIALOG:
910 // FIXME
911 break;
912
913 case WM_COMMAND:
914 switch (LOWORD(wParam))
915 {
916 case IDC_FOLDER_OPTIONS_COMMONTASKS:
917 case IDC_FOLDER_OPTIONS_CLASSICFOLDERS:
918 case IDC_FOLDER_OPTIONS_SAMEWINDOW:
919 case IDC_FOLDER_OPTIONS_OWNWINDOW:
920 case IDC_FOLDER_OPTIONS_SINGLECLICK:
921 case IDC_FOLDER_OPTIONS_DOUBLECLICK:
922 if (HIWORD(wParam) == BN_CLICKED)
923 {
924 UpdateGeneralIcons(hwndDlg);
925
926 /* Enable the 'Apply' button */
927 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
928 }
929 break;
930 }
931 break;
932
933 case WM_NOTIFY:
934 {
935 LPNMHDR pnmh = (LPNMHDR)lParam;
936
937 switch (pnmh->code)
938 {
939 case PSN_SETACTIVE:
940 break;
941
942 case PSN_APPLY:
943 break;
944 }
945 break;
946 }
947
948 case WM_DESTROY:
949 break;
950
951 default:
952 return FALSE;
953 }
954 return FALSE;
955 }
956
957 static BOOL
958 ViewDlg_OnInitDialog(HWND hwndDlg)
959 {
960 HWND hwndTreeView = GetDlgItem(hwndDlg, 14003);
961
962 s_hImageList = CreateTreeImageList();
963 TreeView_SetImageList(hwndTreeView, s_hImageList, TVSIL_NORMAL);
964
965 Advanced_LoadAll();
966 Advanced_SortAll();
967 Advanced_InsertAll(hwndTreeView);
968
969 return TRUE; // set focus
970 }
971
972 static BOOL
973 ViewDlg_ToggleCheckItem(HWND hwndDlg, HTREEITEM hItem)
974 {
975 HWND hwndTreeView = GetDlgItem(hwndDlg, 14003);
976
977 // get the item
978 TV_ITEM Item;
979 INT i;
980 ZeroMemory(&Item, sizeof(Item));
981 Item.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_PARAM;
982 Item.hItem = hItem;
983 if (!TreeView_GetItem(hwndTreeView, &Item))
984 return FALSE; // no such item
985
986 ADVANCED_ENTRY *pEntry = Advanced_GetItem(Item.lParam);
987 if (pEntry == NULL)
988 return FALSE; // no such item
989 if (pEntry->bGrayed)
990 return FALSE; // disabled
991
992 // toggle check mark
993 Item.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
994 switch (pEntry->dwType)
995 {
996 case AETYPE_CHECKBOX:
997 pEntry->bChecked = !pEntry->bChecked;
998 break;
999 case AETYPE_RADIO:
1000 // reset all the entries of the same parent
1001 for (i = 0; i < s_AdvancedCount; ++i)
1002 {
1003 ADVANCED_ENTRY *pEntry2 = &s_Advanced[i];
1004 if (pEntry->dwParentID == pEntry2->dwParentID)
1005 {
1006 pEntry2->bChecked = FALSE;
1007
1008 Item.hItem = pEntry2->hItem;
1009 INT iImage = Advanced_GetImage(pEntry2);
1010 Item.iImage = Item.iSelectedImage = iImage;
1011 TreeView_SetItem(hwndTreeView, &Item);
1012 }
1013 }
1014 pEntry->bChecked = TRUE;
1015 break;
1016 default:
1017 return FALSE; // failure
1018 }
1019 Item.iImage = Item.iSelectedImage = Advanced_GetImage(pEntry);
1020 Item.hItem = hItem;
1021 TreeView_SetItem(hwndTreeView, &Item);
1022
1023 // redraw the item
1024 RECT rcItem;
1025 TreeView_GetItemRect(hwndTreeView, hItem, &rcItem, FALSE);
1026 InvalidateRect(hwndTreeView, &rcItem, TRUE);
1027 return TRUE; // success
1028 }
1029
1030 static VOID
1031 ViewDlg_OnTreeViewClick(HWND hwndDlg)
1032 {
1033 HWND hwndTreeView = GetDlgItem(hwndDlg, 14003);
1034
1035 // do hit test to get the clicked item
1036 TV_HITTESTINFO HitTest;
1037 ZeroMemory(&HitTest, sizeof(HitTest));
1038 DWORD dwPos = GetMessagePos();
1039 HitTest.pt.x = LOWORD(dwPos);
1040 HitTest.pt.y = HIWORD(dwPos);
1041 ScreenToClient(hwndTreeView, &HitTest.pt);
1042 HTREEITEM hItem = TreeView_HitTest(hwndTreeView, &HitTest);
1043
1044 // toggle the check mark if possible
1045 if (ViewDlg_ToggleCheckItem(hwndDlg, hItem))
1046 {
1047 // property sheet was changed
1048 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
1049 }
1050 }
1051
1052 static void
1053 ViewDlg_OnTreeViewKeyDown(HWND hwndDlg, TV_KEYDOWN *KeyDown)
1054 {
1055 HWND hwndTreeView = GetDlgItem(hwndDlg, 14003);
1056
1057 if (KeyDown->wVKey == VK_SPACE)
1058 {
1059 // [Space] key was pressed
1060 HTREEITEM hItem = TreeView_GetSelection(hwndTreeView);
1061 if (ViewDlg_ToggleCheckItem(hwndDlg, hItem))
1062 {
1063 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
1064 }
1065 }
1066 }
1067
1068 static INT_PTR
1069 ViewDlg_OnTreeCustomDraw(HWND hwndDlg, NMTVCUSTOMDRAW *Draw)
1070 {
1071 NMCUSTOMDRAW& nmcd = Draw->nmcd;
1072 switch (nmcd.dwDrawStage)
1073 {
1074 case CDDS_PREPAINT:
1075 return CDRF_NOTIFYITEMDRAW; // for CDDS_ITEMPREPAINT
1076
1077 case CDDS_ITEMPREPAINT:
1078 if (!(nmcd.uItemState & CDIS_SELECTED)) // not selected
1079 {
1080 LPARAM lParam = nmcd.lItemlParam;
1081 ADVANCED_ENTRY *pEntry = Advanced_GetItem(lParam);
1082 if (pEntry && pEntry->bGrayed) // disabled
1083 {
1084 // draw as grayed
1085 Draw->clrText = GetSysColor(COLOR_GRAYTEXT);
1086 Draw->clrTextBk = GetSysColor(COLOR_WINDOW);
1087 return CDRF_NEWFONT;
1088 }
1089 }
1090 break;
1091
1092 default:
1093 break;
1094 }
1095 return CDRF_DODEFAULT;
1096 }
1097
1098 static VOID
1099 Advanced_RestoreDefaults(HWND hwndDlg)
1100 {
1101 HWND hwndTreeView = GetDlgItem(hwndDlg, 14003);
1102
1103 for (INT i = 0; i < s_AdvancedCount; ++i)
1104 {
1105 // ignore if the type is group
1106 ADVANCED_ENTRY *pEntry = &s_Advanced[i];
1107 if (pEntry->dwType == AETYPE_GROUP)
1108 continue;
1109
1110 // set default value on registry
1111 HKEY hKey;
1112 if (RegOpenKeyExW(HKEY(pEntry->hkeyRoot), pEntry->szRegPath,
1113 0, KEY_WRITE, &hKey) != ERROR_SUCCESS)
1114 {
1115 continue;
1116 }
1117 RegSetValueExW(hKey, pEntry->szValueName, 0, REG_DWORD,
1118 LPBYTE(pEntry->dwDefaultValue), sizeof(DWORD));
1119 RegCloseKey(hKey);
1120
1121 // update check status
1122 pEntry->bChecked = (pEntry->dwCheckedValue == pEntry->dwDefaultValue);
1123
1124 // update the image
1125 TV_ITEM Item;
1126 ZeroMemory(&Item, sizeof(Item));
1127 Item.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
1128 Item.hItem = pEntry->hItem;
1129 Item.iImage = Item.iSelectedImage = Advanced_GetImage(pEntry);
1130 TreeView_SetItem(hwndTreeView, &Item);
1131 }
1132
1133 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
1134 }
1135
1136 /* FIXME: These macros should not be defined here */
1137 #ifndef SSF_SHOWSUPERHIDDEN
1138 #define SSF_SHOWSUPERHIDDEN 0x00040000
1139 #endif
1140 #ifndef SSF_SEPPROCESS
1141 #define SSF_SEPPROCESS 0x00080000
1142 #endif
1143
1144 static VOID
1145 ScanAdvancedSettings(SHELLSTATE *pSS, DWORD *pdwMask)
1146 {
1147 for (INT i = 0; i < s_AdvancedCount; ++i)
1148 {
1149 const ADVANCED_ENTRY *pEntry = &s_Advanced[i];
1150 if (pEntry->dwType == AETYPE_GROUP || pEntry->bGrayed)
1151 continue;
1152
1153 BOOL bChecked = pEntry->bChecked;
1154
1155 // FIXME: Add more items
1156 if (lstrcmpiW(pEntry->szKeyName, L"SuperHidden") == 0)
1157 {
1158 pSS->fShowSuperHidden = !bChecked ? 1 : 0;
1159 *pdwMask |= SSF_SHOWSUPERHIDDEN;
1160 continue;
1161 }
1162 if (lstrcmpiW(pEntry->szKeyName, L"DesktopProcess") == 0)
1163 {
1164 pSS->fSepProcess = bChecked ? 1 : 0;
1165 *pdwMask |= SSF_SEPPROCESS;
1166 continue;
1167 }
1168 if (lstrcmpiW(pEntry->szKeyName, L"SHOWALL") == 0)
1169 {
1170 pSS->fShowAllObjects = !bChecked ? 1 : 0;
1171 *pdwMask |= SSF_SHOWALLOBJECTS;
1172 continue;
1173 }
1174 if (lstrcmpiW(pEntry->szKeyName, L"HideFileExt") == 0)
1175 {
1176 pSS->fShowExtensions = !bChecked ? 1 : 0;
1177 *pdwMask |= SSF_SHOWEXTENSIONS;
1178 continue;
1179 }
1180 if (lstrcmpiW(pEntry->szKeyName, L"ShowCompColor") == 0)
1181 {
1182 pSS->fShowCompColor = bChecked ? 1 : 0;
1183 *pdwMask |= SSF_SHOWCOMPCOLOR;
1184 continue;
1185 }
1186 if (lstrcmpiW(pEntry->szKeyName, L"ShowInfoTip") == 0)
1187 {
1188 pSS->fShowInfoTip = bChecked ? 1 : 0;
1189 *pdwMask |= SSF_SHOWINFOTIP;
1190 continue;
1191 }
1192 }
1193 }
1194
1195 extern "C"
1196 VOID WINAPI SHGetSetSettings(LPSHELLSTATE lpss, DWORD dwMask, BOOL bSet);
1197
1198 static BOOL CALLBACK RefreshBrowsersCallback (HWND hWnd, LPARAM msg)
1199 {
1200 WCHAR ClassName[100];
1201 if (GetClassName(hWnd, ClassName, 100))
1202 {
1203 if (!wcscmp(ClassName, L"Progman") ||
1204 !wcscmp(ClassName, L"CabinetWClass") ||
1205 !wcscmp(ClassName, L"ExploreWClass"))
1206 {
1207 PostMessage(hWnd, WM_COMMAND, FCIDM_DESKBROWSER_REFRESH, 0);
1208 }
1209 }
1210 return TRUE;
1211 }
1212
1213 static VOID
1214 ViewDlg_Apply(HWND hwndDlg)
1215 {
1216 for (INT i = 0; i < s_AdvancedCount; ++i)
1217 {
1218 // ignore the entry if the type is group or the entry is grayed
1219 ADVANCED_ENTRY *pEntry = &s_Advanced[i];
1220 if (pEntry->dwType == AETYPE_GROUP || pEntry->bGrayed)
1221 continue;
1222
1223 // open the registry key
1224 HKEY hkeyTarget;
1225 if (RegOpenKeyExW(HKEY(pEntry->hkeyRoot), pEntry->szRegPath, 0,
1226 KEY_WRITE, &hkeyTarget) != ERROR_SUCCESS)
1227 {
1228 continue;
1229 }
1230
1231 // checked or unchecked?
1232 DWORD dwValue, dwSize;
1233 if (pEntry->bChecked)
1234 {
1235 dwValue = pEntry->dwCheckedValue;
1236 }
1237 else
1238 {
1239 if (pEntry->bHasUncheckedValue)
1240 {
1241 dwValue = pEntry->dwUncheckedValue;
1242 }
1243 else
1244 {
1245 // there is no unchecked value
1246 RegCloseKey(hkeyTarget);
1247 continue; // ignore
1248 }
1249 }
1250
1251 // set the value
1252 dwSize = sizeof(dwValue);
1253 RegSetValueExW(hkeyTarget, pEntry->szValueName, 0, REG_DWORD,
1254 LPBYTE(&dwValue), dwSize);
1255
1256 // close now
1257 RegCloseKey(hkeyTarget);
1258 }
1259
1260 // scan advanced settings for user's settings
1261 DWORD dwMask = 0;
1262 SHELLSTATE ShellState;
1263 ZeroMemory(&ShellState, sizeof(ShellState));
1264 ScanAdvancedSettings(&ShellState, &dwMask);
1265
1266 // update user's settings
1267 SHGetSetSettings(&ShellState, dwMask, TRUE);
1268
1269 // notify all
1270 SendMessage(HWND_BROADCAST, WM_WININICHANGE, 0, 0);
1271
1272 EnumWindows(RefreshBrowsersCallback, NULL);
1273 }
1274
1275 INT_PTR CALLBACK
1276 FolderOptionsViewDlg(
1277 HWND hwndDlg,
1278 UINT uMsg,
1279 WPARAM wParam,
1280 LPARAM lParam)
1281 {
1282 INT_PTR Result;
1283 NMTVCUSTOMDRAW *Draw;
1284
1285 switch(uMsg)
1286 {
1287 case WM_INITDIALOG:
1288 return ViewDlg_OnInitDialog(hwndDlg);
1289 case WM_COMMAND:
1290 switch (LOWORD(wParam))
1291 {
1292 case 14004: // Restore Defaults
1293 Advanced_RestoreDefaults(hwndDlg);
1294 break;
1295 }
1296 break;
1297 case WM_NOTIFY:
1298 switch (LPNMHDR(lParam)->code)
1299 {
1300 case NM_CLICK: // clicked on treeview
1301 ViewDlg_OnTreeViewClick(hwndDlg);
1302 break;
1303 case NM_CUSTOMDRAW: // custom draw (for graying)
1304 Draw = (NMTVCUSTOMDRAW *)lParam;
1305 Result = ViewDlg_OnTreeCustomDraw(hwndDlg, Draw);
1306 SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, Result);
1307 return Result;
1308 case TVN_KEYDOWN: // key is down
1309 ViewDlg_OnTreeViewKeyDown(hwndDlg, (TV_KEYDOWN *)lParam);
1310 break;
1311 case PSN_APPLY: // [Apply] is clicked
1312 ViewDlg_Apply(hwndDlg);
1313 break;
1314 default:
1315 break;
1316 }
1317 break;
1318 }
1319
1320 return FALSE;
1321 }
1322
1323 static
1324 VOID
1325 InitializeFileTypesListCtrlColumns(HWND hDlgCtrl)
1326 {
1327 RECT clientRect;
1328 LVCOLUMNW col;
1329 WCHAR szName[50];
1330 DWORD dwStyle;
1331 int columnSize = 140;
1332
1333
1334 if (!LoadStringW(shell32_hInstance, IDS_COLUMN_EXTENSION, szName, sizeof(szName) / sizeof(WCHAR)))
1335 {
1336 /* default to english */
1337 wcscpy(szName, L"Extensions");
1338 }
1339
1340 /* make sure its null terminated */
1341 szName[(sizeof(szName)/sizeof(WCHAR))-1] = 0;
1342
1343 GetClientRect(hDlgCtrl, &clientRect);
1344 ZeroMemory(&col, sizeof(LV_COLUMN));
1345 columnSize = 140; //FIXME
1346 col.iSubItem = 0;
1347 col.mask = LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM | LVCF_FMT;
1348 col.fmt = LVCFMT_FIXED_WIDTH;
1349 col.cx = columnSize | LVCFMT_LEFT;
1350 col.cchTextMax = wcslen(szName);
1351 col.pszText = szName;
1352 (void)SendMessageW(hDlgCtrl, LVM_INSERTCOLUMNW, 0, (LPARAM)&col);
1353
1354 if (!LoadStringW(shell32_hInstance, IDS_FILE_TYPES, szName, sizeof(szName) / sizeof(WCHAR)))
1355 {
1356 /* default to english */
1357 wcscpy(szName, L"File Types");
1358 ERR("Failed to load localized string!\n");
1359 }
1360
1361 col.iSubItem = 1;
1362 col.cx = clientRect.right - clientRect.left - columnSize;
1363 col.cchTextMax = wcslen(szName);
1364 col.pszText = szName;
1365 (void)SendMessageW(hDlgCtrl, LVM_INSERTCOLUMNW, 1, (LPARAM)&col);
1366
1367 /* set full select style */
1368 dwStyle = (DWORD) SendMessage(hDlgCtrl, LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
1369 dwStyle = dwStyle | LVS_EX_FULLROWSELECT;
1370 SendMessage(hDlgCtrl, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, dwStyle);
1371 }
1372
1373 static BOOL
1374 DeleteExt(HWND hwndDlg, LPCWSTR pszExt)
1375 {
1376 if (*pszExt != L'.')
1377 return FALSE;
1378
1379 // open ".ext" key
1380 HKEY hKey;
1381 if (RegOpenKeyExW(HKEY_CLASSES_ROOT, pszExt, 0, KEY_READ, &hKey) != ERROR_SUCCESS)
1382 return FALSE;
1383
1384 // query "extfile" key name
1385 WCHAR szValue[64] = { 0 };
1386 DWORD cbValue = sizeof(szValue);
1387 RegQueryValueExW(hKey, NULL, NULL, NULL, LPBYTE(szValue), &cbValue);
1388 RegCloseKey(hKey);
1389
1390 // delete "extfile" key (if any)
1391 if (szValue[0])
1392 SHDeleteKeyW(HKEY_CLASSES_ROOT, szValue);
1393
1394 // delete ".ext" key
1395 return SHDeleteKeyW(HKEY_CLASSES_ROOT, pszExt) == ERROR_SUCCESS;
1396 }
1397
1398 static inline HICON
1399 DoExtractIcon(PFOLDER_FILE_TYPE_ENTRY Entry, LPCWSTR IconPath,
1400 INT iIndex = 0, BOOL bSmall = FALSE)
1401 {
1402 HICON hIcon = NULL;
1403
1404 if (iIndex < 0)
1405 {
1406 // A negative value will be interpreted as a negated resource ID.
1407 iIndex = -iIndex;
1408
1409 INT cx, cy;
1410 HINSTANCE hDLL = LoadLibraryExW(IconPath, NULL, LOAD_LIBRARY_AS_DATAFILE);
1411 if (bSmall)
1412 {
1413 cx = GetSystemMetrics(SM_CXSMICON);
1414 cy = GetSystemMetrics(SM_CYSMICON);
1415 }
1416 else
1417 {
1418 cx = GetSystemMetrics(SM_CXICON);
1419 cy = GetSystemMetrics(SM_CYICON);
1420 }
1421 hIcon = HICON(LoadImageW(hDLL, MAKEINTRESOURCEW(iIndex), IMAGE_ICON,
1422 cx, cy, 0));
1423 FreeLibrary(hDLL);
1424 }
1425 else
1426 {
1427 // A positive value is icon index.
1428 if (bSmall)
1429 ExtractIconExW(IconPath, iIndex, NULL, &hIcon, 1);
1430 else
1431 ExtractIconExW(IconPath, iIndex, &hIcon, NULL, 1);
1432 }
1433 return hIcon;
1434 }
1435
1436 static void
1437 DoFileTypeIconLocation(PFOLDER_FILE_TYPE_ENTRY Entry, LPCWSTR IconLocation)
1438 {
1439 // Expand the REG_EXPAND_SZ string by environment variables
1440 WCHAR szLocation[MAX_PATH + 32];
1441 if (!ExpandEnvironmentStringsW(IconLocation, szLocation, _countof(szLocation)))
1442 {
1443 return;
1444 }
1445
1446 INT nIndex = PathParseIconLocationW(szLocation);
1447 Entry->hIconLarge = DoExtractIcon(Entry, szLocation, nIndex, FALSE);
1448 Entry->hIconSmall = DoExtractIcon(Entry, szLocation, nIndex, TRUE);
1449 }
1450
1451 static BOOL
1452 GetFileTypeIconsEx(PFOLDER_FILE_TYPE_ENTRY Entry, LPCWSTR IconLocation)
1453 {
1454 Entry->hIconLarge = Entry->hIconSmall = NULL;
1455
1456 if (lstrcmpiW(Entry->FileExtension, L".exe") == 0 ||
1457 lstrcmpiW(Entry->FileExtension, L".scr") == 0)
1458 {
1459 // It's an executable
1460 Entry->hIconLarge = LoadIconW(shell32_hInstance, MAKEINTRESOURCEW(IDI_SHELL_EXE));
1461 Entry->hIconSmall = HICON(LoadImageW(shell32_hInstance, MAKEINTRESOURCEW(IDI_SHELL_EXE), IMAGE_ICON,
1462 GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 0));
1463 }
1464 else if (lstrcmpW(IconLocation, L"%1") == 0)
1465 {
1466 return FALSE; // self icon
1467 }
1468 else
1469 {
1470 DoFileTypeIconLocation(Entry, IconLocation);
1471 }
1472
1473 return Entry->hIconLarge && Entry->hIconSmall;
1474 }
1475
1476 static BOOL
1477 GetFileTypeIconsByKey(HKEY hKey, PFOLDER_FILE_TYPE_ENTRY Entry)
1478 {
1479 Entry->hIconLarge = Entry->hIconSmall = NULL;
1480
1481 // Open the "DefaultIcon" registry key
1482 HKEY hDefIconKey;
1483 LONG nResult = RegOpenKeyExW(hKey, L"DefaultIcon", 0, KEY_READ, &hDefIconKey);
1484 if (nResult != ERROR_SUCCESS)
1485 return FALSE;
1486
1487 // Get the icon location
1488 WCHAR szLocation[MAX_PATH + 32] = { 0 };
1489 DWORD dwSize = sizeof(szLocation);
1490 nResult = RegQueryValueExW(hDefIconKey, NULL, NULL, NULL, LPBYTE(szLocation), &dwSize);
1491
1492 RegCloseKey(hDefIconKey);
1493
1494 if (nResult != ERROR_SUCCESS || szLocation[0] == 0)
1495 return FALSE;
1496
1497 return GetFileTypeIconsEx(Entry, szLocation);
1498 }
1499
1500 static BOOL
1501 QueryFileDescription(LPCWSTR ProgramPath, LPWSTR pszName, INT cchName)
1502 {
1503 SHFILEINFOW FileInfo = { 0 };
1504 if (SHGetFileInfoW(ProgramPath, 0, &FileInfo, sizeof(FileInfo), SHGFI_DISPLAYNAME))
1505 {
1506 StringCchCopyW(pszName, cchName, FileInfo.szDisplayName);
1507 return TRUE;
1508 }
1509
1510 return !!GetFileTitleW(ProgramPath, pszName, cchName);
1511 }
1512
1513 static BOOL
1514 InsertFileType(HWND hListView, LPCWSTR szName, INT iItem, LPCWSTR szFile)
1515 {
1516 PFOLDER_FILE_TYPE_ENTRY Entry;
1517 HKEY hKey;
1518 LVITEMW lvItem;
1519 DWORD dwSize;
1520 DWORD dwType;
1521
1522 if (szName[0] != L'.')
1523 {
1524 /* FIXME handle URL protocol handlers */
1525 return FALSE;
1526 }
1527
1528 // get imagelists of listview
1529 HIMAGELIST himlLarge = ListView_GetImageList(hListView, LVSIL_NORMAL);
1530 HIMAGELIST himlSmall = ListView_GetImageList(hListView, LVSIL_SMALL);
1531
1532 /* allocate file type entry */
1533 Entry = (PFOLDER_FILE_TYPE_ENTRY)HeapAlloc(GetProcessHeap(), 0, sizeof(FOLDER_FILE_TYPE_ENTRY));
1534 if (!Entry)
1535 return FALSE;
1536
1537 /* open key */
1538 if (RegOpenKeyExW(HKEY_CLASSES_ROOT, szName, 0, KEY_READ, &hKey) != ERROR_SUCCESS)
1539 {
1540 HeapFree(GetProcessHeap(), 0, Entry);
1541 return FALSE;
1542 }
1543
1544 /* FIXME check for duplicates */
1545
1546 /* query for the default key */
1547 dwSize = sizeof(Entry->ClassKey);
1548 if (RegQueryValueExW(hKey, NULL, NULL, NULL, (LPBYTE)Entry->ClassKey, &dwSize) != ERROR_SUCCESS)
1549 {
1550 /* no link available */
1551 Entry->ClassKey[0] = 0;
1552 }
1553
1554 if (Entry->ClassKey[0])
1555 {
1556 HKEY hTemp;
1557 /* try open linked key */
1558 if (RegOpenKeyExW(HKEY_CLASSES_ROOT, Entry->ClassKey, 0, KEY_READ, &hTemp) == ERROR_SUCCESS)
1559 {
1560 /* use linked key */
1561 RegCloseKey(hKey);
1562 hKey = hTemp;
1563 }
1564 }
1565
1566 /* read friendly type name */
1567 if (RegLoadMUIStringW(hKey, L"FriendlyTypeName", Entry->FileDescription, sizeof(Entry->FileDescription), NULL, 0, NULL) != ERROR_SUCCESS)
1568 {
1569 /* read file description */
1570 dwSize = sizeof(Entry->FileDescription);
1571 Entry->FileDescription[0] = 0;
1572
1573 /* read default key */
1574 RegQueryValueExW(hKey, NULL, NULL, NULL, (LPBYTE)Entry->FileDescription, &dwSize);
1575 }
1576
1577 /* Read the EditFlags value */
1578 Entry->EditFlags = 0;
1579 if (!RegQueryValueExW(hKey, L"EditFlags", NULL, &dwType, NULL, &dwSize))
1580 {
1581 if ((dwType == REG_DWORD || dwType == REG_BINARY) && dwSize == sizeof(DWORD))
1582 RegQueryValueExW(hKey, L"EditFlags", NULL, NULL, (LPBYTE)&Entry->EditFlags, &dwSize);
1583 }
1584
1585 /* convert extension to upper case */
1586 wcscpy(Entry->FileExtension, szName);
1587 _wcsupr(Entry->FileExtension);
1588
1589 /* get icon */
1590 if (!GetFileTypeIconsByKey(hKey, Entry))
1591 {
1592 // set default icon
1593 Entry->hIconLarge = LoadIconW(shell32_hInstance, MAKEINTRESOURCEW(IDI_SHELL_FOLDER_OPTIONS));
1594 INT cxSmall = GetSystemMetrics(SM_CXSMICON);
1595 INT cySmall = GetSystemMetrics(SM_CYSMICON);
1596 Entry->hIconSmall = HICON(LoadImageW(shell32_hInstance, MAKEINTRESOURCEW(IDI_SHELL_FOLDER_OPTIONS),
1597 IMAGE_ICON, cxSmall, cySmall, 0));
1598 }
1599
1600 /* close key */
1601 RegCloseKey(hKey);
1602
1603 // get program path and app name
1604 DWORD cch = _countof(Entry->ProgramPath);
1605 if (S_OK == AssocQueryStringW(ASSOCF_INIT_IGNOREUNKNOWN, ASSOCSTR_EXECUTABLE,
1606 Entry->FileExtension, NULL, Entry->ProgramPath, &cch))
1607 {
1608 QueryFileDescription(Entry->ProgramPath, Entry->AppName, _countof(Entry->AppName));
1609 }
1610 else
1611 {
1612 Entry->ProgramPath[0] = Entry->AppName[0] = 0;
1613 }
1614
1615 // add icon to imagelist
1616 INT iLargeImage = -1, iSmallImage = -1;
1617 if (Entry->hIconLarge && Entry->hIconSmall)
1618 {
1619 iLargeImage = ImageList_AddIcon(himlLarge, Entry->hIconLarge);
1620 iSmallImage = ImageList_AddIcon(himlSmall, Entry->hIconSmall);
1621 ASSERT(iLargeImage == iSmallImage);
1622 }
1623
1624 /* Do not add excluded entries */
1625 if (Entry->EditFlags & 0x00000001) //FTA_Exclude
1626 {
1627 DestroyIcon(Entry->hIconLarge);
1628 DestroyIcon(Entry->hIconSmall);
1629 HeapFree(GetProcessHeap(), 0, Entry);
1630 return FALSE;
1631 }
1632
1633 if (!Entry->FileDescription[0])
1634 {
1635 /* construct default 'FileExtensionFile' by formatting the uppercase extension
1636 with IDS_FILE_EXT_TYPE, outputting something like a l18n 'INI File' */
1637
1638 StringCchPrintf(Entry->FileDescription, _countof(Entry->FileDescription), szFile, &Entry->FileExtension[1]);
1639 }
1640
1641 ZeroMemory(&lvItem, sizeof(LVITEMW));
1642 lvItem.mask = LVIF_TEXT | LVIF_PARAM | LVIF_IMAGE;
1643 lvItem.iSubItem = 0;
1644 lvItem.pszText = &Entry->FileExtension[1];
1645 lvItem.iItem = iItem;
1646 lvItem.lParam = (LPARAM)Entry;
1647 lvItem.iImage = iSmallImage;
1648 SendMessageW(hListView, LVM_INSERTITEMW, 0, (LPARAM)&lvItem);
1649
1650 ZeroMemory(&lvItem, sizeof(LVITEMW));
1651 lvItem.mask = LVIF_TEXT;
1652 lvItem.pszText = Entry->FileDescription;
1653 lvItem.iItem = iItem;
1654 lvItem.iSubItem = 1;
1655 ListView_SetItem(hListView, &lvItem);
1656
1657 return TRUE;
1658 }
1659
1660 static
1661 int
1662 CALLBACK
1663 ListViewCompareProc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
1664 {
1665 PFOLDER_FILE_TYPE_ENTRY Entry1, Entry2;
1666 int x;
1667
1668 Entry1 = (PFOLDER_FILE_TYPE_ENTRY)lParam1;
1669 Entry2 = (PFOLDER_FILE_TYPE_ENTRY)lParam2;
1670
1671 x = wcsicmp(Entry1->FileExtension, Entry2->FileExtension);
1672 if (x != 0)
1673 return x;
1674
1675 return wcsicmp(Entry1->FileDescription, Entry2->FileDescription);
1676 }
1677
1678 static
1679 PFOLDER_FILE_TYPE_ENTRY
1680 InitializeFileTypesListCtrl(HWND hwndDlg)
1681 {
1682 HWND hDlgCtrl;
1683 DWORD dwIndex = 0;
1684 WCHAR szName[50];
1685 WCHAR szFile[100];
1686 DWORD dwName;
1687 LVITEMW lvItem;
1688 INT iItem = 0;
1689 HIMAGELIST himlLarge, himlSmall;
1690
1691 // create imagelists
1692 himlLarge = ImageList_Create(GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON),
1693 ILC_COLOR32 | ILC_MASK, 256, 20);
1694 himlSmall = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
1695 ILC_COLOR32 | ILC_MASK, 256, 20);
1696
1697 // set imagelists to listview.
1698 hDlgCtrl = GetDlgItem(hwndDlg, IDC_FILETYPES_LISTVIEW);
1699 ListView_SetImageList(hDlgCtrl, himlLarge, LVSIL_NORMAL);
1700 ListView_SetImageList(hDlgCtrl, himlSmall, LVSIL_SMALL);
1701
1702 InitializeFileTypesListCtrlColumns(hDlgCtrl);
1703
1704 szFile[0] = 0;
1705 if (!LoadStringW(shell32_hInstance, IDS_FILE_EXT_TYPE, szFile, _countof(szFile)))
1706 {
1707 /* default to english */
1708 wcscpy(szFile, L"%s File");
1709 }
1710 szFile[(_countof(szFile)) - 1] = 0;
1711
1712 dwName = _countof(szName);
1713
1714 while (RegEnumKeyExW(HKEY_CLASSES_ROOT, dwIndex++, szName, &dwName, NULL, NULL, NULL, NULL) == ERROR_SUCCESS)
1715 {
1716 if (InsertFileType(hDlgCtrl, szName, iItem, szFile))
1717 ++iItem;
1718 dwName = _countof(szName);
1719 }
1720
1721 /* Leave if the list is empty */
1722 if (iItem == 0)
1723 return NULL;
1724
1725 /* sort list */
1726 ListView_SortItems(hDlgCtrl, ListViewCompareProc, NULL);
1727
1728 /* select first item */
1729 ZeroMemory(&lvItem, sizeof(LVITEMW));
1730 lvItem.mask = LVIF_STATE;
1731 lvItem.stateMask = (UINT)-1;
1732 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
1733 lvItem.iItem = 0;
1734 ListView_SetItem(hDlgCtrl, &lvItem);
1735
1736 lvItem.mask = LVIF_PARAM;
1737 ListView_GetItem(hDlgCtrl, &lvItem);
1738
1739 return (PFOLDER_FILE_TYPE_ENTRY)lvItem.lParam;
1740 }
1741
1742 static inline
1743 PFOLDER_FILE_TYPE_ENTRY
1744 GetListViewEntry(HWND hListView, INT iItem = -1)
1745 {
1746 if (iItem == -1)
1747 {
1748 iItem = ListView_GetNextItem(hListView, -1, LVNI_SELECTED);
1749 if (iItem == -1)
1750 return NULL;
1751 }
1752
1753 LV_ITEMW lvItem = { LVIF_PARAM, iItem };
1754 if (ListView_GetItem(hListView, &lvItem))
1755 return (PFOLDER_FILE_TYPE_ENTRY)lvItem.lParam;
1756
1757 return NULL;
1758 }
1759
1760 struct NEWEXT_DIALOG
1761 {
1762 HWND hwndDlg;
1763 HWND hwndLV;
1764 RECT rcDlg;
1765 BOOL bAdvanced;
1766 INT dy;
1767 WCHAR szExt[16];
1768 WCHAR szFileType[64];
1769 };
1770
1771 static VOID
1772 NewExtDlg_OnAdvanced(HWND hwndDlg, NEWEXT_DIALOG *pNewExt)
1773 {
1774 // If "Advanced" button was clicked, then we shrink or expand the dialog.
1775 WCHAR szText[64];
1776 RECT rc, rc1, rc2;
1777
1778 GetWindowRect(hwndDlg, &rc);
1779 rc.bottom = rc.top + (pNewExt->rcDlg.bottom - pNewExt->rcDlg.top);
1780
1781 GetWindowRect(GetDlgItem(hwndDlg, IDOK), &rc1);
1782 MapWindowPoints(NULL, hwndDlg, (POINT *)&rc1, 2);
1783
1784 GetWindowRect(GetDlgItem(hwndDlg, IDCANCEL), &rc2);
1785 MapWindowPoints(NULL, hwndDlg, (POINT *)&rc2, 2);
1786
1787 if (pNewExt->bAdvanced)
1788 {
1789 rc1.top += pNewExt->dy;
1790 rc1.bottom += pNewExt->dy;
1791
1792 rc2.top += pNewExt->dy;
1793 rc2.bottom += pNewExt->dy;
1794
1795 ShowWindow(GetDlgItem(hwndDlg, IDC_NEWEXT_ASSOC), SW_SHOWNOACTIVATE);
1796 ShowWindow(GetDlgItem(hwndDlg, IDC_NEWEXT_COMBOBOX), SW_SHOWNOACTIVATE);
1797
1798 LoadStringW(shell32_hInstance, IDS_NEWEXT_ADVANCED_LEFT, szText, _countof(szText));
1799 SetDlgItemTextW(hwndDlg, IDC_NEWEXT_ADVANCED, szText);
1800
1801 SetFocus(GetDlgItem(hwndDlg, IDC_NEWEXT_COMBOBOX));
1802 }
1803 else
1804 {
1805 rc1.top -= pNewExt->dy;
1806 rc1.bottom -= pNewExt->dy;
1807
1808 rc2.top -= pNewExt->dy;
1809 rc2.bottom -= pNewExt->dy;
1810
1811 ShowWindow(GetDlgItem(hwndDlg, IDC_NEWEXT_ASSOC), SW_HIDE);
1812 ShowWindow(GetDlgItem(hwndDlg, IDC_NEWEXT_COMBOBOX), SW_HIDE);
1813
1814 LoadStringW(shell32_hInstance, IDS_NEWEXT_ADVANCED_RIGHT, szText, _countof(szText));
1815 SetDlgItemTextW(hwndDlg, IDC_NEWEXT_ADVANCED, szText);
1816
1817 rc.bottom -= pNewExt->dy;
1818
1819 LoadStringW(shell32_hInstance, IDS_NEWEXT_NEW, szText, _countof(szText));
1820 SetDlgItemTextW(hwndDlg, IDC_NEWEXT_COMBOBOX, szText);
1821 }
1822
1823 HDWP hDWP = BeginDeferWindowPos(3);
1824
1825 if (hDWP)
1826 hDWP = DeferWindowPos(hDWP, GetDlgItem(hwndDlg, IDOK), NULL,
1827 rc1.left, rc1.top, rc1.right - rc1.left, rc1.bottom - rc1.top,
1828 SWP_NOACTIVATE | SWP_NOZORDER);
1829 if (hDWP)
1830 hDWP = DeferWindowPos(hDWP, GetDlgItem(hwndDlg, IDCANCEL), NULL,
1831 rc2.left, rc2.top, rc2.right - rc2.left, rc2.bottom - rc2.top,
1832 SWP_NOACTIVATE | SWP_NOZORDER);
1833 if (hDWP)
1834 hDWP = DeferWindowPos(hDWP, hwndDlg, NULL,
1835 rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top,
1836 SWP_NOACTIVATE | SWP_NOZORDER);
1837
1838 if (hDWP)
1839 EndDeferWindowPos(hDWP);
1840 }
1841
1842 static BOOL
1843 NewExtDlg_OnInitDialog(HWND hwndDlg, NEWEXT_DIALOG *pNewExt)
1844 {
1845 WCHAR szText[64];
1846
1847 pNewExt->hwndDlg = hwndDlg;
1848 pNewExt->bAdvanced = FALSE;
1849
1850 GetWindowRect(hwndDlg, &pNewExt->rcDlg);
1851
1852 RECT rc1, rc2;
1853 GetWindowRect(GetDlgItem(hwndDlg, IDC_NEWEXT_EDIT), &rc1);
1854 GetWindowRect(GetDlgItem(hwndDlg, IDC_NEWEXT_COMBOBOX), &rc2);
1855 pNewExt->dy = rc2.top - rc1.top;
1856
1857 LoadStringW(shell32_hInstance, IDS_NEWEXT_NEW, szText, _countof(szText));
1858 SendDlgItemMessageW(hwndDlg, IDC_NEWEXT_COMBOBOX, CB_ADDSTRING, 0, (LPARAM)szText);
1859 SendDlgItemMessageW(hwndDlg, IDC_NEWEXT_COMBOBOX, CB_SETCURSEL, 0, 0);
1860
1861 SendDlgItemMessageW(hwndDlg, IDC_NEWEXT_EDIT, EM_SETLIMITTEXT, _countof(pNewExt->szExt) - 1, 0);
1862
1863 NewExtDlg_OnAdvanced(hwndDlg, pNewExt);
1864
1865 return TRUE;
1866 }
1867
1868 static void
1869 StringTrimW(LPWSTR pszText)
1870 {
1871 LPWSTR pch = pszText;
1872 while (iswspace(*pch))
1873 pch++;
1874
1875 LPWSTR pchFirst, pchLast;
1876 pchFirst = pchLast = pch;
1877 while (*pch && !iswspace(*pch))
1878 {
1879 ++pch;
1880 pchLast = pch;
1881 }
1882
1883 INT_PTR cch = pchLast - pchFirst;
1884 MoveMemory(pszText, pchFirst, cch * sizeof(WCHAR));
1885 pszText[cch] = 0;
1886 }
1887
1888 static BOOL
1889 NewExtDlg_OnOK(HWND hwndDlg, NEWEXT_DIALOG *pNewExt)
1890 {
1891 LV_FINDINFO find;
1892 INT iItem;
1893
1894 GetDlgItemTextW(hwndDlg, IDC_NEWEXT_EDIT, pNewExt->szExt, _countof(pNewExt->szExt));
1895 StringTrimW(pNewExt->szExt);
1896 CharUpperW(pNewExt->szExt);
1897
1898 GetDlgItemTextW(hwndDlg, IDC_NEWEXT_COMBOBOX, pNewExt->szFileType, _countof(pNewExt->szFileType));
1899 StringTrimW(pNewExt->szFileType);
1900
1901 if (pNewExt->szExt[0] == 0)
1902 {
1903 WCHAR szText[128], szTitle[128];
1904 LoadStringW(shell32_hInstance, IDS_NEWEXT_SPECIFY_EXT, szText, _countof(szText));
1905 szText[_countof(szText) - 1] = 0;
1906 LoadStringW(shell32_hInstance, IDS_FILE_TYPES, szTitle, _countof(szTitle));
1907 szTitle[_countof(szTitle) - 1] = 0;
1908 MessageBoxW(hwndDlg, szText, szTitle, MB_ICONERROR);
1909 return FALSE;
1910 }
1911
1912 ZeroMemory(&find, sizeof(find));
1913 find.flags = LVFI_STRING;
1914 if (pNewExt->szExt[0] == L'.')
1915 {
1916 find.psz = &pNewExt->szExt[1];
1917 }
1918 else
1919 {
1920 find.psz = pNewExt->szExt;
1921 }
1922
1923 iItem = ListView_FindItem(pNewExt->hwndLV, -1, &find);
1924 if (iItem >= 0)
1925 {
1926 // already exists
1927 WCHAR szText[256], szFormat[256], szTitle[64], szFileType[64];
1928
1929 // get file type
1930 LV_ITEM item;
1931 ZeroMemory(&item, sizeof(item));
1932 item.mask = LVIF_TEXT;
1933 item.pszText = szFileType;
1934 item.cchTextMax = _countof(szFileType);
1935 item.iItem = iItem;
1936 item.iSubItem = 1;
1937 ListView_GetItem(pNewExt->hwndLV, &item);
1938
1939 // get text
1940 LoadStringW(shell32_hInstance, IDS_NEWEXT_ALREADY_ASSOC, szFormat, _countof(szFormat));
1941 szText[_countof(szFormat) - 1] = 0;
1942 StringCchPrintfW(szText, _countof(szText), szFormat, find.psz, szFileType, find.psz, szFileType);
1943
1944 // get title
1945 LoadStringW(shell32_hInstance, IDS_NEWEXT_EXT_IN_USE, szTitle, _countof(szTitle));
1946 szTitle[_countof(szTitle) - 1] = 0;
1947
1948 if (MessageBoxW(hwndDlg, szText, szTitle, MB_ICONWARNING | MB_YESNO) == IDNO)
1949 {
1950 return FALSE;
1951 }
1952
1953 // Delete the extension
1954 CStringW strExt(L".");
1955 strExt += find.psz;
1956 strExt.MakeLower();
1957 DeleteExt(hwndDlg, strExt);
1958
1959 // Delete the item
1960 ListView_DeleteItem(pNewExt->hwndLV, iItem);
1961 }
1962
1963 EndDialog(hwndDlg, IDOK);
1964 return TRUE;
1965 }
1966
1967 // IDD_NEWEXTENSION dialog
1968 INT_PTR
1969 CALLBACK
1970 NewExtensionDlgProc(
1971 HWND hwndDlg,
1972 UINT uMsg,
1973 WPARAM wParam,
1974 LPARAM lParam)
1975 {
1976 static NEWEXT_DIALOG *s_pNewExt = NULL;
1977
1978 switch (uMsg)
1979 {
1980 case WM_INITDIALOG:
1981 s_pNewExt = (NEWEXT_DIALOG *)lParam;
1982 NewExtDlg_OnInitDialog(hwndDlg, s_pNewExt);
1983 return TRUE;
1984 case WM_COMMAND:
1985 switch (LOWORD(wParam))
1986 {
1987 case IDOK:
1988 NewExtDlg_OnOK(hwndDlg, s_pNewExt);
1989 break;
1990 case IDCANCEL:
1991 EndDialog(hwndDlg, IDCANCEL);
1992 break;
1993 case IDC_NEWEXT_ADVANCED:
1994 s_pNewExt->bAdvanced = !s_pNewExt->bAdvanced;
1995 NewExtDlg_OnAdvanced(hwndDlg, s_pNewExt);
1996 break;
1997 }
1998 break;
1999 }
2000 return 0;
2001 }
2002
2003 static BOOL
2004 FileTypesDlg_AddExt(HWND hwndDlg, LPCWSTR pszExt, LPCWSTR pszFileType)
2005 {
2006 DWORD dwValue = 1;
2007 HKEY hKey;
2008 WCHAR szKey[13]; // max. "ft4294967295" + "\0"
2009 LONG nResult;
2010
2011 // Search the next "ft%06u" key name
2012 do
2013 {
2014 StringCchPrintfW(szKey, _countof(szKey), TEXT("ft%06u"), dwValue);
2015
2016 nResult = RegOpenKeyEx(HKEY_CLASSES_ROOT, szKey, 0, KEY_READ, &hKey);
2017 if (nResult != ERROR_SUCCESS)
2018 break;
2019
2020 RegCloseKey(hKey);
2021 ++dwValue;
2022 } while (dwValue != 0);
2023
2024 RegCloseKey(hKey);
2025
2026 if (dwValue == 0)
2027 return FALSE;
2028
2029 // Create new "ft%06u" key
2030 nResult = RegCreateKeyEx(HKEY_CLASSES_ROOT, szKey, 0, NULL, 0, KEY_WRITE, NULL, &hKey, NULL);
2031 if (ERROR_SUCCESS != nResult)
2032 return FALSE;
2033
2034 RegCloseKey(hKey);
2035
2036 // Create the ".ext" key
2037 WCHAR szExt[16];
2038 if (*pszExt == L'.')
2039 ++pszExt;
2040 StringCchPrintfW(szExt, _countof(szExt), TEXT(".%s"), pszExt);
2041 CharLowerW(szExt);
2042 nResult = RegCreateKeyEx(HKEY_CLASSES_ROOT, szExt, 0, NULL, 0, KEY_WRITE, NULL, &hKey, NULL);
2043 CharUpperW(szExt);
2044 if (ERROR_SUCCESS != nResult)
2045 return FALSE;
2046
2047 // Set the default value of ".ext" to "ft%06u"
2048 DWORD dwSize = (lstrlen(szKey) + 1) * sizeof(WCHAR);
2049 RegSetValueExW(hKey, NULL, 0, REG_SZ, (BYTE *)szKey, dwSize);
2050
2051 RegCloseKey(hKey);
2052
2053 // Make up the file type name
2054 WCHAR szFile[100], szFileFormat[100];
2055 LoadStringW(shell32_hInstance, IDS_FILE_EXT_TYPE, szFileFormat, _countof(szFileFormat));
2056 szFile[_countof(szFileFormat) - 1] = 0;
2057 StringCchPrintfW(szFile, _countof(szFile), szFileFormat, &szExt[1]);
2058
2059 // Insert an item to the listview
2060 HWND hListView = GetDlgItem(hwndDlg, IDC_FILETYPES_LISTVIEW);
2061 INT iItem = ListView_GetItemCount(hListView);
2062 if (!InsertFileType(hListView, szExt, iItem, szFile))
2063 return FALSE;
2064
2065 LV_ITEM item;
2066 ZeroMemory(&item, sizeof(item));
2067 item.mask = LVIF_STATE | LVIF_TEXT;
2068 item.iItem = iItem;
2069 item.state = LVIS_SELECTED | LVIS_FOCUSED;
2070 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
2071 item.pszText = &szExt[1];
2072 ListView_SetItem(hListView, &item);
2073
2074 item.pszText = szFile;
2075 item.iSubItem = 1;
2076 ListView_SetItem(hListView, &item);
2077
2078 ListView_EnsureVisible(hListView, iItem, FALSE);
2079
2080 return TRUE;
2081 }
2082
2083 static BOOL
2084 FileTypesDlg_RemoveExt(HWND hwndDlg)
2085 {
2086 HWND hListView = GetDlgItem(hwndDlg, IDC_FILETYPES_LISTVIEW);
2087
2088 INT iItem = ListView_GetNextItem(hListView, -1, LVNI_SELECTED);
2089 if (iItem == -1)
2090 return FALSE;
2091
2092 WCHAR szExt[20];
2093 szExt[0] = L'.';
2094 ListView_GetItemText(hListView, iItem, 0, &szExt[1], _countof(szExt) - 1);
2095 CharLowerW(szExt);
2096
2097 DeleteExt(hwndDlg, szExt);
2098 ListView_DeleteItem(hListView, iItem);
2099 return TRUE;
2100 }
2101
2102 static void
2103 FileTypesDlg_OnItemChanging(HWND hwndDlg, PFOLDER_FILE_TYPE_ENTRY pEntry)
2104 {
2105 WCHAR Buffer[255];
2106 static HBITMAP s_hbmProgram = NULL;
2107
2108 // format buffer and set groupbox text
2109 CStringW strFormat(MAKEINTRESOURCEW(IDS_FILE_DETAILS));
2110 StringCchPrintfW(Buffer, _countof(Buffer), strFormat, &pEntry->FileExtension[1]);
2111 SetDlgItemTextW(hwndDlg, IDC_FILETYPES_DETAILS_GROUPBOX, Buffer);
2112
2113 // format buffer and set description
2114 strFormat.LoadString(IDS_FILE_DETAILSADV);
2115 StringCchPrintfW(Buffer, _countof(Buffer), strFormat,
2116 &pEntry->FileExtension[1], pEntry->FileDescription,
2117 pEntry->FileDescription);
2118 SetDlgItemTextW(hwndDlg, IDC_FILETYPES_DESCRIPTION, Buffer);
2119
2120 // delete previous program image
2121 if (s_hbmProgram)
2122 {
2123 DeleteObject(s_hbmProgram);
2124 s_hbmProgram = NULL;
2125 }
2126
2127 // set program image
2128 HICON hIconSm = NULL;
2129 ExtractIconExW(pEntry->ProgramPath, 0, NULL, &hIconSm, 1);
2130 s_hbmProgram = BitmapFromIcon(hIconSm, 16, 16);
2131 DestroyIcon(hIconSm);
2132 SendDlgItemMessageW(hwndDlg, IDC_FILETYPES_ICON, STM_SETIMAGE, IMAGE_BITMAP, LPARAM(s_hbmProgram));
2133
2134 // set program name
2135 if (pEntry->AppName[0])
2136 SetDlgItemTextW(hwndDlg, IDC_FILETYPES_APPNAME, pEntry->AppName);
2137 else
2138 SetDlgItemTextW(hwndDlg, IDC_FILETYPES_APPNAME, L"ReactOS");
2139
2140 /* Enable the Delete button */
2141 if (pEntry->EditFlags & 0x00000010) // FTA_NoRemove
2142 EnableWindow(GetDlgItem(hwndDlg, IDC_FILETYPES_DELETE), FALSE);
2143 else
2144 EnableWindow(GetDlgItem(hwndDlg, IDC_FILETYPES_DELETE), TRUE);
2145 }
2146
2147 // IDD_FOLDER_OPTIONS_FILETYPES dialog
2148 INT_PTR
2149 CALLBACK
2150 FolderOptionsFileTypesDlg(
2151 HWND hwndDlg,
2152 UINT uMsg,
2153 WPARAM wParam,
2154 LPARAM lParam)
2155 {
2156 LPNMLISTVIEW lppl;
2157 PFOLDER_FILE_TYPE_ENTRY pItem;
2158 OPENASINFO Info;
2159 NEWEXT_DIALOG newext;
2160
2161 switch(uMsg)
2162 {
2163 case WM_INITDIALOG:
2164 pItem = InitializeFileTypesListCtrl(hwndDlg);
2165
2166 /* Disable the Delete button if the listview is empty or
2167 the selected item should not be deleted by the user */
2168 if (pItem == NULL || (pItem->EditFlags & 0x00000010)) // FTA_NoRemove
2169 EnableWindow(GetDlgItem(hwndDlg, IDC_FILETYPES_DELETE), FALSE);
2170 return TRUE;
2171
2172 case WM_COMMAND:
2173 switch(LOWORD(wParam))
2174 {
2175 case IDC_FILETYPES_NEW:
2176 newext.hwndLV = GetDlgItem(hwndDlg, IDC_FILETYPES_LISTVIEW);
2177 if (IDOK == DialogBoxParamW(shell32_hInstance, MAKEINTRESOURCEW(IDD_NEWEXTENSION),
2178 hwndDlg, NewExtensionDlgProc, (LPARAM)&newext))
2179 {
2180 FileTypesDlg_AddExt(hwndDlg, newext.szExt, newext.szFileType);
2181 }
2182 break;
2183 case IDC_FILETYPES_DELETE:
2184 {
2185 CStringW strRemoveExt(MAKEINTRESOURCEW(IDS_REMOVE_EXT));
2186 CStringW strTitle(MAKEINTRESOURCEW(IDS_FILE_TYPES));
2187 if (MessageBoxW(hwndDlg, strRemoveExt, strTitle, MB_ICONQUESTION | MB_YESNO) == IDYES)
2188 {
2189 FileTypesDlg_RemoveExt(hwndDlg);
2190 }
2191 }
2192 break;
2193 case IDC_FILETYPES_CHANGE:
2194 pItem = GetListViewEntry(GetDlgItem(hwndDlg, IDC_FILETYPES_LISTVIEW));
2195 if (pItem)
2196 {
2197 Info.oaifInFlags = OAIF_ALLOW_REGISTRATION | OAIF_REGISTER_EXT;
2198 Info.pcszClass = pItem->FileExtension;
2199 SHOpenWithDialog(hwndDlg, &Info);
2200 }
2201 break;
2202 }
2203 break;
2204
2205 case WM_NOTIFY:
2206 lppl = (LPNMLISTVIEW) lParam;
2207 switch (lppl->hdr.code)
2208 {
2209 case LVN_DELETEALLITEMS:
2210 return FALSE; // send LVN_DELETEITEM
2211
2212 case LVN_DELETEITEM:
2213 pItem = GetListViewEntry(lppl->hdr.hwndFrom, lppl->iItem);
2214 if (pItem)
2215 {
2216 DestroyIcon(pItem->hIconLarge);
2217 DestroyIcon(pItem->hIconSmall);
2218 HeapFree(GetProcessHeap(), 0, pItem);
2219 }
2220 return FALSE;
2221
2222 case LVN_ITEMCHANGING:
2223 pItem = GetListViewEntry(lppl->hdr.hwndFrom, lppl->iItem);
2224 if (!pItem)
2225 return TRUE;
2226
2227 if (!(lppl->uOldState & LVIS_FOCUSED) && (lppl->uNewState & LVIS_FOCUSED))
2228 {
2229 FileTypesDlg_OnItemChanging(hwndDlg, pItem);
2230 }
2231 break;
2232
2233 case PSN_SETACTIVE:
2234 /* On page activation, set the focus to the listview */
2235 SetFocus(GetDlgItem(hwndDlg, IDC_FILETYPES_LISTVIEW));
2236 break;
2237 }
2238 break;
2239 }
2240
2241 return FALSE;
2242 }
2243
2244 static
2245 VOID
2246 ShowFolderOptionsDialog(HWND hWnd, HINSTANCE hInst)
2247 {
2248 PROPSHEETHEADERW pinfo;
2249 HPROPSHEETPAGE hppages[3];
2250 HPROPSHEETPAGE hpage;
2251 UINT num_pages = 0;
2252 WCHAR szOptions[100];
2253
2254 hpage = SH_CreatePropertySheetPage(IDD_FOLDER_OPTIONS_GENERAL, FolderOptionsGeneralDlg, 0, NULL);
2255 if (hpage)
2256 hppages[num_pages++] = hpage;
2257
2258 hpage = SH_CreatePropertySheetPage(IDD_FOLDER_OPTIONS_VIEW, FolderOptionsViewDlg, 0, NULL);
2259 if (hpage)
2260 hppages[num_pages++] = hpage;
2261
2262 hpage = SH_CreatePropertySheetPage(IDD_FOLDER_OPTIONS_FILETYPES, FolderOptionsFileTypesDlg, 0, NULL);
2263 if (hpage)
2264 hppages[num_pages++] = hpage;
2265
2266 szOptions[0] = L'\0';
2267 LoadStringW(shell32_hInstance, IDS_FOLDER_OPTIONS, szOptions, sizeof(szOptions) / sizeof(WCHAR));
2268 szOptions[(sizeof(szOptions)/sizeof(WCHAR))-1] = L'\0';
2269
2270 memset(&pinfo, 0x0, sizeof(PROPSHEETHEADERW));
2271 pinfo.dwSize = sizeof(PROPSHEETHEADERW);
2272 pinfo.dwFlags = PSH_NOCONTEXTHELP;
2273 pinfo.nPages = num_pages;
2274 pinfo.phpage = hppages;
2275 pinfo.pszCaption = szOptions;
2276
2277 PropertySheetW(&pinfo);
2278 }
2279
2280 static
2281 VOID
2282 Options_RunDLLCommon(HWND hWnd, HINSTANCE hInst, int fOptions, DWORD nCmdShow)
2283 {
2284 switch(fOptions)
2285 {
2286 case 0:
2287 ShowFolderOptionsDialog(hWnd, hInst);
2288 break;
2289 case 1:
2290 // show taskbar options dialog
2291 FIXME("notify explorer to show taskbar options dialog");
2292 //PostMessage(GetShellWindow(), WM_USER+22, fOptions, 0);
2293 break;
2294 default:
2295 FIXME("unrecognized options id %d\n", fOptions);
2296 }
2297 }
2298
2299 /*************************************************************************
2300 * Options_RunDLL (SHELL32.@)
2301 */
2302 EXTERN_C VOID WINAPI Options_RunDLL(HWND hWnd, HINSTANCE hInst, LPCSTR cmd, DWORD nCmdShow)
2303 {
2304 Options_RunDLLCommon(hWnd, hInst, StrToIntA(cmd), nCmdShow);
2305 }
2306
2307 /*************************************************************************
2308 * Options_RunDLLA (SHELL32.@)
2309 */
2310 EXTERN_C VOID WINAPI Options_RunDLLA(HWND hWnd, HINSTANCE hInst, LPCSTR cmd, DWORD nCmdShow)
2311 {
2312 Options_RunDLLCommon(hWnd, hInst, StrToIntA(cmd), nCmdShow);
2313 }
2314
2315 /*************************************************************************
2316 * Options_RunDLLW (SHELL32.@)
2317 */
2318 EXTERN_C VOID WINAPI Options_RunDLLW(HWND hWnd, HINSTANCE hInst, LPCWSTR cmd, DWORD nCmdShow)
2319 {
2320 Options_RunDLLCommon(hWnd, hInst, StrToIntW(cmd), nCmdShow);
2321 }