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