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. 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 29 VOID OnRemoveStartmenuItems(HWND hDlg) 30 { 31 ShellExecuteW(hDlg, L"open", L"rundll32.exe", L"appwiz.cpl,ConfigStartMenu", NULL, SW_SHOWNORMAL); 32 } 33 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 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 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 82 static BOOL CALLBACK CustomGetAdvanced(const CUSTOM_ENTRY *entry) 83 { 84 return GetAdvancedBool(entry->name, entry->bDefaultValue); 85 } 86 87 static VOID CALLBACK CustomSetAdvanced(const CUSTOM_ENTRY *entry, BOOL bValue) 88 { 89 SetAdvancedDword(entry->name, bValue); 90 } 91 92 static BOOL CALLBACK CustomGetSmallStartMenu(const CUSTOM_ENTRY *entry) 93 { 94 return g_TaskbarSettings.sr.SmSmallIcons; 95 } 96 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 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 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 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 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 261 VOID ShowCustomizeClassic(HINSTANCE hInst, HWND hExplorer) 262 { 263 DialogBoxW(hInst, MAKEINTRESOURCEW(IDD_CLASSICSTART_CUSTOMIZE), hExplorer, CustomizeClassicProc); 264 } 265