xref: /reactos/dll/win32/shell32/dialogs/view.cpp (revision 01e5cb0c)
1 /*
2  *     'View' tab property sheet of Folder Options
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 /////////////////////////////////////////////////////////////////////////////
27 // View Tree
28 
29 // predefined icon IDs (See ViewDlg_CreateTreeImageList function below)
30 #define I_CHECKED                   0
31 #define I_UNCHECKED                 1
32 #define I_CHECKED_DISABLED          2
33 #define I_UNCHECKED_DISABLED        3
34 #define I_RADIO_CHECKED             4
35 #define I_RADIO_UNCHECKED           5
36 #define I_RADIO_CHECKED_DISABLED    6
37 #define I_RADIO_UNCHECKED_DISABLED  7
38 #define PREDEFINED_ICON_COUNT       8
39 
40 // uniquely-defined icon entry for View Advanced Settings
41 typedef struct VIEWTREE_ICON
42 {
43     WCHAR   szPath[MAX_PATH];
44     UINT    nIconIndex;
45 } VIEWTREE_ICON, *PVIEWTREE_ICON;
46 
47 // types of View Advanced Setting entry
48 typedef enum VIEWTREE_ENTRY_TYPE
49 {
50     AETYPE_GROUP,
51     AETYPE_CHECKBOX,
52     AETYPE_RADIO,
53 } VIEWTREE_ENTRY_TYPE, *PVIEWTREE_ENTRY_TYPE;
54 
55 // an entry info of View Advanced Settings
56 typedef struct VIEWTREE_ENTRY
57 {
58     DWORD   dwID;                   // entry ID
59     DWORD   dwParentID;             // parent entry ID
60     DWORD   dwResourceID;           // resource ID
61     WCHAR   szKeyName[64];          // entry key name
62     DWORD   dwType;                 // VIEWTREE_ENTRY_TYPE
63     WCHAR   szText[MAX_PATH];       // text
64     INT     nIconID;                // icon ID (See VIEWTREE_ICON)
65 
66     HKEY    hkeyRoot;               // registry root key
67     WCHAR   szRegPath[MAX_PATH];    // registry path
68     WCHAR   szValueName[64];        // registry value name
69 
70     DWORD   dwCheckedValue;         // checked value
71     DWORD   dwUncheckedValue;       // unchecked value
72     DWORD   dwDefaultValue;         // defalut value
73     BOOL    bHasUncheckedValue;     // If FALSE, UncheckedValue is invalid
74 
75     HTREEITEM   hItem;              // for TreeView
76     BOOL        bGrayed;            // disabled?
77     BOOL        bChecked;           // checked?
78 } VIEWTREE_ENTRY, *PVIEWTREE_ENTRY;
79 
80 // definition of view advanced entries
81 static PVIEWTREE_ENTRY  s_ViewTreeEntries       = NULL;
82 static INT              s_ViewTreeEntryCount    = 0;
83 
84 // definition of icon stock
85 static PVIEWTREE_ICON   s_ViewTreeIcons         = NULL;
86 static INT              s_ViewTreeIconCount     = 0;
87 static HIMAGELIST       s_hTreeImageList        = NULL;
88 
89 static INT
90 ViewTree_FindIcon(LPCWSTR pszPath, UINT nIconIndex)
91 {
92     for (INT i = PREDEFINED_ICON_COUNT; i < s_ViewTreeIconCount; ++i)
93     {
94         PVIEWTREE_ICON pIcon = &s_ViewTreeIcons[i];
95         if (pIcon->nIconIndex == nIconIndex &&
96             lstrcmpiW(pIcon->szPath, pszPath) == 0)
97         {
98             return i;   // icon ID
99         }
100     }
101     return -1;  // not found
102 }
103 
104 static INT
105 ViewTree_AddIcon(LPCWSTR pszPath, UINT nIconIndex)
106 {
107     PVIEWTREE_ICON pAllocated;
108 
109     // return the ID if already existed
110     INT nIconID = ViewTree_FindIcon(pszPath, nIconIndex);
111     if (nIconID != -1)
112         return nIconID;     // already exists
113 
114     // extract a small icon
115     HICON hIconSmall = NULL;
116     ExtractIconExW(pszPath, nIconIndex, NULL, &hIconSmall, 1);
117     if (hIconSmall == NULL)
118         return -1;      // failure
119 
120     // resize s_ViewTreeIcons
121     size_t Size = (s_ViewTreeIconCount + 1) * sizeof(VIEWTREE_ICON);
122     pAllocated = (PVIEWTREE_ICON)realloc(s_ViewTreeIcons, Size);
123     if (pAllocated == NULL)
124         return -1;      // failure
125     else
126         s_ViewTreeIcons = pAllocated;
127 
128     // save icon information
129     PVIEWTREE_ICON pIcon = &s_ViewTreeIcons[s_ViewTreeIconCount];
130     lstrcpynW(pIcon->szPath, pszPath, _countof(pIcon->szPath));
131     pIcon->nIconIndex = nIconIndex;
132 
133     // add the icon to the image list
134     ImageList_AddIcon(s_hTreeImageList, hIconSmall);
135 
136     // increment the counter
137     nIconID = s_ViewTreeIconCount;
138     ++s_ViewTreeIconCount;
139 
140     DestroyIcon(hIconSmall);
141 
142     return nIconID;     // newly-added icon ID
143 }
144 
145 static PVIEWTREE_ENTRY
146 ViewTree_GetItem(DWORD dwID)
147 {
148     if (dwID == DWORD(-1))
149         return NULL;
150 
151     for (INT i = 0; i < s_ViewTreeEntryCount; ++i)
152     {
153         PVIEWTREE_ENTRY pEntry = &s_ViewTreeEntries[i];
154         if (pEntry->dwID == dwID)
155             return pEntry;
156     }
157     return NULL;    // failure
158 }
159 
160 static INT
161 ViewTree_GetImage(PVIEWTREE_ENTRY pEntry)
162 {
163     switch (pEntry->dwType)
164     {
165         case AETYPE_GROUP:
166             return pEntry->nIconID;
167 
168         case AETYPE_CHECKBOX:
169             if (pEntry->bGrayed)
170             {
171                 if (pEntry->bChecked)
172                     return I_CHECKED_DISABLED;
173                 else
174                     return I_UNCHECKED_DISABLED;
175             }
176             else
177             {
178                 if (pEntry->bChecked)
179                     return I_CHECKED;
180                 else
181                     return I_UNCHECKED;
182             }
183 
184         case AETYPE_RADIO:
185             if (pEntry->bGrayed)
186             {
187                 if (pEntry->bChecked)
188                     return I_RADIO_CHECKED_DISABLED;
189                 else
190                     return I_RADIO_UNCHECKED_DISABLED;
191             }
192             else
193             {
194                 if (pEntry->bChecked)
195                     return I_RADIO_CHECKED;
196                 else
197                     return I_RADIO_UNCHECKED;
198             }
199     }
200     return -1;  // failure
201 }
202 
203 static VOID
204 ViewTree_InsertEntry(HWND hwndTreeView, PVIEWTREE_ENTRY pEntry)
205 {
206     PVIEWTREE_ENTRY pParent = ViewTree_GetItem(pEntry->dwParentID);
207     HTREEITEM hParent = TVI_ROOT;
208     if (pParent)
209         hParent = pParent->hItem;
210 
211     TV_INSERTSTRUCT Insertion;
212     ZeroMemory(&Insertion, sizeof(Insertion));
213     Insertion.hParent = hParent;
214     Insertion.hInsertAfter = TVI_LAST;
215     Insertion.item.mask =
216         TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM;
217     Insertion.item.pszText = pEntry->szText;
218 
219     INT iImage = ViewTree_GetImage(pEntry);
220     Insertion.item.iImage = Insertion.item.iSelectedImage = iImage;
221     Insertion.item.lParam = pEntry->dwID;
222     pEntry->hItem = TreeView_InsertItem(hwndTreeView, &Insertion);
223 }
224 
225 static VOID
226 ViewTree_InsertAll(HWND hwndTreeView)
227 {
228     TreeView_DeleteAllItems(hwndTreeView);
229 
230     // insert the entries
231     PVIEWTREE_ENTRY pEntry;
232     for (INT i = 0; i < s_ViewTreeEntryCount; ++i)
233     {
234         pEntry = &s_ViewTreeEntries[i];
235         ViewTree_InsertEntry(hwndTreeView, pEntry);
236     }
237 
238     // expand all
239     for (INT i = 0; i < s_ViewTreeEntryCount; ++i)
240     {
241         pEntry = &s_ViewTreeEntries[i];
242         if (pEntry->dwType == AETYPE_GROUP)
243         {
244             TreeView_Expand(hwndTreeView, pEntry->hItem, TVE_EXPAND);
245         }
246     }
247 }
248 
249 static BOOL
250 ViewTree_LoadTree(HKEY hKey, LPCWSTR pszKeyName, DWORD dwParentID)
251 {
252     DWORD dwIndex;
253     WCHAR szKeyName[64], szText[MAX_PATH], *pch;
254     DWORD Size, Value;
255     PVIEWTREE_ENTRY pAllocated;
256 
257     // resize s_ViewTreeEntries
258     Size = (s_ViewTreeEntryCount + 1) * sizeof(VIEWTREE_ENTRY);
259     pAllocated = (PVIEWTREE_ENTRY)realloc(s_ViewTreeEntries, Size);
260     if (pAllocated == NULL)
261         return FALSE;   // failure
262     else
263         s_ViewTreeEntries = pAllocated;
264 
265     PVIEWTREE_ENTRY pEntry = &s_ViewTreeEntries[s_ViewTreeEntryCount];
266 
267     // dwID, dwParentID, szKeyName
268     pEntry->dwID = s_ViewTreeEntryCount;
269     pEntry->dwParentID = dwParentID;
270     lstrcpynW(pEntry->szKeyName, pszKeyName, _countof(pEntry->szKeyName));
271 
272     // Text, ResourceID
273     pEntry->szText[0] = 0;
274     pEntry->dwResourceID = 0;
275     szText[0] = 0;
276     Size = sizeof(szText);
277     RegQueryValueExW(hKey, L"Text", NULL, NULL, LPBYTE(szText), &Size);
278     if (szText[0] == L'@')
279     {
280         pch = wcsrchr(szText, L',');
281         if (pch)
282         {
283             *pch = 0;
284             dwIndex = abs(_wtoi(pch + 1));
285             pEntry->dwResourceID = dwIndex;
286         }
287         HINSTANCE hInst = LoadLibraryW(&szText[1]);
288         LoadStringW(hInst, dwIndex, szText, _countof(szText));
289         FreeLibrary(hInst);
290     }
291     else
292     {
293         pEntry->dwResourceID = DWORD(-1);
294     }
295     lstrcpynW(pEntry->szText, szText, _countof(pEntry->szText));
296 
297     // Type
298     szText[0] = 0;
299     RegQueryValueExW(hKey, L"Type", NULL, NULL, LPBYTE(szText), &Size);
300     if (lstrcmpiW(szText, L"checkbox") == 0)
301         pEntry->dwType = AETYPE_CHECKBOX;
302     else if (lstrcmpiW(szText, L"radio") == 0)
303         pEntry->dwType = AETYPE_RADIO;
304     else if (lstrcmpiW(szText, L"group") == 0)
305         pEntry->dwType = AETYPE_GROUP;
306     else
307         return FALSE;   // failure
308 
309     pEntry->nIconID = -1;
310     if (pEntry->dwType == AETYPE_GROUP)
311     {
312         // Bitmap (Icon)
313         UINT nIconIndex = 0;
314         Size = sizeof(szText);
315         szText[0] = 0;
316         RegQueryValueExW(hKey, L"Bitmap", NULL, NULL, LPBYTE(szText), &Size);
317 
318         WCHAR szExpanded[MAX_PATH];
319         ExpandEnvironmentStringsW(szText, szExpanded, _countof(szExpanded));
320         pch = wcsrchr(szExpanded, L',');
321         if (pch)
322         {
323             *pch = 0;
324             nIconIndex = abs(_wtoi(pch + 1));
325         }
326         pEntry->nIconID = ViewTree_AddIcon(szExpanded, nIconIndex);
327     }
328 
329     if (pEntry->dwType == AETYPE_GROUP)
330     {
331         pEntry->hkeyRoot = NULL;
332         pEntry->szRegPath[0] = 0;
333         pEntry->szValueName[0] = 0;
334         pEntry->dwCheckedValue = 0;
335         pEntry->bHasUncheckedValue = FALSE;
336         pEntry->dwUncheckedValue = 0;
337         pEntry->dwDefaultValue = 0;
338         pEntry->hItem = NULL;
339         pEntry->bGrayed = FALSE;
340         pEntry->bChecked = FALSE;
341     }
342     else
343     {
344         // HKeyRoot
345         HKEY HKeyRoot = HKEY_CURRENT_USER;
346         Size = sizeof(HKeyRoot);
347         RegQueryValueExW(hKey, L"HKeyRoot", NULL, NULL, LPBYTE(&HKeyRoot), &Size);
348         pEntry->hkeyRoot = HKeyRoot;
349 
350         // RegPath
351         pEntry->szRegPath[0] = 0;
352         Size = sizeof(szText);
353         RegQueryValueExW(hKey, L"RegPath", NULL, NULL, LPBYTE(szText), &Size);
354         lstrcpynW(pEntry->szRegPath, szText, _countof(pEntry->szRegPath));
355 
356         // ValueName
357         pEntry->szValueName[0] = 0;
358         Size = sizeof(szText);
359         RegQueryValueExW(hKey, L"ValueName", NULL, NULL, LPBYTE(szText), &Size);
360         lstrcpynW(pEntry->szValueName, szText, _countof(pEntry->szValueName));
361 
362         // CheckedValue
363         Size = sizeof(Value);
364         Value = 0x00000001;
365         RegQueryValueExW(hKey, L"CheckedValue", NULL, NULL, LPBYTE(&Value), &Size);
366         pEntry->dwCheckedValue = Value;
367 
368         // UncheckedValue
369         Size = sizeof(Value);
370         Value = 0x00000000;
371         pEntry->bHasUncheckedValue = TRUE;
372         if (RegQueryValueExW(hKey, L"UncheckedValue", NULL,
373                              NULL, LPBYTE(&Value), &Size) != ERROR_SUCCESS)
374         {
375             pEntry->bHasUncheckedValue = FALSE;
376         }
377         pEntry->dwUncheckedValue = Value;
378 
379         // DefaultValue
380         Size = sizeof(Value);
381         Value = 0x00000001;
382         RegQueryValueExW(hKey, L"DefaultValue", NULL, NULL, LPBYTE(&Value), &Size);
383         pEntry->dwDefaultValue = Value;
384 
385         // hItem
386         pEntry->hItem = NULL;
387 
388         // bGrayed, bChecked
389         HKEY hkeyTarget;
390         Value = pEntry->dwDefaultValue;
391         pEntry->bGrayed = TRUE;
392         if (RegOpenKeyExW(HKEY(pEntry->hkeyRoot), pEntry->szRegPath, 0,
393                           KEY_READ, &hkeyTarget) == ERROR_SUCCESS)
394         {
395             Size = sizeof(Value);
396             if (RegQueryValueExW(hkeyTarget, pEntry->szValueName, NULL, NULL,
397                                  LPBYTE(&Value), &Size) == ERROR_SUCCESS)
398             {
399                 pEntry->bGrayed = FALSE;
400             }
401             RegCloseKey(hkeyTarget);
402         }
403         pEntry->bChecked = (Value == pEntry->dwCheckedValue);
404     }
405 
406     // Grayed (ReactOS extension)
407     Size = sizeof(Value);
408     Value = FALSE;
409     RegQueryValueExW(hKey, L"Grayed", NULL, NULL, LPBYTE(&Value), &Size);
410     if (!pEntry->bGrayed)
411         pEntry->bGrayed = Value;
412 
413     BOOL bIsGroup = (pEntry->dwType == AETYPE_GROUP);
414     dwParentID = pEntry->dwID;
415     ++s_ViewTreeEntryCount;
416 
417     if (!bIsGroup)
418         return TRUE;    // success
419 
420     // load the children
421     dwIndex = 0;
422     while (RegEnumKeyW(hKey, dwIndex, szKeyName,
423                        _countof(szKeyName)) == ERROR_SUCCESS)
424     {
425         HKEY hkeyChild;
426         if (RegOpenKeyExW(hKey, szKeyName, 0, KEY_READ,
427                           &hkeyChild) != ERROR_SUCCESS)
428         {
429             ++dwIndex;
430             continue;   // failure
431         }
432 
433         ViewTree_LoadTree(hkeyChild, szKeyName, dwParentID);
434         RegCloseKey(hkeyChild);
435 
436         ++dwIndex;
437     }
438 
439     return TRUE;    // success
440 }
441 
442 static BOOL ViewTree_LoadAll(VOID)
443 {
444     static const WCHAR s_szAdvanced[] =
445         L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced";
446 
447     // free if already existed
448     if (s_ViewTreeEntries)
449     {
450         free(s_ViewTreeEntries);
451         s_ViewTreeEntries = NULL;
452     }
453     s_ViewTreeEntryCount = 0;
454 
455     HKEY hKey;
456     if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, s_szAdvanced, 0,
457                       KEY_READ, &hKey) != ERROR_SUCCESS)
458     {
459         return FALSE;   // failure
460     }
461 
462     // load the children
463     WCHAR szKeyName[64];
464     DWORD dwIndex = 0;
465     while (RegEnumKeyW(hKey, dwIndex, szKeyName,
466                        _countof(szKeyName)) == ERROR_SUCCESS)
467     {
468         HKEY hkeyChild;
469         if (RegOpenKeyExW(hKey, szKeyName, 0, KEY_READ,
470                           &hkeyChild) != ERROR_SUCCESS)
471         {
472             ++dwIndex;
473             continue;   // failure
474         }
475 
476         ViewTree_LoadTree(hkeyChild, szKeyName, DWORD(-1));
477         RegCloseKey(hkeyChild);
478 
479         ++dwIndex;
480     }
481 
482     RegCloseKey(hKey);
483 
484     return TRUE;    // success
485 }
486 
487 static int ViewTree_Compare(const void *x, const void *y)
488 {
489     PVIEWTREE_ENTRY pEntry1 = (PVIEWTREE_ENTRY)x;
490     PVIEWTREE_ENTRY pEntry2 = (PVIEWTREE_ENTRY)y;
491 
492     DWORD dwParentID1 = pEntry1->dwParentID;
493     DWORD dwParentID2 = pEntry2->dwParentID;
494 
495     if (dwParentID1 == dwParentID2)
496         return lstrcmpi(pEntry1->szText, pEntry2->szText);
497 
498     DWORD i, m, n;
499     const UINT MAX_DEPTH = 32;
500     PVIEWTREE_ENTRY pArray1[MAX_DEPTH];
501     PVIEWTREE_ENTRY pArray2[MAX_DEPTH];
502 
503     // Make ancestor lists
504     for (i = m = n = 0; i < MAX_DEPTH; ++i)
505     {
506         PVIEWTREE_ENTRY pParent1 = ViewTree_GetItem(dwParentID1);
507         PVIEWTREE_ENTRY pParent2 = ViewTree_GetItem(dwParentID2);
508         if (!pParent1 && !pParent2)
509             break;
510 
511         if (pParent1)
512         {
513             pArray1[m++] = pParent1;
514             dwParentID1 = pParent1->dwParentID;
515         }
516         if (pParent2)
517         {
518             pArray2[n++] = pParent2;
519             dwParentID2 = pParent2->dwParentID;
520         }
521     }
522 
523     UINT k = min(m, n);
524     for (i = 0; i < k; ++i)
525     {
526         INT nCompare = lstrcmpi(pArray1[m - i - 1]->szText, pArray2[n - i - 1]->szText);
527         if (nCompare < 0)
528             return -1;
529         if (nCompare > 0)
530             return 1;
531     }
532 
533     if (m < n)
534         return -1;
535     if (m > n)
536         return 1;
537     return lstrcmpi(pEntry1->szText, pEntry2->szText);
538 }
539 
540 static VOID
541 ViewTree_SortAll(VOID)
542 {
543     qsort(s_ViewTreeEntries, s_ViewTreeEntryCount, sizeof(VIEWTREE_ENTRY), ViewTree_Compare);
544 }
545 
546 /////////////////////////////////////////////////////////////////////////////
547 // ViewDlg
548 
549 static HIMAGELIST
550 ViewDlg_CreateTreeImageList(VOID)
551 {
552     HIMAGELIST hImageList;
553     hImageList = ImageList_Create(16, 16, ILC_COLOR24 | ILC_MASK, 9, 1);
554     if (hImageList == NULL)
555         return NULL;    // failure
556 
557     // free if existed
558     if (s_ViewTreeIcons)
559     {
560         free(s_ViewTreeIcons);
561         s_ViewTreeIcons = NULL;
562     }
563     s_ViewTreeIconCount = 0;
564 
565     // allocate now
566     PVIEWTREE_ICON pAllocated;
567     size_t Size = PREDEFINED_ICON_COUNT * sizeof(VIEWTREE_ICON);
568     pAllocated = (PVIEWTREE_ICON)calloc(1, Size);
569     if (pAllocated == NULL)
570         return NULL;    // failure
571 
572     s_ViewTreeIconCount = PREDEFINED_ICON_COUNT;
573     s_ViewTreeIcons = pAllocated;
574 
575     // add the predefined icons
576 
577     HDC hDC = CreateCompatibleDC(NULL);
578     HBITMAP hbmMask = CreateCheckMask(hDC);
579 
580     HBITMAP hbmChecked, hbmUnchecked;
581 
582     hbmChecked = CreateCheckImage(hDC, TRUE);
583     ImageList_Add(hImageList, hbmChecked, hbmMask);
584     DeleteObject(hbmChecked);
585 
586     hbmUnchecked = CreateCheckImage(hDC, FALSE);
587     ImageList_Add(hImageList, hbmUnchecked, hbmMask);
588     DeleteObject(hbmUnchecked);
589 
590     hbmChecked = CreateCheckImage(hDC, TRUE, FALSE);
591     ImageList_Add(hImageList, hbmChecked, hbmMask);
592     DeleteObject(hbmChecked);
593 
594     hbmUnchecked = CreateCheckImage(hDC, FALSE, FALSE);
595     ImageList_Add(hImageList, hbmUnchecked, hbmMask);
596     DeleteObject(hbmUnchecked);
597 
598     DeleteObject(hbmMask);
599     hbmMask = CreateRadioMask(hDC);
600 
601     hbmChecked = CreateRadioImage(hDC, TRUE);
602     ImageList_Add(hImageList, hbmChecked, hbmMask);
603     DeleteObject(hbmChecked);
604 
605     hbmUnchecked = CreateRadioImage(hDC, FALSE);
606     ImageList_Add(hImageList, hbmUnchecked, hbmMask);
607     DeleteObject(hbmUnchecked);
608 
609     hbmChecked = CreateRadioImage(hDC, TRUE, FALSE);
610     ImageList_Add(hImageList, hbmChecked, hbmMask);
611     DeleteObject(hbmChecked);
612 
613     hbmUnchecked = CreateRadioImage(hDC, FALSE, FALSE);
614     ImageList_Add(hImageList, hbmUnchecked, hbmMask);
615     DeleteObject(hbmUnchecked);
616 
617     DeleteObject(hbmMask);
618 
619     return hImageList;
620 }
621 
622 static BOOL
623 ViewDlg_OnInitDialog(HWND hwndDlg)
624 {
625     HWND hwndTreeView = GetDlgItem(hwndDlg, IDC_VIEW_TREEVIEW);
626 
627     s_hTreeImageList = ViewDlg_CreateTreeImageList();
628     TreeView_SetImageList(hwndTreeView, s_hTreeImageList, TVSIL_NORMAL);
629 
630     ViewTree_LoadAll();
631     ViewTree_SortAll();
632     ViewTree_InsertAll(hwndTreeView);
633 
634     return TRUE;    // set focus
635 }
636 
637 static BOOL
638 ViewDlg_ToggleCheckItem(HWND hwndDlg, HTREEITEM hItem)
639 {
640     HWND hwndTreeView = GetDlgItem(hwndDlg, IDC_VIEW_TREEVIEW);
641 
642     // get the item
643     TV_ITEM Item;
644     INT i;
645     ZeroMemory(&Item, sizeof(Item));
646     Item.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_PARAM;
647     Item.hItem = hItem;
648     if (!TreeView_GetItem(hwndTreeView, &Item))
649         return FALSE;       // no such item
650 
651     VIEWTREE_ENTRY *pEntry = ViewTree_GetItem(Item.lParam);
652     if (pEntry == NULL)
653         return FALSE;       // no such item
654     if (pEntry->bGrayed)
655         return FALSE;       // disabled
656 
657     // toggle check mark
658     Item.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
659     switch (pEntry->dwType)
660     {
661         case AETYPE_CHECKBOX:
662             pEntry->bChecked = !pEntry->bChecked;
663             break;
664 
665         case AETYPE_RADIO:
666             // reset all the entries of the same parent
667             for (i = 0; i < s_ViewTreeEntryCount; ++i)
668             {
669                 VIEWTREE_ENTRY *pEntry2 = &s_ViewTreeEntries[i];
670                 if (pEntry->dwParentID == pEntry2->dwParentID)
671                 {
672                     pEntry2->bChecked = FALSE;
673 
674                     Item.hItem = pEntry2->hItem;
675                     INT iImage = ViewTree_GetImage(pEntry2);
676                     Item.iImage = Item.iSelectedImage = iImage;
677                     TreeView_SetItem(hwndTreeView, &Item);
678                 }
679             }
680             pEntry->bChecked = TRUE;
681             break;
682 
683         default:
684             return FALSE;   // failure
685     }
686     Item.iImage = Item.iSelectedImage = ViewTree_GetImage(pEntry);
687     Item.hItem = hItem;
688     TreeView_SetItem(hwndTreeView, &Item);
689 
690     // redraw the item
691     RECT rcItem;
692     TreeView_GetItemRect(hwndTreeView, hItem, &rcItem, FALSE);
693     InvalidateRect(hwndTreeView, &rcItem, TRUE);
694     return TRUE;    // success
695 }
696 
697 static VOID
698 ViewDlg_OnTreeViewClick(HWND hwndDlg)
699 {
700     HWND hwndTreeView = GetDlgItem(hwndDlg, IDC_VIEW_TREEVIEW);
701 
702     // do hit test to get the clicked item
703     TV_HITTESTINFO HitTest;
704     ZeroMemory(&HitTest, sizeof(HitTest));
705     DWORD dwPos = GetMessagePos();
706     HitTest.pt.x = LOWORD(dwPos);
707     HitTest.pt.y = HIWORD(dwPos);
708     ScreenToClient(hwndTreeView, &HitTest.pt);
709     HTREEITEM hItem = TreeView_HitTest(hwndTreeView, &HitTest);
710 
711     // toggle the check mark if possible
712     if (ViewDlg_ToggleCheckItem(hwndDlg, hItem))
713     {
714         // property sheet was changed
715         PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
716     }
717 }
718 
719 static void
720 ViewDlg_OnTreeViewKeyDown(HWND hwndDlg, TV_KEYDOWN *KeyDown)
721 {
722     HWND hwndTreeView = GetDlgItem(hwndDlg, IDC_VIEW_TREEVIEW);
723 
724     if (KeyDown->wVKey == VK_SPACE)
725     {
726         // [Space] key was pressed
727         HTREEITEM hItem = TreeView_GetSelection(hwndTreeView);
728         if (ViewDlg_ToggleCheckItem(hwndDlg, hItem))
729         {
730             PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
731         }
732     }
733 }
734 
735 static INT_PTR
736 ViewDlg_OnTreeCustomDraw(HWND hwndDlg, NMTVCUSTOMDRAW *Draw)
737 {
738     NMCUSTOMDRAW& nmcd = Draw->nmcd;
739     switch (nmcd.dwDrawStage)
740     {
741         case CDDS_PREPAINT:
742             return CDRF_NOTIFYITEMDRAW;     // for CDDS_ITEMPREPAINT
743 
744         case CDDS_ITEMPREPAINT:
745             if (!(nmcd.uItemState & CDIS_SELECTED)) // not selected
746             {
747                 LPARAM lParam = nmcd.lItemlParam;
748                 VIEWTREE_ENTRY *pEntry = ViewTree_GetItem(lParam);
749                 if (pEntry && pEntry->bGrayed) // disabled
750                 {
751                     // draw as grayed
752                     Draw->clrText = GetSysColor(COLOR_GRAYTEXT);
753                     Draw->clrTextBk = GetSysColor(COLOR_WINDOW);
754                     return CDRF_NEWFONT;
755                 }
756             }
757             break;
758 
759         default:
760             break;
761     }
762     return CDRF_DODEFAULT;
763 }
764 
765 static VOID
766 ViewDlg_RestoreDefaults(HWND hwndDlg)
767 {
768     HWND hwndTreeView = GetDlgItem(hwndDlg, IDC_VIEW_TREEVIEW);
769 
770     for (INT i = 0; i < s_ViewTreeEntryCount; ++i)
771     {
772         // ignore if the type is group
773         VIEWTREE_ENTRY *pEntry = &s_ViewTreeEntries[i];
774         if (pEntry->dwType == AETYPE_GROUP)
775             continue;
776 
777         // set default value on registry
778         HKEY hKey;
779         if (RegOpenKeyExW(HKEY(pEntry->hkeyRoot), pEntry->szRegPath,
780                           0, KEY_WRITE, &hKey) != ERROR_SUCCESS)
781         {
782             continue;
783         }
784         RegSetValueExW(hKey, pEntry->szValueName, 0, REG_DWORD,
785                        LPBYTE(&pEntry->dwDefaultValue), sizeof(DWORD));
786         RegCloseKey(hKey);
787 
788         // update check status
789         pEntry->bChecked = (pEntry->dwCheckedValue == pEntry->dwDefaultValue);
790 
791         // update the image
792         TV_ITEM Item;
793         ZeroMemory(&Item, sizeof(Item));
794         Item.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
795         Item.hItem = pEntry->hItem;
796         Item.iImage = Item.iSelectedImage = ViewTree_GetImage(pEntry);
797         TreeView_SetItem(hwndTreeView, &Item);
798     }
799 
800     PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
801 }
802 
803 static VOID
804 ScanAdvancedSettings(SHELLSTATE *pSS, DWORD *pdwMask)
805 {
806     for (INT i = 0; i < s_ViewTreeEntryCount; ++i)
807     {
808         const VIEWTREE_ENTRY *pEntry = &s_ViewTreeEntries[i];
809         if (pEntry->dwType == AETYPE_GROUP || pEntry->bGrayed)
810             continue;
811 
812         BOOL bChecked = pEntry->bChecked;
813 
814         // FIXME: Add more items
815         if (lstrcmpiW(pEntry->szKeyName, L"SuperHidden") == 0)
816         {
817             pSS->fShowSuperHidden = !bChecked ? 1 : 0;
818             *pdwMask |= SSF_SHOWSUPERHIDDEN;
819             continue;
820         }
821         if (lstrcmpiW(pEntry->szKeyName, L"DesktopProcess") == 0)
822         {
823             pSS->fSepProcess = bChecked ? 1 : 0;
824             *pdwMask |= SSF_SEPPROCESS;
825             continue;
826         }
827         if (lstrcmpiW(pEntry->szKeyName, L"SHOWALL") == 0)
828         {
829             pSS->fShowAllObjects = !bChecked ? 1 : 0;
830             *pdwMask |= SSF_SHOWALLOBJECTS;
831             continue;
832         }
833         if (lstrcmpiW(pEntry->szKeyName, L"HideFileExt") == 0)
834         {
835             pSS->fShowExtensions = !bChecked ? 1 : 0;
836             *pdwMask |= SSF_SHOWEXTENSIONS;
837             continue;
838         }
839         if (lstrcmpiW(pEntry->szKeyName, L"ShowCompColor") == 0)
840         {
841             pSS->fShowCompColor = bChecked ? 1 : 0;
842             *pdwMask |= SSF_SHOWCOMPCOLOR;
843             continue;
844         }
845         if (lstrcmpiW(pEntry->szKeyName, L"ShowInfoTip") == 0)
846         {
847             pSS->fShowInfoTip = bChecked ? 1 : 0;
848             *pdwMask |= SSF_SHOWINFOTIP;
849             continue;
850         }
851     }
852 }
853 
854 static BOOL CALLBACK
855 RefreshBrowsersCallback(HWND hWnd, LPARAM msg)
856 {
857     WCHAR ClassName[100];
858     if (GetClassNameW(hWnd, ClassName, _countof(ClassName)))
859     {
860         if (!wcscmp(ClassName, L"Progman") ||
861             !wcscmp(ClassName, L"CabinetWClass") ||
862             !wcscmp(ClassName, L"ExploreWClass"))
863         {
864             PostMessage(hWnd, WM_COMMAND, FCIDM_DESKBROWSER_REFRESH, 0);
865         }
866     }
867     return TRUE;
868 }
869 
870 static VOID
871 ViewDlg_Apply(HWND hwndDlg)
872 {
873     for (INT i = 0; i < s_ViewTreeEntryCount; ++i)
874     {
875         // ignore the entry if the type is group or the entry is grayed
876         VIEWTREE_ENTRY *pEntry = &s_ViewTreeEntries[i];
877         if (pEntry->dwType == AETYPE_GROUP || pEntry->bGrayed)
878             continue;
879 
880         // open the registry key
881         HKEY hkeyTarget;
882         if (RegOpenKeyExW(HKEY(pEntry->hkeyRoot), pEntry->szRegPath, 0,
883                           KEY_WRITE, &hkeyTarget) != ERROR_SUCCESS)
884         {
885             continue;
886         }
887 
888         // checked or unchecked?
889         DWORD dwValue, dwSize;
890         if (pEntry->bChecked)
891         {
892             dwValue = pEntry->dwCheckedValue;
893         }
894         else
895         {
896             if (pEntry->bHasUncheckedValue)
897             {
898                 dwValue = pEntry->dwUncheckedValue;
899             }
900             else
901             {
902                 // there is no unchecked value
903                 RegCloseKey(hkeyTarget);
904                 continue;   // ignore
905             }
906         }
907 
908         // set the value
909         dwSize = sizeof(dwValue);
910         RegSetValueExW(hkeyTarget, pEntry->szValueName, 0, REG_DWORD,
911                        LPBYTE(&dwValue), dwSize);
912 
913         // close now
914         RegCloseKey(hkeyTarget);
915     }
916 
917     // scan advanced settings for user's settings
918     DWORD dwMask = 0;
919     SHELLSTATE ShellState;
920     ZeroMemory(&ShellState, sizeof(ShellState));
921     ScanAdvancedSettings(&ShellState, &dwMask);
922 
923     // update user's settings
924     SHGetSetSettings(&ShellState, dwMask, TRUE);
925 
926     // notify all
927     SendMessage(HWND_BROADCAST, WM_WININICHANGE, 0, 0);
928 
929     EnumWindows(RefreshBrowsersCallback, NULL);
930 }
931 
932 // IDD_FOLDER_OPTIONS_VIEW
933 INT_PTR CALLBACK
934 FolderOptionsViewDlg(
935     HWND    hwndDlg,
936     UINT    uMsg,
937     WPARAM  wParam,
938     LPARAM  lParam)
939 {
940     INT_PTR Result;
941     NMTVCUSTOMDRAW *Draw;
942 
943     switch (uMsg)
944     {
945         case WM_INITDIALOG:
946             return ViewDlg_OnInitDialog(hwndDlg);
947 
948         case WM_COMMAND:
949             switch (LOWORD(wParam))
950             {
951                 case IDC_VIEW_RESTORE_DEFAULTS: // Restore Defaults
952                     ViewDlg_RestoreDefaults(hwndDlg);
953                     break;
954             }
955             break;
956 
957         case WM_NOTIFY:
958             switch (LPNMHDR(lParam)->code)
959             {
960                 case NM_CLICK:  // clicked on treeview
961                     ViewDlg_OnTreeViewClick(hwndDlg);
962                     break;
963 
964                 case NM_CUSTOMDRAW:     // custom draw (for graying)
965                     Draw = (NMTVCUSTOMDRAW *)lParam;
966                     Result = ViewDlg_OnTreeCustomDraw(hwndDlg, Draw);
967                     SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, Result);
968                     return Result;
969 
970                 case TVN_KEYDOWN:       // key is down
971                     ViewDlg_OnTreeViewKeyDown(hwndDlg, (TV_KEYDOWN *)lParam);
972                     break;
973 
974                 case PSN_APPLY:         // [Apply] is clicked
975                     ViewDlg_Apply(hwndDlg);
976                     break;
977 
978                 default:
979                     break;
980             }
981             break;
982     }
983 
984     return FALSE;
985 }
986