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