1 /*
2  * Regedit treeview
3  *
4  * Copyright (C) 2002 Robert Dickenson <robd@reactos.org>
5  * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later)
6  */
7 
8 #include "regedit.h"
9 
10 /* Global variables and constants */
11 /* Image_Open, Image_Closed, and Image_Root - integer variables for indexes of the images */
12 static int Image_Open;
13 static int Image_Closed;
14 static int Image_Root;
15 
16 static LPWSTR pathBuffer;
17 
18 #define NUM_ICONS   3 /* number of icons to add to the image list */
19 
20 /* External resources in shell32.dll */
21 #define IDI_SHELL_FOLDER             4
22 #define IDI_SHELL_FOLDER_OPEN        5
23 #define IDI_SHELL_MY_COMPUTER       16
24 
25 static BOOL get_item_path(HWND hwndTV, HTREEITEM hItem, HKEY* phKey, LPWSTR* pKeyPath, int* pPathLen, int* pMaxLen)
26 {
27     TVITEMW item;
28     size_t maxLen, len;
29     LPWSTR newStr;
30 
31     item.mask = TVIF_PARAM;
32     item.hItem = hItem;
33     if (!TreeView_GetItem(hwndTV, &item)) return FALSE;
34 
35     if (item.lParam)
36     {
37         /* found root key with valid key value */
38         *phKey = (HKEY)item.lParam;
39         return TRUE;
40     }
41 
42     if(!get_item_path(hwndTV, TreeView_GetParent(hwndTV, hItem), phKey, pKeyPath, pPathLen, pMaxLen)) return FALSE;
43     if (*pPathLen)
44     {
45         (*pKeyPath)[*pPathLen] = L'\\';
46         ++(*pPathLen);
47     }
48 
49     do
50     {
51         item.mask = TVIF_TEXT;
52         item.hItem = hItem;
53         item.pszText = *pKeyPath + *pPathLen;
54         maxLen = *pMaxLen - *pPathLen;
55         item.cchTextMax = (int) maxLen;
56         if (!TreeView_GetItem(hwndTV, &item)) return FALSE;
57         len = wcslen(item.pszText);
58         if (len < maxLen - 1)
59         {
60             *pPathLen += (int) len;
61             break;
62         }
63         newStr = HeapReAlloc(GetProcessHeap(), 0, *pKeyPath, *pMaxLen * 2);
64         if (!newStr) return FALSE;
65         *pKeyPath = newStr;
66         *pMaxLen *= 2;
67     }
68     while(TRUE);
69 
70     return TRUE;
71 }
72 
73 LPCWSTR GetItemPath(HWND hwndTV, HTREEITEM hItem, HKEY* phRootKey)
74 {
75     int pathLen = 0, maxLen;
76 
77     *phRootKey = NULL;
78 
79     if (!pathBuffer)
80         pathBuffer = HeapAlloc(GetProcessHeap(), 0, 1024);
81     if (!pathBuffer)
82         return NULL;
83 
84     *pathBuffer = UNICODE_NULL;
85 
86     maxLen = (int)HeapSize(GetProcessHeap(), 0, pathBuffer);
87 
88     if (!hItem)
89     {
90         hItem = TreeView_GetSelection(hwndTV);
91     }
92     if (maxLen == -1 || !hItem || !get_item_path(hwndTV, hItem, phRootKey, &pathBuffer, &pathLen, &maxLen))
93     {
94         return NULL;
95     }
96     return pathBuffer;
97 }
98 
99 BOOL DeleteNode(HWND hwndTV, HTREEITEM hItem)
100 {
101     if (!hItem) hItem = TreeView_GetSelection(hwndTV);
102     if (!hItem) return FALSE;
103     return TreeView_DeleteItem(hwndTV, hItem);
104 }
105 
106 /* Add an entry to the tree. Only give hKey for root nodes (HKEY_ constants) */
107 static HTREEITEM AddEntryToTree(HWND hwndTV, HTREEITEM hParent, LPWSTR label, HKEY hKey, DWORD dwChildren)
108 {
109     TVITEMW tvi;
110     TVINSERTSTRUCTW tvins;
111 
112     if (hKey)
113     {
114         if (RegQueryInfoKeyW(hKey, 0, 0, 0, &dwChildren, 0, 0, 0, 0, 0, 0, 0) != ERROR_SUCCESS)
115         {
116             dwChildren = 0;
117         }
118     }
119 
120     tvi.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_CHILDREN | TVIF_PARAM;
121     tvi.pszText = label;
122     tvi.cchTextMax = wcslen(tvi.pszText);
123     tvi.iImage = Image_Closed;
124     tvi.iSelectedImage = Image_Open;
125     tvi.cChildren = dwChildren;
126     tvi.lParam = (LPARAM)hKey;
127     tvins.item = tvi;
128     tvins.hInsertAfter = (HTREEITEM)(hKey ? TVI_LAST : TVI_FIRST);
129     tvins.hParent = hParent;
130     return TreeView_InsertItem(hwndTV, &tvins);
131 }
132 
133 BOOL RefreshTreeItem(HWND hwndTV, HTREEITEM hItem)
134 {
135     HKEY hRoot, hKey, hSubKey;
136     HTREEITEM childItem;
137     LPCWSTR KeyPath;
138     DWORD dwCount, dwIndex, dwMaxSubKeyLen;
139     LPWSTR Name = NULL;
140     TVITEMW tvItem;
141     LPWSTR pszNodes = NULL;
142     BOOL bSuccess = FALSE;
143     LPWSTR s;
144     BOOL bAddedAny;
145 
146     KeyPath = GetItemPath(hwndTV, hItem, &hRoot);
147 
148     if (*KeyPath)
149     {
150         if (RegOpenKeyExW(hRoot, KeyPath, 0, KEY_READ, &hKey) != ERROR_SUCCESS)
151         {
152             goto done;
153         }
154     }
155     else
156     {
157         hKey = hRoot;
158     }
159 
160     if (RegQueryInfoKeyW(hKey, 0, 0, 0, &dwCount, &dwMaxSubKeyLen, 0, 0, 0, 0, 0, 0) != ERROR_SUCCESS)
161     {
162         goto done;
163     }
164 
165     /* Set the number of children again */
166     tvItem.mask = TVIF_CHILDREN;
167     tvItem.hItem = hItem;
168     tvItem.cChildren = dwCount;
169     if (!TreeView_SetItem(hwndTV, &tvItem))
170     {
171         goto done;
172     }
173 
174     /* We don't have to bother with the rest if it's not expanded. */
175     if (TreeView_GetItemState(hwndTV, hItem, TVIS_EXPANDED) == 0)
176     {
177         RegCloseKey(hKey);
178         bSuccess = TRUE;
179         goto done;
180     }
181 
182     dwMaxSubKeyLen++; /* account for the \0 terminator */
183     if (!(Name = HeapAlloc(GetProcessHeap(), 0, dwMaxSubKeyLen * sizeof(WCHAR))))
184     {
185         goto done;
186     }
187     tvItem.cchTextMax = dwMaxSubKeyLen;
188     /*if (!(tvItem.pszText = HeapAlloc(GetProcessHeap(), 0, dwMaxSubKeyLen * sizeof(WCHAR)))) {
189         goto done;
190     }*/
191 
192     /* Get all of the tree node siblings in one contiguous block of memory */
193     {
194         DWORD dwPhysicalSize = 0;
195         DWORD dwActualSize = 0;
196         DWORD dwNewPhysicalSize;
197         LPWSTR pszNewNodes;
198         DWORD dwStep = 10000;
199 
200         for (childItem = TreeView_GetChild(hwndTV, hItem); childItem;
201                 childItem = TreeView_GetNextSibling(hwndTV, childItem))
202         {
203 
204             if (dwActualSize + dwMaxSubKeyLen + 1 > dwPhysicalSize)
205             {
206                 dwNewPhysicalSize = dwActualSize + dwMaxSubKeyLen + 1 + dwStep;
207 
208                 if (pszNodes)
209                     pszNewNodes = (LPWSTR) HeapReAlloc(GetProcessHeap(), 0, pszNodes, dwNewPhysicalSize * sizeof(WCHAR));
210                 else
211                     pszNewNodes = (LPWSTR) HeapAlloc(GetProcessHeap(), 0, dwNewPhysicalSize * sizeof(WCHAR));
212                 if (!pszNewNodes)
213                     goto done;
214 
215                 dwPhysicalSize = dwNewPhysicalSize;
216                 pszNodes = pszNewNodes;
217             }
218 
219             tvItem.mask = TVIF_TEXT;
220             tvItem.hItem = childItem;
221             tvItem.pszText = &pszNodes[dwActualSize];
222             tvItem.cchTextMax = dwPhysicalSize - dwActualSize;
223             if (!TreeView_GetItem(hwndTV, &tvItem))
224                 goto done;
225 
226             dwActualSize += (DWORD) wcslen(&pszNodes[dwActualSize]) + 1;
227         }
228 
229         if (pszNodes)
230             pszNodes[dwActualSize] = L'\0';
231     }
232 
233     /* Now go through all the children in the tree, and check if any have to be removed. */
234     childItem = TreeView_GetChild(hwndTV, hItem);
235     while (childItem)
236     {
237         HTREEITEM nextItem = TreeView_GetNextSibling(hwndTV, childItem);
238         if (RefreshTreeItem(hwndTV, childItem) == FALSE)
239         {
240             (void)TreeView_DeleteItem(hwndTV, childItem);
241         }
242         childItem = nextItem;
243     }
244 
245     /* Now go through all the children in the registry, and check if any have to be added. */
246     bAddedAny = FALSE;
247     for (dwIndex = 0; dwIndex < dwCount; dwIndex++)
248     {
249         DWORD cName = dwMaxSubKeyLen, dwSubCount;
250         BOOL found;
251 
252         found = FALSE;
253         if (RegEnumKeyExW(hKey, dwIndex, Name, &cName, 0, 0, 0, NULL) != ERROR_SUCCESS)
254         {
255             continue;
256         }
257 
258         /* Check if the node is already in there. */
259         if (pszNodes)
260         {
261             for (s = pszNodes; *s; s += wcslen(s) + 1)
262             {
263                 if (!wcscmp(s, Name))
264                 {
265                     found = TRUE;
266                     break;
267                 }
268             }
269         }
270 
271         if (found == FALSE)
272         {
273             /* Find the number of children of the node. */
274             dwSubCount = 0;
275             if (RegOpenKeyExW(hKey, Name, 0, KEY_QUERY_VALUE, &hSubKey) == ERROR_SUCCESS)
276             {
277                 if (RegQueryInfoKeyW(hSubKey, 0, 0, 0, &dwSubCount, 0, 0, 0, 0, 0, 0, 0) != ERROR_SUCCESS)
278                 {
279                     dwSubCount = 0;
280                 }
281                 RegCloseKey(hSubKey);
282             }
283 
284             AddEntryToTree(hwndTV, hItem, Name, NULL, dwSubCount);
285             bAddedAny = TRUE;
286         }
287     }
288     RegCloseKey(hKey);
289 
290     if (bAddedAny)
291         SendMessageW(hwndTV, TVM_SORTCHILDREN, 0, (LPARAM) hItem);
292 
293     bSuccess = TRUE;
294 
295 done:
296     if (pszNodes)
297         HeapFree(GetProcessHeap(), 0, pszNodes);
298     if (Name)
299         HeapFree(GetProcessHeap(), 0, Name);
300     return bSuccess;
301 }
302 
303 BOOL RefreshTreeView(HWND hwndTV)
304 {
305     HTREEITEM hItem;
306     HTREEITEM hSelectedItem;
307     HCURSOR hcursorOld;
308 
309     hSelectedItem = TreeView_GetSelection(hwndTV);
310     hcursorOld = SetCursor(LoadCursor(NULL, IDC_WAIT));
311     SendMessageW(hwndTV, WM_SETREDRAW, FALSE, 0);
312 
313     hItem = TreeView_GetChild(hwndTV, TreeView_GetRoot(hwndTV));
314     while (hItem)
315     {
316         RefreshTreeItem(hwndTV, hItem);
317         hItem = TreeView_GetNextSibling(hwndTV, hItem);
318     }
319 
320     SendMessageW(hwndTV, WM_SETREDRAW, TRUE, 0);
321     SetCursor(hcursorOld);
322 
323     /* We reselect the currently selected node, this will prompt a refresh of the listview. */
324     (void)TreeView_SelectItem(hwndTV, hSelectedItem);
325     return TRUE;
326 }
327 
328 HTREEITEM InsertNode(HWND hwndTV, HTREEITEM hItem, LPWSTR name)
329 {
330     WCHAR buf[MAX_NEW_KEY_LEN];
331     HTREEITEM hNewItem = 0;
332     TVITEMEXW item;
333 
334     /* Default to the current selection */
335     if (!hItem)
336     {
337         hItem = TreeView_GetSelection(hwndTV);
338         if (!hItem)
339             return FALSE;
340     }
341 
342     memset(&item, 0, sizeof(item));
343     item.hItem = hItem;
344     item.mask = TVIF_CHILDREN | TVIF_HANDLE | TVIF_STATE;
345     if (!TreeView_GetItem(hwndTV, &item))
346         return FALSE;
347 
348     if (item.state & TVIS_EXPANDEDONCE)
349     {
350         hNewItem = AddEntryToTree(hwndTV, hItem, name, 0, 0);
351         SendMessageW(hwndTV, TVM_SORTCHILDREN, 0, (LPARAM) hItem);
352     }
353     else
354     {
355         item.mask = TVIF_CHILDREN | TVIF_HANDLE;
356         item.hItem = hItem;
357         item.cChildren = 1;
358         if (!TreeView_SetItem(hwndTV, &item))
359             return FALSE;
360     }
361 
362     (void)TreeView_Expand(hwndTV, hItem, TVE_EXPAND);
363     if (!hNewItem)
364     {
365         for(hNewItem = TreeView_GetChild(hwndTV, hItem); hNewItem; hNewItem = TreeView_GetNextSibling(hwndTV, hNewItem))
366         {
367             item.mask = TVIF_HANDLE | TVIF_TEXT;
368             item.hItem = hNewItem;
369             item.pszText = buf;
370             item.cchTextMax = ARRAY_SIZE(buf);
371             if (!TreeView_GetItem(hwndTV, &item)) continue;
372             if (wcscmp(name, item.pszText) == 0) break;
373         }
374     }
375     if (hNewItem) (void)TreeView_SelectItem(hwndTV, hNewItem);
376 
377     return hNewItem;
378 }
379 
380 HWND StartKeyRename(HWND hwndTV)
381 {
382     HTREEITEM hItem;
383 
384     if(!(hItem = TreeView_GetSelection(hwndTV))) return 0;
385     return TreeView_EditLabel(hwndTV, hItem);
386 }
387 
388 static BOOL InitTreeViewItems(HWND hwndTV, LPWSTR pHostName)
389 {
390     TVITEMW tvi;
391     TVINSERTSTRUCTW tvins;
392     HTREEITEM hRoot;
393 
394     tvi.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_CHILDREN | TVIF_PARAM;
395     /* Set the text of the item.  */
396     tvi.pszText = pHostName;
397     tvi.cchTextMax = wcslen(tvi.pszText);
398     /* Assume the item is not a parent item, so give it an image.  */
399     tvi.iImage = Image_Root;
400     tvi.iSelectedImage = Image_Root;
401     tvi.cChildren = 5;
402     /* Save the heading level in the item's application-defined data area.  */
403     tvi.lParam = (LPARAM)NULL;
404     tvins.item = tvi;
405     tvins.hInsertAfter = (HTREEITEM)TVI_FIRST;
406     tvins.hParent = TVI_ROOT;
407     /* Add the item to the tree view control.  */
408     if (!(hRoot = TreeView_InsertItem(hwndTV, &tvins))) return FALSE;
409 
410     if (!AddEntryToTree(hwndTV, hRoot, L"HKEY_CLASSES_ROOT", HKEY_CLASSES_ROOT, 1)) return FALSE;
411     if (!AddEntryToTree(hwndTV, hRoot, L"HKEY_CURRENT_USER", HKEY_CURRENT_USER, 1)) return FALSE;
412     if (!AddEntryToTree(hwndTV, hRoot, L"HKEY_LOCAL_MACHINE", HKEY_LOCAL_MACHINE, 1)) return FALSE;
413     if (!AddEntryToTree(hwndTV, hRoot, L"HKEY_USERS", HKEY_USERS, 1)) return FALSE;
414     if (!AddEntryToTree(hwndTV, hRoot, L"HKEY_CURRENT_CONFIG", HKEY_CURRENT_CONFIG, 1)) return FALSE;
415 
416     if (GetVersion() & 0x80000000)
417     {
418         /* Win9x specific key */
419         if (!AddEntryToTree(hwndTV, hRoot, L"HKEY_DYN_DATA", HKEY_DYN_DATA, 1)) return FALSE;
420     }
421 
422     /* expand and select host name */
423     (void)TreeView_Expand(hwndTV, hRoot, TVE_EXPAND);
424     (void)TreeView_Select(hwndTV, hRoot, TVGN_CARET);
425     return TRUE;
426 }
427 
428 /*
429  * InitTreeViewImageLists - creates an image list, adds three bitmaps
430  * to it, and associates the image list with a tree view control.
431  * Returns TRUE if successful, or FALSE otherwise.
432  * hwndTV - handle to the tree view control.
433  */
434 static BOOL InitTreeViewImageLists(HWND hwndTV)
435 {
436     HIMAGELIST himl;  /* handle to image list  */
437     HICON hico;       /* handle to icon  */
438     INT cx = GetSystemMetrics(SM_CXSMICON);
439     INT cy = GetSystemMetrics(SM_CYSMICON);
440     HMODULE hShell32 = GetModuleHandleW(L"shell32.dll");
441 
442     /* Create the image list.  */
443     if ((himl = ImageList_Create(cx, cy, ILC_MASK | ILC_COLOR32, 0, NUM_ICONS)) == NULL)
444         return FALSE;
445 
446     /* Add the open file, closed file, and document bitmaps.  */
447     hico = LoadImageW(hShell32,
448                       MAKEINTRESOURCEW(IDI_SHELL_FOLDER_OPEN),
449                       IMAGE_ICON,
450                       cx,
451                       cy,
452                       LR_DEFAULTCOLOR);
453     if (hico)
454     {
455         Image_Open = ImageList_AddIcon(himl, hico);
456         DestroyIcon(hico);
457     }
458 
459     hico = LoadImageW(hShell32,
460                       MAKEINTRESOURCEW(IDI_SHELL_FOLDER),
461                       IMAGE_ICON,
462                       cx,
463                       cy,
464                       LR_DEFAULTCOLOR);
465     if (hico)
466     {
467         Image_Closed = ImageList_AddIcon(himl, hico);
468         DestroyIcon(hico);
469     }
470 
471     hico = LoadImageW(hShell32,
472                       MAKEINTRESOURCEW(IDI_SHELL_MY_COMPUTER),
473                       IMAGE_ICON,
474                       cx,
475                       cy,
476                       LR_DEFAULTCOLOR);
477     if (hico)
478     {
479         Image_Root = ImageList_AddIcon(himl, hico);
480         DestroyIcon(hico);
481     }
482 
483     /* Fail if not all of the images were added.  */
484     if (ImageList_GetImageCount(himl) < NUM_ICONS)
485     {
486         ImageList_Destroy(himl);
487         return FALSE;
488     }
489 
490     /* Associate the image list with the tree view control.  */
491     (void)TreeView_SetImageList(hwndTV, himl, TVSIL_NORMAL);
492 
493     return TRUE;
494 }
495 
496 BOOL OnTreeExpanding(HWND hwndTV, NMTREEVIEW* pnmtv)
497 {
498     DWORD dwCount, dwIndex, dwMaxSubKeyLen;
499     HKEY hRoot, hNewKey, hKey;
500     LPCWSTR keyPath;
501     LPWSTR Name;
502     LONG errCode;
503     HCURSOR hcursorOld;
504 
505     static int expanding;
506     if (expanding) return FALSE;
507     if (pnmtv->itemNew.state & TVIS_EXPANDEDONCE )
508     {
509         return TRUE;
510     }
511     expanding = TRUE;
512     hcursorOld = SetCursor(LoadCursor(NULL, IDC_WAIT));
513     SendMessageW(hwndTV, WM_SETREDRAW, FALSE, 0);
514 
515     keyPath = GetItemPath(hwndTV, pnmtv->itemNew.hItem, &hRoot);
516     if (!keyPath) goto done;
517 
518     if (*keyPath)
519     {
520         errCode = RegOpenKeyExW(hRoot, keyPath, 0, KEY_READ, &hNewKey);
521         if (errCode != ERROR_SUCCESS) goto done;
522     }
523     else
524     {
525         hNewKey = hRoot;
526     }
527 
528     errCode = RegQueryInfoKeyW(hNewKey, 0, 0, 0, &dwCount, &dwMaxSubKeyLen, 0, 0, 0, 0, 0, 0);
529     if (errCode != ERROR_SUCCESS) goto done;
530     dwMaxSubKeyLen++; /* account for the \0 terminator */
531     Name = HeapAlloc(GetProcessHeap(), 0, dwMaxSubKeyLen * sizeof(WCHAR));
532     if (!Name) goto done;
533 
534     for (dwIndex = 0; dwIndex < dwCount; dwIndex++)
535     {
536         DWORD cName = dwMaxSubKeyLen, dwSubCount;
537 
538         errCode = RegEnumKeyExW(hNewKey, dwIndex, Name, &cName, 0, 0, 0, 0);
539         if (errCode != ERROR_SUCCESS) continue;
540         errCode = RegOpenKeyExW(hNewKey, Name, 0, KEY_QUERY_VALUE, &hKey);
541         if (errCode == ERROR_SUCCESS)
542         {
543             errCode = RegQueryInfoKeyW(hKey, 0, 0, 0, &dwSubCount, 0, 0, 0, 0, 0, 0, 0);
544             RegCloseKey(hKey);
545         }
546         if (errCode != ERROR_SUCCESS) dwSubCount = 0;
547         AddEntryToTree(hwndTV, pnmtv->itemNew.hItem, Name, NULL, dwSubCount);
548     }
549 
550     SendMessageW(hwndTV, TVM_SORTCHILDREN, 0, (LPARAM)pnmtv->itemNew.hItem);
551 
552     RegCloseKey(hNewKey);
553     HeapFree(GetProcessHeap(), 0, Name);
554 
555 done:
556     SendMessageW(hwndTV, WM_SETREDRAW, TRUE, 0);
557     SetCursor(hcursorOld);
558     expanding = FALSE;
559 
560     return TRUE;
561 }
562 
563 BOOL CreateNewKey(HWND hwndTV, HTREEITEM hItem)
564 {
565     WCHAR szNewKeyFormat[128];
566     WCHAR szNewKey[128];
567     LPCWSTR pszKeyPath;
568     int iIndex = 1;
569     LONG nResult;
570     HKEY hRootKey = NULL, hKey = NULL, hNewKey = NULL;
571     BOOL bSuccess = FALSE;
572     DWORD dwDisposition;
573     HTREEITEM hNewItem;
574 
575     pszKeyPath = GetItemPath(hwndTV, hItem, &hRootKey);
576     if (!pszKeyPath)
577         return bSuccess;
578     if (pszKeyPath[0] == L'\0')
579         hKey = hRootKey;
580     else if (RegOpenKeyW(hRootKey, pszKeyPath, &hKey) != ERROR_SUCCESS)
581         goto done;
582 
583     if (LoadStringW(hInst, IDS_NEW_KEY, szNewKeyFormat, ARRAY_SIZE(szNewKeyFormat)) <= 0)
584         goto done;
585 
586     /* Need to create a new key with a unique name */
587     do
588     {
589         wsprintf(szNewKey, szNewKeyFormat, iIndex++);
590         nResult = RegCreateKeyExW(hKey, szNewKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hNewKey, &dwDisposition);
591         if (hNewKey && dwDisposition == REG_OPENED_EXISTING_KEY)
592         {
593             RegCloseKey(hNewKey);
594             hNewKey = NULL;
595         }
596         else if (!hNewKey)
597         {
598             InfoMessageBox(hFrameWnd, MB_OK | MB_ICONERROR, NULL, L"Cannot create new key!\n\nError Code: %d", nResult);
599             goto done;
600         }
601     }
602     while(!hNewKey);
603 
604     /* Insert the new key */
605     hNewItem = InsertNode(hwndTV, hItem, szNewKey);
606     if (!hNewItem)
607         goto done;
608 
609     /* The new key's name is probably not appropriate yet */
610     (void)TreeView_EditLabel(hwndTV, hNewItem);
611 
612     bSuccess = TRUE;
613 
614 done:
615     if (hKey != hRootKey && hKey)
616         RegCloseKey(hKey);
617     if (hNewKey)
618         RegCloseKey(hNewKey);
619     return bSuccess;
620 }
621 
622 BOOL TreeWndNotifyProc(HWND hWnd, WPARAM wParam, LPARAM lParam, BOOL *Result)
623 {
624     UNREFERENCED_PARAMETER(wParam);
625     *Result = TRUE;
626 
627     switch (((LPNMHDR)lParam)->code)
628     {
629         case TVN_ITEMEXPANDING:
630             *Result = !OnTreeExpanding(g_pChildWnd->hTreeWnd, (NMTREEVIEW*)lParam);
631             return TRUE;
632         case TVN_SELCHANGED:
633         {
634             NMTREEVIEW* pnmtv = (NMTREEVIEW*)lParam;
635             /* Get the parent of the current item */
636             HTREEITEM hParentItem = TreeView_GetParent(g_pChildWnd->hTreeWnd, pnmtv->itemNew.hItem);
637 
638             UpdateAddress(pnmtv->itemNew.hItem, NULL, NULL, TRUE);
639 
640             /* Disable the Permissions and new key menu items for 'My Computer' */
641             EnableMenuItem(hMenuFrame, ID_EDIT_PERMISSIONS, MF_BYCOMMAND | (hParentItem ? MF_ENABLED : MF_GRAYED));
642             EnableMenuItem(hMenuFrame, ID_EDIT_NEW_KEY, MF_BYCOMMAND | (hParentItem ? MF_ENABLED : MF_GRAYED));
643 
644             /*
645              * Disable Delete/Rename menu options for 'My Computer' (first item so doesn't have any parent)
646              * and HKEY_* keys (their parent is 'My Computer' and the previous remark applies).
647              */
648             if (!hParentItem || !TreeView_GetParent(g_pChildWnd->hTreeWnd, hParentItem))
649             {
650                 EnableMenuItem(hMenuFrame , ID_EDIT_DELETE, MF_BYCOMMAND | MF_GRAYED);
651                 EnableMenuItem(hMenuFrame , ID_EDIT_RENAME, MF_BYCOMMAND | MF_GRAYED);
652                 EnableMenuItem(hPopupMenus, ID_TREE_DELETE, MF_BYCOMMAND | MF_GRAYED);
653                 EnableMenuItem(hPopupMenus, ID_TREE_RENAME, MF_BYCOMMAND | MF_GRAYED);
654             }
655             else
656             {
657                 EnableMenuItem(hMenuFrame , ID_EDIT_DELETE, MF_BYCOMMAND | MF_ENABLED);
658                 EnableMenuItem(hMenuFrame , ID_EDIT_RENAME, MF_BYCOMMAND | MF_ENABLED);
659                 EnableMenuItem(hPopupMenus, ID_TREE_DELETE, MF_BYCOMMAND | MF_ENABLED);
660                 EnableMenuItem(hPopupMenus, ID_TREE_RENAME, MF_BYCOMMAND | MF_ENABLED);
661             }
662 
663             return TRUE;
664         }
665         case NM_SETFOCUS:
666             g_pChildWnd->nFocusPanel = 0;
667             break;
668         case TVN_BEGINLABELEDIT:
669         {
670             LPNMTVDISPINFO ptvdi = (LPNMTVDISPINFO)lParam;
671             /* cancel label edit for rootkeys */
672             if (!TreeView_GetParent(g_pChildWnd->hTreeWnd, ptvdi->item.hItem) ||
673                 !TreeView_GetParent(g_pChildWnd->hTreeWnd, TreeView_GetParent(g_pChildWnd->hTreeWnd, ptvdi->item.hItem)))
674             {
675                 *Result = TRUE;
676             }
677             else
678             {
679                 *Result = FALSE;
680             }
681             return TRUE;
682         }
683         case TVN_ENDLABELEDIT:
684         {
685             LPCWSTR keyPath;
686             HKEY hRootKey;
687             HKEY hKey = NULL;
688             LPNMTVDISPINFO ptvdi = (LPNMTVDISPINFO)lParam;
689             LONG nRenResult;
690             LONG lResult = TRUE;
691             WCHAR szBuffer[MAX_PATH];
692             WCHAR Caption[128];
693 
694             if (ptvdi->item.pszText)
695             {
696                 keyPath = GetItemPath(g_pChildWnd->hTreeWnd, TreeView_GetParent(g_pChildWnd->hTreeWnd, ptvdi->item.hItem), &hRootKey);
697                 if (wcslen(keyPath))
698                     _snwprintf(szBuffer, ARRAY_SIZE(szBuffer), L"%s\\%s", keyPath, ptvdi->item.pszText);
699                 else
700                     _snwprintf(szBuffer, ARRAY_SIZE(szBuffer), L"%s", ptvdi->item.pszText);
701                 keyPath = GetItemPath(g_pChildWnd->hTreeWnd, ptvdi->item.hItem, &hRootKey);
702                 if (RegOpenKeyExW(hRootKey, szBuffer, 0, KEY_READ, &hKey) == ERROR_SUCCESS)
703                 {
704                     lResult = FALSE;
705                     RegCloseKey(hKey);
706                     TreeView_EditLabel(g_pChildWnd->hTreeWnd, ptvdi->item.hItem);
707                 }
708                 else
709                 {
710                     nRenResult = RenameKey(hRootKey, keyPath, ptvdi->item.pszText);
711                     if (nRenResult != ERROR_SUCCESS)
712                     {
713                         LoadStringW(hInst, IDS_ERROR, Caption, ARRAY_SIZE(Caption));
714                         ErrorMessageBox(hWnd, Caption, nRenResult);
715                         lResult = FALSE;
716                     }
717                     else
718                         UpdateAddress(ptvdi->item.hItem, hRootKey, szBuffer, FALSE);
719                 }
720                 *Result = lResult;
721             }
722             return TRUE;
723         }
724     }
725     return FALSE;
726 }
727 
728 /*
729  * CreateTreeView - creates a tree view control.
730  * Returns the handle to the new control if successful, or NULL otherwise.
731  * hwndParent - handle to the control's parent window.
732  */
733 HWND CreateTreeView(HWND hwndParent, LPWSTR pHostName, HMENU id)
734 {
735     RECT rcClient;
736     HWND hwndTV;
737 
738     /* Get the dimensions of the parent window's client area, and create the tree view control.  */
739     GetClientRect(hwndParent, &rcClient);
740     hwndTV = CreateWindowExW(WS_EX_CLIENTEDGE, WC_TREEVIEW, NULL,
741                             WS_VISIBLE | WS_CHILD | WS_TABSTOP | TVS_HASLINES | TVS_HASBUTTONS | TVS_LINESATROOT | TVS_EDITLABELS | TVS_SHOWSELALWAYS,
742                             0, 0, rcClient.right, rcClient.bottom,
743                             hwndParent, id, hInst, NULL);
744     if (!hwndTV) return NULL;
745 
746     /* Initialize the image list, and add items to the control.  */
747     if (!InitTreeViewImageLists(hwndTV) || !InitTreeViewItems(hwndTV, pHostName))
748     {
749         DestroyWindow(hwndTV);
750         return NULL;
751     }
752     return hwndTV;
753 }
754 
755 void DestroyTreeView(HWND hwndTV)
756 {
757     HIMAGELIST himl;
758 
759     if (pathBuffer) HeapFree(GetProcessHeap(), 0, pathBuffer);
760 
761     /* Destroy the image list associated with the tree view control */
762     himl = TreeView_GetImageList(hwndTV, TVSIL_NORMAL);
763     if (himl) ImageList_Destroy(himl);
764 }
765 
766 BOOL SelectNode(HWND hwndTV, LPCWSTR keyPath)
767 {
768     HTREEITEM hRoot, hItem;
769     HTREEITEM hChildItem;
770     WCHAR szPathPart[128];
771     WCHAR szBuffer[128];
772     LPCWSTR s;
773     TVITEMW tvi;
774 
775     /* Load "My Computer" string... */
776     LoadStringW(hInst, IDS_MY_COMPUTER, szBuffer, ARRAY_SIZE(szBuffer));
777     StringCbCatW(szBuffer, sizeof(szBuffer), L"\\");
778 
779     /* ... and remove it from the key path */
780     if (!_wcsnicmp(keyPath, szBuffer, wcslen(szBuffer)))
781         keyPath += wcslen(szBuffer);
782 
783     /* Reinitialize szBuffer */
784     szBuffer[0] = L'\0';
785 
786     hRoot = TreeView_GetRoot(hwndTV);
787     hItem = hRoot;
788 
789     while(keyPath[0])
790     {
791         size_t copyLength;
792         s = wcschr(keyPath, L'\\');
793         if (s != NULL)
794         {
795             copyLength = (s - keyPath) * sizeof(WCHAR);
796         }
797         else
798         {
799             copyLength = sizeof(szPathPart);
800         }
801         StringCbCopyNW(szPathPart, sizeof(szPathPart), keyPath, copyLength);
802 
803         /* Special case for root to expand root key abbreviations */
804         if (hItem == hRoot)
805         {
806             if (!_wcsicmp(szPathPart, L"HKCR"))
807                 StringCbCopyW(szPathPart, sizeof(szPathPart), L"HKEY_CLASSES_ROOT");
808             else if (!_wcsicmp(szPathPart, L"HKCU"))
809                 StringCbCopyW(szPathPart, sizeof(szPathPart), L"HKEY_CURRENT_USER");
810             else if (!_wcsicmp(szPathPart, L"HKLM"))
811                 StringCbCopyW(szPathPart, sizeof(szPathPart), L"HKEY_LOCAL_MACHINE");
812             else if (!_wcsicmp(szPathPart, L"HKU"))
813                 StringCbCopyW(szPathPart, sizeof(szPathPart), L"HKEY_USERS");
814             else if (!_wcsicmp(szPathPart, L"HKCC"))
815                 StringCbCopyW(szPathPart, sizeof(szPathPart), L"HKEY_CURRENT_CONFIG");
816             else if (!_wcsicmp(szPathPart, L"HKDD"))
817                 StringCbCopyW(szPathPart, sizeof(szPathPart), L"HKEY_DYN_DATA");
818         }
819 
820         for (hChildItem = TreeView_GetChild(hwndTV, hItem); hChildItem;
821                 hChildItem = TreeView_GetNextSibling(hwndTV, hChildItem))
822         {
823             memset(&tvi, 0, sizeof(tvi));
824             tvi.hItem = hChildItem;
825             tvi.mask = TVIF_TEXT | TVIF_CHILDREN;
826             tvi.pszText = szBuffer;
827             tvi.cchTextMax = ARRAY_SIZE(szBuffer);
828 
829             (void)TreeView_GetItem(hwndTV, &tvi);
830 
831             if (!_wcsicmp(szBuffer, szPathPart))
832                 break;
833         }
834 
835         if (!hChildItem)
836             return FALSE;
837 
838         if (tvi.cChildren > 0)
839         {
840             if (!TreeView_Expand(hwndTV, hChildItem, TVE_EXPAND))
841                 return FALSE;
842         }
843 
844         keyPath = s ? s + 1 : L"";
845         hItem = hChildItem;
846     }
847 
848     (void)TreeView_SelectItem(hwndTV, hItem);
849     (void)TreeView_EnsureVisible(hwndTV, hItem);
850 
851     return TRUE;
852 }
853 
854 /* EOF */
855