1 /* 2 * Regedit child window 3 * 4 * Copyright (C) 2002 Robert Dickenson <robd@reactos.org> 5 * Copyright (C) 2024 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com> 6 * 7 * This library is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public 9 * License as published by the Free Software Foundation; either 10 * version 2.1 of the License, or (at your option) any later version. 11 * 12 * This library is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with this library; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20 */ 21 22 #include "regedit.h" 23 #include <shldisp.h> 24 #include <shlguid.h> 25 26 ChildWnd* g_pChildWnd; 27 static int last_split; 28 HBITMAP SizingPattern = 0; 29 HBRUSH SizingBrush = 0; 30 WCHAR Suggestions[256]; 31 32 static HRESULT WINAPI DummyEnumStringsQI(LPVOID This, REFIID riid, void**ppv) 33 { 34 if (ppv) 35 *ppv = NULL; 36 if (IsEqualIID(riid, &IID_IEnumString) || IsEqualIID(riid, &IID_IUnknown)) 37 { 38 *ppv = This; 39 return S_OK; 40 } 41 return E_NOINTERFACE; 42 } 43 44 static ULONG WINAPI DummyEnumStringsAddRefRelease(LPVOID This) 45 { 46 return 1; 47 } 48 49 static HRESULT WINAPI DummyEnumStringsNext(LPVOID This, ULONG celt, LPWSTR *parr, ULONG *pceltFetched) 50 { 51 if (pceltFetched) 52 *pceltFetched = 0; 53 return S_FALSE; 54 } 55 56 static HRESULT WINAPI DummyEnumStringsSkip(LPVOID This, ULONG celt) 57 { 58 return S_OK; 59 } 60 61 static HRESULT WINAPI DummyEnumStringsReset(LPVOID This) 62 { 63 return S_OK; 64 } 65 66 static HRESULT WINAPI DummyEnumStringsClone(LPVOID This, void**ppv) 67 { 68 return E_NOTIMPL; 69 } 70 71 struct DummyEnumStringsVtbl { 72 LPVOID QI, AddRef, Release, Next, Skip, Reset, Clone; 73 } g_DummyEnumStringsVtbl = { 74 &DummyEnumStringsQI, 75 &DummyEnumStringsAddRefRelease, 76 &DummyEnumStringsAddRefRelease, 77 &DummyEnumStringsNext, 78 &DummyEnumStringsSkip, 79 &DummyEnumStringsReset, 80 &DummyEnumStringsClone 81 }; 82 83 struct DummyEnumStrings { 84 struct DummyEnumStringsVtbl *lpVtbl; 85 } g_DummyEnumStrings = { 86 &g_DummyEnumStringsVtbl 87 }; 88 89 extern LPCWSTR get_root_key_name(HKEY hRootKey) 90 { 91 if (hRootKey == HKEY_CLASSES_ROOT) return L"HKEY_CLASSES_ROOT"; 92 if (hRootKey == HKEY_CURRENT_USER) return L"HKEY_CURRENT_USER"; 93 if (hRootKey == HKEY_LOCAL_MACHINE) return L"HKEY_LOCAL_MACHINE"; 94 if (hRootKey == HKEY_USERS) return L"HKEY_USERS"; 95 if (hRootKey == HKEY_CURRENT_CONFIG) return L"HKEY_CURRENT_CONFIG"; 96 if (hRootKey == HKEY_DYN_DATA) return L"HKEY_DYN_DATA"; 97 98 return L"UNKNOWN HKEY, PLEASE REPORT"; 99 } 100 101 static INT ClampSplitBarX(HWND hWnd, INT x) 102 { 103 RECT rc; 104 GetClientRect(hWnd, &rc); 105 return min(max(x, SPLIT_MIN), rc.right - SPLIT_MIN); 106 } 107 108 extern void ResizeWnd(int cx, int cy) 109 { 110 HDWP hdwp = BeginDeferWindowPos(4); 111 RECT rt, rs, rb; 112 const int nButtonWidth = 44; 113 const int nButtonHeight = 22; 114 int cyEdge = GetSystemMetrics(SM_CYEDGE); 115 const UINT uFlags = SWP_NOZORDER | SWP_NOACTIVATE; 116 SetRect(&rt, 0, 0, cx, cy); 117 cy = 0; 118 if (hStatusBar != NULL) 119 { 120 GetWindowRect(hStatusBar, &rs); 121 cy = rs.bottom - rs.top; 122 } 123 GetWindowRect(g_pChildWnd->hAddressBtnWnd, &rb); 124 125 g_pChildWnd->nSplitPos = ClampSplitBarX(g_pChildWnd->hWnd, g_pChildWnd->nSplitPos); 126 127 cx = g_pChildWnd->nSplitPos + SPLIT_WIDTH / 2; 128 if (hdwp) 129 hdwp = DeferWindowPos(hdwp, g_pChildWnd->hAddressBarWnd, NULL, 130 rt.left, rt.top, 131 rt.right - rt.left - nButtonWidth, nButtonHeight, 132 uFlags); 133 if (hdwp) 134 hdwp = DeferWindowPos(hdwp, g_pChildWnd->hAddressBtnWnd, NULL, 135 rt.right - nButtonWidth, rt.top, 136 nButtonWidth, nButtonHeight, 137 uFlags); 138 if (hdwp) 139 hdwp = DeferWindowPos(hdwp, g_pChildWnd->hTreeWnd, NULL, 140 rt.left, 141 rt.top + nButtonHeight + cyEdge, 142 g_pChildWnd->nSplitPos - SPLIT_WIDTH/2 - rt.left, 143 rt.bottom - rt.top - cy - 2 * cyEdge, 144 uFlags); 145 if (hdwp) 146 hdwp = DeferWindowPos(hdwp, g_pChildWnd->hListWnd, NULL, 147 rt.left + cx, 148 rt.top + nButtonHeight + cyEdge, 149 rt.right - cx, 150 rt.bottom - rt.top - cy - 2 * cyEdge, 151 uFlags); 152 if (hdwp) 153 EndDeferWindowPos(hdwp); 154 } 155 156 /******************************************************************************* 157 * Local module support methods 158 */ 159 160 static void draw_splitbar(HWND hWnd, int x) 161 { 162 RECT rt; 163 HGDIOBJ OldObj; 164 HDC hdc = GetDC(hWnd); 165 166 if(!SizingPattern) 167 { 168 const DWORD Pattern[4] = {0x5555AAAA, 0x5555AAAA, 0x5555AAAA, 0x5555AAAA}; 169 SizingPattern = CreateBitmap(8, 8, 1, 1, Pattern); 170 } 171 if(!SizingBrush) 172 { 173 SizingBrush = CreatePatternBrush(SizingPattern); 174 } 175 GetClientRect(hWnd, &rt); 176 rt.left = x - SPLIT_WIDTH/2; 177 rt.right = x + SPLIT_WIDTH/2+1; 178 OldObj = SelectObject(hdc, SizingBrush); 179 PatBlt(hdc, rt.left, rt.top, rt.right - rt.left, rt.bottom - rt.top, PATINVERT); 180 SelectObject(hdc, OldObj); 181 ReleaseDC(hWnd, hdc); 182 } 183 184 /******************************************************************************* 185 * finish_splitbar [internal] 186 * 187 * make the splitbar invisible and resize the windows 188 * (helper for ChildWndProc) 189 */ 190 static void finish_splitbar(HWND hWnd, int x) 191 { 192 RECT rt; 193 194 draw_splitbar(hWnd, last_split); 195 last_split = -1; 196 GetClientRect(hWnd, &rt); 197 g_pChildWnd->nSplitPos = x; 198 ResizeWnd(rt.right, rt.bottom); 199 InvalidateRect(hWnd, &rt, FALSE); // HACK: See CORE-19576 200 ReleaseCapture(); 201 } 202 203 /******************************************************************************* 204 * 205 * Key suggestion 206 */ 207 208 #define MIN(a,b) ((a < b) ? (a) : (b)) 209 210 static void SuggestKeys(HKEY hRootKey, LPCWSTR pszKeyPath, LPWSTR pszSuggestions, 211 size_t iSuggestionsLength) 212 { 213 WCHAR szBuffer[256]; 214 WCHAR szLastFound[256]; 215 size_t i; 216 HKEY hOtherKey, hSubKey; 217 BOOL bFound; 218 const REGSAM regsam = KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE; 219 220 memset(pszSuggestions, 0, iSuggestionsLength * sizeof(*pszSuggestions)); 221 iSuggestionsLength--; 222 223 /* Are we a root key in HKEY_CLASSES_ROOT? */ 224 if ((hRootKey == HKEY_CLASSES_ROOT) && pszKeyPath[0] && !wcschr(pszKeyPath, L'\\')) 225 { 226 do 227 { 228 bFound = FALSE; 229 230 /* Check default key */ 231 if (QueryStringValue(hRootKey, pszKeyPath, NULL, 232 szBuffer, ARRAY_SIZE(szBuffer)) == ERROR_SUCCESS) 233 { 234 /* Sanity check this key; it cannot be empty, nor can it be a 235 * loop back */ 236 if ((szBuffer[0] != L'\0') && _wcsicmp(szBuffer, pszKeyPath)) 237 { 238 if (RegOpenKeyExW(hRootKey, szBuffer, 0, regsam, &hOtherKey) == ERROR_SUCCESS) 239 { 240 lstrcpynW(pszSuggestions, L"HKCR\\", (int) iSuggestionsLength); 241 i = wcslen(pszSuggestions); 242 pszSuggestions += i; 243 iSuggestionsLength -= i; 244 245 lstrcpynW(pszSuggestions, szBuffer, (int) iSuggestionsLength); 246 i = MIN(wcslen(pszSuggestions) + 1, iSuggestionsLength); 247 pszSuggestions += i; 248 iSuggestionsLength -= i; 249 RegCloseKey(hOtherKey); 250 251 bFound = TRUE; 252 StringCbCopyW(szLastFound, sizeof(szLastFound), szBuffer); 253 pszKeyPath = szLastFound; 254 } 255 } 256 } 257 } 258 while(bFound && (iSuggestionsLength > 0)); 259 260 /* Check CLSID key */ 261 if (RegOpenKeyExW(hRootKey, pszKeyPath, 0, regsam, &hSubKey) == ERROR_SUCCESS) 262 { 263 if (QueryStringValue(hSubKey, L"CLSID", NULL, szBuffer, 264 ARRAY_SIZE(szBuffer)) == ERROR_SUCCESS) 265 { 266 lstrcpynW(pszSuggestions, L"HKCR\\CLSID\\", (int)iSuggestionsLength); 267 i = wcslen(pszSuggestions); 268 pszSuggestions += i; 269 iSuggestionsLength -= i; 270 271 lstrcpynW(pszSuggestions, szBuffer, (int)iSuggestionsLength); 272 i = MIN(wcslen(pszSuggestions) + 1, iSuggestionsLength); 273 pszSuggestions += i; 274 iSuggestionsLength -= i; 275 } 276 RegCloseKey(hSubKey); 277 } 278 } 279 else if ((hRootKey == HKEY_CURRENT_USER || hRootKey == HKEY_LOCAL_MACHINE) && *pszKeyPath) 280 { 281 LPCWSTR rootstr = hRootKey == HKEY_CURRENT_USER ? L"HKLM" : L"HKCU"; 282 hOtherKey = hRootKey == HKEY_CURRENT_USER ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; 283 if (RegOpenKeyExW(hOtherKey, pszKeyPath, 0, regsam, &hSubKey) == ERROR_SUCCESS) 284 { 285 int cch; 286 RegCloseKey(hSubKey); 287 cch = _snwprintf(pszSuggestions, iSuggestionsLength, L"%s\\%s", rootstr, pszKeyPath); 288 if (cch <= 0 || cch > iSuggestionsLength) 289 pszSuggestions[0] = UNICODE_NULL; 290 } 291 } 292 } 293 294 295 LRESULT CALLBACK AddressBarProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 296 { 297 WNDPROC oldwndproc; 298 static WCHAR s_szNode[256]; 299 oldwndproc = (WNDPROC)GetWindowLongPtr(hwnd, GWLP_USERDATA); 300 301 switch (uMsg) 302 { 303 case WM_KEYUP: 304 if (wParam == VK_RETURN) 305 { 306 GetWindowTextW(hwnd, s_szNode, ARRAY_SIZE(s_szNode)); 307 SelectNode(g_pChildWnd->hTreeWnd, s_szNode); 308 } 309 break; 310 default: 311 break; 312 } 313 return CallWindowProcW(oldwndproc, hwnd, uMsg, wParam, lParam); 314 } 315 316 VOID 317 UpdateAddress(HTREEITEM hItem, HKEY hRootKey, LPCWSTR pszPath, BOOL bSelectNone) 318 { 319 LPCWSTR keyPath, rootName; 320 LPWSTR fullPath; 321 DWORD cbFullPath; 322 323 /* Wipe the listview, the status bar and the address bar if the root key was selected */ 324 if (TreeView_GetParent(g_pChildWnd->hTreeWnd, hItem) == NULL) 325 { 326 ListView_DeleteAllItems(g_pChildWnd->hListWnd); 327 SendMessageW(hStatusBar, SB_SETTEXTW, 0, (LPARAM)NULL); 328 SendMessageW(g_pChildWnd->hAddressBarWnd, WM_SETTEXT, 0, (LPARAM)NULL); 329 return; 330 } 331 332 if (pszPath == NULL) 333 keyPath = GetItemPath(g_pChildWnd->hTreeWnd, hItem, &hRootKey); 334 else 335 keyPath = pszPath; 336 337 if (keyPath) 338 { 339 RefreshListView(g_pChildWnd->hListWnd, hRootKey, keyPath, bSelectNone); 340 rootName = get_root_key_name(hRootKey); 341 cbFullPath = (wcslen(rootName) + 1 + wcslen(keyPath) + 1) * sizeof(WCHAR); 342 fullPath = malloc(cbFullPath); 343 if (fullPath) 344 { 345 /* set (correct) the address bar text */ 346 if (keyPath[0] != UNICODE_NULL) 347 StringCbPrintfW(fullPath, cbFullPath, L"%s%s%s", rootName, 348 ((keyPath[0] == L'\\') ? L"" : L"\\"), keyPath); 349 else 350 StringCbCopyW(fullPath, cbFullPath, rootName); 351 352 SendMessageW(hStatusBar, SB_SETTEXTW, 0, (LPARAM)fullPath); 353 SendMessageW(g_pChildWnd->hAddressBarWnd, WM_SETTEXT, 0, (LPARAM)fullPath); 354 free(fullPath); 355 356 /* disable hive manipulation items temporarily (enable only if necessary) */ 357 EnableMenuItem(hMenuFrame, ID_REGISTRY_LOADHIVE, MF_BYCOMMAND | MF_GRAYED); 358 EnableMenuItem(hMenuFrame, ID_REGISTRY_UNLOADHIVE, MF_BYCOMMAND | MF_GRAYED); 359 /* compare the strings to see if we should enable/disable the "Load Hive" menus accordingly */ 360 if (_wcsicmp(rootName, L"HKEY_LOCAL_MACHINE") == 0 || 361 _wcsicmp(rootName, L"HKEY_USERS") == 0) 362 { 363 /* 364 * enable the unload menu item if at the root, otherwise 365 * enable the load menu item if there is no slash in 366 * keyPath (ie. immediate child selected) 367 */ 368 if (keyPath[0] == UNICODE_NULL) 369 EnableMenuItem(hMenuFrame, ID_REGISTRY_LOADHIVE, MF_BYCOMMAND | MF_ENABLED); 370 else if (!wcschr(keyPath, L'\\')) 371 EnableMenuItem(hMenuFrame, ID_REGISTRY_UNLOADHIVE, MF_BYCOMMAND | MF_ENABLED); 372 } 373 } 374 } 375 } 376 377 /******************************************************************************* 378 * 379 * FUNCTION: ChildWndProc(HWND, unsigned, WORD, LONG) 380 * 381 * PURPOSE: Processes messages for the child windows. 382 * 383 * WM_COMMAND - process the application menu 384 * WM_DESTROY - post a quit message and return 385 * 386 */ 387 LRESULT CALLBACK ChildWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) 388 { 389 BOOL Result; 390 RECT rc; 391 392 switch (message) 393 { 394 case WM_CREATE: 395 { 396 WNDPROC oldproc; 397 HFONT hFont; 398 WCHAR buffer[MAX_PATH]; 399 DWORD style; 400 IAutoComplete *pAutoComplete; 401 402 /* Load "My Computer" string */ 403 LoadStringW(hInst, IDS_MY_COMPUTER, buffer, ARRAY_SIZE(buffer)); 404 405 g_pChildWnd = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ChildWnd)); 406 if (!g_pChildWnd) return 0; 407 408 wcsncpy(g_pChildWnd->szPath, buffer, MAX_PATH); 409 g_pChildWnd->nSplitPos = 190; 410 g_pChildWnd->hWnd = hWnd; 411 412 style = WS_CHILD | WS_VISIBLE | WS_TABSTOP; 413 g_pChildWnd->hAddressBarWnd = CreateWindowExW(WS_EX_CLIENTEDGE, L"Edit", NULL, style, 414 CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 415 hWnd, (HMENU)0, hInst, 0); 416 417 style = WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_ICON | BS_CENTER | 418 BS_VCENTER | BS_FLAT | BS_DEFPUSHBUTTON; 419 g_pChildWnd->hAddressBtnWnd = CreateWindowExW(0, L"Button", L"\x00BB", style, 420 CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 421 hWnd, (HMENU)0, hInst, 0); 422 g_pChildWnd->hArrowIcon = (HICON)LoadImageW(hInst, MAKEINTRESOURCEW(IDI_ARROW), 423 IMAGE_ICON, 12, 12, 0); 424 SendMessageW(g_pChildWnd->hAddressBtnWnd, BM_SETIMAGE, IMAGE_ICON, (LPARAM)g_pChildWnd->hArrowIcon); 425 426 if (SUCCEEDED(CoCreateInstance(&CLSID_AutoComplete, NULL, CLSCTX_INPROC_SERVER, &IID_IAutoComplete, (void**)&pAutoComplete))) 427 { 428 IAutoComplete_Init(pAutoComplete, g_pChildWnd->hAddressBarWnd, (IUnknown*)&g_DummyEnumStrings, NULL, NULL); 429 IAutoComplete_Release(pAutoComplete); 430 } 431 432 GetClientRect(hWnd, &rc); 433 g_pChildWnd->hTreeWnd = CreateTreeView(hWnd, g_pChildWnd->szPath, (HMENU) TREE_WINDOW); 434 g_pChildWnd->hListWnd = CreateListView(hWnd, (HMENU) LIST_WINDOW, rc.right - g_pChildWnd->nSplitPos); 435 SetFocus(g_pChildWnd->hTreeWnd); 436 437 /* set the address bar and button font */ 438 if ((g_pChildWnd->hAddressBarWnd) && (g_pChildWnd->hAddressBtnWnd)) 439 { 440 hFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT); 441 SendMessageW(g_pChildWnd->hAddressBarWnd, 442 WM_SETFONT, 443 (WPARAM)hFont, 444 0); 445 SendMessageW(g_pChildWnd->hAddressBtnWnd, 446 WM_SETFONT, 447 (WPARAM)hFont, 448 0); 449 } 450 /* Subclass the AddressBar */ 451 oldproc = (WNDPROC)GetWindowLongPtr(g_pChildWnd->hAddressBarWnd, GWLP_WNDPROC); 452 SetWindowLongPtr(g_pChildWnd->hAddressBarWnd, GWLP_USERDATA, (DWORD_PTR)oldproc); 453 SetWindowLongPtr(g_pChildWnd->hAddressBarWnd, GWLP_WNDPROC, (DWORD_PTR)AddressBarProc); 454 break; 455 } 456 case WM_COMMAND: 457 if(HIWORD(wParam) == BN_CLICKED) 458 { 459 PostMessageW(g_pChildWnd->hAddressBarWnd, WM_KEYUP, VK_RETURN, 0); 460 } 461 break; //goto def; 462 case WM_SETCURSOR: 463 if (LOWORD(lParam) == HTCLIENT) 464 { 465 POINT pt; 466 GetCursorPos(&pt); 467 ScreenToClient(hWnd, &pt); 468 if (pt.x>=g_pChildWnd->nSplitPos-SPLIT_WIDTH/2 && pt.x<g_pChildWnd->nSplitPos+SPLIT_WIDTH/2+1) 469 { 470 SetCursor(LoadCursorW(0, IDC_SIZEWE)); 471 return TRUE; 472 } 473 } 474 goto def; 475 476 case WM_DESTROY: 477 DestroyListView(g_pChildWnd->hListWnd); 478 DestroyTreeView(g_pChildWnd->hTreeWnd); 479 DestroyMainMenu(); 480 DestroyIcon(g_pChildWnd->hArrowIcon); 481 HeapFree(GetProcessHeap(), 0, g_pChildWnd); 482 g_pChildWnd = NULL; 483 PostQuitMessage(0); 484 break; 485 486 case WM_LBUTTONDOWN: 487 { 488 INT x = (SHORT)LOWORD(lParam); 489 if (x >= g_pChildWnd->nSplitPos - SPLIT_WIDTH / 2 && 490 x < g_pChildWnd->nSplitPos + SPLIT_WIDTH / 2 + 1) 491 { 492 x = ClampSplitBarX(hWnd, x); 493 draw_splitbar(hWnd, x); 494 last_split = x; 495 SetCapture(hWnd); 496 } 497 break; 498 } 499 500 case WM_LBUTTONUP: 501 case WM_RBUTTONDOWN: 502 if (GetCapture() == hWnd) 503 { 504 INT x = (SHORT)LOWORD(lParam); 505 x = ClampSplitBarX(hWnd, x); 506 finish_splitbar(hWnd, x); 507 } 508 break; 509 510 case WM_CAPTURECHANGED: 511 if (GetCapture() == hWnd && last_split >= 0) 512 draw_splitbar(hWnd, last_split); 513 break; 514 515 case WM_KEYDOWN: 516 if (wParam == VK_ESCAPE) 517 if (GetCapture() == hWnd) 518 { 519 RECT rt; 520 draw_splitbar(hWnd, last_split); 521 GetClientRect(hWnd, &rt); 522 ResizeWnd(rt.right, rt.bottom); 523 last_split = -1; 524 ReleaseCapture(); 525 SetCursor(LoadCursorW(0, IDC_ARROW)); 526 } 527 break; 528 529 case WM_MOUSEMOVE: 530 if (GetCapture() == hWnd) 531 { 532 INT x = (SHORT)LOWORD(lParam); 533 x = ClampSplitBarX(hWnd, x); 534 if (last_split != x) 535 { 536 draw_splitbar(hWnd, last_split); 537 last_split = x; 538 draw_splitbar(hWnd, last_split); 539 } 540 } 541 break; 542 543 case WM_SETFOCUS: 544 if (g_pChildWnd != NULL) 545 { 546 SetFocus(g_pChildWnd->nFocusPanel? g_pChildWnd->hListWnd: g_pChildWnd->hTreeWnd); 547 } 548 break; 549 550 case WM_NOTIFY: 551 if (g_pChildWnd == NULL) break; 552 553 if (((LPNMHDR)lParam)->idFrom == TREE_WINDOW) 554 { 555 if (!TreeWndNotifyProc(g_pChildWnd->hListWnd, wParam, lParam, &Result)) 556 { 557 goto def; 558 } 559 560 return Result; 561 } 562 else 563 { 564 if (((LPNMHDR)lParam)->idFrom == LIST_WINDOW) 565 { 566 if (!ListWndNotifyProc(g_pChildWnd->hListWnd, wParam, lParam, &Result)) 567 { 568 goto def; 569 } 570 571 return Result; 572 } 573 else 574 { 575 goto def; 576 } 577 } 578 break; 579 580 case WM_CONTEXTMENU: 581 { 582 POINT pt; 583 if((HWND)wParam == g_pChildWnd->hListWnd) 584 { 585 int i, cnt; 586 BOOL IsDefault; 587 pt.x = (short) LOWORD(lParam); 588 pt.y = (short) HIWORD(lParam); 589 cnt = ListView_GetSelectedCount(g_pChildWnd->hListWnd); 590 i = ListView_GetNextItem(g_pChildWnd->hListWnd, -1, LVNI_FOCUSED | LVNI_SELECTED); 591 if (pt.x == -1 && pt.y == -1) 592 { 593 RECT rc; 594 if (i != -1) 595 { 596 rc.left = LVIR_BOUNDS; 597 SendMessageW(g_pChildWnd->hListWnd, LVM_GETITEMRECT, i, (LPARAM) &rc); 598 pt.x = rc.left + 8; 599 pt.y = rc.top + 8; 600 } 601 else 602 pt.x = pt.y = 0; 603 ClientToScreen(g_pChildWnd->hListWnd, &pt); 604 } 605 if(i == -1) 606 { 607 TrackPopupMenu(GetSubMenu(hPopupMenus, PM_NEW), TPM_RIGHTBUTTON, pt.x, pt.y, 0, hFrameWnd, NULL); 608 } 609 else 610 { 611 HMENU mnu = GetSubMenu(hPopupMenus, PM_MODIFYVALUE); 612 SetMenuDefaultItem(mnu, ID_EDIT_MODIFY, MF_BYCOMMAND); 613 IsDefault = IsDefaultValue(g_pChildWnd->hListWnd, i); 614 if(cnt == 1) 615 EnableMenuItem(mnu, ID_EDIT_RENAME, MF_BYCOMMAND | (IsDefault ? MF_DISABLED | MF_GRAYED : MF_ENABLED)); 616 else 617 EnableMenuItem(mnu, ID_EDIT_RENAME, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED); 618 EnableMenuItem(mnu, ID_EDIT_MODIFY, MF_BYCOMMAND | (cnt == 1 ? MF_ENABLED : MF_DISABLED | MF_GRAYED)); 619 EnableMenuItem(mnu, ID_EDIT_MODIFY_BIN, MF_BYCOMMAND | (cnt == 1 ? MF_ENABLED : MF_DISABLED | MF_GRAYED)); 620 621 TrackPopupMenu(mnu, TPM_RIGHTBUTTON, pt.x, pt.y, 0, hFrameWnd, NULL); 622 } 623 } 624 else if ((HWND)wParam == g_pChildWnd->hTreeWnd) 625 { 626 TVHITTESTINFO hti; 627 HMENU hContextMenu; 628 TVITEMW item; 629 MENUITEMINFOW mii; 630 WCHAR resource[256]; 631 WCHAR buffer[256]; 632 LPWSTR s; 633 LPCWSTR keyPath; 634 HKEY hRootKey; 635 int iLastPos; 636 WORD wID; 637 BOOL isRoot; 638 639 pt.x = (short) LOWORD(lParam); 640 pt.y = (short) HIWORD(lParam); 641 642 if (pt.x == -1 && pt.y == -1) 643 { 644 RECT rc; 645 hti.hItem = TreeView_GetSelection(g_pChildWnd->hTreeWnd); 646 if (hti.hItem != NULL) 647 { 648 TreeView_GetItemRect(g_pChildWnd->hTreeWnd, hti.hItem, &rc, TRUE); 649 pt.x = rc.left + 8; 650 pt.y = rc.top + 8; 651 ClientToScreen(g_pChildWnd->hTreeWnd, &pt); 652 hti.flags = TVHT_ONITEM; 653 } 654 else 655 hti.flags = 0; 656 } 657 else 658 { 659 hti.pt.x = pt.x; 660 hti.pt.y = pt.y; 661 ScreenToClient(g_pChildWnd->hTreeWnd, &hti.pt); 662 TreeView_HitTest(g_pChildWnd->hTreeWnd, &hti); 663 } 664 665 if (hti.flags & TVHT_ONITEM) 666 { 667 TreeView_SelectItem(g_pChildWnd->hTreeWnd, hti.hItem); 668 669 isRoot = (TreeView_GetParent(g_pChildWnd->hTreeWnd, hti.hItem) == NULL); 670 hContextMenu = GetSubMenu(hPopupMenus, isRoot ? PM_ROOTITEM : PM_TREECONTEXT); 671 672 memset(&item, 0, sizeof(item)); 673 item.mask = TVIF_STATE | TVIF_CHILDREN; 674 item.hItem = hti.hItem; 675 TreeView_GetItem(g_pChildWnd->hTreeWnd, &item); 676 677 /* Set the Expand/Collapse menu item appropriately */ 678 LoadStringW(hInst, (item.state & TVIS_EXPANDED) ? IDS_COLLAPSE : IDS_EXPAND, buffer, ARRAY_SIZE(buffer)); 679 memset(&mii, 0, sizeof(mii)); 680 mii.cbSize = sizeof(mii); 681 mii.fMask = MIIM_STRING | MIIM_STATE | MIIM_ID; 682 mii.fState = (item.cChildren > 0) ? MFS_DEFAULT : MFS_GRAYED; 683 mii.wID = (item.state & TVIS_EXPANDED) ? ID_TREE_COLLAPSEBRANCH : ID_TREE_EXPANDBRANCH; 684 mii.dwTypeData = (LPWSTR) buffer; 685 SetMenuItemInfo(hContextMenu, 0, TRUE, &mii); 686 687 if (isRoot == FALSE) 688 { 689 /* Remove any existing suggestions */ 690 memset(&mii, 0, sizeof(mii)); 691 mii.cbSize = sizeof(mii); 692 mii.fMask = MIIM_ID; 693 GetMenuItemInfo(hContextMenu, GetMenuItemCount(hContextMenu) - 1, TRUE, &mii); 694 if ((mii.wID >= ID_TREE_SUGGESTION_MIN) && (mii.wID <= ID_TREE_SUGGESTION_MAX)) 695 { 696 do 697 { 698 iLastPos = GetMenuItemCount(hContextMenu) - 1; 699 GetMenuItemInfo(hContextMenu, iLastPos, TRUE, &mii); 700 RemoveMenu(hContextMenu, iLastPos, MF_BYPOSITION); 701 } 702 while((mii.wID >= ID_TREE_SUGGESTION_MIN) && (mii.wID <= ID_TREE_SUGGESTION_MAX)); 703 } 704 705 /* Come up with suggestions */ 706 keyPath = GetItemPath(g_pChildWnd->hTreeWnd, NULL, &hRootKey); 707 SuggestKeys(hRootKey, keyPath, Suggestions, ARRAY_SIZE(Suggestions)); 708 if (Suggestions[0]) 709 { 710 AppendMenu(hContextMenu, MF_SEPARATOR, 0, NULL); 711 712 LoadStringW(hInst, IDS_GOTO_SUGGESTED_KEY, resource, ARRAY_SIZE(resource)); 713 714 s = Suggestions; 715 wID = ID_TREE_SUGGESTION_MIN; 716 while(*s && (wID <= ID_TREE_SUGGESTION_MAX)) 717 { 718 WCHAR *path = s, buf[MAX_PATH]; 719 if (hRootKey == HKEY_CURRENT_USER || hRootKey == HKEY_LOCAL_MACHINE) 720 { 721 // Windows 10 only displays the root name 722 LPCWSTR next = PathFindNextComponentW(s); 723 if (next > s) 724 lstrcpynW(path = buf, s, min(next - s, _countof(buf))); 725 } 726 _snwprintf(buffer, ARRAY_SIZE(buffer), resource, path); 727 728 memset(&mii, 0, sizeof(mii)); 729 mii.cbSize = sizeof(mii); 730 mii.fMask = MIIM_STRING | MIIM_ID; 731 mii.wID = wID++; 732 mii.dwTypeData = buffer; 733 InsertMenuItem(hContextMenu, GetMenuItemCount(hContextMenu), TRUE, &mii); 734 735 s += wcslen(s) + 1; 736 } 737 } 738 } 739 TrackPopupMenu(hContextMenu, TPM_RIGHTBUTTON, pt.x, pt.y, 0, hFrameWnd, NULL); 740 } 741 } 742 break; 743 } 744 745 case WM_SIZE: 746 if (wParam != SIZE_MINIMIZED && g_pChildWnd != NULL) 747 { 748 ResizeWnd(LOWORD(lParam), HIWORD(lParam)); 749 } 750 break; 751 752 default: 753 def: 754 return DefWindowProcW(hWnd, message, wParam, lParam); 755 } 756 return 0; 757 } 758