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