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 style = WS_CHILD | WS_VISIBLE | WS_TABSTOP;
392 g_pChildWnd->hAddressBarWnd = CreateWindowExW(WS_EX_CLIENTEDGE, L"Edit", NULL, style,
393 CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
394 hWnd, (HMENU)0, hInst, 0);
395
396 style = WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_ICON | BS_CENTER |
397 BS_VCENTER | BS_FLAT | BS_DEFPUSHBUTTON;
398 g_pChildWnd->hAddressBtnWnd = CreateWindowExW(0, L"Button", L"\x00BB", style,
399 CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
400 hWnd, (HMENU)0, hInst, 0);
401 g_pChildWnd->hArrowIcon = (HICON)LoadImageW(hInst, MAKEINTRESOURCEW(IDI_ARROW),
402 IMAGE_ICON, 12, 12, 0);
403 SendMessageW(g_pChildWnd->hAddressBtnWnd, BM_SETIMAGE, IMAGE_ICON, (LPARAM)g_pChildWnd->hArrowIcon);
404
405 if (SUCCEEDED(CoCreateInstance(&CLSID_AutoComplete, NULL, CLSCTX_INPROC_SERVER, &IID_IAutoComplete, (void**)&pAutoComplete)))
406 {
407 IAutoComplete_Init(pAutoComplete, g_pChildWnd->hAddressBarWnd, (IUnknown*)&g_DummyEnumStrings, NULL, NULL);
408 IAutoComplete_Release(pAutoComplete);
409 }
410
411 GetClientRect(hWnd, &rc);
412 g_pChildWnd->hTreeWnd = CreateTreeView(hWnd, g_pChildWnd->szPath, (HMENU) TREE_WINDOW);
413 g_pChildWnd->hListWnd = CreateListView(hWnd, (HMENU) LIST_WINDOW, rc.right - g_pChildWnd->nSplitPos);
414 SetFocus(g_pChildWnd->hTreeWnd);
415
416 /* set the address bar and button font */
417 if ((g_pChildWnd->hAddressBarWnd) && (g_pChildWnd->hAddressBtnWnd))
418 {
419 hFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
420 SendMessageW(g_pChildWnd->hAddressBarWnd,
421 WM_SETFONT,
422 (WPARAM)hFont,
423 0);
424 SendMessageW(g_pChildWnd->hAddressBtnWnd,
425 WM_SETFONT,
426 (WPARAM)hFont,
427 0);
428 }
429 /* Subclass the AddressBar */
430 oldproc = (WNDPROC)GetWindowLongPtr(g_pChildWnd->hAddressBarWnd, GWLP_WNDPROC);
431 SetWindowLongPtr(g_pChildWnd->hAddressBarWnd, GWLP_USERDATA, (DWORD_PTR)oldproc);
432 SetWindowLongPtr(g_pChildWnd->hAddressBarWnd, GWLP_WNDPROC, (DWORD_PTR)AddressBarProc);
433 break;
434 }
435 case WM_COMMAND:
436 if(HIWORD(wParam) == BN_CLICKED)
437 {
438 PostMessageW(g_pChildWnd->hAddressBarWnd, WM_KEYUP, VK_RETURN, 0);
439 }
440 break; //goto def;
441 case WM_SETCURSOR:
442 if (LOWORD(lParam) == HTCLIENT)
443 {
444 POINT pt;
445 GetCursorPos(&pt);
446 ScreenToClient(hWnd, &pt);
447 if (pt.x>=g_pChildWnd->nSplitPos-SPLIT_WIDTH/2 && pt.x<g_pChildWnd->nSplitPos+SPLIT_WIDTH/2+1)
448 {
449 SetCursor(LoadCursorW(0, IDC_SIZEWE));
450 return TRUE;
451 }
452 }
453 goto def;
454
455 case WM_DESTROY:
456 DestroyListView(g_pChildWnd->hListWnd);
457 DestroyTreeView(g_pChildWnd->hTreeWnd);
458 DestroyMainMenu();
459 DestroyIcon(g_pChildWnd->hArrowIcon);
460 HeapFree(GetProcessHeap(), 0, g_pChildWnd);
461 g_pChildWnd = NULL;
462 PostQuitMessage(0);
463 break;
464
465 case WM_LBUTTONDOWN:
466 {
467 INT x = (SHORT)LOWORD(lParam);
468 if (x >= g_pChildWnd->nSplitPos - SPLIT_WIDTH / 2 &&
469 x < g_pChildWnd->nSplitPos + SPLIT_WIDTH / 2 + 1)
470 {
471 x = ClampSplitBarX(hWnd, x);
472 draw_splitbar(hWnd, x);
473 last_split = x;
474 SetCapture(hWnd);
475 }
476 break;
477 }
478
479 case WM_LBUTTONUP:
480 case WM_RBUTTONDOWN:
481 if (GetCapture() == hWnd)
482 {
483 INT x = (SHORT)LOWORD(lParam);
484 x = ClampSplitBarX(hWnd, x);
485 finish_splitbar(hWnd, x);
486 }
487 break;
488
489 case WM_CAPTURECHANGED:
490 if (GetCapture() == hWnd && last_split >= 0)
491 draw_splitbar(hWnd, last_split);
492 break;
493
494 case WM_KEYDOWN:
495 if (wParam == VK_ESCAPE)
496 if (GetCapture() == hWnd)
497 {
498 RECT rt;
499 draw_splitbar(hWnd, last_split);
500 GetClientRect(hWnd, &rt);
501 ResizeWnd(rt.right, rt.bottom);
502 last_split = -1;
503 ReleaseCapture();
504 SetCursor(LoadCursorW(0, IDC_ARROW));
505 }
506 break;
507
508 case WM_MOUSEMOVE:
509 if (GetCapture() == hWnd)
510 {
511 INT x = (SHORT)LOWORD(lParam);
512 x = ClampSplitBarX(hWnd, x);
513 if (last_split != x)
514 {
515 draw_splitbar(hWnd, last_split);
516 last_split = x;
517 draw_splitbar(hWnd, last_split);
518 }
519 }
520 break;
521
522 case WM_SETFOCUS:
523 if (g_pChildWnd != NULL)
524 {
525 SetFocus(g_pChildWnd->nFocusPanel? g_pChildWnd->hListWnd: g_pChildWnd->hTreeWnd);
526 }
527 break;
528
529 case WM_NOTIFY:
530 if (g_pChildWnd == NULL) break;
531
532 if (((LPNMHDR)lParam)->idFrom == TREE_WINDOW)
533 {
534 if (!TreeWndNotifyProc(g_pChildWnd->hListWnd, wParam, lParam, &Result))
535 {
536 goto def;
537 }
538
539 return Result;
540 }
541 else
542 {
543 if (((LPNMHDR)lParam)->idFrom == LIST_WINDOW)
544 {
545 if (!ListWndNotifyProc(g_pChildWnd->hListWnd, wParam, lParam, &Result))
546 {
547 goto def;
548 }
549
550 return Result;
551 }
552 else
553 {
554 goto def;
555 }
556 }
557 break;
558
559 case WM_CONTEXTMENU:
560 {
561 POINT pt;
562 if((HWND)wParam == g_pChildWnd->hListWnd)
563 {
564 int i, cnt;
565 BOOL IsDefault;
566 pt.x = (short) LOWORD(lParam);
567 pt.y = (short) HIWORD(lParam);
568 cnt = ListView_GetSelectedCount(g_pChildWnd->hListWnd);
569 i = ListView_GetNextItem(g_pChildWnd->hListWnd, -1, LVNI_FOCUSED | LVNI_SELECTED);
570 if (pt.x == -1 && pt.y == -1)
571 {
572 RECT rc;
573 if (i != -1)
574 {
575 rc.left = LVIR_BOUNDS;
576 SendMessageW(g_pChildWnd->hListWnd, LVM_GETITEMRECT, i, (LPARAM) &rc);
577 pt.x = rc.left + 8;
578 pt.y = rc.top + 8;
579 }
580 else
581 pt.x = pt.y = 0;
582 ClientToScreen(g_pChildWnd->hListWnd, &pt);
583 }
584 if(i == -1)
585 {
586 TrackPopupMenu(GetSubMenu(hPopupMenus, PM_NEW), TPM_RIGHTBUTTON, pt.x, pt.y, 0, hFrameWnd, NULL);
587 }
588 else
589 {
590 HMENU mnu = GetSubMenu(hPopupMenus, PM_MODIFYVALUE);
591 SetMenuDefaultItem(mnu, ID_EDIT_MODIFY, MF_BYCOMMAND);
592 IsDefault = IsDefaultValue(g_pChildWnd->hListWnd, i);
593 if(cnt == 1)
594 EnableMenuItem(mnu, ID_EDIT_RENAME, MF_BYCOMMAND | (IsDefault ? MF_DISABLED | MF_GRAYED : MF_ENABLED));
595 else
596 EnableMenuItem(mnu, ID_EDIT_RENAME, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
597 EnableMenuItem(mnu, ID_EDIT_MODIFY, MF_BYCOMMAND | (cnt == 1 ? MF_ENABLED : MF_DISABLED | MF_GRAYED));
598 EnableMenuItem(mnu, ID_EDIT_MODIFY_BIN, MF_BYCOMMAND | (cnt == 1 ? MF_ENABLED : MF_DISABLED | MF_GRAYED));
599
600 TrackPopupMenu(mnu, TPM_RIGHTBUTTON, pt.x, pt.y, 0, hFrameWnd, NULL);
601 }
602 }
603 else if ((HWND)wParam == g_pChildWnd->hTreeWnd)
604 {
605 TVHITTESTINFO hti;
606 HMENU hContextMenu;
607 TVITEMW item;
608 MENUITEMINFOW mii;
609 WCHAR resource[256];
610 WCHAR buffer[256];
611 LPWSTR s;
612 LPCWSTR keyPath;
613 HKEY hRootKey;
614 int iLastPos;
615 WORD wID;
616 BOOL isRoot;
617
618 pt.x = (short) LOWORD(lParam);
619 pt.y = (short) HIWORD(lParam);
620
621 if (pt.x == -1 && pt.y == -1)
622 {
623 RECT rc;
624 hti.hItem = TreeView_GetSelection(g_pChildWnd->hTreeWnd);
625 if (hti.hItem != NULL)
626 {
627 TreeView_GetItemRect(g_pChildWnd->hTreeWnd, hti.hItem, &rc, TRUE);
628 pt.x = rc.left + 8;
629 pt.y = rc.top + 8;
630 ClientToScreen(g_pChildWnd->hTreeWnd, &pt);
631 hti.flags = TVHT_ONITEM;
632 }
633 else
634 hti.flags = 0;
635 }
636 else
637 {
638 hti.pt.x = pt.x;
639 hti.pt.y = pt.y;
640 ScreenToClient(g_pChildWnd->hTreeWnd, &hti.pt);
641 TreeView_HitTest(g_pChildWnd->hTreeWnd, &hti);
642 }
643
644 if (hti.flags & TVHT_ONITEM)
645 {
646 TreeView_SelectItem(g_pChildWnd->hTreeWnd, hti.hItem);
647
648 isRoot = (TreeView_GetParent(g_pChildWnd->hTreeWnd, hti.hItem) == NULL);
649 hContextMenu = GetSubMenu(hPopupMenus, isRoot ? PM_ROOTITEM : PM_TREECONTEXT);
650
651 memset(&item, 0, sizeof(item));
652 item.mask = TVIF_STATE | TVIF_CHILDREN;
653 item.hItem = hti.hItem;
654 TreeView_GetItem(g_pChildWnd->hTreeWnd, &item);
655
656 /* Set the Expand/Collapse menu item appropriately */
657 LoadStringW(hInst, (item.state & TVIS_EXPANDED) ? IDS_COLLAPSE : IDS_EXPAND, buffer, ARRAY_SIZE(buffer));
658 memset(&mii, 0, sizeof(mii));
659 mii.cbSize = sizeof(mii);
660 mii.fMask = MIIM_STRING | MIIM_STATE | MIIM_ID;
661 mii.fState = (item.cChildren > 0) ? MFS_DEFAULT : MFS_GRAYED;
662 mii.wID = (item.state & TVIS_EXPANDED) ? ID_TREE_COLLAPSEBRANCH : ID_TREE_EXPANDBRANCH;
663 mii.dwTypeData = (LPWSTR) buffer;
664 SetMenuItemInfo(hContextMenu, 0, TRUE, &mii);
665
666 if (isRoot == FALSE)
667 {
668 /* Remove any existing suggestions */
669 memset(&mii, 0, sizeof(mii));
670 mii.cbSize = sizeof(mii);
671 mii.fMask = MIIM_ID;
672 GetMenuItemInfo(hContextMenu, GetMenuItemCount(hContextMenu) - 1, TRUE, &mii);
673 if ((mii.wID >= ID_TREE_SUGGESTION_MIN) && (mii.wID <= ID_TREE_SUGGESTION_MAX))
674 {
675 do
676 {
677 iLastPos = GetMenuItemCount(hContextMenu) - 1;
678 GetMenuItemInfo(hContextMenu, iLastPos, TRUE, &mii);
679 RemoveMenu(hContextMenu, iLastPos, MF_BYPOSITION);
680 }
681 while((mii.wID >= ID_TREE_SUGGESTION_MIN) && (mii.wID <= ID_TREE_SUGGESTION_MAX));
682 }
683
684 /* Come up with suggestions */
685 keyPath = GetItemPath(g_pChildWnd->hTreeWnd, NULL, &hRootKey);
686 SuggestKeys(hRootKey, keyPath, Suggestions, ARRAY_SIZE(Suggestions));
687 if (Suggestions[0])
688 {
689 AppendMenu(hContextMenu, MF_SEPARATOR, 0, NULL);
690
691 LoadStringW(hInst, IDS_GOTO_SUGGESTED_KEY, resource, ARRAY_SIZE(resource));
692
693 s = Suggestions;
694 wID = ID_TREE_SUGGESTION_MIN;
695 while(*s && (wID <= ID_TREE_SUGGESTION_MAX))
696 {
697 WCHAR *path = s, buf[MAX_PATH];
698 if (hRootKey == HKEY_CURRENT_USER || hRootKey == HKEY_LOCAL_MACHINE)
699 {
700 // Windows 10 only displays the root name
701 LPCWSTR next = PathFindNextComponentW(s);
702 if (next > s)
703 lstrcpynW(path = buf, s, min(next - s, _countof(buf)));
704 }
705 _snwprintf(buffer, ARRAY_SIZE(buffer), resource, path);
706
707 memset(&mii, 0, sizeof(mii));
708 mii.cbSize = sizeof(mii);
709 mii.fMask = MIIM_STRING | MIIM_ID;
710 mii.wID = wID++;
711 mii.dwTypeData = buffer;
712 InsertMenuItem(hContextMenu, GetMenuItemCount(hContextMenu), TRUE, &mii);
713
714 s += wcslen(s) + 1;
715 }
716 }
717 }
718 TrackPopupMenu(hContextMenu, TPM_RIGHTBUTTON, pt.x, pt.y, 0, hFrameWnd, NULL);
719 }
720 }
721 break;
722 }
723
724 case WM_SIZE:
725 if (wParam != SIZE_MINIMIZED && g_pChildWnd != NULL)
726 {
727 ResizeWnd(LOWORD(lParam), HIWORD(lParam));
728 }
729 break;
730
731 default:
732 def:
733 return DefWindowProcW(hWnd, message, wParam, lParam);
734 }
735 return 0;
736 }
737