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