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     RECT rc;
389 
390     switch (message)
391     {
392     case WM_CREATE:
393     {
394         WNDPROC oldproc;
395         HFONT hFont;
396         WCHAR buffer[MAX_PATH];
397         DWORD style;
398 
399         /* Load "My Computer" string */
400         LoadStringW(hInst, IDS_MY_COMPUTER, buffer, COUNT_OF(buffer));
401 
402         g_pChildWnd = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ChildWnd));
403         if (!g_pChildWnd) return 0;
404 
405         wcsncpy(g_pChildWnd->szPath, buffer, MAX_PATH);
406         g_pChildWnd->nSplitPos = 190;
407         g_pChildWnd->hWnd = hWnd;
408 
409         style = WS_CHILD | WS_VISIBLE | WS_TABSTOP;
410         g_pChildWnd->hAddressBarWnd = CreateWindowExW(WS_EX_CLIENTEDGE, L"Edit", NULL, style,
411                                                       CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
412                                                       hWnd, (HMENU)0, hInst, 0);
413 
414         style = WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_ICON | BS_CENTER |
415                 BS_VCENTER | BS_FLAT | BS_DEFPUSHBUTTON;
416         g_pChildWnd->hAddressBtnWnd = CreateWindowExW(0, L"Button", L"\x00BB", style,
417                                                       CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
418                                                       hWnd, (HMENU)0, hInst, 0);
419         g_pChildWnd->hArrowIcon = (HICON)LoadImageW(hInst, MAKEINTRESOURCEW(IDI_ARROW),
420                                                     IMAGE_ICON, 12, 12, 0);
421         SendMessageW(g_pChildWnd->hAddressBtnWnd, BM_SETIMAGE, IMAGE_ICON, (LPARAM)g_pChildWnd->hArrowIcon);
422 
423         GetClientRect(hWnd, &rc);
424         g_pChildWnd->hTreeWnd = CreateTreeView(hWnd, g_pChildWnd->szPath, (HMENU) TREE_WINDOW);
425         g_pChildWnd->hListWnd = CreateListView(hWnd, (HMENU) LIST_WINDOW, rc.right - g_pChildWnd->nSplitPos);
426         SetFocus(g_pChildWnd->hTreeWnd);
427 
428         /* set the address bar and button font */
429         if ((g_pChildWnd->hAddressBarWnd) && (g_pChildWnd->hAddressBtnWnd))
430         {
431             hFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
432             SendMessageW(g_pChildWnd->hAddressBarWnd,
433                          WM_SETFONT,
434                          (WPARAM)hFont,
435                          0);
436             SendMessageW(g_pChildWnd->hAddressBtnWnd,
437                          WM_SETFONT,
438                          (WPARAM)hFont,
439                          0);
440         }
441         /* Subclass the AddressBar */
442         oldproc = (WNDPROC)GetWindowLongPtr(g_pChildWnd->hAddressBarWnd, GWLP_WNDPROC);
443         SetWindowLongPtr(g_pChildWnd->hAddressBarWnd, GWLP_USERDATA, (DWORD_PTR)oldproc);
444         SetWindowLongPtr(g_pChildWnd->hAddressBarWnd, GWLP_WNDPROC, (DWORD_PTR)AddressBarProc);
445         break;
446     }
447     case WM_COMMAND:
448         if(HIWORD(wParam) == BN_CLICKED)
449         {
450             PostMessageW(g_pChildWnd->hAddressBarWnd, WM_KEYUP, VK_RETURN, 0);
451         }
452 
453         if (!ChildWnd_CmdWndProc(hWnd, message, wParam, lParam))
454         {
455             goto def;
456         }
457         break;
458     case WM_SETCURSOR:
459         if (LOWORD(lParam) == HTCLIENT)
460         {
461             POINT pt;
462             GetCursorPos(&pt);
463             ScreenToClient(hWnd, &pt);
464             if (pt.x>=g_pChildWnd->nSplitPos-SPLIT_WIDTH/2 && pt.x<g_pChildWnd->nSplitPos+SPLIT_WIDTH/2+1)
465             {
466                 SetCursor(LoadCursorW(0, IDC_SIZEWE));
467                 return TRUE;
468             }
469         }
470         goto def;
471     case WM_DESTROY:
472         DestroyListView(g_pChildWnd->hListWnd);
473         DestroyTreeView(g_pChildWnd->hTreeWnd);
474         DestroyMainMenu();
475         DestroyIcon(g_pChildWnd->hArrowIcon);
476         HeapFree(GetProcessHeap(), 0, g_pChildWnd);
477         g_pChildWnd = NULL;
478         PostQuitMessage(0);
479         break;
480     case WM_LBUTTONDOWN:
481     {
482         RECT rt;
483         int x = (short)LOWORD(lParam);
484         GetClientRect(hWnd, &rt);
485         if (x>=g_pChildWnd->nSplitPos-SPLIT_WIDTH/2 && x<g_pChildWnd->nSplitPos+SPLIT_WIDTH/2+1)
486         {
487             last_split = g_pChildWnd->nSplitPos;
488             draw_splitbar(hWnd, last_split);
489             SetCapture(hWnd);
490         }
491         break;
492     }
493 
494     case WM_LBUTTONUP:
495     case WM_RBUTTONDOWN:
496         if (GetCapture() == hWnd)
497         {
498             finish_splitbar(hWnd, LOWORD(lParam));
499         }
500         break;
501 
502     case WM_CAPTURECHANGED:
503         if (GetCapture()==hWnd && last_split>=0)
504             draw_splitbar(hWnd, last_split);
505         break;
506 
507     case WM_KEYDOWN:
508         if (wParam == VK_ESCAPE)
509             if (GetCapture() == hWnd)
510             {
511                 RECT rt;
512                 draw_splitbar(hWnd, last_split);
513                 GetClientRect(hWnd, &rt);
514                 ResizeWnd(rt.right, rt.bottom);
515                 last_split = -1;
516                 ReleaseCapture();
517                 SetCursor(LoadCursorW(0, IDC_ARROW));
518             }
519         break;
520 
521     case WM_MOUSEMOVE:
522         if (GetCapture() == hWnd)
523         {
524             HDC hdc;
525             RECT rt;
526             HGDIOBJ OldObj;
527             int x = LOWORD(lParam);
528             if(!SizingPattern)
529             {
530                 const DWORD Pattern[4] = {0x5555AAAA, 0x5555AAAA, 0x5555AAAA, 0x5555AAAA};
531                 SizingPattern = CreateBitmap(8, 8, 1, 1, Pattern);
532             }
533             if(!SizingBrush)
534             {
535                 SizingBrush = CreatePatternBrush(SizingPattern);
536             }
537 
538             GetClientRect(hWnd, &rt);
539             x = (SHORT) min(max(x, SPLIT_MIN), rt.right - SPLIT_MIN);
540             if(last_split != x)
541             {
542                 rt.left = last_split-SPLIT_WIDTH/2;
543                 rt.right = last_split+SPLIT_WIDTH/2+1;
544                 hdc = GetDC(hWnd);
545                 OldObj = SelectObject(hdc, SizingBrush);
546                 PatBlt(hdc, rt.left, rt.top, rt.right - rt.left, rt.bottom - rt.top, PATINVERT);
547                 last_split = x;
548                 rt.left = x-SPLIT_WIDTH/2;
549                 rt.right = x+SPLIT_WIDTH/2+1;
550                 PatBlt(hdc, rt.left, rt.top, rt.right - rt.left, rt.bottom - rt.top, PATINVERT);
551                 SelectObject(hdc, OldObj);
552                 ReleaseDC(hWnd, hdc);
553             }
554         }
555         break;
556 
557     case WM_SETFOCUS:
558         if (g_pChildWnd != NULL)
559         {
560             SetFocus(g_pChildWnd->nFocusPanel? g_pChildWnd->hListWnd: g_pChildWnd->hTreeWnd);
561         }
562         break;
563 
564     case WM_TIMER:
565         break;
566 
567     case WM_NOTIFY:
568         if ((int)wParam == TREE_WINDOW && g_pChildWnd != NULL)
569         {
570             switch (((LPNMHDR)lParam)->code)
571             {
572             case TVN_ITEMEXPANDING:
573                 return !OnTreeExpanding(g_pChildWnd->hTreeWnd, (NMTREEVIEW*)lParam);
574             case TVN_SELCHANGED:
575             {
576                 NMTREEVIEW* pnmtv = (NMTREEVIEW*)lParam;
577                 /* Get the parent of the current item */
578                 HTREEITEM hParentItem = TreeView_GetParent(g_pChildWnd->hTreeWnd, pnmtv->itemNew.hItem);
579 
580                 UpdateAddress(pnmtv->itemNew.hItem, NULL, NULL);
581 
582                 /*
583                  * Disable Delete/Rename menu options for 'My Computer' (first item so doesn't have any parent)
584                  * and HKEY_* keys (their parent is 'My Computer' and the previous remark applies).
585                  */
586                 if (!hParentItem || !TreeView_GetParent(g_pChildWnd->hTreeWnd, hParentItem))
587                 {
588                     EnableMenuItem(hMenuFrame , ID_EDIT_DELETE, MF_BYCOMMAND | MF_GRAYED);
589                     EnableMenuItem(hMenuFrame , ID_EDIT_RENAME, MF_BYCOMMAND | MF_GRAYED);
590                     EnableMenuItem(hPopupMenus, ID_TREE_DELETE, MF_BYCOMMAND | MF_GRAYED);
591                     EnableMenuItem(hPopupMenus, ID_TREE_RENAME, MF_BYCOMMAND | MF_GRAYED);
592                 }
593                 else
594                 {
595                     EnableMenuItem(hMenuFrame , ID_EDIT_DELETE, MF_BYCOMMAND | MF_ENABLED);
596                     EnableMenuItem(hMenuFrame , ID_EDIT_RENAME, MF_BYCOMMAND | MF_ENABLED);
597                     EnableMenuItem(hPopupMenus, ID_TREE_DELETE, MF_BYCOMMAND | MF_ENABLED);
598                     EnableMenuItem(hPopupMenus, ID_TREE_RENAME, MF_BYCOMMAND | MF_ENABLED);
599                 }
600 
601                 break;
602             }
603             case NM_SETFOCUS:
604                 g_pChildWnd->nFocusPanel = 0;
605                 break;
606             case TVN_BEGINLABELEDIT:
607             {
608                 LPNMTVDISPINFO ptvdi;
609                 /* cancel label edit for rootkeys  */
610                 ptvdi = (LPNMTVDISPINFO) lParam;
611                 if (!TreeView_GetParent(g_pChildWnd->hTreeWnd, ptvdi->item.hItem) ||
612                     !TreeView_GetParent(g_pChildWnd->hTreeWnd, TreeView_GetParent(g_pChildWnd->hTreeWnd, ptvdi->item.hItem)))
613                     return TRUE;
614                 break;
615             }
616             case TVN_ENDLABELEDIT:
617             {
618                 LPCWSTR keyPath;
619                 HKEY hRootKey;
620                 HKEY hKey = NULL;
621                 LPNMTVDISPINFO ptvdi;
622                 LONG lResult = TRUE;
623                 WCHAR szBuffer[MAX_PATH];
624 
625                 ptvdi = (LPNMTVDISPINFO) lParam;
626                 if (ptvdi->item.pszText)
627                 {
628                     keyPath = GetItemPath(g_pChildWnd->hTreeWnd, TreeView_GetParent(g_pChildWnd->hTreeWnd, ptvdi->item.hItem), &hRootKey);
629                     _snwprintf(szBuffer, COUNT_OF(szBuffer), L"%s\\%s", keyPath, ptvdi->item.pszText);
630                     keyPath = GetItemPath(g_pChildWnd->hTreeWnd, ptvdi->item.hItem, &hRootKey);
631                     if (RegOpenKeyExW(hRootKey, szBuffer, 0, KEY_READ, &hKey) == ERROR_SUCCESS)
632                     {
633                         lResult = FALSE;
634                         RegCloseKey(hKey);
635                         TreeView_EditLabel(g_pChildWnd->hTreeWnd, ptvdi->item.hItem);
636                     }
637                     else
638                     {
639                         if (RenameKey(hRootKey, keyPath, ptvdi->item.pszText) != ERROR_SUCCESS)
640                             lResult = FALSE;
641                         else
642                             UpdateAddress(ptvdi->item.hItem, hRootKey, szBuffer);
643                     }
644                     return lResult;
645                 }
646             }
647             default:
648                 return 0;
649             }
650         }
651         else
652         {
653             if ((int)wParam == LIST_WINDOW && g_pChildWnd != NULL)
654             {
655                 switch (((LPNMHDR)lParam)->code)
656                 {
657                 case NM_SETFOCUS:
658                     g_pChildWnd->nFocusPanel = 1;
659                     break;
660                 default:
661                     if(!ListWndNotifyProc(g_pChildWnd->hListWnd, wParam, lParam, &Result))
662                     {
663                         goto def;
664                     }
665                     return Result;
666                     break;
667                 }
668             }
669         }
670         break;
671 
672     case WM_CONTEXTMENU:
673     {
674         POINT pt;
675         if((HWND)wParam == g_pChildWnd->hListWnd)
676         {
677             int i, cnt;
678             BOOL IsDefault;
679             pt.x = (short) LOWORD(lParam);
680             pt.y = (short) HIWORD(lParam);
681             cnt = ListView_GetSelectedCount(g_pChildWnd->hListWnd);
682             i = ListView_GetNextItem(g_pChildWnd->hListWnd, -1, LVNI_FOCUSED | LVNI_SELECTED);
683             if (pt.x == -1 && pt.y == -1)
684             {
685                 RECT rc;
686                 if (i != -1)
687                 {
688                     rc.left = LVIR_BOUNDS;
689                     SendMessageW(g_pChildWnd->hListWnd, LVM_GETITEMRECT, i, (LPARAM) &rc);
690                     pt.x = rc.left + 8;
691                     pt.y = rc.top + 8;
692                 }
693                 else
694                     pt.x = pt.y = 0;
695                 ClientToScreen(g_pChildWnd->hListWnd, &pt);
696             }
697             if(i == -1)
698             {
699                 TrackPopupMenu(GetSubMenu(hPopupMenus, PM_NEW), TPM_RIGHTBUTTON, pt.x, pt.y, 0, hFrameWnd, NULL);
700             }
701             else
702             {
703                 HMENU mnu = GetSubMenu(hPopupMenus, PM_MODIFYVALUE);
704                 SetMenuDefaultItem(mnu, ID_EDIT_MODIFY, MF_BYCOMMAND);
705                 IsDefault = IsDefaultValue(g_pChildWnd->hListWnd, i);
706                 if(cnt == 1)
707                     EnableMenuItem(mnu, ID_EDIT_RENAME, MF_BYCOMMAND | (IsDefault ? MF_DISABLED | MF_GRAYED : MF_ENABLED));
708                 else
709                     EnableMenuItem(mnu, ID_EDIT_RENAME, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
710                 EnableMenuItem(mnu, ID_EDIT_MODIFY, MF_BYCOMMAND | (cnt == 1 ? MF_ENABLED : MF_DISABLED | MF_GRAYED));
711                 EnableMenuItem(mnu, ID_EDIT_MODIFY_BIN, MF_BYCOMMAND | (cnt == 1 ? MF_ENABLED : MF_DISABLED | MF_GRAYED));
712 
713                 TrackPopupMenu(mnu, TPM_RIGHTBUTTON, pt.x, pt.y, 0, hFrameWnd, NULL);
714             }
715         }
716         else if ((HWND)wParam == g_pChildWnd->hTreeWnd)
717         {
718             TVHITTESTINFO hti;
719             HMENU hContextMenu;
720             TVITEMW item;
721             MENUITEMINFOW mii;
722             WCHAR resource[256];
723             WCHAR buffer[256];
724             LPWSTR s;
725             LPCWSTR keyPath;
726             HKEY hRootKey;
727             int iLastPos;
728             WORD wID;
729 
730             pt.x = (short) LOWORD(lParam);
731             pt.y = (short) HIWORD(lParam);
732 
733             if (pt.x == -1 && pt.y == -1)
734             {
735                 RECT rc;
736                 hti.hItem = TreeView_GetSelection(g_pChildWnd->hTreeWnd);
737                 if (hti.hItem != NULL)
738                 {
739                     TreeView_GetItemRect(g_pChildWnd->hTreeWnd, hti.hItem, &rc, TRUE);
740                     pt.x = rc.left + 8;
741                     pt.y = rc.top + 8;
742                     ClientToScreen(g_pChildWnd->hTreeWnd, &pt);
743                     hti.flags = TVHT_ONITEM;
744                 }
745                 else
746                     hti.flags = 0;
747             }
748             else
749             {
750                 hti.pt.x = pt.x;
751                 hti.pt.y = pt.y;
752                 ScreenToClient(g_pChildWnd->hTreeWnd, &hti.pt);
753                 TreeView_HitTest(g_pChildWnd->hTreeWnd, &hti);
754             }
755 
756             if (hti.flags & TVHT_ONITEM)
757             {
758                 hContextMenu = GetSubMenu(hPopupMenus, PM_TREECONTEXT);
759                 TreeView_SelectItem(g_pChildWnd->hTreeWnd, hti.hItem);
760 
761                 memset(&item, 0, sizeof(item));
762                 item.mask = TVIF_STATE | TVIF_CHILDREN;
763                 item.hItem = hti.hItem;
764                 TreeView_GetItem(g_pChildWnd->hTreeWnd, &item);
765 
766                 /* Set the Expand/Collapse menu item appropriately */
767                 LoadStringW(hInst, (item.state & TVIS_EXPANDED) ? IDS_COLLAPSE : IDS_EXPAND, buffer, COUNT_OF(buffer));
768                 memset(&mii, 0, sizeof(mii));
769                 mii.cbSize = sizeof(mii);
770                 mii.fMask = MIIM_STRING | MIIM_STATE | MIIM_ID;
771                 mii.fState = (item.cChildren > 0) ? MFS_DEFAULT : MFS_GRAYED;
772                 mii.wID = (item.state & TVIS_EXPANDED) ? ID_TREE_COLLAPSEBRANCH : ID_TREE_EXPANDBRANCH;
773                 mii.dwTypeData = (LPWSTR) buffer;
774                 SetMenuItemInfo(hContextMenu, 0, TRUE, &mii);
775 
776                 /* Remove any existing suggestions */
777                 memset(&mii, 0, sizeof(mii));
778                 mii.cbSize = sizeof(mii);
779                 mii.fMask = MIIM_ID;
780                 GetMenuItemInfo(hContextMenu, GetMenuItemCount(hContextMenu) - 1, TRUE, &mii);
781                 if ((mii.wID >= ID_TREE_SUGGESTION_MIN) && (mii.wID <= ID_TREE_SUGGESTION_MAX))
782                 {
783                     do
784                     {
785                         iLastPos = GetMenuItemCount(hContextMenu) - 1;
786                         GetMenuItemInfo(hContextMenu, iLastPos, TRUE, &mii);
787                         RemoveMenu(hContextMenu, iLastPos, MF_BYPOSITION);
788                     }
789                     while((mii.wID >= ID_TREE_SUGGESTION_MIN) && (mii.wID <= ID_TREE_SUGGESTION_MAX));
790                 }
791 
792                 /* Come up with suggestions */
793                 keyPath = GetItemPath(g_pChildWnd->hTreeWnd, NULL, &hRootKey);
794                 SuggestKeys(hRootKey, keyPath, Suggestions, COUNT_OF(Suggestions));
795                 if (Suggestions[0])
796                 {
797                     AppendMenu(hContextMenu, MF_SEPARATOR, 0, NULL);
798 
799                     LoadStringW(hInst, IDS_GOTO_SUGGESTED_KEY, resource, COUNT_OF(resource));
800 
801                     s = Suggestions;
802                     wID = ID_TREE_SUGGESTION_MIN;
803                     while(*s && (wID <= ID_TREE_SUGGESTION_MAX))
804                     {
805                         _snwprintf(buffer, COUNT_OF(buffer), resource, s);
806 
807                         memset(&mii, 0, sizeof(mii));
808                         mii.cbSize = sizeof(mii);
809                         mii.fMask = MIIM_STRING | MIIM_ID;
810                         mii.wID = wID++;
811                         mii.dwTypeData = buffer;
812                         InsertMenuItem(hContextMenu, GetMenuItemCount(hContextMenu), TRUE, &mii);
813 
814                         s += wcslen(s) + 1;
815                     }
816                 }
817                 TrackPopupMenu(hContextMenu, TPM_RIGHTBUTTON, pt.x, pt.y, 0, g_pChildWnd->hWnd, NULL);
818             }
819         }
820         break;
821     }
822 
823     case WM_SIZE:
824         if (wParam != SIZE_MINIMIZED && g_pChildWnd != NULL)
825         {
826             ResizeWnd(LOWORD(lParam), HIWORD(lParam));
827         }
828         /* fall through */
829     default:
830 def:
831         return DefWindowProcW(hWnd, message, wParam, lParam);
832     }
833     return 0;
834 }
835