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