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                         StringCbCopyW(szLastFound, sizeof(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] != UNICODE_NULL)
262                 StringCbPrintfW(fullPath, cbFullPath, L"%s%s%s", rootName,
263                                 ((keyPath[0] == L'\\') ? L"" : L"\\"), keyPath);
264             else
265                 StringCbCopyW(fullPath, cbFullPath, rootName);
266 
267             SendMessageW(hStatusBar, SB_SETTEXTW, 0, (LPARAM)fullPath);
268             SendMessageW(g_pChildWnd->hAddressBarWnd, WM_SETTEXT, 0, (LPARAM)fullPath);
269             free(fullPath);
270 
271             /* disable hive manipulation items temporarily (enable only if necessary) */
272             EnableMenuItem(hMenuFrame, ID_REGISTRY_LOADHIVE, MF_BYCOMMAND | MF_GRAYED);
273             EnableMenuItem(hMenuFrame, ID_REGISTRY_UNLOADHIVE, MF_BYCOMMAND | MF_GRAYED);
274             /* compare the strings to see if we should enable/disable the "Load Hive" menus accordingly */
275             if (_wcsicmp(rootName, L"HKEY_LOCAL_MACHINE") != 0 ||
276                 _wcsicmp(rootName, L"HKEY_USERS") != 0)
277             {
278                 /*
279                  * enable the unload menu item if at the root, otherwise
280                  * enable the load menu item if there is no slash in
281                  * keyPath (ie. immediate child selected)
282                  */
283                 if (keyPath[0] == UNICODE_NULL)
284                     EnableMenuItem(hMenuFrame, ID_REGISTRY_LOADHIVE, MF_BYCOMMAND | MF_ENABLED);
285                 else if (!wcschr(keyPath, L'\\'))
286                     EnableMenuItem(hMenuFrame, ID_REGISTRY_UNLOADHIVE, MF_BYCOMMAND | MF_ENABLED);
287             }
288         }
289     }
290 }
291 
292 /*******************************************************************************
293  *
294  *  FUNCTION: ChildWndProc(HWND, unsigned, WORD, LONG)
295  *
296  *  PURPOSE:  Processes messages for the child windows.
297  *
298  *  WM_COMMAND  - process the application menu
299  *  WM_DESTROY  - post a quit message and return
300  *
301  */
302 LRESULT CALLBACK ChildWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
303 {
304     BOOL Result;
305     RECT rc;
306 
307     switch (message)
308     {
309     case WM_CREATE:
310     {
311         WNDPROC oldproc;
312         HFONT hFont;
313         WCHAR buffer[MAX_PATH];
314         DWORD style;
315 
316         /* Load "My Computer" string */
317         LoadStringW(hInst, IDS_MY_COMPUTER, buffer, ARRAY_SIZE(buffer));
318 
319         g_pChildWnd = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ChildWnd));
320         if (!g_pChildWnd) return 0;
321 
322         wcsncpy(g_pChildWnd->szPath, buffer, MAX_PATH);
323         g_pChildWnd->nSplitPos = 190;
324         g_pChildWnd->hWnd = hWnd;
325 
326         style = WS_CHILD | WS_VISIBLE | WS_TABSTOP;
327         g_pChildWnd->hAddressBarWnd = CreateWindowExW(WS_EX_CLIENTEDGE, L"Edit", NULL, style,
328                                                       CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
329                                                       hWnd, (HMENU)0, hInst, 0);
330 
331         style = WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_ICON | BS_CENTER |
332                 BS_VCENTER | BS_FLAT | BS_DEFPUSHBUTTON;
333         g_pChildWnd->hAddressBtnWnd = CreateWindowExW(0, L"Button", L"\x00BB", style,
334                                                       CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
335                                                       hWnd, (HMENU)0, hInst, 0);
336         g_pChildWnd->hArrowIcon = (HICON)LoadImageW(hInst, MAKEINTRESOURCEW(IDI_ARROW),
337                                                     IMAGE_ICON, 12, 12, 0);
338         SendMessageW(g_pChildWnd->hAddressBtnWnd, BM_SETIMAGE, IMAGE_ICON, (LPARAM)g_pChildWnd->hArrowIcon);
339 
340         GetClientRect(hWnd, &rc);
341         g_pChildWnd->hTreeWnd = CreateTreeView(hWnd, g_pChildWnd->szPath, (HMENU) TREE_WINDOW);
342         g_pChildWnd->hListWnd = CreateListView(hWnd, (HMENU) LIST_WINDOW, rc.right - g_pChildWnd->nSplitPos);
343         SetFocus(g_pChildWnd->hTreeWnd);
344 
345         /* set the address bar and button font */
346         if ((g_pChildWnd->hAddressBarWnd) && (g_pChildWnd->hAddressBtnWnd))
347         {
348             hFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
349             SendMessageW(g_pChildWnd->hAddressBarWnd,
350                          WM_SETFONT,
351                          (WPARAM)hFont,
352                          0);
353             SendMessageW(g_pChildWnd->hAddressBtnWnd,
354                          WM_SETFONT,
355                          (WPARAM)hFont,
356                          0);
357         }
358         /* Subclass the AddressBar */
359         oldproc = (WNDPROC)GetWindowLongPtr(g_pChildWnd->hAddressBarWnd, GWLP_WNDPROC);
360         SetWindowLongPtr(g_pChildWnd->hAddressBarWnd, GWLP_USERDATA, (DWORD_PTR)oldproc);
361         SetWindowLongPtr(g_pChildWnd->hAddressBarWnd, GWLP_WNDPROC, (DWORD_PTR)AddressBarProc);
362         break;
363     }
364     case WM_COMMAND:
365         if(HIWORD(wParam) == BN_CLICKED)
366         {
367             PostMessageW(g_pChildWnd->hAddressBarWnd, WM_KEYUP, VK_RETURN, 0);
368         }
369         break; //goto def;
370     case WM_SETCURSOR:
371         if (LOWORD(lParam) == HTCLIENT)
372         {
373             POINT pt;
374             GetCursorPos(&pt);
375             ScreenToClient(hWnd, &pt);
376             if (pt.x>=g_pChildWnd->nSplitPos-SPLIT_WIDTH/2 && pt.x<g_pChildWnd->nSplitPos+SPLIT_WIDTH/2+1)
377             {
378                 SetCursor(LoadCursorW(0, IDC_SIZEWE));
379                 return TRUE;
380             }
381         }
382         goto def;
383     case WM_DESTROY:
384         DestroyListView(g_pChildWnd->hListWnd);
385         DestroyTreeView(g_pChildWnd->hTreeWnd);
386         DestroyMainMenu();
387         DestroyIcon(g_pChildWnd->hArrowIcon);
388         HeapFree(GetProcessHeap(), 0, g_pChildWnd);
389         g_pChildWnd = NULL;
390         PostQuitMessage(0);
391         break;
392     case WM_LBUTTONDOWN:
393     {
394         RECT rt;
395         int x = (short)LOWORD(lParam);
396         GetClientRect(hWnd, &rt);
397         if (x>=g_pChildWnd->nSplitPos-SPLIT_WIDTH/2 && x<g_pChildWnd->nSplitPos+SPLIT_WIDTH/2+1)
398         {
399             last_split = g_pChildWnd->nSplitPos;
400             draw_splitbar(hWnd, last_split);
401             SetCapture(hWnd);
402         }
403         break;
404     }
405 
406     case WM_LBUTTONUP:
407     case WM_RBUTTONDOWN:
408         if (GetCapture() == hWnd)
409         {
410             finish_splitbar(hWnd, LOWORD(lParam));
411         }
412         break;
413 
414     case WM_CAPTURECHANGED:
415         if (GetCapture()==hWnd && last_split>=0)
416             draw_splitbar(hWnd, last_split);
417         break;
418 
419     case WM_KEYDOWN:
420         if (wParam == VK_ESCAPE)
421             if (GetCapture() == hWnd)
422             {
423                 RECT rt;
424                 draw_splitbar(hWnd, last_split);
425                 GetClientRect(hWnd, &rt);
426                 ResizeWnd(rt.right, rt.bottom);
427                 last_split = -1;
428                 ReleaseCapture();
429                 SetCursor(LoadCursorW(0, IDC_ARROW));
430             }
431         break;
432 
433     case WM_MOUSEMOVE:
434         if (GetCapture() == hWnd)
435         {
436             HDC hdc;
437             RECT rt;
438             HGDIOBJ OldObj;
439             int x = LOWORD(lParam);
440             if(!SizingPattern)
441             {
442                 const DWORD Pattern[4] = {0x5555AAAA, 0x5555AAAA, 0x5555AAAA, 0x5555AAAA};
443                 SizingPattern = CreateBitmap(8, 8, 1, 1, Pattern);
444             }
445             if(!SizingBrush)
446             {
447                 SizingBrush = CreatePatternBrush(SizingPattern);
448             }
449 
450             GetClientRect(hWnd, &rt);
451             x = (SHORT) min(max(x, SPLIT_MIN), rt.right - SPLIT_MIN);
452             if(last_split != x)
453             {
454                 rt.left = last_split-SPLIT_WIDTH/2;
455                 rt.right = last_split+SPLIT_WIDTH/2+1;
456                 hdc = GetDC(hWnd);
457                 OldObj = SelectObject(hdc, SizingBrush);
458                 PatBlt(hdc, rt.left, rt.top, rt.right - rt.left, rt.bottom - rt.top, PATINVERT);
459                 last_split = x;
460                 rt.left = x-SPLIT_WIDTH/2;
461                 rt.right = x+SPLIT_WIDTH/2+1;
462                 PatBlt(hdc, rt.left, rt.top, rt.right - rt.left, rt.bottom - rt.top, PATINVERT);
463                 SelectObject(hdc, OldObj);
464                 ReleaseDC(hWnd, hdc);
465             }
466         }
467         break;
468 
469     case WM_SETFOCUS:
470         if (g_pChildWnd != NULL)
471         {
472             SetFocus(g_pChildWnd->nFocusPanel? g_pChildWnd->hListWnd: g_pChildWnd->hTreeWnd);
473         }
474         break;
475 
476     case WM_TIMER:
477         break;
478 
479     case WM_NOTIFY:
480         if (g_pChildWnd == NULL) break;
481 
482         if (((LPNMHDR)lParam)->idFrom == TREE_WINDOW)
483         {
484             if (!TreeWndNotifyProc(g_pChildWnd->hListWnd, wParam, lParam, &Result))
485             {
486                 goto def;
487             }
488 
489             return Result;
490         }
491         else
492         {
493             if (((LPNMHDR)lParam)->idFrom == LIST_WINDOW)
494             {
495                 if (!ListWndNotifyProc(g_pChildWnd->hListWnd, wParam, lParam, &Result))
496                 {
497                     goto def;
498                 }
499 
500                 return Result;
501             }
502             else
503             {
504                 goto def;
505             }
506         }
507         break;
508 
509     case WM_CONTEXTMENU:
510     {
511         POINT pt;
512         if((HWND)wParam == g_pChildWnd->hListWnd)
513         {
514             int i, cnt;
515             BOOL IsDefault;
516             pt.x = (short) LOWORD(lParam);
517             pt.y = (short) HIWORD(lParam);
518             cnt = ListView_GetSelectedCount(g_pChildWnd->hListWnd);
519             i = ListView_GetNextItem(g_pChildWnd->hListWnd, -1, LVNI_FOCUSED | LVNI_SELECTED);
520             if (pt.x == -1 && pt.y == -1)
521             {
522                 RECT rc;
523                 if (i != -1)
524                 {
525                     rc.left = LVIR_BOUNDS;
526                     SendMessageW(g_pChildWnd->hListWnd, LVM_GETITEMRECT, i, (LPARAM) &rc);
527                     pt.x = rc.left + 8;
528                     pt.y = rc.top + 8;
529                 }
530                 else
531                     pt.x = pt.y = 0;
532                 ClientToScreen(g_pChildWnd->hListWnd, &pt);
533             }
534             if(i == -1)
535             {
536                 TrackPopupMenu(GetSubMenu(hPopupMenus, PM_NEW), TPM_RIGHTBUTTON, pt.x, pt.y, 0, hFrameWnd, NULL);
537             }
538             else
539             {
540                 HMENU mnu = GetSubMenu(hPopupMenus, PM_MODIFYVALUE);
541                 SetMenuDefaultItem(mnu, ID_EDIT_MODIFY, MF_BYCOMMAND);
542                 IsDefault = IsDefaultValue(g_pChildWnd->hListWnd, i);
543                 if(cnt == 1)
544                     EnableMenuItem(mnu, ID_EDIT_RENAME, MF_BYCOMMAND | (IsDefault ? MF_DISABLED | MF_GRAYED : MF_ENABLED));
545                 else
546                     EnableMenuItem(mnu, ID_EDIT_RENAME, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
547                 EnableMenuItem(mnu, ID_EDIT_MODIFY, MF_BYCOMMAND | (cnt == 1 ? MF_ENABLED : MF_DISABLED | MF_GRAYED));
548                 EnableMenuItem(mnu, ID_EDIT_MODIFY_BIN, MF_BYCOMMAND | (cnt == 1 ? MF_ENABLED : MF_DISABLED | MF_GRAYED));
549 
550                 TrackPopupMenu(mnu, TPM_RIGHTBUTTON, pt.x, pt.y, 0, hFrameWnd, NULL);
551             }
552         }
553         else if ((HWND)wParam == g_pChildWnd->hTreeWnd)
554         {
555             TVHITTESTINFO hti;
556             HMENU hContextMenu;
557             TVITEMW item;
558             MENUITEMINFOW mii;
559             WCHAR resource[256];
560             WCHAR buffer[256];
561             LPWSTR s;
562             LPCWSTR keyPath;
563             HKEY hRootKey;
564             int iLastPos;
565             WORD wID;
566             BOOL isRoot;
567 
568             pt.x = (short) LOWORD(lParam);
569             pt.y = (short) HIWORD(lParam);
570 
571             if (pt.x == -1 && pt.y == -1)
572             {
573                 RECT rc;
574                 hti.hItem = TreeView_GetSelection(g_pChildWnd->hTreeWnd);
575                 if (hti.hItem != NULL)
576                 {
577                     TreeView_GetItemRect(g_pChildWnd->hTreeWnd, hti.hItem, &rc, TRUE);
578                     pt.x = rc.left + 8;
579                     pt.y = rc.top + 8;
580                     ClientToScreen(g_pChildWnd->hTreeWnd, &pt);
581                     hti.flags = TVHT_ONITEM;
582                 }
583                 else
584                     hti.flags = 0;
585             }
586             else
587             {
588                 hti.pt.x = pt.x;
589                 hti.pt.y = pt.y;
590                 ScreenToClient(g_pChildWnd->hTreeWnd, &hti.pt);
591                 TreeView_HitTest(g_pChildWnd->hTreeWnd, &hti);
592             }
593 
594             if (hti.flags & TVHT_ONITEM)
595             {
596                 TreeView_SelectItem(g_pChildWnd->hTreeWnd, hti.hItem);
597 
598                 isRoot = (TreeView_GetParent(g_pChildWnd->hTreeWnd, hti.hItem) == NULL);
599                 hContextMenu = GetSubMenu(hPopupMenus, isRoot ?  PM_ROOTITEM : PM_TREECONTEXT);
600 
601                 memset(&item, 0, sizeof(item));
602                 item.mask = TVIF_STATE | TVIF_CHILDREN;
603                 item.hItem = hti.hItem;
604                 TreeView_GetItem(g_pChildWnd->hTreeWnd, &item);
605 
606                 /* Set the Expand/Collapse menu item appropriately */
607                 LoadStringW(hInst, (item.state & TVIS_EXPANDED) ? IDS_COLLAPSE : IDS_EXPAND, buffer, ARRAY_SIZE(buffer));
608                 memset(&mii, 0, sizeof(mii));
609                 mii.cbSize = sizeof(mii);
610                 mii.fMask = MIIM_STRING | MIIM_STATE | MIIM_ID;
611                 mii.fState = (item.cChildren > 0) ? MFS_DEFAULT : MFS_GRAYED;
612                 mii.wID = (item.state & TVIS_EXPANDED) ? ID_TREE_COLLAPSEBRANCH : ID_TREE_EXPANDBRANCH;
613                 mii.dwTypeData = (LPWSTR) buffer;
614                 SetMenuItemInfo(hContextMenu, 0, TRUE, &mii);
615 
616                 if (isRoot == FALSE)
617                 {
618                     /* Remove any existing suggestions */
619                     memset(&mii, 0, sizeof(mii));
620                     mii.cbSize = sizeof(mii);
621                     mii.fMask = MIIM_ID;
622                     GetMenuItemInfo(hContextMenu, GetMenuItemCount(hContextMenu) - 1, TRUE, &mii);
623                     if ((mii.wID >= ID_TREE_SUGGESTION_MIN) && (mii.wID <= ID_TREE_SUGGESTION_MAX))
624                     {
625                         do
626                         {
627                             iLastPos = GetMenuItemCount(hContextMenu) - 1;
628                             GetMenuItemInfo(hContextMenu, iLastPos, TRUE, &mii);
629                             RemoveMenu(hContextMenu, iLastPos, MF_BYPOSITION);
630                         }
631                         while((mii.wID >= ID_TREE_SUGGESTION_MIN) && (mii.wID <= ID_TREE_SUGGESTION_MAX));
632                     }
633 
634                     /* Come up with suggestions */
635                     keyPath = GetItemPath(g_pChildWnd->hTreeWnd, NULL, &hRootKey);
636                     SuggestKeys(hRootKey, keyPath, Suggestions, ARRAY_SIZE(Suggestions));
637                     if (Suggestions[0])
638                     {
639                         AppendMenu(hContextMenu, MF_SEPARATOR, 0, NULL);
640 
641                         LoadStringW(hInst, IDS_GOTO_SUGGESTED_KEY, resource, ARRAY_SIZE(resource));
642 
643                         s = Suggestions;
644                         wID = ID_TREE_SUGGESTION_MIN;
645                         while(*s && (wID <= ID_TREE_SUGGESTION_MAX))
646                         {
647                             _snwprintf(buffer, ARRAY_SIZE(buffer), resource, s);
648 
649                             memset(&mii, 0, sizeof(mii));
650                             mii.cbSize = sizeof(mii);
651                             mii.fMask = MIIM_STRING | MIIM_ID;
652                             mii.wID = wID++;
653                             mii.dwTypeData = buffer;
654                             InsertMenuItem(hContextMenu, GetMenuItemCount(hContextMenu), TRUE, &mii);
655 
656                             s += wcslen(s) + 1;
657                         }
658                     }
659                 }
660                 TrackPopupMenu(hContextMenu, TPM_RIGHTBUTTON, pt.x, pt.y, 0, hFrameWnd, NULL);
661             }
662         }
663         break;
664     }
665 
666     case WM_SIZE:
667         if (wParam != SIZE_MINIMIZED && g_pChildWnd != NULL)
668         {
669             ResizeWnd(LOWORD(lParam), HIWORD(lParam));
670         }
671         /* fall through */
672     default:
673 def:
674         return DefWindowProcW(hWnd, message, wParam, lParam);
675     }
676     return 0;
677 }
678