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