Reactos
at master 308 lines 9.2 kB view raw
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. 17VOID 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 29VOID OnRemoveStartmenuItems(HWND hDlg) 30{ 31 ShellExecuteW(hDlg, L"open", L"rundll32.exe", L"appwiz.cpl,ConfigStartMenu", NULL, SW_SHOWNORMAL); 32} 33 34VOID 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 44static 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 61static const PCWSTR g_MruKeys[] = 62{ 63 L"Software\\Microsoft\\Internet Explorer\\TypedURLs", 64 L"Explorer\\RunMRU", 65 L"Explorer\\Comdlg32\\OpenSaveMRU", 66 L"Explorer\\Comdlg32\\LastVisitedMRU", 67}; 68 69static BOOL HandleMruData(BOOL Delete) 70{ 71 for (UINT i = 0; i < _countof(g_MruKeys); ++i) 72 { 73 WCHAR szKey[200]; 74 PCWSTR pszKey = g_MruKeys[i]; 75 if (*pszKey != 'S') // Keys not starting with S[oftware] are assumed to be relative to "SMWCV" 76 { 77 wsprintfW(szKey, L"%s\\%s", L"Software\\Microsoft\\Windows\\CurrentVersion", pszKey); 78 pszKey = szKey; 79 } 80 81 HKEY hKey; 82 if (Delete) 83 { 84 SHDeleteKeyW(HKEY_CURRENT_USER, pszKey); 85 } 86 else if (RegOpenKeyExW(HKEY_CURRENT_USER, pszKey, 0, KEY_WRITE, &hKey) == ERROR_SUCCESS) 87 { 88 RegCloseKey(hKey); 89 return TRUE; 90 } 91 } 92 return FALSE; 93} 94 95VOID ClearRecentAndMru() 96{ 97 SHAddToRecentDocs(SHARD_PIDL, NULL); 98 HandleMruData(TRUE); 99} 100 101static VOID InitializeClearButton(HWND hwnd) 102{ 103 HWND hWndClear = GetDlgItem(hwnd, IDC_CLASSICSTART_CLEAR); 104 BOOL bHasData = RecentHasShortcut(hwnd) || HandleMruData(FALSE); 105 if (!bHasData && hWndClear == GetFocus()) 106 SendMessage(hwnd, WM_NEXTDLGCTL, 0, FALSE); 107 EnableWindow(hWndClear, bHasData); 108} 109 110struct CUSTOM_ENTRY; 111 112typedef BOOL (CALLBACK *FN_CUSTOM_GET)(const CUSTOM_ENTRY *entry); 113typedef VOID (CALLBACK *FN_CUSTOM_SET)(const CUSTOM_ENTRY *entry, BOOL bValue); 114 115struct CUSTOM_ENTRY 116{ 117 LPARAM id; 118 LPCWSTR name; 119 BOOL bDefaultValue; 120 FN_CUSTOM_GET fnGetValue; 121 FN_CUSTOM_SET fnSetValue; 122 RESTRICTIONS policy1, policy2; 123}; 124 125static BOOL CALLBACK CustomGetAdvanced(const CUSTOM_ENTRY *entry) 126{ 127 return GetAdvancedBool(entry->name, entry->bDefaultValue); 128} 129 130static VOID CALLBACK CustomSetAdvanced(const CUSTOM_ENTRY *entry, BOOL bValue) 131{ 132 SetAdvancedDword(entry->name, bValue); 133} 134 135static BOOL CALLBACK CustomGetSmallStartMenu(const CUSTOM_ENTRY *entry) 136{ 137 return g_TaskbarSettings.sr.SmSmallIcons; 138} 139 140static VOID CALLBACK CustomSetSmallStartMenu(const CUSTOM_ENTRY *entry, BOOL bValue) 141{ 142 g_TaskbarSettings.sr.SmSmallIcons = bValue; 143} 144 145static const CUSTOM_ENTRY s_CustomEntries[] = 146{ 147 { 148 IDS_ADVANCED_DISPLAY_ADMINTOOLS, L"StartMenuAdminTools", TRUE, 149 CustomGetAdvanced, CustomSetAdvanced, 150 }, 151 { 152 IDS_ADVANCED_DISPLAY_FAVORITES, L"StartMenuFavorites", FALSE, 153 CustomGetAdvanced, CustomSetAdvanced, 154 REST_NOFAVORITESMENU, 155 }, 156 { 157 IDS_ADVANCED_DISPLAY_LOG_OFF, L"StartMenuLogoff", FALSE, 158 CustomGetAdvanced, CustomSetAdvanced, 159 REST_STARTMENULOGOFF, 160 }, 161 { 162 IDS_ADVANCED_DISPLAY_RUN, L"StartMenuRun", TRUE, 163 CustomGetAdvanced, CustomSetAdvanced, 164 REST_NORUN, 165 }, 166 { 167 IDS_ADVANCED_EXPAND_MY_DOCUMENTS, L"CascadeMyDocuments", FALSE, 168 CustomGetAdvanced, CustomSetAdvanced, 169 REST_NOSMMYDOCS, 170 }, 171 { 172 IDS_ADVANCED_EXPAND_MY_PICTURES, L"CascadeMyPictures", FALSE, 173 CustomGetAdvanced, CustomSetAdvanced, 174 REST_NOSMMYPICS, 175 }, 176 { 177 IDS_ADVANCED_EXPAND_CONTROL_PANEL, L"CascadeControlPanel", FALSE, 178 CustomGetAdvanced, CustomSetAdvanced, 179 REST_NOSETFOLDERS, REST_NOCONTROLPANEL, 180 }, 181 { 182 IDS_ADVANCED_EXPAND_PRINTERS, L"CascadePrinters", FALSE, 183 CustomGetAdvanced, CustomSetAdvanced, 184 REST_NOSETFOLDERS, 185 }, 186 { 187 IDS_ADVANCED_EXPAND_NET_CONNECTIONS, L"CascadeNetworkConnections", FALSE, 188 CustomGetAdvanced, CustomSetAdvanced, 189 REST_NOSETFOLDERS, REST_NONETWORKCONNECTIONS, 190 }, 191 { 192 IDS_ADVANCED_SMALL_START_MENU, NULL, FALSE, 193 CustomGetSmallStartMenu, CustomSetSmallStartMenu, 194 }, 195}; 196 197static VOID AddCustomItem(HWND hTreeView, const CUSTOM_ENTRY *entry) 198{ 199 if (SHRestricted(entry->policy1) || SHRestricted(entry->policy2)) 200 { 201 TRACE("%p: Restricted\n", entry->id); 202 return; // Restricted. Don't show 203 } 204 205 WCHAR szText[MAX_PATH]; 206 LoadStringW(GetModuleHandleW(L"shell32.dll"), entry->id, szText, _countof(szText)); 207 208 BOOL bChecked = entry->fnGetValue(entry); 209 TRACE("%p: %d\n", entry->id, bChecked); 210 211 TV_INSERTSTRUCT Insert = { TVI_ROOT, TVI_LAST, { TVIF_TEXT | TVIF_STATE | TVIF_PARAM } }; 212 Insert.item.pszText = szText; 213 Insert.item.lParam = entry->id; 214 Insert.item.stateMask = TVIS_STATEIMAGEMASK; 215 Insert.item.state = INDEXTOSTATEIMAGEMASK(bChecked ? I_CHECKED : I_UNCHECKED); 216 TreeView_InsertItem(hTreeView, &Insert); 217} 218 219static void CustomizeClassic_OnInitDialog(HWND hwnd) 220{ 221 InitializeClearButton(hwnd); 222 223 HWND hTreeView = GetDlgItem(hwnd, IDC_CLASSICSTART_SETTINGS); 224 225 DWORD_PTR style = GetWindowLongPtrW(hTreeView, GWL_STYLE); 226 SetWindowLongPtrW(hTreeView, GWL_STYLE, style | TVS_CHECKBOXES); 227 228 for (auto& entry : s_CustomEntries) 229 { 230 AddCustomItem(hTreeView, &entry); 231 } 232} 233 234static BOOL CustomizeClassic_OnOK(HWND hwnd) 235{ 236 HWND hTreeView = GetDlgItem(hwnd, IDC_CLASSICSTART_SETTINGS); 237 238 for (HTREEITEM hItem = TreeView_GetRoot(hTreeView); 239 hItem != NULL; 240 hItem = TreeView_GetNextVisible(hTreeView, hItem)) 241 { 242 TV_ITEM item = { TVIF_PARAM | TVIF_STATE, hItem }; 243 item.stateMask = TVIS_STATEIMAGEMASK; 244 TreeView_GetItem(hTreeView, &item); 245 246 BOOL bChecked = !!(item.state & INDEXTOSTATEIMAGEMASK(I_CHECKED)); 247 for (auto& entry : s_CustomEntries) 248 { 249 if (SHRestricted(entry.policy1) || SHRestricted(entry.policy2)) 250 continue; 251 252 if (item.lParam == entry.id) 253 { 254 TRACE("%p: %d\n", item.lParam, bChecked); 255 entry.fnSetValue(&entry, bChecked); 256 break; 257 } 258 } 259 } 260 261 SendMessageTimeoutW(HWND_BROADCAST, WM_SETTINGCHANGE, 0, (LPARAM)L"TraySettings", 262 SMTO_ABORTIFHUNG, 200, NULL); 263 return TRUE; 264} 265 266INT_PTR CALLBACK CustomizeClassicProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) 267{ 268 switch (Message) 269 { 270 case WM_INITDIALOG: 271 CustomizeClassic_OnInitDialog(hwnd); 272 return TRUE; 273 case WM_COMMAND: 274 switch (LOWORD(wParam)) 275 { 276 case IDC_CLASSICSTART_ADD: 277 OnAddStartMenuItems(hwnd); 278 break; 279 case IDC_CLASSICSTART_REMOVE: 280 OnRemoveStartmenuItems(hwnd); 281 break; 282 case IDC_CLASSICSTART_ADVANCED: 283 OnAdvancedStartMenuItems(); 284 break; 285 case IDC_CLASSICSTART_CLEAR: 286 ClearRecentAndMru(); 287 InitializeClearButton(hwnd); 288 break; 289 case IDOK: 290 if (CustomizeClassic_OnOK(hwnd)) 291 EndDialog(hwnd, IDOK); 292 break; 293 case IDCANCEL: 294 EndDialog(hwnd, IDCANCEL); 295 break; 296 } 297 break; 298 default: 299 break; 300 } 301 302 return FALSE; 303} 304 305VOID ShowCustomizeClassic(HINSTANCE hInst, HWND hExplorer) 306{ 307 DialogBoxW(hInst, MAKEINTRESOURCEW(IDD_CLASSICSTART_CUSTOMIZE), hExplorer, CustomizeClassicProc); 308}