1 /*
2  *    Open With  Context Menu extension
3  *
4  * Copyright 2007 Johannes Anderwald <johannes.anderwald@reactos.org>
5  * Copyright 2016-2017 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 } FOLDER_FILE_TYPE_ENTRY, *PFOLDER_FILE_TYPE_ENTRY;
41 
42 // uniquely-defined icon entry for Advanced Settings
43 typedef struct ADVANCED_ICON
44 {
45     WCHAR   szPath[MAX_PATH];
46     UINT    nIconIndex;
47 } ADVANCED_ICON;
48 
49 // predefined icon IDs (See CreateTreeImageList function below)
50 #define I_CHECKED                   0
51 #define I_UNCHECKED                 1
52 #define I_CHECKED_DISABLED          2
53 #define I_UNCHECKED_DISABLED        3
54 #define I_RADIO_CHECKED             4
55 #define I_RADIO_UNCHECKED           5
56 #define I_RADIO_CHECKED_DISABLED    6
57 #define I_RADIO_UNCHECKED_DISABLED  7
58 
59 #define PREDEFINED_ICON_COUNT       8
60 
61 // definition of icon stock
62 static ADVANCED_ICON *  s_AdvancedIcons         = NULL;
63 static INT              s_AdvancedIconCount     = 0;
64 static HIMAGELIST       s_hImageList            = NULL;
65 
66 static INT
67 Advanced_FindIcon(LPCWSTR pszPath, UINT nIconIndex)
68 {
69     for (INT i = PREDEFINED_ICON_COUNT; i < s_AdvancedIconCount; ++i)
70     {
71         ADVANCED_ICON *pIcon = &s_AdvancedIcons[i];
72         if (pIcon->nIconIndex == nIconIndex &&
73             lstrcmpiW(pIcon->szPath, pszPath) == 0)
74         {
75             return i;   // icon ID
76         }
77     }
78     return -1;  // not found
79 }
80 
81 static INT
82 Advanced_AddIcon(LPCWSTR pszPath, UINT nIconIndex)
83 {
84     ADVANCED_ICON *pAllocated;
85 
86     // return the ID if already existed
87     INT nIconID = Advanced_FindIcon(pszPath, nIconIndex);
88     if (nIconID != -1)
89         return nIconID;     // already exists
90 
91     // extract a small icon
92     HICON hIconSmall = NULL;
93     ExtractIconExW(pszPath, nIconIndex, NULL, &hIconSmall, 1);
94     if (hIconSmall == NULL)
95         return -1;      // failure
96 
97     // resize s_AdvancedIcons
98     size_t Size = (s_AdvancedIconCount + 1) * sizeof(ADVANCED_ICON);
99     pAllocated = (ADVANCED_ICON *)realloc(s_AdvancedIcons, Size);
100     if (pAllocated == NULL)
101         return -1;      // failure
102     else
103         s_AdvancedIcons = pAllocated;
104 
105     // save icon information
106     ADVANCED_ICON *pIcon = &s_AdvancedIcons[s_AdvancedIconCount];
107     lstrcpynW(pIcon->szPath, pszPath, _countof(pIcon->szPath));
108     pIcon->nIconIndex = nIconIndex;
109 
110     // add the icon to the image list
111     ImageList_AddIcon(s_hImageList, hIconSmall);
112 
113     // increment the counter
114     nIconID = s_AdvancedIconCount;
115     ++s_AdvancedIconCount;
116 
117     DestroyIcon(hIconSmall);
118 
119     return nIconID;     // newly-added icon ID
120 }
121 
122 // types of Advanced Setting entry
123 typedef enum ADVANCED_ENTRY_TYPE
124 {
125     AETYPE_GROUP,
126     AETYPE_CHECKBOX,
127     AETYPE_RADIO,
128 } ADVANCED_ENTRY_TYPE;
129 
130 // an entry info of Advanced Settings
131 typedef struct ADVANCED_ENTRY
132 {
133     DWORD   dwID;                   // entry ID
134     DWORD   dwParentID;             // parent entry ID
135     DWORD   dwResourceID;           // resource ID
136     WCHAR   szKeyName[64];          // entry key name
137     DWORD   dwType;                 // ADVANCED_ENTRY_TYPE
138     WCHAR   szText[MAX_PATH];       // text
139     INT     nIconID;                // icon ID (See ADVANCED_ICON)
140 
141     HKEY    hkeyRoot;               // registry root key
142     WCHAR   szRegPath[MAX_PATH];    // registry path
143     WCHAR   szValueName[64];        // registry value name
144 
145     DWORD   dwCheckedValue;         // checked value
146     DWORD   dwUncheckedValue;       // unchecked value
147     DWORD   dwDefaultValue;         // defalut value
148     BOOL    bHasUncheckedValue;     // If FALSE, UncheckedValue is invalid
149 
150     HTREEITEM   hItem;              // for TreeView
151     BOOL        bGrayed;            // disabled?
152     BOOL        bChecked;           // checked?
153 } ADVANCED_ENTRY, *PADVANCED_ENTRY;
154 
155 // definition of advanced entries
156 static ADVANCED_ENTRY *     s_Advanced = NULL;
157 static INT                  s_AdvancedCount = 0;
158 
159 static HBITMAP
160 Create24BppBitmap(HDC hDC, INT cx, INT cy)
161 {
162     BITMAPINFO bi;
163     LPVOID pvBits;
164 
165     ZeroMemory(&bi, sizeof(bi));
166     bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
167     bi.bmiHeader.biWidth = cx;
168     bi.bmiHeader.biHeight = cy;
169     bi.bmiHeader.biPlanes = 1;
170     bi.bmiHeader.biBitCount = 24;
171     bi.bmiHeader.biCompression = BI_RGB;
172 
173     HBITMAP hbm = CreateDIBSection(hDC, &bi, DIB_RGB_COLORS, &pvBits, NULL, 0);
174     return hbm;
175 }
176 
177 static HBITMAP
178 CreateCheckImage(HDC hDC, BOOL bCheck, BOOL bEnabled = TRUE)
179 {
180     INT cxSmallIcon = GetSystemMetrics(SM_CXSMICON);
181     INT cySmallIcon = GetSystemMetrics(SM_CYSMICON);
182 
183     HBITMAP hbm = Create24BppBitmap(hDC, cxSmallIcon, cySmallIcon);
184     if (hbm == NULL)
185         return NULL;    // failure
186 
187     RECT Rect, BoxRect;
188     SetRect(&Rect, 0, 0, cxSmallIcon, cySmallIcon);
189     BoxRect = Rect;
190     InflateRect(&BoxRect, -1, -1);
191 
192     HGDIOBJ hbmOld = SelectObject(hDC, hbm);
193     {
194         UINT uState = DFCS_BUTTONCHECK | DFCS_FLAT | DFCS_MONO;
195         if (bCheck)
196             uState |= DFCS_CHECKED;
197         if (!bEnabled)
198             uState |= DFCS_INACTIVE;
199         DrawFrameControl(hDC, &BoxRect, DFC_BUTTON, uState);
200     }
201     SelectObject(hDC, hbmOld);
202 
203     return hbm;     // success
204 }
205 
206 static HBITMAP
207 CreateCheckMask(HDC hDC)
208 {
209     INT cxSmallIcon = GetSystemMetrics(SM_CXSMICON);
210     INT cySmallIcon = GetSystemMetrics(SM_CYSMICON);
211 
212     HBITMAP hbm = CreateBitmap(cxSmallIcon, cySmallIcon, 1, 1, NULL);
213     if (hbm == NULL)
214         return NULL;    // failure
215 
216     RECT Rect, BoxRect;
217     SetRect(&Rect, 0, 0, cxSmallIcon, cySmallIcon);
218     BoxRect = Rect;
219     InflateRect(&BoxRect, -1, -1);
220 
221     HGDIOBJ hbmOld = SelectObject(hDC, hbm);
222     {
223         FillRect(hDC, &Rect, HBRUSH(GetStockObject(WHITE_BRUSH)));
224         FillRect(hDC, &BoxRect, HBRUSH(GetStockObject(BLACK_BRUSH)));
225     }
226     SelectObject(hDC, hbmOld);
227 
228     return hbm;     // success
229 }
230 
231 static HBITMAP
232 CreateRadioImage(HDC hDC, BOOL bCheck, BOOL bEnabled = TRUE)
233 {
234     INT cxSmallIcon = GetSystemMetrics(SM_CXSMICON);
235     INT cySmallIcon = GetSystemMetrics(SM_CYSMICON);
236 
237     HBITMAP hbm = Create24BppBitmap(hDC, cxSmallIcon, cySmallIcon);
238     if (hbm == NULL)
239         return NULL;    // failure
240 
241     RECT Rect, BoxRect;
242     SetRect(&Rect, 0, 0, cxSmallIcon, cySmallIcon);
243     BoxRect = Rect;
244     InflateRect(&BoxRect, -1, -1);
245 
246     HGDIOBJ hbmOld = SelectObject(hDC, hbm);
247     {
248         UINT uState = DFCS_BUTTONRADIOIMAGE | DFCS_FLAT | DFCS_MONO;
249         if (bCheck)
250             uState |= DFCS_CHECKED;
251         if (!bEnabled)
252             uState |= DFCS_INACTIVE;
253         DrawFrameControl(hDC, &BoxRect, DFC_BUTTON, uState);
254     }
255     SelectObject(hDC, hbmOld);
256 
257     return hbm;     // success
258 }
259 
260 static HBITMAP
261 CreateRadioMask(HDC hDC)
262 {
263     INT cxSmallIcon = GetSystemMetrics(SM_CXSMICON);
264     INT cySmallIcon = GetSystemMetrics(SM_CYSMICON);
265 
266     HBITMAP hbm = CreateBitmap(cxSmallIcon, cySmallIcon, 1, 1, NULL);
267     if (hbm == NULL)
268         return NULL;    // failure
269 
270     RECT Rect, BoxRect;
271     SetRect(&Rect, 0, 0, cxSmallIcon, cySmallIcon);
272     BoxRect = Rect;
273     InflateRect(&BoxRect, -1, -1);
274 
275     HGDIOBJ hbmOld = SelectObject(hDC, hbm);
276     {
277         FillRect(hDC, &Rect, HBRUSH(GetStockObject(WHITE_BRUSH)));
278         UINT uState = DFCS_BUTTONRADIOMASK | DFCS_FLAT | DFCS_MONO;
279         DrawFrameControl(hDC, &BoxRect, DFC_BUTTON, uState);
280     }
281     SelectObject(hDC, hbmOld);
282 
283     return hbm;     // success
284 }
285 
286 static HIMAGELIST
287 CreateTreeImageList(VOID)
288 {
289     HIMAGELIST hImageList;
290     hImageList = ImageList_Create(16, 16, ILC_COLOR24 | ILC_MASK, 9, 1);
291     if (hImageList == NULL)
292         return NULL;    // failure
293 
294     // free if existed
295     if (s_AdvancedIcons)
296     {
297         free(s_AdvancedIcons);
298         s_AdvancedIcons = NULL;
299     }
300     s_AdvancedIconCount = 0;
301 
302     // allocate now
303     ADVANCED_ICON *pAllocated;
304     size_t Size = PREDEFINED_ICON_COUNT * sizeof(ADVANCED_ICON);
305     pAllocated = (ADVANCED_ICON *)calloc(1, Size);
306     if (pAllocated == NULL)
307         return NULL;    // failure
308 
309     s_AdvancedIconCount = PREDEFINED_ICON_COUNT;
310     s_AdvancedIcons = pAllocated;
311 
312     // add the predefined icons
313 
314     HDC hDC = CreateCompatibleDC(NULL);
315     HBITMAP hbmMask = CreateCheckMask(hDC);
316 
317     HBITMAP hbmChecked, hbmUnchecked;
318 
319     hbmChecked = CreateCheckImage(hDC, TRUE);
320     ImageList_Add(hImageList, hbmChecked, hbmMask);
321     DeleteObject(hbmChecked);
322 
323     hbmUnchecked = CreateCheckImage(hDC, FALSE);
324     ImageList_Add(hImageList, hbmUnchecked, hbmMask);
325     DeleteObject(hbmUnchecked);
326 
327     hbmChecked = CreateCheckImage(hDC, TRUE, FALSE);
328     ImageList_Add(hImageList, hbmChecked, hbmMask);
329     DeleteObject(hbmChecked);
330 
331     hbmUnchecked = CreateCheckImage(hDC, FALSE, FALSE);
332     ImageList_Add(hImageList, hbmUnchecked, hbmMask);
333     DeleteObject(hbmUnchecked);
334 
335     DeleteObject(hbmMask);
336     hbmMask = CreateRadioMask(hDC);
337 
338     hbmChecked = CreateRadioImage(hDC, TRUE);
339     ImageList_Add(hImageList, hbmChecked, hbmMask);
340     DeleteObject(hbmChecked);
341 
342     hbmUnchecked = CreateRadioImage(hDC, FALSE);
343     ImageList_Add(hImageList, hbmUnchecked, hbmMask);
344     DeleteObject(hbmUnchecked);
345 
346     hbmChecked = CreateRadioImage(hDC, TRUE, FALSE);
347     ImageList_Add(hImageList, hbmChecked, hbmMask);
348     DeleteObject(hbmChecked);
349 
350     hbmUnchecked = CreateRadioImage(hDC, FALSE, FALSE);
351     ImageList_Add(hImageList, hbmUnchecked, hbmMask);
352     DeleteObject(hbmUnchecked);
353 
354     DeleteObject(hbmMask);
355 
356     return hImageList;
357 }
358 
359 static ADVANCED_ENTRY *
360 Advanced_GetItem(DWORD dwID)
361 {
362     if (dwID == DWORD(-1))
363         return NULL;
364 
365     for (INT i = 0; i < s_AdvancedCount; ++i)
366     {
367         ADVANCED_ENTRY *pEntry = &s_Advanced[i];
368         if (pEntry->dwID == dwID)
369             return pEntry;
370     }
371     return NULL;    // failure
372 }
373 
374 static INT
375 Advanced_GetImage(ADVANCED_ENTRY *pEntry)
376 {
377     switch (pEntry->dwType)
378     {
379         case AETYPE_GROUP:
380             return pEntry->nIconID;
381 
382         case AETYPE_CHECKBOX:
383             if (pEntry->bGrayed)
384             {
385                 if (pEntry->bChecked)
386                     return I_CHECKED_DISABLED;
387                 else
388                     return I_UNCHECKED_DISABLED;
389             }
390             else
391             {
392                 if (pEntry->bChecked)
393                     return I_CHECKED;
394                 else
395                     return I_UNCHECKED;
396             }
397 
398         case AETYPE_RADIO:
399             if (pEntry->bGrayed)
400             {
401                 if (pEntry->bChecked)
402                     return I_RADIO_CHECKED_DISABLED;
403                 else
404                     return I_RADIO_UNCHECKED_DISABLED;
405             }
406             else
407             {
408                 if (pEntry->bChecked)
409                     return I_RADIO_CHECKED;
410                 else
411                     return I_RADIO_UNCHECKED;
412             }
413     }
414     return -1;  // failure
415 }
416 
417 static VOID
418 Advanced_InsertEntry(HWND hwndTreeView, ADVANCED_ENTRY *pEntry)
419 {
420     ADVANCED_ENTRY *pParent = Advanced_GetItem(pEntry->dwParentID);
421     HTREEITEM hParent = TVI_ROOT;
422     if (pParent)
423         hParent = pParent->hItem;
424 
425     TV_INSERTSTRUCT Insertion;
426     ZeroMemory(&Insertion, sizeof(Insertion));
427     Insertion.hParent = hParent;
428     Insertion.hInsertAfter = TVI_LAST;
429     Insertion.item.mask =
430         TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM;
431     Insertion.item.pszText = pEntry->szText;
432 
433     INT iImage = Advanced_GetImage(pEntry);
434     Insertion.item.iImage = Insertion.item.iSelectedImage = iImage;
435     Insertion.item.lParam = pEntry->dwID;
436     pEntry->hItem = TreeView_InsertItem(hwndTreeView, &Insertion);
437 }
438 
439 static VOID
440 Advanced_InsertAll(HWND hwndTreeView)
441 {
442     TreeView_DeleteAllItems(hwndTreeView);
443 
444     // insert the entries
445     ADVANCED_ENTRY *pEntry;
446     for (INT i = 0; i < s_AdvancedCount; ++i)
447     {
448         pEntry = &s_Advanced[i];
449         Advanced_InsertEntry(hwndTreeView, pEntry);
450     }
451 
452     // expand all
453     for (INT i = 0; i < s_AdvancedCount; ++i)
454     {
455         pEntry = &s_Advanced[i];
456         if (pEntry->dwType == AETYPE_GROUP)
457         {
458             TreeView_Expand(hwndTreeView, pEntry->hItem, TVE_EXPAND);
459         }
460     }
461 }
462 
463 static BOOL
464 Advanced_LoadTree(HKEY hKey, LPCWSTR pszKeyName, DWORD dwParentID)
465 {
466     DWORD dwIndex;
467     WCHAR szKeyName[64], szText[MAX_PATH], *pch;
468     DWORD Size, Value;
469     ADVANCED_ENTRY *pAllocated;
470 
471     // resize s_Advanced
472     Size = (s_AdvancedCount + 1) * sizeof(ADVANCED_ENTRY);
473     pAllocated = (ADVANCED_ENTRY *)realloc(s_Advanced, Size);
474     if (pAllocated == NULL)
475         return FALSE;   // failure
476     else
477         s_Advanced = pAllocated;
478 
479     ADVANCED_ENTRY *pEntry = &s_Advanced[s_AdvancedCount];
480 
481     // dwID, dwParentID, szKeyName
482     pEntry->dwID = s_AdvancedCount;
483     pEntry->dwParentID = dwParentID;
484     lstrcpynW(pEntry->szKeyName, pszKeyName, _countof(pEntry->szKeyName));
485 
486     // Text, ResourceID
487     pEntry->szText[0] = 0;
488     pEntry->dwResourceID = 0;
489     szText[0] = 0;
490     Size = sizeof(szText);
491     RegQueryValueExW(hKey, L"Text", NULL, NULL, LPBYTE(szText), &Size);
492     if (szText[0] == L'@')
493     {
494         pch = wcsrchr(szText, L',');
495         if (pch)
496         {
497             *pch = 0;
498             dwIndex = abs(_wtoi(pch + 1));
499             pEntry->dwResourceID = dwIndex;
500         }
501         HINSTANCE hInst = LoadLibraryW(&szText[1]);
502         LoadStringW(hInst, dwIndex, szText, _countof(szText));
503         FreeLibrary(hInst);
504     }
505     else
506     {
507         pEntry->dwResourceID = DWORD(-1);
508     }
509     lstrcpynW(pEntry->szText, szText, _countof(pEntry->szText));
510 
511     // Type
512     szText[0] = 0;
513     RegQueryValueExW(hKey, L"Type", NULL, NULL, LPBYTE(szText), &Size);
514     if (lstrcmpiW(szText, L"checkbox") == 0)
515         pEntry->dwType = AETYPE_CHECKBOX;
516     else if (lstrcmpiW(szText, L"radio") == 0)
517         pEntry->dwType = AETYPE_RADIO;
518     else if (lstrcmpiW(szText, L"group") == 0)
519         pEntry->dwType = AETYPE_GROUP;
520     else
521         return FALSE;   // failure
522 
523     pEntry->nIconID = -1;
524     if (pEntry->dwType == AETYPE_GROUP)
525     {
526         // Bitmap (Icon)
527         UINT nIconIndex = 0;
528         Size = sizeof(szText);
529         szText[0] = 0;
530         RegQueryValueExW(hKey, L"Bitmap", NULL, NULL, LPBYTE(szText), &Size);
531 
532         WCHAR szExpanded[MAX_PATH];
533         ExpandEnvironmentStringsW(szText, szExpanded, _countof(szExpanded));
534         pch = wcsrchr(szExpanded, L',');
535         if (pch)
536         {
537             *pch = 0;
538             nIconIndex = abs(_wtoi(pch + 1));
539         }
540         pEntry->nIconID = Advanced_AddIcon(szExpanded, nIconIndex);
541     }
542 
543     if (pEntry->dwType == AETYPE_GROUP)
544     {
545         pEntry->hkeyRoot = NULL;
546         pEntry->szRegPath[0] = 0;
547         pEntry->szValueName[0] = 0;
548         pEntry->dwCheckedValue = 0;
549         pEntry->bHasUncheckedValue = FALSE;
550         pEntry->dwUncheckedValue = 0;
551         pEntry->dwDefaultValue = 0;
552         pEntry->hItem = NULL;
553         pEntry->bGrayed = FALSE;
554         pEntry->bChecked = FALSE;
555     }
556     else
557     {
558         // HKeyRoot
559         Value = DWORD(HKEY_CURRENT_USER);
560         Size = sizeof(Value);
561         RegQueryValueExW(hKey, L"HKeyRoot", NULL, NULL, LPBYTE(&Value), &Size);
562         pEntry->hkeyRoot = HKEY(Value);
563 
564         // RegPath
565         pEntry->szRegPath[0] = 0;
566         Size = sizeof(szText);
567         RegQueryValueExW(hKey, L"RegPath", NULL, NULL, LPBYTE(szText), &Size);
568         lstrcpynW(pEntry->szRegPath, szText, _countof(pEntry->szRegPath));
569 
570         // ValueName
571         pEntry->szValueName[0] = 0;
572         Size = sizeof(szText);
573         RegQueryValueExW(hKey, L"ValueName", NULL, NULL, LPBYTE(szText), &Size);
574         lstrcpynW(pEntry->szValueName, szText, _countof(pEntry->szValueName));
575 
576         // CheckedValue
577         Size = sizeof(Value);
578         Value = 0x00000001;
579         RegQueryValueExW(hKey, L"CheckedValue", NULL, NULL, LPBYTE(&Value), &Size);
580         pEntry->dwCheckedValue = Value;
581 
582         // UncheckedValue
583         Size = sizeof(Value);
584         Value = 0x00000000;
585         pEntry->bHasUncheckedValue = TRUE;
586         if (RegQueryValueExW(hKey, L"UncheckedValue", NULL,
587                              NULL, LPBYTE(&Value), &Size) != ERROR_SUCCESS)
588         {
589             pEntry->bHasUncheckedValue = FALSE;
590         }
591         pEntry->dwUncheckedValue = Value;
592 
593         // DefaultValue
594         Size = sizeof(Value);
595         Value = 0x00000001;
596         RegQueryValueExW(hKey, L"DefaultValue", NULL, NULL, LPBYTE(&Value), &Size);
597         pEntry->dwDefaultValue = Value;
598 
599         // hItem
600         pEntry->hItem = NULL;
601 
602         // bGrayed, bChecked
603         HKEY hkeyTarget;
604         Value = pEntry->dwDefaultValue;
605         pEntry->bGrayed = TRUE;
606         if (RegOpenKeyExW(HKEY(pEntry->hkeyRoot), pEntry->szRegPath, 0,
607                           KEY_READ, &hkeyTarget) == ERROR_SUCCESS)
608         {
609             Size = sizeof(Value);
610             if (RegQueryValueExW(hkeyTarget, pEntry->szValueName, NULL, NULL,
611                                  LPBYTE(&Value), &Size) == ERROR_SUCCESS)
612             {
613                 pEntry->bGrayed = FALSE;
614             }
615             RegCloseKey(hkeyTarget);
616         }
617         pEntry->bChecked = (Value == pEntry->dwCheckedValue);
618     }
619 
620     // Grayed (ReactOS extension)
621     Size = sizeof(Value);
622     Value = FALSE;
623     RegQueryValueExW(hKey, L"Grayed", NULL, NULL, LPBYTE(&Value), &Size);
624     if (!pEntry->bGrayed)
625         pEntry->bGrayed = Value;
626 
627     BOOL bIsGroup = (pEntry->dwType == AETYPE_GROUP);
628     dwParentID = pEntry->dwID;
629     ++s_AdvancedCount;
630 
631     if (!bIsGroup)
632         return TRUE;    // success
633 
634     // load the children
635     dwIndex = 0;
636     while (RegEnumKeyW(hKey, dwIndex, szKeyName,
637                        _countof(szKeyName)) == ERROR_SUCCESS)
638     {
639         HKEY hkeyChild;
640         if (RegOpenKeyExW(hKey, szKeyName, 0, KEY_READ,
641                           &hkeyChild) != ERROR_SUCCESS)
642         {
643             ++dwIndex;
644             continue;   // failure
645         }
646 
647         Advanced_LoadTree(hkeyChild, szKeyName, dwParentID);
648         RegCloseKey(hkeyChild);
649 
650         ++dwIndex;
651     }
652 
653     return TRUE;    // success
654 }
655 
656 static BOOL
657 Advanced_LoadAll(VOID)
658 {
659     static const WCHAR s_szAdvanced[] =
660         L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced";
661 
662     // free if already existed
663     if (s_Advanced)
664     {
665         free(s_Advanced);
666         s_Advanced = NULL;
667     }
668     s_AdvancedCount = 0;
669 
670     HKEY hKey;
671     if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, s_szAdvanced, 0,
672                       KEY_READ, &hKey) != ERROR_SUCCESS)
673     {
674         return FALSE;   // failure
675     }
676 
677     // load the children
678     WCHAR szKeyName[64];
679     DWORD dwIndex = 0;
680     while (RegEnumKeyW(hKey, dwIndex, szKeyName,
681                        _countof(szKeyName)) == ERROR_SUCCESS)
682     {
683         HKEY hkeyChild;
684         if (RegOpenKeyExW(hKey, szKeyName, 0, KEY_READ,
685                           &hkeyChild) != ERROR_SUCCESS)
686         {
687             ++dwIndex;
688             continue;   // failure
689         }
690 
691         Advanced_LoadTree(hkeyChild, szKeyName, DWORD(-1));
692         RegCloseKey(hkeyChild);
693 
694         ++dwIndex;
695     }
696 
697     RegCloseKey(hKey);
698 
699     return TRUE;    // success
700 }
701 
702 static int
703 Advanced_Compare(const void *x, const void *y)
704 {
705     ADVANCED_ENTRY *pEntry1 = (ADVANCED_ENTRY *)x;
706     ADVANCED_ENTRY *pEntry2 = (ADVANCED_ENTRY *)y;
707 
708     DWORD dwParentID1 = pEntry1->dwParentID;
709     DWORD dwParentID2 = pEntry2->dwParentID;
710 
711     if (dwParentID1 == dwParentID2)
712         return lstrcmpi(pEntry1->szText, pEntry2->szText);
713 
714     DWORD i, m, n;
715     const UINT MAX_DEPTH = 32;
716     ADVANCED_ENTRY *pArray1[MAX_DEPTH];
717     ADVANCED_ENTRY *pArray2[MAX_DEPTH];
718 
719     // Make ancestor lists
720     for (i = m = n = 0; i < MAX_DEPTH; ++i)
721     {
722         ADVANCED_ENTRY *pParent1 = Advanced_GetItem(dwParentID1);
723         ADVANCED_ENTRY *pParent2 = Advanced_GetItem(dwParentID2);
724         if (!pParent1 && !pParent2)
725             break;
726 
727         if (pParent1)
728         {
729             pArray1[m++] = pParent1;
730             dwParentID1 = pParent1->dwParentID;
731         }
732         if (pParent2)
733         {
734             pArray2[n++] = pParent2;
735             dwParentID2 = pParent2->dwParentID;
736         }
737     }
738 
739     UINT k = min(m, n);
740     for (i = 0; i < k; ++i)
741     {
742         INT nCompare = lstrcmpi(pArray1[m - i - 1]->szText, pArray2[n - i - 1]->szText);
743         if (nCompare < 0)
744             return -1;
745         if (nCompare > 0)
746             return 1;
747     }
748 
749     if (m < n)
750         return -1;
751     if (m > n)
752         return 1;
753     return lstrcmpi(pEntry1->szText, pEntry2->szText);
754 }
755 
756 static VOID
757 Advanced_SortAll(VOID)
758 {
759     qsort(s_Advanced, s_AdvancedCount, sizeof(ADVANCED_ENTRY), Advanced_Compare);
760 }
761 
762 EXTERN_C HPSXA WINAPI SHCreatePropSheetExtArrayEx(HKEY hKey, LPCWSTR pszSubKey, UINT max_iface, IDataObject *pDataObj);
763 
764 static VOID
765 UpdateGeneralIcons(HWND hDlg)
766 {
767     HWND hwndTaskIcon, hwndFolderIcon, hwndClickIcon;
768     HICON hTaskIcon = NULL, hFolderIcon = NULL, hClickIcon = NULL;
769     LPTSTR lpTaskIconName = NULL, lpFolderIconName = NULL, lpClickIconName = NULL;
770 
771     // show task setting icon
772     if(IsDlgButtonChecked(hDlg, IDC_FOLDER_OPTIONS_COMMONTASKS) == BST_CHECKED)
773         lpTaskIconName = MAKEINTRESOURCE(IDI_SHELL_SHOW_COMMON_TASKS);
774     else if(IsDlgButtonChecked(hDlg, IDC_FOLDER_OPTIONS_CLASSICFOLDERS) == BST_CHECKED)
775         lpTaskIconName = MAKEINTRESOURCE(IDI_SHELL_CLASSIC_FOLDERS);
776 
777     if (lpTaskIconName)
778     {
779         hTaskIcon = (HICON)LoadImage(shell32_hInstance,
780                                               lpTaskIconName,
781                                               IMAGE_ICON,
782                                               0,
783                                               0,
784                                               LR_DEFAULTCOLOR);
785         if (hTaskIcon)
786         {
787             hwndTaskIcon = GetDlgItem(hDlg,
788                                     IDC_FOLDER_OPTIONS_TASKICON);
789             if (hwndTaskIcon)
790             {
791                 SendMessage(hwndTaskIcon,
792                             STM_SETIMAGE,
793                             IMAGE_ICON,
794                             (LPARAM)hTaskIcon);
795             }
796         }
797     }
798 
799     // show Folder setting icons
800     if(IsDlgButtonChecked(hDlg, IDC_FOLDER_OPTIONS_SAMEWINDOW) == BST_CHECKED)
801         lpFolderIconName = MAKEINTRESOURCE(IDI_SHELL_OPEN_IN_SOME_WINDOW);
802     else if(IsDlgButtonChecked(hDlg, IDC_FOLDER_OPTIONS_OWNWINDOW) == BST_CHECKED)
803         lpFolderIconName = MAKEINTRESOURCE(IDI_SHELL_OPEN_IN_NEW_WINDOW);
804 
805     if (lpFolderIconName)
806     {
807         hFolderIcon = (HICON)LoadImage(shell32_hInstance,
808                                               lpFolderIconName,
809                                               IMAGE_ICON,
810                                               0,
811                                               0,
812                                               LR_DEFAULTCOLOR);
813         if (hFolderIcon)
814         {
815             hwndFolderIcon = GetDlgItem(hDlg,
816                                     IDC_FOLDER_OPTIONS_FOLDERICON);
817             if (hwndFolderIcon)
818             {
819                 SendMessage(hwndFolderIcon,
820                             STM_SETIMAGE,
821                             IMAGE_ICON,
822                             (LPARAM)hFolderIcon);
823             }
824         }
825     }
826 
827     // Show click setting icon
828     if(IsDlgButtonChecked(hDlg, IDC_FOLDER_OPTIONS_SINGLECLICK) == BST_CHECKED)
829         lpClickIconName = MAKEINTRESOURCE(IDI_SHELL_SINGLE_CLICK_TO_OPEN);
830     else if(IsDlgButtonChecked(hDlg, IDC_FOLDER_OPTIONS_DOUBLECLICK) == BST_CHECKED)
831         lpClickIconName = MAKEINTRESOURCE(IDI_SHELL_DOUBLE_CLICK_TO_OPEN);
832 
833     if (lpClickIconName)
834     {
835         hClickIcon = (HICON)LoadImage(shell32_hInstance,
836                                               lpClickIconName,
837                                               IMAGE_ICON,
838                                               0,
839                                               0,
840                                               LR_DEFAULTCOLOR);
841         if (hClickIcon)
842         {
843             hwndClickIcon = GetDlgItem(hDlg,
844                                     IDC_FOLDER_OPTIONS_CLICKICON);
845             if (hwndClickIcon)
846             {
847                 SendMessage(hwndClickIcon,
848                             STM_SETIMAGE,
849                             IMAGE_ICON,
850                             (LPARAM)hClickIcon);
851             }
852         }
853     }
854 
855     // Clean up
856     if(hTaskIcon)
857         DeleteObject(hTaskIcon);
858     if(hFolderIcon)
859         DeleteObject(hFolderIcon);
860     if(hClickIcon)
861         DeleteObject(hClickIcon);
862 
863     return;
864 }
865 
866 INT_PTR
867 CALLBACK
868 FolderOptionsGeneralDlg(
869     HWND hwndDlg,
870     UINT uMsg,
871     WPARAM wParam,
872     LPARAM lParam
873 )
874 {
875     switch(uMsg)
876     {
877         case WM_INITDIALOG:
878             // FIXME
879             break;
880 
881         case WM_COMMAND:
882             switch (LOWORD(wParam))
883             {
884                 case IDC_FOLDER_OPTIONS_COMMONTASKS:
885                 case IDC_FOLDER_OPTIONS_CLASSICFOLDERS:
886                 case IDC_FOLDER_OPTIONS_SAMEWINDOW:
887                 case IDC_FOLDER_OPTIONS_OWNWINDOW:
888                 case IDC_FOLDER_OPTIONS_SINGLECLICK:
889                 case IDC_FOLDER_OPTIONS_DOUBLECLICK:
890                     if (HIWORD(wParam) == BN_CLICKED)
891                     {
892                         UpdateGeneralIcons(hwndDlg);
893 
894                         /* Enable the 'Apply' button */
895                         PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
896                     }
897                     break;
898             }
899             break;
900 
901         case WM_NOTIFY:
902         {
903             LPNMHDR pnmh = (LPNMHDR)lParam;
904 
905             switch (pnmh->code)
906             {
907                 case PSN_SETACTIVE:
908                     break;
909 
910                 case PSN_APPLY:
911                     break;
912             }
913             break;
914         }
915 
916         case WM_DESTROY:
917             break;
918 
919          default:
920              return FALSE;
921     }
922     return FALSE;
923 }
924 
925 static BOOL
926 ViewDlg_OnInitDialog(HWND hwndDlg)
927 {
928     HWND hwndTreeView = GetDlgItem(hwndDlg, 14003);
929 
930     s_hImageList = CreateTreeImageList();
931     TreeView_SetImageList(hwndTreeView, s_hImageList, TVSIL_NORMAL);
932 
933     Advanced_LoadAll();
934     Advanced_SortAll();
935     Advanced_InsertAll(hwndTreeView);
936 
937     return TRUE;    // set focus
938 }
939 
940 static BOOL
941 ViewDlg_ToggleCheckItem(HWND hwndDlg, HTREEITEM hItem)
942 {
943     HWND hwndTreeView = GetDlgItem(hwndDlg, 14003);
944 
945     // get the item
946     TV_ITEM Item;
947     INT i;
948     ZeroMemory(&Item, sizeof(Item));
949     Item.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_PARAM;
950     Item.hItem = hItem;
951     if (!TreeView_GetItem(hwndTreeView, &Item))
952         return FALSE;       // no such item
953 
954     ADVANCED_ENTRY *pEntry = Advanced_GetItem(Item.lParam);
955     if (pEntry == NULL)
956         return FALSE;       // no such item
957     if (pEntry->bGrayed)
958         return FALSE;       // disabled
959 
960     // toggle check mark
961     Item.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
962     switch (pEntry->dwType)
963     {
964         case AETYPE_CHECKBOX:
965             pEntry->bChecked = !pEntry->bChecked;
966             break;
967         case AETYPE_RADIO:
968             // reset all the entries of the same parent
969             for (i = 0; i < s_AdvancedCount; ++i)
970             {
971                 ADVANCED_ENTRY *pEntry2 = &s_Advanced[i];
972                 if (pEntry->dwParentID == pEntry2->dwParentID)
973                 {
974                     pEntry2->bChecked = FALSE;
975 
976                     Item.hItem = pEntry2->hItem;
977                     INT iImage = Advanced_GetImage(pEntry2);
978                     Item.iImage = Item.iSelectedImage = iImage;
979                     TreeView_SetItem(hwndTreeView, &Item);
980                 }
981             }
982             pEntry->bChecked = TRUE;
983             break;
984         default:
985             return FALSE;   // failure
986     }
987     Item.iImage = Item.iSelectedImage = Advanced_GetImage(pEntry);
988     Item.hItem = hItem;
989     TreeView_SetItem(hwndTreeView, &Item);
990 
991     // redraw the item
992     RECT rcItem;
993     TreeView_GetItemRect(hwndTreeView, hItem, &rcItem, FALSE);
994     InvalidateRect(hwndTreeView, &rcItem, TRUE);
995     return TRUE;    // success
996 }
997 
998 static VOID
999 ViewDlg_OnTreeViewClick(HWND hwndDlg)
1000 {
1001     HWND hwndTreeView = GetDlgItem(hwndDlg, 14003);
1002 
1003     // do hit test to get the clicked item
1004     TV_HITTESTINFO HitTest;
1005     ZeroMemory(&HitTest, sizeof(HitTest));
1006     DWORD dwPos = GetMessagePos();
1007     HitTest.pt.x = LOWORD(dwPos);
1008     HitTest.pt.y = HIWORD(dwPos);
1009     ScreenToClient(hwndTreeView, &HitTest.pt);
1010     HTREEITEM hItem = TreeView_HitTest(hwndTreeView, &HitTest);
1011 
1012     // toggle the check mark if possible
1013     if (ViewDlg_ToggleCheckItem(hwndDlg, hItem))
1014     {
1015         // property sheet was changed
1016         PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
1017     }
1018 }
1019 
1020 static void
1021 ViewDlg_OnTreeViewKeyDown(HWND hwndDlg, TV_KEYDOWN *KeyDown)
1022 {
1023     HWND hwndTreeView = GetDlgItem(hwndDlg, 14003);
1024 
1025     if (KeyDown->wVKey == VK_SPACE)
1026     {
1027         // [Space] key was pressed
1028         HTREEITEM hItem = TreeView_GetSelection(hwndTreeView);
1029         if (ViewDlg_ToggleCheckItem(hwndDlg, hItem))
1030         {
1031             PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
1032         }
1033     }
1034 }
1035 
1036 static INT_PTR
1037 ViewDlg_OnTreeCustomDraw(HWND hwndDlg, NMTVCUSTOMDRAW *Draw)
1038 {
1039     NMCUSTOMDRAW& nmcd = Draw->nmcd;
1040     switch (nmcd.dwDrawStage)
1041     {
1042         case CDDS_PREPAINT:
1043             return CDRF_NOTIFYITEMDRAW;     // for CDDS_ITEMPREPAINT
1044 
1045         case CDDS_ITEMPREPAINT:
1046             if (!(nmcd.uItemState & CDIS_SELECTED)) // not selected
1047             {
1048                 LPARAM lParam = nmcd.lItemlParam;
1049                 ADVANCED_ENTRY *pEntry = Advanced_GetItem(lParam);
1050                 if (pEntry && pEntry->bGrayed) // disabled
1051                 {
1052                     // draw as grayed
1053                     Draw->clrText = GetSysColor(COLOR_GRAYTEXT);
1054                     Draw->clrTextBk = GetSysColor(COLOR_WINDOW);
1055                     return CDRF_NEWFONT;
1056                 }
1057             }
1058             break;
1059 
1060         default:
1061             break;
1062     }
1063     return CDRF_DODEFAULT;
1064 }
1065 
1066 static VOID
1067 Advanced_RestoreDefaults(HWND hwndDlg)
1068 {
1069     HWND hwndTreeView = GetDlgItem(hwndDlg, 14003);
1070 
1071     for (INT i = 0; i < s_AdvancedCount; ++i)
1072     {
1073         // ignore if the type is group
1074         ADVANCED_ENTRY *pEntry = &s_Advanced[i];
1075         if (pEntry->dwType == AETYPE_GROUP)
1076             continue;
1077 
1078         // set default value on registry
1079         HKEY hKey;
1080         if (RegOpenKeyExW(HKEY(pEntry->hkeyRoot), pEntry->szRegPath,
1081                           0, KEY_WRITE, &hKey) != ERROR_SUCCESS)
1082         {
1083             continue;
1084         }
1085         RegSetValueExW(hKey, pEntry->szValueName, 0, REG_DWORD,
1086                        LPBYTE(pEntry->dwDefaultValue), sizeof(DWORD));
1087         RegCloseKey(hKey);
1088 
1089         // update check status
1090         pEntry->bChecked = (pEntry->dwCheckedValue == pEntry->dwDefaultValue);
1091 
1092         // update the image
1093         TV_ITEM Item;
1094         ZeroMemory(&Item, sizeof(Item));
1095         Item.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
1096         Item.hItem = pEntry->hItem;
1097         Item.iImage = Item.iSelectedImage = Advanced_GetImage(pEntry);
1098         TreeView_SetItem(hwndTreeView, &Item);
1099     }
1100 
1101     PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
1102 }
1103 
1104 /* FIXME: These macros should not be defined here */
1105 #ifndef SSF_SHOWSUPERHIDDEN
1106     #define SSF_SHOWSUPERHIDDEN     0x00040000
1107 #endif
1108 #ifndef SSF_SEPPROCESS
1109     #define SSF_SEPPROCESS          0x00080000
1110 #endif
1111 
1112 static VOID
1113 ScanAdvancedSettings(SHELLSTATE *pSS, DWORD *pdwMask)
1114 {
1115     for (INT i = 0; i < s_AdvancedCount; ++i)
1116     {
1117         const ADVANCED_ENTRY *pEntry = &s_Advanced[i];
1118         if (pEntry->dwType == AETYPE_GROUP || pEntry->bGrayed)
1119             continue;
1120 
1121         BOOL bChecked = pEntry->bChecked;
1122 
1123         // FIXME: Add more items
1124         if (lstrcmpiW(pEntry->szKeyName, L"SuperHidden") == 0)
1125         {
1126             pSS->fShowSuperHidden = !bChecked ? 1 : 0;
1127             *pdwMask |= SSF_SHOWSUPERHIDDEN;
1128             continue;
1129         }
1130         if (lstrcmpiW(pEntry->szKeyName, L"DesktopProcess") == 0)
1131         {
1132             pSS->fSepProcess = bChecked ? 1 : 0;
1133             *pdwMask |= SSF_SEPPROCESS;
1134             continue;
1135         }
1136         if (lstrcmpiW(pEntry->szKeyName, L"SHOWALL") == 0)
1137         {
1138             pSS->fShowAllObjects = !bChecked ? 1 : 0;
1139             *pdwMask |= SSF_SHOWALLOBJECTS;
1140             continue;
1141         }
1142         if (lstrcmpiW(pEntry->szKeyName, L"HideFileExt") == 0)
1143         {
1144             pSS->fShowExtensions = !bChecked ? 1 : 0;
1145             *pdwMask |= SSF_SHOWEXTENSIONS;
1146             continue;
1147         }
1148         if (lstrcmpiW(pEntry->szKeyName, L"ShowCompColor") == 0)
1149         {
1150             pSS->fShowCompColor = bChecked ? 1 : 0;
1151             *pdwMask |= SSF_SHOWCOMPCOLOR;
1152             continue;
1153         }
1154         if (lstrcmpiW(pEntry->szKeyName, L"ShowInfoTip") == 0)
1155         {
1156             pSS->fShowInfoTip = bChecked ? 1 : 0;
1157             *pdwMask |= SSF_SHOWINFOTIP;
1158             continue;
1159         }
1160     }
1161 }
1162 
1163 extern "C"
1164 VOID WINAPI SHGetSetSettings(LPSHELLSTATE lpss, DWORD dwMask, BOOL bSet);
1165 
1166 static BOOL CALLBACK RefreshBrowsersCallback (HWND hWnd, LPARAM msg)
1167 {
1168     WCHAR ClassName[100];
1169     if (GetClassName(hWnd, ClassName, 100))
1170     {
1171         if (!wcscmp(ClassName, L"Progman") ||
1172             !wcscmp(ClassName, L"CabinetWClass") ||
1173             !wcscmp(ClassName, L"ExploreWClass"))
1174         {
1175             PostMessage(hWnd, WM_COMMAND, FCIDM_DESKBROWSER_REFRESH, 0);
1176         }
1177     }
1178     return TRUE;
1179 }
1180 
1181 static VOID
1182 ViewDlg_Apply(HWND hwndDlg)
1183 {
1184     for (INT i = 0; i < s_AdvancedCount; ++i)
1185     {
1186         // ignore the entry if the type is group or the entry is grayed
1187         ADVANCED_ENTRY *pEntry = &s_Advanced[i];
1188         if (pEntry->dwType == AETYPE_GROUP || pEntry->bGrayed)
1189             continue;
1190 
1191         // open the registry key
1192         HKEY hkeyTarget;
1193         if (RegOpenKeyExW(HKEY(pEntry->hkeyRoot), pEntry->szRegPath, 0,
1194                           KEY_WRITE, &hkeyTarget) != ERROR_SUCCESS)
1195         {
1196             continue;
1197         }
1198 
1199         // checked or unchecked?
1200         DWORD dwValue, dwSize;
1201         if (pEntry->bChecked)
1202         {
1203             dwValue = pEntry->dwCheckedValue;
1204         }
1205         else
1206         {
1207             if (pEntry->bHasUncheckedValue)
1208             {
1209                 dwValue = pEntry->dwUncheckedValue;
1210             }
1211             else
1212             {
1213                 // there is no unchecked value
1214                 RegCloseKey(hkeyTarget);
1215                 continue;   // ignore
1216             }
1217         }
1218 
1219         // set the value
1220         dwSize = sizeof(dwValue);
1221         RegSetValueExW(hkeyTarget, pEntry->szValueName, 0, REG_DWORD,
1222                        LPBYTE(&dwValue), dwSize);
1223 
1224         // close now
1225         RegCloseKey(hkeyTarget);
1226     }
1227 
1228     // scan advanced settings for user's settings
1229     DWORD dwMask = 0;
1230     SHELLSTATE ShellState;
1231     ZeroMemory(&ShellState, sizeof(ShellState));
1232     ScanAdvancedSettings(&ShellState, &dwMask);
1233 
1234     // update user's settings
1235     SHGetSetSettings(&ShellState, dwMask, TRUE);
1236 
1237     // notify all
1238     SendMessage(HWND_BROADCAST, WM_WININICHANGE, 0, 0);
1239 
1240     EnumWindows(RefreshBrowsersCallback, NULL);
1241 }
1242 
1243 INT_PTR CALLBACK
1244 FolderOptionsViewDlg(
1245     HWND    hwndDlg,
1246     UINT    uMsg,
1247     WPARAM  wParam,
1248     LPARAM  lParam)
1249 {
1250     INT_PTR Result;
1251     NMTVCUSTOMDRAW *Draw;
1252 
1253     switch(uMsg)
1254     {
1255         case WM_INITDIALOG:
1256             return ViewDlg_OnInitDialog(hwndDlg);
1257         case WM_COMMAND:
1258             switch (LOWORD(wParam))
1259             {
1260                 case 14004: // Restore Defaults
1261                     Advanced_RestoreDefaults(hwndDlg);
1262                     break;
1263             }
1264             break;
1265         case WM_NOTIFY:
1266             switch (LPNMHDR(lParam)->code)
1267             {
1268                 case NM_CLICK:  // clicked on treeview
1269                     ViewDlg_OnTreeViewClick(hwndDlg);
1270                     break;
1271                 case NM_CUSTOMDRAW:     // custom draw (for graying)
1272                     Draw = (NMTVCUSTOMDRAW *)lParam;
1273                     Result = ViewDlg_OnTreeCustomDraw(hwndDlg, Draw);
1274                     SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, Result);
1275                     return Result;
1276                 case TVN_KEYDOWN:       // key is down
1277                     ViewDlg_OnTreeViewKeyDown(hwndDlg, (TV_KEYDOWN *)lParam);
1278                     break;
1279                 case PSN_APPLY:         // [Apply] is clicked
1280                     ViewDlg_Apply(hwndDlg);
1281                     break;
1282                 default:
1283                     break;
1284             }
1285             break;
1286     }
1287 
1288     return FALSE;
1289 }
1290 
1291 static
1292 VOID
1293 InitializeFileTypesListCtrlColumns(HWND hDlgCtrl)
1294 {
1295     RECT clientRect;
1296     LVCOLUMNW col;
1297     WCHAR szName[50];
1298     DWORD dwStyle;
1299     int columnSize = 140;
1300 
1301 
1302     if (!LoadStringW(shell32_hInstance, IDS_COLUMN_EXTENSION, szName, sizeof(szName) / sizeof(WCHAR)))
1303     {
1304         /* default to english */
1305         wcscpy(szName, L"Extensions");
1306     }
1307 
1308     /* make sure its null terminated */
1309     szName[(sizeof(szName)/sizeof(WCHAR))-1] = 0;
1310 
1311     GetClientRect(hDlgCtrl, &clientRect);
1312     ZeroMemory(&col, sizeof(LV_COLUMN));
1313     columnSize = 140; //FIXME
1314     col.iSubItem   = 0;
1315     col.mask      = LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM | LVCF_FMT;
1316     col.fmt = LVCFMT_FIXED_WIDTH;
1317     col.cx         = columnSize | LVCFMT_LEFT;
1318     col.cchTextMax = wcslen(szName);
1319     col.pszText    = szName;
1320     (void)SendMessageW(hDlgCtrl, LVM_INSERTCOLUMNW, 0, (LPARAM)&col);
1321 
1322     if (!LoadStringW(shell32_hInstance, IDS_FILE_TYPES, szName, sizeof(szName) / sizeof(WCHAR)))
1323     {
1324         /* default to english */
1325         wcscpy(szName, L"File Types");
1326         ERR("Failed to load localized string!\n");
1327     }
1328 
1329     col.iSubItem   = 1;
1330     col.cx         = clientRect.right - clientRect.left - columnSize;
1331     col.cchTextMax = wcslen(szName);
1332     col.pszText    = szName;
1333     (void)SendMessageW(hDlgCtrl, LVM_INSERTCOLUMNW, 1, (LPARAM)&col);
1334 
1335     /* set full select style */
1336     dwStyle = (DWORD) SendMessage(hDlgCtrl, LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
1337     dwStyle = dwStyle | LVS_EX_FULLROWSELECT;
1338     SendMessage(hDlgCtrl, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, dwStyle);
1339 }
1340 
1341 INT
1342 FindItem(HWND hDlgCtrl, WCHAR * ItemName)
1343 {
1344     LVFINDINFOW findInfo;
1345     ZeroMemory(&findInfo, sizeof(LVFINDINFOW));
1346 
1347     findInfo.flags = LVFI_STRING;
1348     findInfo.psz = ItemName;
1349     return ListView_FindItem(hDlgCtrl, 0, &findInfo);
1350 }
1351 
1352 static
1353 VOID
1354 InsertFileType(HWND hDlgCtrl, WCHAR * szName, PINT iItem, WCHAR * szFile)
1355 {
1356     PFOLDER_FILE_TYPE_ENTRY Entry;
1357     HKEY hKey;
1358     LVITEMW lvItem;
1359     DWORD dwSize;
1360     DWORD dwType;
1361 
1362     if (szName[0] != L'.')
1363     {
1364         /* FIXME handle URL protocol handlers */
1365         return;
1366     }
1367 
1368     /* allocate file type entry */
1369     Entry = (PFOLDER_FILE_TYPE_ENTRY)HeapAlloc(GetProcessHeap(), 0, sizeof(FOLDER_FILE_TYPE_ENTRY));
1370 
1371     if (!Entry)
1372         return;
1373 
1374     /* open key */
1375     if (RegOpenKeyExW(HKEY_CLASSES_ROOT, szName, 0, KEY_READ, &hKey) != ERROR_SUCCESS)
1376     {
1377         HeapFree(GetProcessHeap(), 0, Entry);
1378         return;
1379     }
1380 
1381     /* FIXME check for duplicates */
1382 
1383     /* query for the default key */
1384     dwSize = sizeof(Entry->ClassKey);
1385     if (RegQueryValueExW(hKey, NULL, NULL, NULL, (LPBYTE)Entry->ClassKey, &dwSize) != ERROR_SUCCESS)
1386     {
1387         /* no link available */
1388         Entry->ClassKey[0] = 0;
1389     }
1390 
1391     if (Entry->ClassKey[0])
1392     {
1393         HKEY hTemp;
1394         /* try open linked key */
1395         if (RegOpenKeyExW(HKEY_CLASSES_ROOT, Entry->ClassKey, 0, KEY_READ, &hTemp) == ERROR_SUCCESS)
1396         {
1397             /* use linked key */
1398             RegCloseKey(hKey);
1399             hKey = hTemp;
1400         }
1401     }
1402 
1403     /* read friendly type name */
1404     if (RegLoadMUIStringW(hKey, L"FriendlyTypeName", Entry->FileDescription, sizeof(Entry->FileDescription), NULL, 0, NULL) != ERROR_SUCCESS)
1405     {
1406         /* read file description */
1407         dwSize = sizeof(Entry->FileDescription);
1408         Entry->FileDescription[0] = 0;
1409 
1410         /* read default key */
1411         RegQueryValueExW(hKey, NULL, NULL, NULL, (LPBYTE)Entry->FileDescription, &dwSize);
1412     }
1413 
1414     /* Read the EditFlags value */
1415     Entry->EditFlags = 0;
1416     if (!RegQueryValueExW(hKey, L"EditFlags", NULL, &dwType, NULL, &dwSize))
1417     {
1418         if ((dwType == REG_DWORD || dwType == REG_BINARY) && dwSize == sizeof(DWORD))
1419             RegQueryValueExW(hKey, L"EditFlags", NULL, NULL, (LPBYTE)&Entry->EditFlags, &dwSize);
1420     }
1421 
1422     /* close key */
1423     RegCloseKey(hKey);
1424 
1425     /* Do not add excluded entries */
1426     if (Entry->EditFlags & 0x00000001) //FTA_Exclude
1427     {
1428         HeapFree(GetProcessHeap(), 0, Entry);
1429         return;
1430     }
1431 
1432     /* convert extension to upper case */
1433     wcscpy(Entry->FileExtension, szName);
1434     _wcsupr(Entry->FileExtension);
1435 
1436     if (!Entry->FileDescription[0])
1437     {
1438         /* construct default 'FileExtensionFile' by formatting the uppercase extension
1439            with IDS_FILE_EXT_TYPE, outputting something like a l18n 'INI File' */
1440 
1441         StringCchPrintf(Entry->FileDescription, _countof(Entry->FileDescription), szFile, &Entry->FileExtension[1]);
1442     }
1443 
1444     ZeroMemory(&lvItem, sizeof(LVITEMW));
1445     lvItem.mask = LVIF_TEXT | LVIF_PARAM;
1446     lvItem.iSubItem = 0;
1447     lvItem.pszText = &Entry->FileExtension[1];
1448     lvItem.iItem = *iItem;
1449     lvItem.lParam = (LPARAM)Entry;
1450     (void)SendMessageW(hDlgCtrl, LVM_INSERTITEMW, 0, (LPARAM)&lvItem);
1451 
1452     ZeroMemory(&lvItem, sizeof(LVITEMW));
1453     lvItem.mask = LVIF_TEXT;
1454     lvItem.pszText = Entry->FileDescription;
1455     lvItem.iItem = *iItem;
1456     lvItem.iSubItem = 1;
1457     ListView_SetItem(hDlgCtrl, &lvItem);
1458 
1459     (*iItem)++;
1460 }
1461 
1462 static
1463 int
1464 CALLBACK
1465 ListViewCompareProc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
1466 {
1467     PFOLDER_FILE_TYPE_ENTRY Entry1, Entry2;
1468     int x;
1469 
1470     Entry1 = (PFOLDER_FILE_TYPE_ENTRY)lParam1;
1471     Entry2 = (PFOLDER_FILE_TYPE_ENTRY)lParam2;
1472 
1473     x = wcsicmp(Entry1->FileExtension, Entry2->FileExtension);
1474     if (x != 0)
1475         return x;
1476 
1477     return wcsicmp(Entry1->FileDescription, Entry2->FileDescription);
1478 }
1479 
1480 static
1481 PFOLDER_FILE_TYPE_ENTRY
1482 InitializeFileTypesListCtrl(HWND hwndDlg)
1483 {
1484     HWND hDlgCtrl;
1485     DWORD dwIndex = 0;
1486     WCHAR szName[50];
1487     WCHAR szFile[100];
1488     DWORD dwName;
1489     LVITEMW lvItem;
1490     INT iItem = 0;
1491 
1492     hDlgCtrl = GetDlgItem(hwndDlg, 14000);
1493     InitializeFileTypesListCtrlColumns(hDlgCtrl);
1494 
1495     szFile[0] = 0;
1496     if (!LoadStringW(shell32_hInstance, IDS_FILE_EXT_TYPE, szFile, _countof(szFile)))
1497     {
1498         /* default to english */
1499         wcscpy(szFile, L"%s File");
1500     }
1501     szFile[(_countof(szFile)) - 1] = 0;
1502 
1503     dwName = _countof(szName);
1504 
1505     while (RegEnumKeyExW(HKEY_CLASSES_ROOT, dwIndex++, szName, &dwName, NULL, NULL, NULL, NULL) == ERROR_SUCCESS)
1506     {
1507         InsertFileType(hDlgCtrl, szName, &iItem, szFile);
1508         dwName = _countof(szName);
1509     }
1510 
1511     /* Leave if the list is empty */
1512     if (iItem == 0)
1513         return NULL;
1514 
1515     /* sort list */
1516     ListView_SortItems(hDlgCtrl, ListViewCompareProc, NULL);
1517 
1518     /* select first item */
1519     ZeroMemory(&lvItem, sizeof(LVITEMW));
1520     lvItem.mask = LVIF_STATE;
1521     lvItem.stateMask = (UINT)-1;
1522     lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
1523     lvItem.iItem = 0;
1524     ListView_SetItem(hDlgCtrl, &lvItem);
1525 
1526     lvItem.mask = LVIF_PARAM;
1527     ListView_GetItem(hDlgCtrl, &lvItem);
1528 
1529     return (PFOLDER_FILE_TYPE_ENTRY)lvItem.lParam;
1530 }
1531 
1532 static
1533 PFOLDER_FILE_TYPE_ENTRY
1534 FindSelectedItem(
1535     HWND hDlgCtrl)
1536 {
1537     UINT Count, Index;
1538     LVITEMW lvItem;
1539 
1540     Count = ListView_GetItemCount(hDlgCtrl);
1541 
1542     for (Index = 0; Index < Count; Index++)
1543     {
1544         ZeroMemory(&lvItem, sizeof(LVITEM));
1545         lvItem.mask = LVIF_PARAM | LVIF_STATE;
1546         lvItem.iItem = Index;
1547         lvItem.stateMask = (UINT) - 1;
1548 
1549         if (ListView_GetItem(hDlgCtrl, &lvItem))
1550         {
1551             if (lvItem.state & LVIS_SELECTED)
1552                 return (PFOLDER_FILE_TYPE_ENTRY)lvItem.lParam;
1553         }
1554     }
1555 
1556     return NULL;
1557 }
1558 
1559 INT_PTR
1560 CALLBACK
1561 FolderOptionsFileTypesDlg(
1562     HWND hwndDlg,
1563     UINT uMsg,
1564     WPARAM wParam,
1565     LPARAM lParam)
1566 {
1567     LPNMLISTVIEW lppl;
1568     LVITEMW lvItem;
1569     WCHAR Buffer[255], FormatBuffer[255];
1570     PFOLDER_FILE_TYPE_ENTRY pItem;
1571     OPENASINFO Info;
1572 
1573     switch(uMsg)
1574     {
1575         case WM_INITDIALOG:
1576             pItem = InitializeFileTypesListCtrl(hwndDlg);
1577 
1578             /* Disable the Delete button if the listview is empty or
1579                the selected item should not be deleted by the user */
1580             if (pItem == NULL || (pItem->EditFlags & 0x00000010)) // FTA_NoRemove
1581                 EnableWindow(GetDlgItem(hwndDlg, 14002), FALSE);
1582             return TRUE;
1583 
1584         case WM_COMMAND:
1585             switch(LOWORD(wParam))
1586             {
1587                 case 14006:
1588                     pItem = FindSelectedItem(GetDlgItem(hwndDlg, 14000));
1589                     if (pItem)
1590                     {
1591                         Info.oaifInFlags = OAIF_ALLOW_REGISTRATION | OAIF_REGISTER_EXT;
1592                         Info.pcszClass = pItem->FileExtension;
1593                         SHOpenWithDialog(hwndDlg, &Info);
1594                     }
1595                     break;
1596             }
1597             break;
1598 
1599         case WM_NOTIFY:
1600             lppl = (LPNMLISTVIEW) lParam;
1601 
1602             if (lppl->hdr.code == LVN_ITEMCHANGING)
1603             {
1604                 ZeroMemory(&lvItem, sizeof(LVITEM));
1605                 lvItem.mask = LVIF_PARAM;
1606                 lvItem.iItem = lppl->iItem;
1607                 if (!SendMessageW(lppl->hdr.hwndFrom, LVM_GETITEMW, 0, (LPARAM)&lvItem))
1608                     return TRUE;
1609 
1610                 pItem = (PFOLDER_FILE_TYPE_ENTRY)lvItem.lParam;
1611                 if (!pItem)
1612                     return TRUE;
1613 
1614                 if (!(lppl->uOldState & LVIS_FOCUSED) && (lppl->uNewState & LVIS_FOCUSED))
1615                 {
1616                     /* new focused item */
1617                     if (!LoadStringW(shell32_hInstance, IDS_FILE_DETAILS, FormatBuffer, sizeof(FormatBuffer) / sizeof(WCHAR)))
1618                     {
1619                         /* use default english format string */
1620                         wcscpy(FormatBuffer, L"Details for '%s' extension");
1621                     }
1622 
1623                     /* format buffer */
1624                     swprintf(Buffer, FormatBuffer, &pItem->FileExtension[1]);
1625                     /* update dialog */
1626                     SetDlgItemTextW(hwndDlg, 14003, Buffer);
1627 
1628                     if (!LoadStringW(shell32_hInstance, IDS_FILE_DETAILSADV, FormatBuffer, sizeof(FormatBuffer) / sizeof(WCHAR)))
1629                     {
1630                         /* use default english format string */
1631                         wcscpy(FormatBuffer, L"Files with extension '%s' are of type '%s'. To change settings that affect all '%s' files, click Advanced.");
1632                     }
1633                     /* format buffer */
1634                     swprintf(Buffer, FormatBuffer, &pItem->FileExtension[1], &pItem->FileDescription[0], &pItem->FileDescription[0]);
1635                     /* update dialog */
1636                     SetDlgItemTextW(hwndDlg, 14007, Buffer);
1637 
1638                     /* Enable the Delete button */
1639                     if (pItem->EditFlags & 0x00000010) // FTA_NoRemove
1640                         EnableWindow(GetDlgItem(hwndDlg, 14002), FALSE);
1641                     else
1642                         EnableWindow(GetDlgItem(hwndDlg, 14002), TRUE);
1643                 }
1644             }
1645             else if (lppl->hdr.code == PSN_SETACTIVE)
1646             {
1647                 /* On page activation, set the focus to the listview */
1648                 SetFocus(GetDlgItem(hwndDlg, 14000));
1649             }
1650             break;
1651     }
1652 
1653     return FALSE;
1654 }
1655 
1656 static
1657 VOID
1658 ShowFolderOptionsDialog(HWND hWnd, HINSTANCE hInst)
1659 {
1660     PROPSHEETHEADERW pinfo;
1661     HPROPSHEETPAGE hppages[3];
1662     HPROPSHEETPAGE hpage;
1663     UINT num_pages = 0;
1664     WCHAR szOptions[100];
1665 
1666     hpage = SH_CreatePropertySheetPage(IDD_FOLDER_OPTIONS_GENERAL, FolderOptionsGeneralDlg, 0, NULL);
1667     if (hpage)
1668         hppages[num_pages++] = hpage;
1669 
1670     hpage = SH_CreatePropertySheetPage(IDD_FOLDER_OPTIONS_VIEW, FolderOptionsViewDlg, 0, NULL);
1671     if (hpage)
1672         hppages[num_pages++] = hpage;
1673 
1674     hpage = SH_CreatePropertySheetPage(IDD_FOLDER_OPTIONS_FILETYPES, FolderOptionsFileTypesDlg, 0, NULL);
1675     if (hpage)
1676         hppages[num_pages++] = hpage;
1677 
1678     szOptions[0] = L'\0';
1679     LoadStringW(shell32_hInstance, IDS_FOLDER_OPTIONS, szOptions, sizeof(szOptions) / sizeof(WCHAR));
1680     szOptions[(sizeof(szOptions)/sizeof(WCHAR))-1] = L'\0';
1681 
1682     memset(&pinfo, 0x0, sizeof(PROPSHEETHEADERW));
1683     pinfo.dwSize = sizeof(PROPSHEETHEADERW);
1684     pinfo.dwFlags = PSH_NOCONTEXTHELP;
1685     pinfo.nPages = num_pages;
1686     pinfo.phpage = hppages;
1687     pinfo.pszCaption = szOptions;
1688 
1689     PropertySheetW(&pinfo);
1690 }
1691 
1692 static
1693 VOID
1694 Options_RunDLLCommon(HWND hWnd, HINSTANCE hInst, int fOptions, DWORD nCmdShow)
1695 {
1696     switch(fOptions)
1697     {
1698         case 0:
1699             ShowFolderOptionsDialog(hWnd, hInst);
1700             break;
1701         case 1:
1702             // show taskbar options dialog
1703             FIXME("notify explorer to show taskbar options dialog");
1704             //PostMessage(GetShellWindow(), WM_USER+22, fOptions, 0);
1705             break;
1706         default:
1707             FIXME("unrecognized options id %d\n", fOptions);
1708     }
1709 }
1710 
1711 /*************************************************************************
1712  *              Options_RunDLL (SHELL32.@)
1713  */
1714 EXTERN_C VOID WINAPI Options_RunDLL(HWND hWnd, HINSTANCE hInst, LPCSTR cmd, DWORD nCmdShow)
1715 {
1716     Options_RunDLLCommon(hWnd, hInst, StrToIntA(cmd), nCmdShow);
1717 }
1718 
1719 /*************************************************************************
1720  *              Options_RunDLLA (SHELL32.@)
1721  */
1722 EXTERN_C VOID WINAPI Options_RunDLLA(HWND hWnd, HINSTANCE hInst, LPCSTR cmd, DWORD nCmdShow)
1723 {
1724     Options_RunDLLCommon(hWnd, hInst, StrToIntA(cmd), nCmdShow);
1725 }
1726 
1727 /*************************************************************************
1728  *              Options_RunDLLW (SHELL32.@)
1729  */
1730 EXTERN_C VOID WINAPI Options_RunDLLW(HWND hWnd, HINSTANCE hInst, LPCWSTR cmd, DWORD nCmdShow)
1731 {
1732     Options_RunDLLCommon(hWnd, hInst, StrToIntW(cmd), nCmdShow);
1733 }
1734