xref: /reactos/dll/win32/shell32/dialogs/view.cpp (revision 5b78381a)
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
ViewTree_FindIcon(LPCWSTR pszPath,UINT nIconIndex)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
ViewTree_AddIcon(LPCWSTR pszPath,UINT nIconIndex)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
ViewTree_GetItem(DWORD dwID)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
ViewTree_GetImage(PVIEWTREE_ENTRY pEntry)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
ViewTree_InsertEntry(HWND hwndTreeView,PVIEWTREE_ENTRY pEntry)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
ViewTree_InsertAll(HWND hwndTreeView)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
ViewTree_LoadTree(HKEY hKey,LPCWSTR pszKeyName,DWORD dwParentID)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 
ViewTree_LoadAll(VOID)442 static BOOL ViewTree_LoadAll(VOID)
443 {
444     // free if already existed
445     if (s_ViewTreeEntries)
446     {
447         free(s_ViewTreeEntries);
448         s_ViewTreeEntries = NULL;
449     }
450     s_ViewTreeEntryCount = 0;
451 
452     HKEY hKey;
453     if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,
454                       L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced",
455                       0, KEY_READ, &hKey) != ERROR_SUCCESS)
456     {
457         return FALSE;   // failure
458     }
459 
460     // load the children
461     WCHAR szKeyName[64];
462     DWORD dwIndex = 0;
463     while (RegEnumKeyW(hKey, dwIndex, szKeyName,
464                        _countof(szKeyName)) == ERROR_SUCCESS)
465     {
466         HKEY hkeyChild;
467         if (RegOpenKeyExW(hKey, szKeyName, 0, KEY_READ,
468                           &hkeyChild) != ERROR_SUCCESS)
469         {
470             ++dwIndex;
471             continue;   // failure
472         }
473 
474         ViewTree_LoadTree(hkeyChild, szKeyName, DWORD(-1));
475         RegCloseKey(hkeyChild);
476 
477         ++dwIndex;
478     }
479 
480     RegCloseKey(hKey);
481 
482     return TRUE;    // success
483 }
484 
ViewTree_Compare(const void * x,const void * y)485 static int ViewTree_Compare(const void *x, const void *y)
486 {
487     PVIEWTREE_ENTRY pEntry1 = (PVIEWTREE_ENTRY)x;
488     PVIEWTREE_ENTRY pEntry2 = (PVIEWTREE_ENTRY)y;
489 
490     DWORD dwParentID1 = pEntry1->dwParentID;
491     DWORD dwParentID2 = pEntry2->dwParentID;
492 
493     if (dwParentID1 == dwParentID2)
494         return lstrcmpi(pEntry1->szText, pEntry2->szText);
495 
496     DWORD i, m, n;
497     const UINT MAX_DEPTH = 32;
498     PVIEWTREE_ENTRY pArray1[MAX_DEPTH];
499     PVIEWTREE_ENTRY pArray2[MAX_DEPTH];
500 
501     // Make ancestor lists
502     for (i = m = n = 0; i < MAX_DEPTH; ++i)
503     {
504         PVIEWTREE_ENTRY pParent1 = ViewTree_GetItem(dwParentID1);
505         PVIEWTREE_ENTRY pParent2 = ViewTree_GetItem(dwParentID2);
506         if (!pParent1 && !pParent2)
507             break;
508 
509         if (pParent1)
510         {
511             pArray1[m++] = pParent1;
512             dwParentID1 = pParent1->dwParentID;
513         }
514         if (pParent2)
515         {
516             pArray2[n++] = pParent2;
517             dwParentID2 = pParent2->dwParentID;
518         }
519     }
520 
521     UINT k = min(m, n);
522     for (i = 0; i < k; ++i)
523     {
524         INT nCompare = lstrcmpi(pArray1[m - i - 1]->szText, pArray2[n - i - 1]->szText);
525         if (nCompare < 0)
526             return -1;
527         if (nCompare > 0)
528             return 1;
529     }
530 
531     if (m < n)
532         return -1;
533     if (m > n)
534         return 1;
535     return lstrcmpi(pEntry1->szText, pEntry2->szText);
536 }
537 
538 static VOID
ViewTree_SortAll(VOID)539 ViewTree_SortAll(VOID)
540 {
541     qsort(s_ViewTreeEntries, s_ViewTreeEntryCount, sizeof(VIEWTREE_ENTRY), ViewTree_Compare);
542 }
543 
544 /////////////////////////////////////////////////////////////////////////////
545 // ViewDlg
546 
547 static HIMAGELIST
ViewDlg_CreateTreeImageList(VOID)548 ViewDlg_CreateTreeImageList(VOID)
549 {
550     HIMAGELIST hImageList;
551     hImageList = ImageList_Create(16, 16, ILC_COLOR24 | ILC_MASK, 9, 1);
552     if (hImageList == NULL)
553         return NULL;    // failure
554 
555     // free if existed
556     if (s_ViewTreeIcons)
557     {
558         free(s_ViewTreeIcons);
559         s_ViewTreeIcons = NULL;
560     }
561     s_ViewTreeIconCount = 0;
562 
563     // allocate now
564     PVIEWTREE_ICON pAllocated;
565     size_t Size = PREDEFINED_ICON_COUNT * sizeof(VIEWTREE_ICON);
566     pAllocated = (PVIEWTREE_ICON)calloc(1, Size);
567     if (pAllocated == NULL)
568         return NULL;    // failure
569 
570     s_ViewTreeIconCount = PREDEFINED_ICON_COUNT;
571     s_ViewTreeIcons = pAllocated;
572 
573     // add the predefined icons
574 
575     HDC hDC = CreateCompatibleDC(NULL);
576     HBITMAP hbmMask = CreateCheckMask(hDC);
577 
578     HBITMAP hbmChecked, hbmUnchecked;
579 
580     hbmChecked = CreateCheckImage(hDC, TRUE);
581     ImageList_Add(hImageList, hbmChecked, hbmMask);
582     DeleteObject(hbmChecked);
583 
584     hbmUnchecked = CreateCheckImage(hDC, FALSE);
585     ImageList_Add(hImageList, hbmUnchecked, hbmMask);
586     DeleteObject(hbmUnchecked);
587 
588     hbmChecked = CreateCheckImage(hDC, TRUE, FALSE);
589     ImageList_Add(hImageList, hbmChecked, hbmMask);
590     DeleteObject(hbmChecked);
591 
592     hbmUnchecked = CreateCheckImage(hDC, FALSE, FALSE);
593     ImageList_Add(hImageList, hbmUnchecked, hbmMask);
594     DeleteObject(hbmUnchecked);
595 
596     DeleteObject(hbmMask);
597     hbmMask = CreateRadioMask(hDC);
598 
599     hbmChecked = CreateRadioImage(hDC, TRUE);
600     ImageList_Add(hImageList, hbmChecked, hbmMask);
601     DeleteObject(hbmChecked);
602 
603     hbmUnchecked = CreateRadioImage(hDC, FALSE);
604     ImageList_Add(hImageList, hbmUnchecked, hbmMask);
605     DeleteObject(hbmUnchecked);
606 
607     hbmChecked = CreateRadioImage(hDC, TRUE, FALSE);
608     ImageList_Add(hImageList, hbmChecked, hbmMask);
609     DeleteObject(hbmChecked);
610 
611     hbmUnchecked = CreateRadioImage(hDC, FALSE, FALSE);
612     ImageList_Add(hImageList, hbmUnchecked, hbmMask);
613     DeleteObject(hbmUnchecked);
614 
615     DeleteObject(hbmMask);
616 
617     return hImageList;
618 }
619 
620 static BOOL
ViewDlg_OnInitDialog(HWND hwndDlg,LPPROPSHEETPAGE psp)621 ViewDlg_OnInitDialog(HWND hwndDlg, LPPROPSHEETPAGE psp)
622 {
623     SetWindowLongPtr(hwndDlg, GWL_USERDATA, psp->lParam);
624     CFolderOptions *pFO = (CFolderOptions*)psp->lParam;
625 
626     if (!pFO || !pFO->CanSetDefFolderSettings())
627     {
628         // The global options (started from rundll32 or control panel)
629         // has no browser to copy the current settings from.
630         EnableWindow(GetDlgItem(hwndDlg, IDC_VIEW_APPLY_TO_ALL), FALSE);
631     }
632 
633     HWND hwndTreeView = GetDlgItem(hwndDlg, IDC_VIEW_TREEVIEW);
634 
635     s_hTreeImageList = ViewDlg_CreateTreeImageList();
636     TreeView_SetImageList(hwndTreeView, s_hTreeImageList, TVSIL_NORMAL);
637 
638     ViewTree_LoadAll();
639     ViewTree_SortAll();
640     ViewTree_InsertAll(hwndTreeView);
641 
642     return TRUE;    // set focus
643 }
644 
645 static BOOL
ViewDlg_ToggleCheckItem(HWND hwndDlg,HTREEITEM hItem)646 ViewDlg_ToggleCheckItem(HWND hwndDlg, HTREEITEM hItem)
647 {
648     HWND hwndTreeView = GetDlgItem(hwndDlg, IDC_VIEW_TREEVIEW);
649 
650     // get the item
651     TV_ITEM Item;
652     INT i;
653     ZeroMemory(&Item, sizeof(Item));
654     Item.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_PARAM;
655     Item.hItem = hItem;
656     if (!TreeView_GetItem(hwndTreeView, &Item))
657         return FALSE;       // no such item
658 
659     VIEWTREE_ENTRY *pEntry = ViewTree_GetItem(Item.lParam);
660     if (pEntry == NULL)
661         return FALSE;       // no such item
662     if (pEntry->bGrayed)
663         return FALSE;       // disabled
664 
665     // toggle check mark
666     Item.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
667     switch (pEntry->dwType)
668     {
669         case AETYPE_CHECKBOX:
670             pEntry->bChecked = !pEntry->bChecked;
671             break;
672 
673         case AETYPE_RADIO:
674             // reset all the entries of the same parent
675             for (i = 0; i < s_ViewTreeEntryCount; ++i)
676             {
677                 VIEWTREE_ENTRY *pEntry2 = &s_ViewTreeEntries[i];
678                 if (pEntry->dwParentID == pEntry2->dwParentID)
679                 {
680                     pEntry2->bChecked = FALSE;
681 
682                     Item.hItem = pEntry2->hItem;
683                     INT iImage = ViewTree_GetImage(pEntry2);
684                     Item.iImage = Item.iSelectedImage = iImage;
685                     TreeView_SetItem(hwndTreeView, &Item);
686                 }
687             }
688             pEntry->bChecked = TRUE;
689             break;
690 
691         default:
692             return FALSE;   // failure
693     }
694     Item.iImage = Item.iSelectedImage = ViewTree_GetImage(pEntry);
695     Item.hItem = hItem;
696     TreeView_SetItem(hwndTreeView, &Item);
697 
698     // redraw the item
699     RECT rcItem;
700     TreeView_GetItemRect(hwndTreeView, hItem, &rcItem, FALSE);
701     InvalidateRect(hwndTreeView, &rcItem, TRUE);
702     return TRUE;    // success
703 }
704 
705 static VOID
ViewDlg_OnTreeViewClick(HWND hwndDlg)706 ViewDlg_OnTreeViewClick(HWND hwndDlg)
707 {
708     HWND hwndTreeView = GetDlgItem(hwndDlg, IDC_VIEW_TREEVIEW);
709 
710     // do hit test to get the clicked item
711     TV_HITTESTINFO HitTest;
712     ZeroMemory(&HitTest, sizeof(HitTest));
713     DWORD dwPos = GetMessagePos();
714     HitTest.pt.x = LOWORD(dwPos);
715     HitTest.pt.y = HIWORD(dwPos);
716     ScreenToClient(hwndTreeView, &HitTest.pt);
717     HTREEITEM hItem = TreeView_HitTest(hwndTreeView, &HitTest);
718 
719     // toggle the check mark if possible
720     if (ViewDlg_ToggleCheckItem(hwndDlg, hItem))
721     {
722         // property sheet was changed
723         PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
724     }
725 }
726 
727 static void
ViewDlg_OnTreeViewKeyDown(HWND hwndDlg,TV_KEYDOWN * KeyDown)728 ViewDlg_OnTreeViewKeyDown(HWND hwndDlg, TV_KEYDOWN *KeyDown)
729 {
730     HWND hwndTreeView = GetDlgItem(hwndDlg, IDC_VIEW_TREEVIEW);
731 
732     if (KeyDown->wVKey == VK_SPACE)
733     {
734         // [Space] key was pressed
735         HTREEITEM hItem = TreeView_GetSelection(hwndTreeView);
736         if (ViewDlg_ToggleCheckItem(hwndDlg, hItem))
737         {
738             PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
739         }
740     }
741 }
742 
743 static INT_PTR
ViewDlg_OnTreeCustomDraw(HWND hwndDlg,NMTVCUSTOMDRAW * Draw)744 ViewDlg_OnTreeCustomDraw(HWND hwndDlg, NMTVCUSTOMDRAW *Draw)
745 {
746     NMCUSTOMDRAW& nmcd = Draw->nmcd;
747     switch (nmcd.dwDrawStage)
748     {
749         case CDDS_PREPAINT:
750             return CDRF_NOTIFYITEMDRAW;     // for CDDS_ITEMPREPAINT
751 
752         case CDDS_ITEMPREPAINT:
753             if (!(nmcd.uItemState & CDIS_SELECTED)) // not selected
754             {
755                 LPARAM lParam = nmcd.lItemlParam;
756                 VIEWTREE_ENTRY *pEntry = ViewTree_GetItem(lParam);
757                 if (pEntry && pEntry->bGrayed) // disabled
758                 {
759                     // draw as grayed
760                     Draw->clrText = GetSysColor(COLOR_GRAYTEXT);
761                     Draw->clrTextBk = GetSysColor(COLOR_WINDOW);
762                     return CDRF_NEWFONT;
763                 }
764             }
765             break;
766 
767         default:
768             break;
769     }
770     return CDRF_DODEFAULT;
771 }
772 
773 static VOID
ViewDlg_RestoreDefaults(HWND hwndDlg)774 ViewDlg_RestoreDefaults(HWND hwndDlg)
775 {
776     HWND hwndTreeView = GetDlgItem(hwndDlg, IDC_VIEW_TREEVIEW);
777 
778     for (INT i = 0; i < s_ViewTreeEntryCount; ++i)
779     {
780         // ignore if the type is group
781         VIEWTREE_ENTRY *pEntry = &s_ViewTreeEntries[i];
782         if (pEntry->dwType == AETYPE_GROUP)
783             continue;
784 
785         // set default value on registry
786         HKEY hKey;
787         if (RegOpenKeyExW(HKEY(pEntry->hkeyRoot), pEntry->szRegPath,
788                           0, KEY_WRITE, &hKey) != ERROR_SUCCESS)
789         {
790             continue;
791         }
792         RegSetValueExW(hKey, pEntry->szValueName, 0, REG_DWORD,
793                        LPBYTE(&pEntry->dwDefaultValue), sizeof(DWORD));
794         RegCloseKey(hKey);
795 
796         // update check status
797         pEntry->bChecked = (pEntry->dwCheckedValue == pEntry->dwDefaultValue);
798 
799         // update the image
800         TV_ITEM Item;
801         ZeroMemory(&Item, sizeof(Item));
802         Item.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
803         Item.hItem = pEntry->hItem;
804         Item.iImage = Item.iSelectedImage = ViewTree_GetImage(pEntry);
805         TreeView_SetItem(hwndTreeView, &Item);
806     }
807 
808     PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
809 }
810 
811 static VOID
ScanAdvancedSettings(SHELLSTATE * pSS,DWORD * pdwMask)812 ScanAdvancedSettings(SHELLSTATE *pSS, DWORD *pdwMask)
813 {
814     for (INT i = 0; i < s_ViewTreeEntryCount; ++i)
815     {
816         const VIEWTREE_ENTRY *pEntry = &s_ViewTreeEntries[i];
817         if (pEntry->dwType == AETYPE_GROUP || pEntry->bGrayed)
818             continue;
819 
820         BOOL bChecked = pEntry->bChecked;
821 
822         // FIXME: Add more items
823         if (lstrcmpiW(pEntry->szKeyName, L"SuperHidden") == 0)
824         {
825             pSS->fShowSuperHidden = !bChecked ? 1 : 0;
826             *pdwMask |= SSF_SHOWSUPERHIDDEN;
827             continue;
828         }
829         if (lstrcmpiW(pEntry->szKeyName, L"DesktopProcess") == 0)
830         {
831             pSS->fSepProcess = bChecked ? 1 : 0;
832             *pdwMask |= SSF_SEPPROCESS;
833             continue;
834         }
835         if (lstrcmpiW(pEntry->szKeyName, L"SHOWALL") == 0)
836         {
837             pSS->fShowAllObjects = bChecked ? 1 : 0;
838             *pdwMask |= SSF_SHOWALLOBJECTS;
839             continue;
840         }
841         if (lstrcmpiW(pEntry->szKeyName, L"HideFileExt") == 0)
842         {
843             pSS->fShowExtensions = !bChecked ? 1 : 0;
844             *pdwMask |= SSF_SHOWEXTENSIONS;
845             continue;
846         }
847         if (lstrcmpiW(pEntry->szKeyName, L"ShowCompColor") == 0)
848         {
849             pSS->fShowCompColor = bChecked ? 1 : 0;
850             *pdwMask |= SSF_SHOWCOMPCOLOR;
851             continue;
852         }
853         if (lstrcmpiW(pEntry->szKeyName, L"ShowInfoTip") == 0)
854         {
855             pSS->fShowInfoTip = bChecked ? 1 : 0;
856             *pdwMask |= SSF_SHOWINFOTIP;
857             continue;
858         }
859     }
860 }
861 
862 static BOOL CALLBACK
PostCabinetMessageCallback(HWND hWnd,LPARAM param)863 PostCabinetMessageCallback(HWND hWnd, LPARAM param)
864 {
865     MSG &data = *(MSG*)param;
866     WCHAR ClassName[100];
867     if (GetClassNameW(hWnd, ClassName, _countof(ClassName)))
868     {
869         if (!wcscmp(ClassName, L"Progman") ||
870             !wcscmp(ClassName, L"CabinetWClass") ||
871             !wcscmp(ClassName, L"ExploreWClass"))
872         {
873             PostMessage(hWnd, data.message, data.wParam, data.lParam);
874         }
875     }
876     return TRUE;
877 }
878 
879 void
PostCabinetMessage(UINT Msg,WPARAM wParam,LPARAM lParam)880 PostCabinetMessage(UINT Msg, WPARAM wParam, LPARAM lParam)
881 {
882     MSG data;
883     data.message = Msg;
884     data.wParam = wParam;
885     data.lParam = lParam;
886     EnumWindows(PostCabinetMessageCallback, (LPARAM)&data);
887 }
888 
889 static VOID
ViewDlg_Apply(HWND hwndDlg)890 ViewDlg_Apply(HWND hwndDlg)
891 {
892     for (INT i = 0; i < s_ViewTreeEntryCount; ++i)
893     {
894         // ignore the entry if the type is group or the entry is grayed
895         VIEWTREE_ENTRY *pEntry = &s_ViewTreeEntries[i];
896         if (pEntry->dwType == AETYPE_GROUP || pEntry->bGrayed)
897             continue;
898 
899         // open the registry key
900         HKEY hkeyTarget;
901         if (RegOpenKeyExW(HKEY(pEntry->hkeyRoot), pEntry->szRegPath, 0,
902                           KEY_WRITE, &hkeyTarget) != ERROR_SUCCESS)
903         {
904             continue;
905         }
906 
907         // checked or unchecked?
908         DWORD dwValue, dwSize;
909         if (pEntry->bChecked)
910         {
911             dwValue = pEntry->dwCheckedValue;
912         }
913         else
914         {
915             if (pEntry->bHasUncheckedValue)
916             {
917                 dwValue = pEntry->dwUncheckedValue;
918             }
919             else
920             {
921                 // there is no unchecked value
922                 RegCloseKey(hkeyTarget);
923                 continue;   // ignore
924             }
925         }
926 
927         // set the value
928         dwSize = sizeof(dwValue);
929         RegSetValueExW(hkeyTarget, pEntry->szValueName, 0, REG_DWORD,
930                        LPBYTE(&dwValue), dwSize);
931 
932         // close now
933         RegCloseKey(hkeyTarget);
934     }
935 
936     // scan advanced settings for user's settings
937     DWORD dwMask = 0;
938     SHELLSTATE ShellState;
939     ZeroMemory(&ShellState, sizeof(ShellState));
940     ScanAdvancedSettings(&ShellState, &dwMask);
941 
942     // update user's settings
943     SHGetSetSettings(&ShellState, dwMask, TRUE);
944 
945     // notify all
946     SendMessage(HWND_BROADCAST, WM_WININICHANGE, 0, 0);
947 
948     PostCabinetMessage(WM_COMMAND, FCIDM_DESKBROWSER_REFRESH, 0);
949 }
950 
951 // IDD_FOLDER_OPTIONS_VIEW
952 INT_PTR CALLBACK
FolderOptionsViewDlg(HWND hwndDlg,UINT uMsg,WPARAM wParam,LPARAM lParam)953 FolderOptionsViewDlg(
954     HWND    hwndDlg,
955     UINT    uMsg,
956     WPARAM  wParam,
957     LPARAM  lParam)
958 {
959     INT_PTR Result;
960     NMTVCUSTOMDRAW *Draw;
961 
962     switch (uMsg)
963     {
964         case WM_INITDIALOG:
965             return ViewDlg_OnInitDialog(hwndDlg, (LPPROPSHEETPAGE)lParam);
966 
967         case WM_COMMAND:
968             switch (LOWORD(wParam))
969             {
970                 case IDC_VIEW_RESTORE_DEFAULTS: // Restore Defaults
971                     ViewDlg_RestoreDefaults(hwndDlg);
972                     break;
973 
974                 case IDC_VIEW_APPLY_TO_ALL:
975                 case IDC_VIEW_RESET_ALL:
976                 {
977                     HRESULT hr = HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
978                     CFolderOptions *pFO = (CFolderOptions*)GetWindowLongPtr(hwndDlg, GWL_USERDATA);
979                     if (pFO)
980                         hr = pFO->ApplyDefFolderSettings(LOWORD(wParam) == IDC_VIEW_RESET_ALL);
981                     if (FAILED(hr))
982                         SHELL_ErrorBox(hwndDlg, hr);
983                     break;
984                 }
985             }
986             break;
987 
988         case WM_NOTIFY:
989             switch (LPNMHDR(lParam)->code)
990             {
991                 case NM_CLICK:  // clicked on treeview
992                     ViewDlg_OnTreeViewClick(hwndDlg);
993                     break;
994 
995                 case NM_CUSTOMDRAW:     // custom draw (for graying)
996                     Draw = (NMTVCUSTOMDRAW *)lParam;
997                     Result = ViewDlg_OnTreeCustomDraw(hwndDlg, Draw);
998                     SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, Result);
999                     return Result;
1000 
1001                 case TVN_KEYDOWN:       // key is down
1002                     ViewDlg_OnTreeViewKeyDown(hwndDlg, (TV_KEYDOWN *)lParam);
1003                     break;
1004 
1005                 case PSN_APPLY:         // [Apply] is clicked
1006                     ViewDlg_Apply(hwndDlg);
1007                     break;
1008 
1009                 default:
1010                     break;
1011             }
1012             break;
1013     }
1014 
1015     return FALSE;
1016 }
1017