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