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