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