1 /*
2 * PROJECT: ReactOS Explorer
3 * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later)
4 * PURPOSE: "Customize Start Menu" dialog
5 * COPYRIGHT: Copyright 2006-2007 Thomas Weidenmueller <w3seek@reactos.org>
6 * Copyright 2015 Robert Naumann <gonzomdx@gmail.com>
7 * Copyright 2024 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
8 */
9
10 #include "precomp.h"
11
12 // TreeView checkbox state indexes (Use with INDEXTOSTATEIMAGEMASK macro)
13 #define I_UNCHECKED 1
14 #define I_CHECKED 2
15
16 // TODO: Windows Explorer appears to be calling NewLinkHere / ConfigStartMenu directly for both items.
OnAddStartMenuItems(HWND hDlg)17 VOID OnAddStartMenuItems(HWND hDlg)
18 {
19 WCHAR szPath[MAX_PATH];
20
21 if (SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_PROGRAMS, NULL, 0, szPath)))
22 {
23 WCHAR szCommand[MAX_PATH] = L"appwiz.cpl,NewLinkHere ";
24 if (SUCCEEDED(StringCchCatW(szCommand, _countof(szCommand), szPath)))
25 ShellExecuteW(hDlg, L"open", L"rundll32.exe", szCommand, NULL, SW_SHOWNORMAL);
26 }
27 }
28
OnRemoveStartmenuItems(HWND hDlg)29 VOID OnRemoveStartmenuItems(HWND hDlg)
30 {
31 ShellExecuteW(hDlg, L"open", L"rundll32.exe", L"appwiz.cpl,ConfigStartMenu", NULL, SW_SHOWNORMAL);
32 }
33
OnAdvancedStartMenuItems()34 VOID OnAdvancedStartMenuItems()
35 {
36 WCHAR szPath[MAX_PATH];
37
38 if (SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_STARTMENU, NULL, 0, szPath)))
39 {
40 ShellExecuteW(NULL, L"explore", szPath, NULL, NULL, SW_SHOWNORMAL);
41 }
42 }
43
RecentHasShortcut(HWND hwnd)44 static BOOL RecentHasShortcut(HWND hwnd)
45 {
46 WCHAR szPath[MAX_PATH];
47 if (FAILED(SHGetFolderPathW(hwnd, CSIDL_RECENT | CSIDL_FLAG_CREATE, NULL, 0, szPath)))
48 return FALSE;
49
50 // Find shortcut files in Recent
51 WIN32_FIND_DATAW find;
52 PathAppendW(szPath, L"*.lnk");
53 HANDLE hFind = FindFirstFileW(szPath, &find);
54 if (hFind == INVALID_HANDLE_VALUE)
55 return FALSE;
56
57 FindClose(hFind);
58 return TRUE;
59 }
60
OnClearRecentItems(HWND hwnd)61 static VOID OnClearRecentItems(HWND hwnd)
62 {
63 SHAddToRecentDocs(SHARD_PIDL, NULL);
64 EnableWindow(GetDlgItem(hwnd, IDC_CLASSICSTART_CLEAR), RecentHasShortcut(hwnd));
65 }
66
67 struct CUSTOM_ENTRY;
68
69 typedef BOOL (CALLBACK *FN_CUSTOM_GET)(const CUSTOM_ENTRY *entry);
70 typedef VOID (CALLBACK *FN_CUSTOM_SET)(const CUSTOM_ENTRY *entry, BOOL bValue);
71
72 struct CUSTOM_ENTRY
73 {
74 LPARAM id;
75 LPCWSTR name;
76 BOOL bDefaultValue;
77 FN_CUSTOM_GET fnGetValue;
78 FN_CUSTOM_SET fnSetValue;
79 RESTRICTIONS policy1, policy2;
80 };
81
CustomGetAdvanced(const CUSTOM_ENTRY * entry)82 static BOOL CALLBACK CustomGetAdvanced(const CUSTOM_ENTRY *entry)
83 {
84 return GetAdvancedBool(entry->name, entry->bDefaultValue);
85 }
86
CustomSetAdvanced(const CUSTOM_ENTRY * entry,BOOL bValue)87 static VOID CALLBACK CustomSetAdvanced(const CUSTOM_ENTRY *entry, BOOL bValue)
88 {
89 SetAdvancedDword(entry->name, bValue);
90 }
91
CustomGetSmallStartMenu(const CUSTOM_ENTRY * entry)92 static BOOL CALLBACK CustomGetSmallStartMenu(const CUSTOM_ENTRY *entry)
93 {
94 return g_TaskbarSettings.sr.SmSmallIcons;
95 }
96
CustomSetSmallStartMenu(const CUSTOM_ENTRY * entry,BOOL bValue)97 static VOID CALLBACK CustomSetSmallStartMenu(const CUSTOM_ENTRY *entry, BOOL bValue)
98 {
99 g_TaskbarSettings.sr.SmSmallIcons = bValue;
100 }
101
102 static const CUSTOM_ENTRY s_CustomEntries[] =
103 {
104 {
105 IDS_ADVANCED_DISPLAY_ADMINTOOLS, L"StartMenuAdminTools", TRUE,
106 CustomGetAdvanced, CustomSetAdvanced,
107 },
108 {
109 IDS_ADVANCED_DISPLAY_FAVORITES, L"StartMenuFavorites", FALSE,
110 CustomGetAdvanced, CustomSetAdvanced,
111 REST_NOFAVORITESMENU,
112 },
113 {
114 IDS_ADVANCED_DISPLAY_LOG_OFF, L"StartMenuLogoff", FALSE,
115 CustomGetAdvanced, CustomSetAdvanced,
116 REST_STARTMENULOGOFF,
117 },
118 {
119 IDS_ADVANCED_DISPLAY_RUN, L"StartMenuRun", TRUE,
120 CustomGetAdvanced, CustomSetAdvanced,
121 REST_NORUN,
122 },
123 {
124 IDS_ADVANCED_EXPAND_MY_DOCUMENTS, L"CascadeMyDocuments", FALSE,
125 CustomGetAdvanced, CustomSetAdvanced,
126 REST_NOSMMYDOCS,
127 },
128 {
129 IDS_ADVANCED_EXPAND_MY_PICTURES, L"CascadeMyPictures", FALSE,
130 CustomGetAdvanced, CustomSetAdvanced,
131 REST_NOSMMYPICS,
132 },
133 {
134 IDS_ADVANCED_EXPAND_CONTROL_PANEL, L"CascadeControlPanel", FALSE,
135 CustomGetAdvanced, CustomSetAdvanced,
136 REST_NOSETFOLDERS, REST_NOCONTROLPANEL,
137 },
138 {
139 IDS_ADVANCED_EXPAND_PRINTERS, L"CascadePrinters", FALSE,
140 CustomGetAdvanced, CustomSetAdvanced,
141 REST_NOSETFOLDERS,
142 },
143 {
144 IDS_ADVANCED_EXPAND_NET_CONNECTIONS, L"CascadeNetworkConnections", FALSE,
145 CustomGetAdvanced, CustomSetAdvanced,
146 REST_NOSETFOLDERS, REST_NONETWORKCONNECTIONS,
147 },
148 {
149 IDS_ADVANCED_SMALL_START_MENU, NULL, FALSE,
150 CustomGetSmallStartMenu, CustomSetSmallStartMenu,
151 },
152 };
153
AddCustomItem(HWND hTreeView,const CUSTOM_ENTRY * entry)154 static VOID AddCustomItem(HWND hTreeView, const CUSTOM_ENTRY *entry)
155 {
156 if (SHRestricted(entry->policy1) || SHRestricted(entry->policy2))
157 {
158 TRACE("%p: Restricted\n", entry->id);
159 return; // Restricted. Don't show
160 }
161
162 WCHAR szText[MAX_PATH];
163 LoadStringW(GetModuleHandleW(L"shell32.dll"), entry->id, szText, _countof(szText));
164
165 BOOL bChecked = entry->fnGetValue(entry);
166 TRACE("%p: %d\n", entry->id, bChecked);
167
168 TV_INSERTSTRUCT Insert = { TVI_ROOT, TVI_LAST, { TVIF_TEXT | TVIF_STATE | TVIF_PARAM } };
169 Insert.item.pszText = szText;
170 Insert.item.lParam = entry->id;
171 Insert.item.stateMask = TVIS_STATEIMAGEMASK;
172 Insert.item.state = INDEXTOSTATEIMAGEMASK(bChecked ? I_CHECKED : I_UNCHECKED);
173 TreeView_InsertItem(hTreeView, &Insert);
174 }
175
CustomizeClassic_OnInitDialog(HWND hwnd)176 static void CustomizeClassic_OnInitDialog(HWND hwnd)
177 {
178 EnableWindow(GetDlgItem(hwnd, IDC_CLASSICSTART_CLEAR), RecentHasShortcut(hwnd));
179
180 HWND hTreeView = GetDlgItem(hwnd, IDC_CLASSICSTART_SETTINGS);
181
182 DWORD_PTR style = GetWindowLongPtrW(hTreeView, GWL_STYLE);
183 SetWindowLongPtrW(hTreeView, GWL_STYLE, style | TVS_CHECKBOXES);
184
185 for (auto& entry : s_CustomEntries)
186 {
187 AddCustomItem(hTreeView, &entry);
188 }
189 }
190
CustomizeClassic_OnOK(HWND hwnd)191 static BOOL CustomizeClassic_OnOK(HWND hwnd)
192 {
193 HWND hTreeView = GetDlgItem(hwnd, IDC_CLASSICSTART_SETTINGS);
194
195 for (HTREEITEM hItem = TreeView_GetRoot(hTreeView);
196 hItem != NULL;
197 hItem = TreeView_GetNextVisible(hTreeView, hItem))
198 {
199 TV_ITEM item = { TVIF_PARAM | TVIF_STATE, hItem };
200 item.stateMask = TVIS_STATEIMAGEMASK;
201 TreeView_GetItem(hTreeView, &item);
202
203 BOOL bChecked = !!(item.state & INDEXTOSTATEIMAGEMASK(I_CHECKED));
204 for (auto& entry : s_CustomEntries)
205 {
206 if (SHRestricted(entry.policy1) || SHRestricted(entry.policy2))
207 continue;
208
209 if (item.lParam == entry.id)
210 {
211 TRACE("%p: %d\n", item.lParam, bChecked);
212 entry.fnSetValue(&entry, bChecked);
213 break;
214 }
215 }
216 }
217
218 SendMessageTimeoutW(HWND_BROADCAST, WM_SETTINGCHANGE, 0, (LPARAM)L"TraySettings",
219 SMTO_ABORTIFHUNG, 200, NULL);
220 return TRUE;
221 }
222
CustomizeClassicProc(HWND hwnd,UINT Message,WPARAM wParam,LPARAM lParam)223 INT_PTR CALLBACK CustomizeClassicProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
224 {
225 switch (Message)
226 {
227 case WM_INITDIALOG:
228 CustomizeClassic_OnInitDialog(hwnd);
229 return TRUE;
230 case WM_COMMAND:
231 switch (LOWORD(wParam))
232 {
233 case IDC_CLASSICSTART_ADD:
234 OnAddStartMenuItems(hwnd);
235 break;
236 case IDC_CLASSICSTART_REMOVE:
237 OnRemoveStartmenuItems(hwnd);
238 break;
239 case IDC_CLASSICSTART_ADVANCED:
240 OnAdvancedStartMenuItems();
241 break;
242 case IDC_CLASSICSTART_CLEAR:
243 OnClearRecentItems(hwnd);
244 break;
245 case IDOK:
246 if (CustomizeClassic_OnOK(hwnd))
247 EndDialog(hwnd, IDOK);
248 break;
249 case IDCANCEL:
250 EndDialog(hwnd, IDCANCEL);
251 break;
252 }
253 break;
254 default:
255 break;
256 }
257
258 return FALSE;
259 }
260
ShowCustomizeClassic(HINSTANCE hInst,HWND hExplorer)261 VOID ShowCustomizeClassic(HINSTANCE hInst, HWND hExplorer)
262 {
263 DialogBoxW(hInst, MAKEINTRESOURCEW(IDD_CLASSICSTART_CUSTOMIZE), hExplorer, CustomizeClassicProc);
264 }
265