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