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 static 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  *  FUNCTION: ChildWnd_CmdWndProc(HWND, unsigned, WORD, LONG)
135  *
136  *  PURPOSE:  Processes WM_COMMAND messages for the main frame window.
137  *
138  */
139 
140 static BOOL ChildWnd_CmdWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
141 {
142     HTREEITEM hSelection;
143     HKEY hRootKey;
144     LPCWSTR keyPath, s;
145     WORD wID = LOWORD(wParam);
146 
147     UNREFERENCED_PARAMETER(message);
148 
149     switch (wID)
150     {
151         /* Parse the menu selections: */
152     case ID_REGISTRY_EXIT:
153         DestroyWindow(hWnd);
154         break;
155     case ID_VIEW_REFRESH:
156         /* TODO */
157         break;
158     case ID_TREE_EXPANDBRANCH:
159         TreeView_Expand(g_pChildWnd->hTreeWnd, TreeView_GetSelection(g_pChildWnd->hTreeWnd), TVE_EXPAND);
160         break;
161     case ID_TREE_COLLAPSEBRANCH:
162         TreeView_Expand(g_pChildWnd->hTreeWnd, TreeView_GetSelection(g_pChildWnd->hTreeWnd), TVE_COLLAPSE);
163         break;
164     case ID_TREE_RENAME:
165         SetFocus(g_pChildWnd->hTreeWnd);
166         TreeView_EditLabel(g_pChildWnd->hTreeWnd, TreeView_GetSelection(g_pChildWnd->hTreeWnd));
167         break;
168     case ID_TREE_DELETE:
169         hSelection = TreeView_GetSelection(g_pChildWnd->hTreeWnd);
170         keyPath = GetItemPath(g_pChildWnd->hTreeWnd, hSelection, &hRootKey);
171 
172         if (keyPath == 0 || *keyPath == 0)
173         {
174             MessageBeep(MB_ICONHAND);
175         }
176         else if (DeleteKey(hWnd, hRootKey, keyPath))
177             DeleteNode(g_pChildWnd->hTreeWnd, 0);
178         break;
179     case ID_TREE_EXPORT:
180         ExportRegistryFile(g_pChildWnd->hTreeWnd);
181         break;
182     case ID_EDIT_FIND:
183         FindDialog(hWnd);
184         break;
185     case ID_EDIT_COPYKEYNAME:
186         hSelection = TreeView_GetSelection(g_pChildWnd->hTreeWnd);
187         keyPath = GetItemPath(g_pChildWnd->hTreeWnd, hSelection, &hRootKey);
188         CopyKeyName(hWnd, hRootKey, keyPath);
189         break;
190     case ID_EDIT_NEW_KEY:
191         CreateNewKey(g_pChildWnd->hTreeWnd, TreeView_GetSelection(g_pChildWnd->hTreeWnd));
192         break;
193     case ID_EDIT_NEW_STRINGVALUE:
194     case ID_EDIT_NEW_BINARYVALUE:
195     case ID_EDIT_NEW_DWORDVALUE:
196         SendMessageW(hFrameWnd, WM_COMMAND, wParam, lParam);
197         break;
198     case ID_SWITCH_PANELS:
199         g_pChildWnd->nFocusPanel = !g_pChildWnd->nFocusPanel;
200         SetFocus(g_pChildWnd->nFocusPanel? g_pChildWnd->hListWnd: g_pChildWnd->hTreeWnd);
201         break;
202     default:
203         if ((wID >= ID_TREE_SUGGESTION_MIN) && (wID <= ID_TREE_SUGGESTION_MAX))
204         {
205             s = Suggestions;
206             while(wID > ID_TREE_SUGGESTION_MIN)
207             {
208                 if (*s)
209                     s += wcslen(s) + 1;
210                 wID--;
211             }
212             SelectNode(g_pChildWnd->hTreeWnd, s);
213             break;
214         }
215         return FALSE;
216     }
217     return TRUE;
218 }
219 
220 /*******************************************************************************
221  *
222  *  Key suggestion
223  */
224 
225 #define MIN(a,b)    ((a < b) ? (a) : (b))
226 
227 static void SuggestKeys(HKEY hRootKey, LPCWSTR pszKeyPath, LPWSTR pszSuggestions,
228                         size_t iSuggestionsLength)
229 {
230     WCHAR szBuffer[256];
231     WCHAR szLastFound[256];
232     size_t i;
233     HKEY hOtherKey, hSubKey;
234     BOOL bFound;
235 
236     memset(pszSuggestions, 0, iSuggestionsLength * sizeof(*pszSuggestions));
237     iSuggestionsLength--;
238 
239     /* Are we a root key in HKEY_CLASSES_ROOT? */
240     if ((hRootKey == HKEY_CLASSES_ROOT) && pszKeyPath[0] && !wcschr(pszKeyPath, L'\\'))
241     {
242         do
243         {
244             bFound = FALSE;
245 
246             /* Check default key */
247             if (QueryStringValue(hRootKey, pszKeyPath, NULL,
248                                  szBuffer, COUNT_OF(szBuffer)) == ERROR_SUCCESS)
249             {
250                 /* Sanity check this key; it cannot be empty, nor can it be a
251                  * loop back */
252                 if ((szBuffer[0] != L'\0') && _wcsicmp(szBuffer, pszKeyPath))
253                 {
254                     if (RegOpenKeyW(hRootKey, szBuffer, &hOtherKey) == ERROR_SUCCESS)
255                     {
256                         lstrcpynW(pszSuggestions, L"HKCR\\", (int) iSuggestionsLength);
257                         i = wcslen(pszSuggestions);
258                         pszSuggestions += i;
259                         iSuggestionsLength -= i;
260 
261                         lstrcpynW(pszSuggestions, szBuffer, (int) iSuggestionsLength);
262                         i = MIN(wcslen(pszSuggestions) + 1, iSuggestionsLength);
263                         pszSuggestions += i;
264                         iSuggestionsLength -= i;
265                         RegCloseKey(hOtherKey);
266 
267                         bFound = TRUE;
268                         wcscpy(szLastFound, szBuffer);
269                         pszKeyPath = szLastFound;
270                     }
271                 }
272             }
273         }
274         while(bFound && (iSuggestionsLength > 0));
275 
276         /* Check CLSID key */
277         if (RegOpenKeyW(hRootKey, pszKeyPath, &hSubKey) == ERROR_SUCCESS)
278         {
279             if (QueryStringValue(hSubKey, L"CLSID", NULL, szBuffer,
280                                  COUNT_OF(szBuffer)) == ERROR_SUCCESS)
281             {
282                 lstrcpynW(pszSuggestions, L"HKCR\\CLSID\\", (int)iSuggestionsLength);
283                 i = wcslen(pszSuggestions);
284                 pszSuggestions += i;
285                 iSuggestionsLength -= i;
286 
287                 lstrcpynW(pszSuggestions, szBuffer, (int)iSuggestionsLength);
288                 i = MIN(wcslen(pszSuggestions) + 1, iSuggestionsLength);
289                 pszSuggestions += i;
290                 iSuggestionsLength -= i;
291             }
292             RegCloseKey(hSubKey);
293         }
294     }
295 }
296 
297 
298 LRESULT CALLBACK AddressBarProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
299 {
300     WNDPROC oldwndproc;
301     static WCHAR s_szNode[256];
302     oldwndproc = (WNDPROC)GetWindowLongPtr(hwnd, GWLP_USERDATA);
303 
304     switch (uMsg)
305     {
306     case WM_KEYUP:
307         if (wParam == VK_RETURN)
308         {
309             GetWindowTextW(hwnd, s_szNode, COUNT_OF(s_szNode));
310             SelectNode(g_pChildWnd->hTreeWnd, s_szNode);
311         }
312         break;
313     default:
314         break;
315     }
316     return CallWindowProcW(oldwndproc, hwnd, uMsg, wParam, lParam);
317 }
318 
319 static VOID
320 UpdateAddress(HTREEITEM hItem, HKEY hRootKey, LPCWSTR pszPath)
321 {
322     LPCWSTR keyPath, rootName;
323     LPWSTR fullPath;
324 
325     /* Wipe the listview, the status bar and the address bar if the root key was selected */
326     if (TreeView_GetParent(g_pChildWnd->hTreeWnd, hItem) == NULL)
327     {
328         ListView_DeleteAllItems(g_pChildWnd->hListWnd);
329         SendMessageW(hStatusBar, SB_SETTEXTW, 0, (LPARAM)NULL);
330         SendMessageW(g_pChildWnd->hAddressBarWnd, WM_SETTEXT, 0, (LPARAM)NULL);
331         return;
332     }
333 
334     if (pszPath == NULL)
335         keyPath = GetItemPath(g_pChildWnd->hTreeWnd, hItem, &hRootKey);
336     else
337         keyPath = pszPath;
338 
339     if (keyPath)
340     {
341         RefreshListView(g_pChildWnd->hListWnd, hRootKey, keyPath);
342         rootName = get_root_key_name(hRootKey);
343         fullPath = HeapAlloc(GetProcessHeap(), 0, (wcslen(rootName) + 1 + wcslen(keyPath) + 1) * sizeof(WCHAR));
344         if (fullPath)
345         {
346             /* set (correct) the address bar text */
347             if (keyPath[0] != L'\0')
348                 swprintf(fullPath, L"%s\\%s", rootName, keyPath);
349             else
350                 fullPath = wcscpy(fullPath, rootName);
351             SendMessageW(hStatusBar, SB_SETTEXTW, 0, (LPARAM)fullPath);
352             SendMessageW(g_pChildWnd->hAddressBarWnd, WM_SETTEXT, 0, (LPARAM)fullPath);
353             HeapFree(GetProcessHeap(), 0, fullPath);
354             /* disable hive manipulation items temporarily (enable only if necessary) */
355             EnableMenuItem(GetSubMenu(hMenuFrame,0), ID_REGISTRY_LOADHIVE, MF_BYCOMMAND | MF_GRAYED);
356             EnableMenuItem(GetSubMenu(hMenuFrame,0), ID_REGISTRY_UNLOADHIVE, MF_BYCOMMAND | MF_GRAYED);
357             /* compare the strings to see if we should enable/disable the "Load Hive" menus accordingly */
358             if (!(_wcsicmp(rootName, L"HKEY_LOCAL_MACHINE") &&
359                   _wcsicmp(rootName, L"HKEY_USERS")))
360             {
361                 /*
362                  * enable the unload menu item if at the root, otherwise
363                  * enable the load menu item if there is no slash in
364                  * keyPath (ie. immediate child selected)
365                  */
366                 if(keyPath[0] == L'\0')
367                     EnableMenuItem(GetSubMenu(hMenuFrame,0), ID_REGISTRY_LOADHIVE, MF_BYCOMMAND | MF_ENABLED);
368                 else if(!wcschr(keyPath, L'\\'))
369                     EnableMenuItem(GetSubMenu(hMenuFrame,0), ID_REGISTRY_UNLOADHIVE, MF_BYCOMMAND | MF_ENABLED);
370             }
371         }
372     }
373 }
374 
375 /*******************************************************************************
376  *
377  *  FUNCTION: ChildWndProc(HWND, unsigned, WORD, LONG)
378  *
379  *  PURPOSE:  Processes messages for the child windows.
380  *
381  *  WM_COMMAND  - process the application menu
382  *  WM_DESTROY  - post a quit message and return
383  *
384  */
385 LRESULT CALLBACK ChildWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
386 {
387     BOOL Result;
388 
389     switch (message)
390     {
391     case WM_CREATE:
392     {
393         WNDPROC oldproc;
394         HFONT hFont;
395         WCHAR buffer[MAX_PATH];
396 
397         /* Load "My Computer" string */
398         LoadStringW(hInst, IDS_MY_COMPUTER, buffer, COUNT_OF(buffer));
399 
400         g_pChildWnd = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ChildWnd));
401         if (!g_pChildWnd) return 0;
402 
403         wcsncpy(g_pChildWnd->szPath, buffer, MAX_PATH);
404         g_pChildWnd->nSplitPos = 250;
405         g_pChildWnd->hWnd = hWnd;
406         g_pChildWnd->hAddressBarWnd = CreateWindowExW(WS_EX_CLIENTEDGE, L"Edit", NULL, WS_CHILD | WS_VISIBLE | WS_CHILDWINDOW | WS_TABSTOP,
407                                                       CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
408                                                       hWnd, (HMENU)0, hInst, 0);
409         g_pChildWnd->hAddressBtnWnd = CreateWindowExW(0, L"Button", L"\x00BB", WS_CHILD | WS_VISIBLE | WS_CHILDWINDOW | WS_TABSTOP | BS_TEXT | BS_CENTER | BS_VCENTER | BS_FLAT | BS_DEFPUSHBUTTON,
410                                                       CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
411                                                       hWnd, (HMENU)0, hInst, 0);
412         g_pChildWnd->hTreeWnd = CreateTreeView(hWnd, g_pChildWnd->szPath, (HMENU) TREE_WINDOW);
413         g_pChildWnd->hListWnd = CreateListView(hWnd, (HMENU) LIST_WINDOW/*, g_pChildWnd->szPath*/);
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 
441         if (!ChildWnd_CmdWndProc(hWnd, message, wParam, lParam))
442         {
443             goto def;
444         }
445         break;
446     case WM_SETCURSOR:
447         if (LOWORD(lParam) == HTCLIENT)
448         {
449             POINT pt;
450             GetCursorPos(&pt);
451             ScreenToClient(hWnd, &pt);
452             if (pt.x>=g_pChildWnd->nSplitPos-SPLIT_WIDTH/2 && pt.x<g_pChildWnd->nSplitPos+SPLIT_WIDTH/2+1)
453             {
454                 SetCursor(LoadCursorW(0, IDC_SIZEWE));
455                 return TRUE;
456             }
457         }
458         goto def;
459     case WM_DESTROY:
460         DestroyListView(g_pChildWnd->hListWnd);
461         DestroyTreeView(g_pChildWnd->hTreeWnd);
462         DestroyMainMenu();
463         HeapFree(GetProcessHeap(), 0, g_pChildWnd);
464         g_pChildWnd = NULL;
465         PostQuitMessage(0);
466         break;
467     case WM_LBUTTONDOWN:
468     {
469         RECT rt;
470         int x = (short)LOWORD(lParam);
471         GetClientRect(hWnd, &rt);
472         if (x>=g_pChildWnd->nSplitPos-SPLIT_WIDTH/2 && x<g_pChildWnd->nSplitPos+SPLIT_WIDTH/2+1)
473         {
474             last_split = g_pChildWnd->nSplitPos;
475             draw_splitbar(hWnd, last_split);
476             SetCapture(hWnd);
477         }
478         break;
479     }
480 
481     case WM_LBUTTONUP:
482     case WM_RBUTTONDOWN:
483         if (GetCapture() == hWnd)
484         {
485             finish_splitbar(hWnd, LOWORD(lParam));
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             HDC hdc;
512             RECT rt;
513             HGDIOBJ OldObj;
514             int x = LOWORD(lParam);
515             if(!SizingPattern)
516             {
517                 const DWORD Pattern[4] = {0x5555AAAA, 0x5555AAAA, 0x5555AAAA, 0x5555AAAA};
518                 SizingPattern = CreateBitmap(8, 8, 1, 1, Pattern);
519             }
520             if(!SizingBrush)
521             {
522                 SizingBrush = CreatePatternBrush(SizingPattern);
523             }
524 
525             GetClientRect(hWnd, &rt);
526             x = (SHORT) min(max(x, SPLIT_MIN), rt.right - SPLIT_MIN);
527             if(last_split != x)
528             {
529                 rt.left = last_split-SPLIT_WIDTH/2;
530                 rt.right = last_split+SPLIT_WIDTH/2+1;
531                 hdc = GetDC(hWnd);
532                 OldObj = SelectObject(hdc, SizingBrush);
533                 PatBlt(hdc, rt.left, rt.top, rt.right - rt.left, rt.bottom - rt.top, PATINVERT);
534                 last_split = x;
535                 rt.left = x-SPLIT_WIDTH/2;
536                 rt.right = x+SPLIT_WIDTH/2+1;
537                 PatBlt(hdc, rt.left, rt.top, rt.right - rt.left, rt.bottom - rt.top, PATINVERT);
538                 SelectObject(hdc, OldObj);
539                 ReleaseDC(hWnd, hdc);
540             }
541         }
542         break;
543 
544     case WM_SETFOCUS:
545         if (g_pChildWnd != NULL)
546         {
547             SetFocus(g_pChildWnd->nFocusPanel? g_pChildWnd->hListWnd: g_pChildWnd->hTreeWnd);
548         }
549         break;
550 
551     case WM_TIMER:
552         break;
553 
554     case WM_NOTIFY:
555         if ((int)wParam == TREE_WINDOW && g_pChildWnd != NULL)
556         {
557             switch (((LPNMHDR)lParam)->code)
558             {
559             case TVN_ITEMEXPANDING:
560                 return !OnTreeExpanding(g_pChildWnd->hTreeWnd, (NMTREEVIEW*)lParam);
561             case TVN_SELCHANGED:
562             {
563                 NMTREEVIEW* pnmtv = (NMTREEVIEW*)lParam;
564                 /* Get the parent of the current item */
565                 HTREEITEM hParentItem = TreeView_GetParent(g_pChildWnd->hTreeWnd, pnmtv->itemNew.hItem);
566 
567                 UpdateAddress(pnmtv->itemNew.hItem, NULL, NULL);
568 
569                 /*
570                  * Disable Delete/Rename menu options for 'My Computer' (first item so doesn't have any parent)
571                  * and HKEY_* keys (their parent is 'My Computer' and the previous remark applies).
572                  */
573                 if (!hParentItem || !TreeView_GetParent(g_pChildWnd->hTreeWnd, hParentItem))
574                 {
575                     EnableMenuItem(hMenuFrame , ID_EDIT_DELETE, MF_BYCOMMAND | MF_GRAYED);
576                     EnableMenuItem(hMenuFrame , ID_EDIT_RENAME, MF_BYCOMMAND | MF_GRAYED);
577                     EnableMenuItem(hPopupMenus, ID_TREE_DELETE, MF_BYCOMMAND | MF_GRAYED);
578                     EnableMenuItem(hPopupMenus, ID_TREE_RENAME, MF_BYCOMMAND | MF_GRAYED);
579                 }
580                 else
581                 {
582                     EnableMenuItem(hMenuFrame , ID_EDIT_DELETE, MF_BYCOMMAND | MF_ENABLED);
583                     EnableMenuItem(hMenuFrame , ID_EDIT_RENAME, MF_BYCOMMAND | MF_ENABLED);
584                     EnableMenuItem(hPopupMenus, ID_TREE_DELETE, MF_BYCOMMAND | MF_ENABLED);
585                     EnableMenuItem(hPopupMenus, ID_TREE_RENAME, MF_BYCOMMAND | MF_ENABLED);
586                 }
587 
588                 break;
589             }
590             case NM_SETFOCUS:
591                 g_pChildWnd->nFocusPanel = 0;
592                 break;
593             case TVN_BEGINLABELEDIT:
594             {
595                 LPNMTVDISPINFO ptvdi;
596                 /* cancel label edit for rootkeys  */
597                 ptvdi = (LPNMTVDISPINFO) lParam;
598                 if (!TreeView_GetParent(g_pChildWnd->hTreeWnd, ptvdi->item.hItem) ||
599                     !TreeView_GetParent(g_pChildWnd->hTreeWnd, TreeView_GetParent(g_pChildWnd->hTreeWnd, ptvdi->item.hItem)))
600                     return TRUE;
601                 break;
602             }
603             case TVN_ENDLABELEDIT:
604             {
605                 LPCWSTR keyPath;
606                 HKEY hRootKey;
607                 HKEY hKey = NULL;
608                 LPNMTVDISPINFO ptvdi;
609                 LONG lResult = TRUE;
610                 WCHAR szBuffer[MAX_PATH];
611 
612                 ptvdi = (LPNMTVDISPINFO) lParam;
613                 if (ptvdi->item.pszText)
614                 {
615                     keyPath = GetItemPath(g_pChildWnd->hTreeWnd, TreeView_GetParent(g_pChildWnd->hTreeWnd, ptvdi->item.hItem), &hRootKey);
616                     _snwprintf(szBuffer, COUNT_OF(szBuffer), L"%s\\%s", keyPath, ptvdi->item.pszText);
617                     keyPath = GetItemPath(g_pChildWnd->hTreeWnd, ptvdi->item.hItem, &hRootKey);
618                     if (RegOpenKeyExW(hRootKey, szBuffer, 0, KEY_READ, &hKey) == ERROR_SUCCESS)
619                     {
620                         lResult = FALSE;
621                         RegCloseKey(hKey);
622                         TreeView_EditLabel(g_pChildWnd->hTreeWnd, ptvdi->item.hItem);
623                     }
624                     else
625                     {
626                         if (RenameKey(hRootKey, keyPath, ptvdi->item.pszText) != ERROR_SUCCESS)
627                             lResult = FALSE;
628                         else
629                             UpdateAddress(ptvdi->item.hItem, hRootKey, szBuffer);
630                     }
631                     return lResult;
632                 }
633             }
634             default:
635                 return 0;
636             }
637         }
638         else
639         {
640             if ((int)wParam == LIST_WINDOW && g_pChildWnd != NULL)
641             {
642                 switch (((LPNMHDR)lParam)->code)
643                 {
644                 case NM_SETFOCUS:
645                     g_pChildWnd->nFocusPanel = 1;
646                     break;
647                 default:
648                     if(!ListWndNotifyProc(g_pChildWnd->hListWnd, wParam, lParam, &Result))
649                     {
650                         goto def;
651                     }
652                     return Result;
653                     break;
654                 }
655             }
656         }
657         break;
658 
659     case WM_CONTEXTMENU:
660     {
661         POINT pt;
662         if((HWND)wParam == g_pChildWnd->hListWnd)
663         {
664             int i, cnt;
665             BOOL IsDefault;
666             pt.x = (short) LOWORD(lParam);
667             pt.y = (short) HIWORD(lParam);
668             cnt = ListView_GetSelectedCount(g_pChildWnd->hListWnd);
669             i = ListView_GetNextItem(g_pChildWnd->hListWnd, -1, LVNI_FOCUSED | LVNI_SELECTED);
670             if (pt.x == -1 && pt.y == -1)
671             {
672                 RECT rc;
673                 if (i != -1)
674                 {
675                     rc.left = LVIR_BOUNDS;
676                     SendMessageW(g_pChildWnd->hListWnd, LVM_GETITEMRECT, i, (LPARAM) &rc);
677                     pt.x = rc.left + 8;
678                     pt.y = rc.top + 8;
679                 }
680                 else
681                     pt.x = pt.y = 0;
682                 ClientToScreen(g_pChildWnd->hListWnd, &pt);
683             }
684             if(i == -1)
685             {
686                 TrackPopupMenu(GetSubMenu(hPopupMenus, PM_NEW), TPM_RIGHTBUTTON, pt.x, pt.y, 0, hFrameWnd, NULL);
687             }
688             else
689             {
690                 HMENU mnu = GetSubMenu(hPopupMenus, PM_MODIFYVALUE);
691                 SetMenuDefaultItem(mnu, ID_EDIT_MODIFY, MF_BYCOMMAND);
692                 IsDefault = IsDefaultValue(g_pChildWnd->hListWnd, i);
693                 if(cnt == 1)
694                     EnableMenuItem(mnu, ID_EDIT_RENAME, MF_BYCOMMAND | (IsDefault ? MF_DISABLED | MF_GRAYED : MF_ENABLED));
695                 else
696                     EnableMenuItem(mnu, ID_EDIT_RENAME, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
697                 EnableMenuItem(mnu, ID_EDIT_MODIFY, MF_BYCOMMAND | (cnt == 1 ? MF_ENABLED : MF_DISABLED | MF_GRAYED));
698                 EnableMenuItem(mnu, ID_EDIT_MODIFY_BIN, MF_BYCOMMAND | (cnt == 1 ? MF_ENABLED : MF_DISABLED | MF_GRAYED));
699 
700                 TrackPopupMenu(mnu, TPM_RIGHTBUTTON, pt.x, pt.y, 0, hFrameWnd, NULL);
701             }
702         }
703         else if ((HWND)wParam == g_pChildWnd->hTreeWnd)
704         {
705             TVHITTESTINFO hti;
706             HMENU hContextMenu;
707             TVITEMW item;
708             MENUITEMINFOW mii;
709             WCHAR resource[256];
710             WCHAR buffer[256];
711             LPWSTR s;
712             LPCWSTR keyPath;
713             HKEY hRootKey;
714             int iLastPos;
715             WORD wID;
716 
717             pt.x = (short) LOWORD(lParam);
718             pt.y = (short) HIWORD(lParam);
719 
720             if (pt.x == -1 && pt.y == -1)
721             {
722                 RECT rc;
723                 hti.hItem = TreeView_GetSelection(g_pChildWnd->hTreeWnd);
724                 if (hti.hItem != NULL)
725                 {
726                     TreeView_GetItemRect(g_pChildWnd->hTreeWnd, hti.hItem, &rc, TRUE);
727                     pt.x = rc.left + 8;
728                     pt.y = rc.top + 8;
729                     ClientToScreen(g_pChildWnd->hTreeWnd, &pt);
730                     hti.flags = TVHT_ONITEM;
731                 }
732                 else
733                     hti.flags = 0;
734             }
735             else
736             {
737                 hti.pt.x = pt.x;
738                 hti.pt.y = pt.y;
739                 ScreenToClient(g_pChildWnd->hTreeWnd, &hti.pt);
740                 TreeView_HitTest(g_pChildWnd->hTreeWnd, &hti);
741             }
742 
743             if (hti.flags & TVHT_ONITEM)
744             {
745                 hContextMenu = GetSubMenu(hPopupMenus, PM_TREECONTEXT);
746                 TreeView_SelectItem(g_pChildWnd->hTreeWnd, hti.hItem);
747 
748                 memset(&item, 0, sizeof(item));
749                 item.mask = TVIF_STATE | TVIF_CHILDREN;
750                 item.hItem = hti.hItem;
751                 TreeView_GetItem(g_pChildWnd->hTreeWnd, &item);
752 
753                 /* Set the Expand/Collapse menu item appropriately */
754                 LoadStringW(hInst, (item.state & TVIS_EXPANDED) ? IDS_COLLAPSE : IDS_EXPAND, buffer, COUNT_OF(buffer));
755                 memset(&mii, 0, sizeof(mii));
756                 mii.cbSize = sizeof(mii);
757                 mii.fMask = MIIM_STRING | MIIM_STATE | MIIM_ID;
758                 mii.fState = (item.cChildren > 0) ? MFS_DEFAULT : MFS_GRAYED;
759                 mii.wID = (item.state & TVIS_EXPANDED) ? ID_TREE_COLLAPSEBRANCH : ID_TREE_EXPANDBRANCH;
760                 mii.dwTypeData = (LPWSTR) buffer;
761                 SetMenuItemInfo(hContextMenu, 0, TRUE, &mii);
762 
763                 /* Remove any existing suggestions */
764                 memset(&mii, 0, sizeof(mii));
765                 mii.cbSize = sizeof(mii);
766                 mii.fMask = MIIM_ID;
767                 GetMenuItemInfo(hContextMenu, GetMenuItemCount(hContextMenu) - 1, TRUE, &mii);
768                 if ((mii.wID >= ID_TREE_SUGGESTION_MIN) && (mii.wID <= ID_TREE_SUGGESTION_MAX))
769                 {
770                     do
771                     {
772                         iLastPos = GetMenuItemCount(hContextMenu) - 1;
773                         GetMenuItemInfo(hContextMenu, iLastPos, TRUE, &mii);
774                         RemoveMenu(hContextMenu, iLastPos, MF_BYPOSITION);
775                     }
776                     while((mii.wID >= ID_TREE_SUGGESTION_MIN) && (mii.wID <= ID_TREE_SUGGESTION_MAX));
777                 }
778 
779                 /* Come up with suggestions */
780                 keyPath = GetItemPath(g_pChildWnd->hTreeWnd, NULL, &hRootKey);
781                 SuggestKeys(hRootKey, keyPath, Suggestions, COUNT_OF(Suggestions));
782                 if (Suggestions[0])
783                 {
784                     AppendMenu(hContextMenu, MF_SEPARATOR, 0, NULL);
785 
786                     LoadStringW(hInst, IDS_GOTO_SUGGESTED_KEY, resource, COUNT_OF(resource));
787 
788                     s = Suggestions;
789                     wID = ID_TREE_SUGGESTION_MIN;
790                     while(*s && (wID <= ID_TREE_SUGGESTION_MAX))
791                     {
792                         _snwprintf(buffer, COUNT_OF(buffer), resource, s);
793 
794                         memset(&mii, 0, sizeof(mii));
795                         mii.cbSize = sizeof(mii);
796                         mii.fMask = MIIM_STRING | MIIM_ID;
797                         mii.wID = wID++;
798                         mii.dwTypeData = buffer;
799                         InsertMenuItem(hContextMenu, GetMenuItemCount(hContextMenu), TRUE, &mii);
800 
801                         s += wcslen(s) + 1;
802                     }
803                 }
804                 TrackPopupMenu(hContextMenu, TPM_RIGHTBUTTON, pt.x, pt.y, 0, g_pChildWnd->hWnd, NULL);
805             }
806         }
807         break;
808     }
809 
810     case WM_SIZE:
811         if (wParam != SIZE_MINIMIZED && g_pChildWnd != NULL)
812         {
813             ResizeWnd(LOWORD(lParam), HIWORD(lParam));
814         }
815         /* fall through */
816     default:
817 def:
818         return DefWindowProcW(hWnd, message, wParam, lParam);
819     }
820     return 0;
821 }
822