regedit: Remove unnecessary use of class atoms.
[wine] / programs / regedit / treeview.c
1 /*
2  * Regedit treeview
3  *
4  * Copyright (C) 2002 Robert Dickenson <robd@reactos.org>
5  * Copyright (C) 2008 Alexander N. Sørnes <alex@thehandofagony.com>
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21
22 #define WIN32_LEAN_AND_MEAN     /* Exclude rarely-used stuff from Windows headers */
23
24 #define NONAMELESSUNION
25 #define NONAMELESSSTRUCT
26 #include <windows.h>
27 #include <commctrl.h>
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <wine/debug.h>
31 #include <shlwapi.h>
32
33 #include "main.h"
34 #include "regproc.h"
35
36 WINE_DEFAULT_DEBUG_CHANNEL(regedit);
37
38 /* Global variables and constants  */
39 /* Image_Open, Image_Closed, and Image_Root - integer variables for indexes of the images.  */
40 /* CX_BITMAP and CY_BITMAP - width and height of an icon.  */
41 /* NUM_BITMAPS - number of bitmaps to add to the image list.  */
42 int Image_Open;
43 int Image_Closed;
44 int Image_Root;
45
46 #define CX_ICON    16
47 #define CY_ICON    16
48 #define NUM_ICONS    3
49
50 static BOOL UpdateExpandingTree(HWND hwndTV, HTREEITEM hItem, int state);
51
52 static BOOL get_item_path(HWND hwndTV, HTREEITEM hItem, HKEY* phKey, LPWSTR* pKeyPath, int* pPathLen, int* pMaxChars)
53 {
54     TVITEMW item;
55     int maxChars, chars;
56     LPWSTR newStr;
57     HTREEITEM hParent;
58
59     item.mask = TVIF_PARAM;
60     item.hItem = hItem;
61     if (!TreeView_GetItemW(hwndTV, &item)) return FALSE;
62
63     if (item.lParam) {
64     /* found root key with valid key value */
65     *phKey = (HKEY)item.lParam;
66     return TRUE;
67     }
68
69     hParent = (HTREEITEM)SendMessageW(hwndTV, TVM_GETNEXTITEM, TVGN_PARENT, (LPARAM)hItem);
70     if(!get_item_path(hwndTV, hParent, phKey, pKeyPath, pPathLen, pMaxChars)) return FALSE;
71     if (*pPathLen) {
72         (*pKeyPath)[*pPathLen] = '\\';
73         ++(*pPathLen);
74     }
75
76     do {
77         item.mask = TVIF_TEXT;
78         item.hItem = hItem;
79         item.pszText = *pKeyPath + *pPathLen;
80         item.cchTextMax = maxChars = *pMaxChars - *pPathLen;
81         if (!TreeView_GetItemW(hwndTV, &item)) return FALSE;
82         chars = lstrlenW(item.pszText);
83     if (chars < maxChars - 1) {
84             *pPathLen += chars;
85             break;
86     }
87     newStr = HeapReAlloc(GetProcessHeap(), 0, *pKeyPath, *pMaxChars * 2);
88     if (!newStr) return FALSE;
89     *pKeyPath = newStr;
90     *pMaxChars *= 2;
91     } while(TRUE);
92
93     return TRUE;
94 }
95
96 LPWSTR GetItemPath(HWND hwndTV, HTREEITEM hItem, HKEY* phRootKey)
97 {
98     int pathLen = 0, maxLen;
99     WCHAR *pathBuffer;
100
101     pathBuffer = HeapAlloc(GetProcessHeap(), 0, 1024*sizeof(WCHAR));
102     if (!pathBuffer) return NULL;
103     *pathBuffer = 0;
104     maxLen = HeapSize(GetProcessHeap(), 0, pathBuffer);
105     if (maxLen == (SIZE_T) - 1) return NULL;
106     maxLen = maxLen / sizeof(WCHAR);
107     if (!hItem) hItem = (HTREEITEM)SendMessageW(hwndTV, TVM_GETNEXTITEM, TVGN_CARET, 0);
108     if (!hItem) return NULL;
109     if (!get_item_path(hwndTV, hItem, phRootKey, &pathBuffer, &pathLen, &maxLen)) return NULL;
110     return pathBuffer;
111 }
112
113 static LPWSTR get_path_component(LPCWSTR *lplpKeyName) {
114      LPCWSTR lpPos = *lplpKeyName;
115      LPWSTR lpResult = NULL;
116      int len;
117      if (!lpPos)
118          return NULL;
119      while(*lpPos && *lpPos != '\\')
120          lpPos++;
121      if (*lpPos && lpPos == *lplpKeyName)
122          return NULL;
123      len = lpPos+1-(*lplpKeyName);
124      lpResult = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
125      if (!lpResult) /* that would be very odd */
126          return NULL;
127      lstrcpynW(lpResult, *lplpKeyName, len);
128      *lplpKeyName = *lpPos ? lpPos+1 : NULL;
129      return lpResult;
130 }
131
132 HTREEITEM FindPathInTree(HWND hwndTV, LPCWSTR lpKeyName) {
133     TVITEMEXW tvi;
134     WCHAR buf[261]; /* tree view has 260 character limitation on item name */
135     HTREEITEM hItem, hOldItem;
136
137     buf[260] = '\0';
138     hItem = (HTREEITEM)SendMessageW(hwndTV, TVM_GETNEXTITEM, TVGN_ROOT, 0);
139     SendMessageW(hwndTV, TVM_EXPAND, TVE_EXPAND, (LPARAM)hItem );
140     hItem = (HTREEITEM)SendMessageW(hwndTV, TVM_GETNEXTITEM, TVGN_CHILD, (LPARAM)hItem);
141     hOldItem = hItem;
142     while(1) {
143         LPWSTR lpItemName = get_path_component(&lpKeyName);
144
145         if (lpItemName) {
146             while(hItem) {
147                 tvi.mask = TVIF_TEXT | TVIF_HANDLE;
148                 tvi.hItem = hItem;
149                 tvi.pszText = buf;
150                 tvi.cchTextMax = 260;
151                 SendMessageW(hwndTV, TVM_GETITEMW, 0, (LPARAM) &tvi);
152                 if (!lstrcmpiW(tvi.pszText, lpItemName)) {
153                      SendMessageW(hwndTV, TVM_EXPAND, TVE_EXPAND, (LPARAM)hItem );
154                      if (!lpKeyName)
155                      {
156                          HeapFree(GetProcessHeap(), 0, lpItemName);
157                          return hItem;
158                      }
159                      hOldItem = hItem;
160                      hItem = (HTREEITEM)SendMessageW(hwndTV, TVM_GETNEXTITEM, TVGN_CHILD, (LPARAM)hItem);
161                      break;
162                 }
163                 hItem = (HTREEITEM)SendMessageW(hwndTV, TVM_GETNEXTITEM, TVGN_NEXT, (LPARAM)hItem);
164             }
165             HeapFree(GetProcessHeap(), 0, lpItemName);
166             if (!hItem)
167                 return hOldItem;
168         }
169         else
170             return hItem;
171     }
172 }
173
174 BOOL DeleteNode(HWND hwndTV, HTREEITEM hItem)
175 {
176     if (!hItem) hItem = (HTREEITEM)SendMessageW(hwndTV, TVM_GETNEXTITEM, TVGN_CARET, 0);
177     if (!hItem) return FALSE;
178     return (BOOL)SendMessageW(hwndTV, TVM_DELETEITEM, 0, (LPARAM)hItem);
179 }
180
181 /* Add an entry to the tree. Only give hKey for root nodes (HKEY_ constants) */
182 static HTREEITEM AddEntryToTree(HWND hwndTV, HTREEITEM hParent, LPWSTR label, HKEY hKey, DWORD dwChildren)
183 {
184     TVINSERTSTRUCTW tvins;
185
186     if (hKey) {
187         if (RegQueryInfoKeyW(hKey, 0, 0, 0, &dwChildren, 0, 0, 0, 0, 0, 0, 0) != ERROR_SUCCESS) {
188             dwChildren = 0;
189         }
190     }
191
192     tvins.u.item.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_CHILDREN | TVIF_PARAM;
193     tvins.u.item.pszText = label;
194     tvins.u.item.cchTextMax = lstrlenW(label);
195     tvins.u.item.iImage = Image_Closed;
196     tvins.u.item.iSelectedImage = Image_Open;
197     tvins.u.item.cChildren = dwChildren;
198     tvins.u.item.lParam = (LPARAM)hKey;
199     tvins.hInsertAfter = hKey ? TVI_LAST : TVI_SORT;
200     tvins.hParent = hParent;
201
202     return TreeView_InsertItemW(hwndTV, &tvins);
203 }
204
205 static BOOL match_string(LPCWSTR sstring1, LPCWSTR sstring2, int mode)
206 {
207     if (mode & SEARCH_WHOLE)
208         return !lstrcmpiW(sstring1, sstring2);
209     else
210         return NULL != StrStrIW(sstring1, sstring2);
211 }
212
213 static BOOL match_item(HWND hwndTV, HTREEITEM hItem, LPCWSTR sstring, int mode, int *row)
214 {
215     TVITEMW item;
216     WCHAR keyname[KEY_MAX_LEN];
217     item.mask = TVIF_TEXT;
218     item.hItem = hItem;
219     item.pszText = keyname;
220     item.cchTextMax = KEY_MAX_LEN;
221     if (!TreeView_GetItemW(hwndTV, &item)) return FALSE;
222     if ((mode & SEARCH_KEYS) && match_string(keyname, sstring, mode)) {
223         *row = -1;
224         return TRUE;
225     }
226
227     if (mode & (SEARCH_VALUES | SEARCH_CONTENT)) {
228         int i, adjust;
229         WCHAR valName[KEY_MAX_LEN], *KeyPath;
230         HKEY hKey, hRoot;
231         DWORD lenName;
232         
233         KeyPath = GetItemPath(hwndTV, hItem, &hRoot);
234
235         if (!KeyPath || !hRoot)
236              return FALSE;
237
238         if (RegOpenKeyExW(hRoot, KeyPath, 0, KEY_READ, &hKey) != ERROR_SUCCESS) {
239             HeapFree(GetProcessHeap(), 0, KeyPath);
240             return FALSE;
241         }
242
243         HeapFree(GetProcessHeap(), 0, KeyPath);
244         lenName = KEY_MAX_LEN;
245         adjust = 0;
246         /* RegEnumValue won't return empty default value, so fake it when dealing with *row,
247            which corresponds to list view rows, not value ids */
248         if (ERROR_SUCCESS == RegEnumValueW(hKey, 0, valName, &lenName, NULL, NULL, NULL, NULL) && *valName)
249             adjust = 1;
250         
251         i = (*row)-adjust;
252         if (i < 0) i = 0;
253         while(1) {
254             DWORD lenValue = 0, type = 0;
255             lenName = KEY_MAX_LEN;
256             
257             if (ERROR_SUCCESS != RegEnumValueW(hKey,
258                 i, valName, &lenName, NULL, &type, NULL, &lenValue))
259                 break;
260             
261             if (mode & SEARCH_VALUES) {
262                 if (match_string(valName, sstring, mode)) {
263                     RegCloseKey(hKey);
264                     *row = i+adjust;
265                     return TRUE;
266                 }
267             }
268             
269             if ((mode & SEARCH_CONTENT) && (type == REG_EXPAND_SZ || type == REG_SZ)) {
270                 LPWSTR buffer;
271                 buffer = HeapAlloc(GetProcessHeap(), 0, lenValue);
272                 if (!buffer)
273                     break;
274                 if (ERROR_SUCCESS != RegEnumValueW(hKey, i, NULL, NULL, NULL, &type, (LPBYTE)buffer, &lenValue))
275                     break;
276                 if (match_string(buffer, sstring, mode)) {
277                     HeapFree(GetProcessHeap(), 0, buffer);
278                     RegCloseKey(hKey);
279                     *row = i+adjust;
280                     return TRUE;
281                 }
282                 HeapFree(GetProcessHeap(), 0, buffer);
283             }
284                             
285             i++;
286         }
287         RegCloseKey(hKey);
288     }        
289     return FALSE;
290 }
291
292 HTREEITEM FindNext(HWND hwndTV, HTREEITEM hItem, LPCWSTR sstring, int mode, int *row)
293 {
294     HTREEITEM hTry, hLast;
295     
296     hLast = hItem;
297     (*row)++;
298     if (match_item(hwndTV, hLast, sstring, mode & ~SEARCH_KEYS, row)) {
299         return hLast;
300     }
301     *row = 0;
302     
303     while(hLast) {
304         /* first look in subtree */
305         /* no children? maybe we haven't loaded them yet? */
306         if (!SendMessageW(hwndTV, TVM_GETNEXTITEM, TVGN_CHILD, (LPARAM)hLast)) {
307             UINT state = (UINT)SendMessageW(hwndTV, TVM_GETITEMSTATE, (WPARAM)hLast, -1);
308             UpdateExpandingTree(hwndTV, hLast, state);
309         }
310         hTry = (HTREEITEM)SendMessageW(hwndTV, TVM_GETNEXTITEM, TVGN_CHILD, (LPARAM)hLast);
311         if (hTry) {
312             if (match_item(hwndTV, hTry, sstring, mode, row))
313                 return hTry;
314             hLast = hTry;
315             continue;
316         }
317         /* no more children, maybe there are any siblings? */
318         hTry = (HTREEITEM)SendMessageW(hwndTV, TVM_GETNEXTITEM, TVGN_NEXT, (LPARAM)hLast);
319         if (hTry) {
320             if (match_item(hwndTV, hTry, sstring, mode, row))
321                 return hTry;
322             hLast = hTry;
323             continue;
324         }
325         /* no more siblings, look at the next siblings in parent(s) */
326         hLast = (HTREEITEM)SendMessageW(hwndTV, TVM_GETNEXTITEM, TVGN_PARENT, (LPARAM)hLast);
327         if (!hLast)
328             return NULL;
329         while (hLast && (hTry = (HTREEITEM)SendMessageW(hwndTV, TVM_GETNEXTITEM, TVGN_NEXT, (LPARAM)hLast)) == NULL) {
330             hLast = (HTREEITEM)SendMessageW(hwndTV, TVM_GETNEXTITEM, TVGN_PARENT, (LPARAM)hLast);
331         }
332         if (match_item(hwndTV, hTry, sstring, mode, row))
333             return hTry;
334         hLast = hTry;
335     }
336     return NULL;
337 }
338
339 static BOOL RefreshTreeItem(HWND hwndTV, HTREEITEM hItem)
340 {
341     HKEY hRoot, hKey, hSubKey;
342     HTREEITEM childItem;
343     LPWSTR KeyPath;
344     DWORD dwCount, dwIndex, dwMaxSubKeyLen;
345     LPWSTR Name;
346     TVITEMW tvItem;
347     
348     hRoot = NULL;
349     KeyPath = GetItemPath(hwndTV, hItem, &hRoot);
350
351     if (!KeyPath || !hRoot)
352         return FALSE;
353
354     if (*KeyPath) {
355         if (RegOpenKeyExW(hRoot, KeyPath, 0, KEY_READ, &hKey) != ERROR_SUCCESS) {
356             WINE_TRACE("RegOpenKeyEx failed, %s was probably removed.\n", wine_dbgstr_w(KeyPath));
357             return FALSE;
358         }
359     } else {
360         hKey = hRoot;
361     }
362     HeapFree(GetProcessHeap(), 0, KeyPath);
363
364     if (RegQueryInfoKeyW(hKey, 0, 0, 0, &dwCount, &dwMaxSubKeyLen, 0, 0, 0, 0, 0, 0) != ERROR_SUCCESS) {
365         return FALSE;
366     }
367
368     /* Set the number of children again */
369     tvItem.mask = TVIF_CHILDREN;
370     tvItem.hItem = hItem;
371     tvItem.cChildren = dwCount;
372     if (!TreeView_SetItemW(hwndTV, &tvItem)) {
373         return FALSE;
374     }
375
376     /* We don't have to bother with the rest if it's not expanded. */
377     if (SendMessageW(hwndTV, TVM_GETITEMSTATE, (WPARAM)hItem, TVIS_EXPANDED) == 0) {
378         RegCloseKey(hKey);
379         return TRUE;
380     }
381
382     dwMaxSubKeyLen++; /* account for the \0 terminator */
383     if (!(Name = HeapAlloc(GetProcessHeap(), 0, dwMaxSubKeyLen * sizeof(WCHAR)))) {
384         return FALSE;
385     }
386     tvItem.cchTextMax = dwMaxSubKeyLen;
387     if (!(tvItem.pszText = HeapAlloc(GetProcessHeap(), 0, dwMaxSubKeyLen * sizeof(WCHAR)))) {
388         HeapFree(GetProcessHeap(), 0, Name);
389         return FALSE;
390     }
391
392     /* Now go through all the children in the registry, and check if any have to be added. */
393     for (dwIndex = 0; dwIndex < dwCount; dwIndex++) {
394         DWORD cName = dwMaxSubKeyLen, dwSubCount;
395         BOOL found;
396
397         found = FALSE;
398         if (RegEnumKeyExW(hKey, dwIndex, Name, &cName, 0, 0, 0, NULL) != ERROR_SUCCESS) {
399             continue;
400         }
401
402         /* Find the number of children of the node. */
403         dwSubCount = 0;
404         if (RegOpenKeyExW(hKey, Name, 0, KEY_QUERY_VALUE, &hSubKey) == ERROR_SUCCESS) {
405             if (RegQueryInfoKeyW(hSubKey, 0, 0, 0, &dwSubCount, 0, 0, 0, 0, 0, 0, 0) != ERROR_SUCCESS) {
406                 dwSubCount = 0;
407             }
408             RegCloseKey(hSubKey);
409         }
410
411         /* Check if the node is already in there. */
412         for (childItem = (HTREEITEM)SendMessageW(hwndTV, TVM_GETNEXTITEM, TVGN_CHILD, (LPARAM)hItem); childItem;
413                 childItem = (HTREEITEM)SendMessageW(hwndTV, TVM_GETNEXTITEM, TVGN_NEXT, (LPARAM)childItem)) {
414             tvItem.mask = TVIF_TEXT;
415             tvItem.hItem = childItem;
416             if (!TreeView_GetItemW(hwndTV, &tvItem)) {
417                 HeapFree(GetProcessHeap(), 0, Name);
418                 HeapFree(GetProcessHeap(), 0, tvItem.pszText);
419                 return FALSE;
420             }
421
422             if (!lstrcmpiW(tvItem.pszText, Name)) {
423                 found = TRUE;
424                 break;
425             }
426         }
427
428         if (found == FALSE) {
429             WINE_TRACE("New subkey %s\n", wine_dbgstr_w(Name));
430             AddEntryToTree(hwndTV, hItem, Name, NULL, dwSubCount);
431         }
432     }
433     HeapFree(GetProcessHeap(), 0, Name);
434     HeapFree(GetProcessHeap(), 0, tvItem.pszText);
435     RegCloseKey(hKey);
436
437     /* Now go through all the children in the tree, and check if any have to be removed. */
438     childItem = (HTREEITEM)SendMessageW(hwndTV, TVM_GETNEXTITEM, TVGN_CHILD, (LPARAM)hItem);
439     while (childItem) {
440         HTREEITEM nextItem = (HTREEITEM)SendMessageW(hwndTV, TVM_GETNEXTITEM, TVGN_NEXT, (LPARAM)childItem);
441         if (RefreshTreeItem(hwndTV, childItem) == FALSE) {
442             SendMessageW(hwndTV, TVM_DELETEITEM, 0, (LPARAM)childItem);
443         }
444         childItem = nextItem;
445     }
446
447     return TRUE;
448 }
449
450 BOOL RefreshTreeView(HWND hwndTV)
451 {
452     HTREEITEM hItem;
453     HTREEITEM hSelectedItem;
454     HCURSOR hcursorOld;
455     HTREEITEM hRoot;
456
457     WINE_TRACE("\n");
458     hSelectedItem = (HTREEITEM)SendMessageW(hwndTV, TVM_GETNEXTITEM, TVGN_CARET, 0);
459     hcursorOld = SetCursor(LoadCursorW(NULL, (LPCWSTR)IDC_WAIT));
460     SendMessageW(hwndTV, WM_SETREDRAW, FALSE, 0);
461
462     hRoot = (HTREEITEM)SendMessageW(hwndTV, TVM_GETNEXTITEM, TVGN_ROOT, 0);
463     hItem = (HTREEITEM)SendMessageW(hwndTV, TVM_GETNEXTITEM, TVGN_CHILD, (LPARAM)hRoot);
464     while (hItem) {
465         RefreshTreeItem(hwndTV, hItem);
466         hItem = (HTREEITEM)SendMessageW(hwndTV, TVM_GETNEXTITEM, TVGN_NEXT, (LPARAM)hItem);
467     }
468
469     SendMessageW(hwndTV, WM_SETREDRAW, TRUE, 0);
470     InvalidateRect(hwndTV, NULL, FALSE);
471     SetCursor(hcursorOld);
472     
473     /* We reselect the currently selected node, this will prompt a refresh of the listview. */
474     SendMessageW(hwndTV, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hSelectedItem);
475     return TRUE;
476 }
477
478 HTREEITEM InsertNode(HWND hwndTV, HTREEITEM hItem, LPWSTR name)
479 {
480     WCHAR buf[MAX_NEW_KEY_LEN];
481     HTREEITEM hNewItem = 0;
482     TVITEMEXW item;
483
484     if (!hItem) hItem = (HTREEITEM)SendMessageW(hwndTV, TVM_GETNEXTITEM, TVGN_CARET, 0);
485     if (!hItem) return FALSE;
486     if (SendMessageW(hwndTV, TVM_GETITEMSTATE, (WPARAM)hItem, TVIS_EXPANDEDONCE)) {
487         hNewItem = AddEntryToTree(hwndTV, hItem, name, 0, 0);
488     } else {
489         item.mask = TVIF_CHILDREN | TVIF_HANDLE;
490         item.hItem = hItem;
491         if (!TreeView_GetItemW(hwndTV, &item)) return FALSE;
492         item.cChildren = 1;
493         if (!TreeView_SetItemW(hwndTV, &item)) return FALSE;
494     }
495     SendMessageW(hwndTV, TVM_EXPAND, TVE_EXPAND, (LPARAM)hItem );
496     if (!hNewItem) {
497         for(hNewItem = (HTREEITEM)SendMessageW(hwndTV, TVM_GETNEXTITEM, TVGN_CHILD, (LPARAM)hItem); hNewItem;
498             hNewItem = (HTREEITEM)SendMessageW(hwndTV, TVM_GETNEXTITEM, TVGN_NEXT, (LPARAM)hNewItem)) {
499             item.mask = TVIF_HANDLE | TVIF_TEXT;
500             item.hItem = hNewItem;
501             item.pszText = buf;
502             item.cchTextMax = COUNT_OF(buf);
503             if (!TreeView_GetItemW(hwndTV, &item)) continue;
504             if (lstrcmpW(name, item.pszText) == 0) break;
505         }
506     }
507     if (hNewItem)
508         SendMessageW(hwndTV, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hNewItem);
509
510     return hNewItem;
511 }
512
513 HWND StartKeyRename(HWND hwndTV)
514 {
515     HTREEITEM hItem;
516
517     if(!(hItem = (HTREEITEM)SendMessageW(hwndTV, TVM_GETNEXTITEM, TVGN_CARET, 0))) return 0;
518     return (HWND)SendMessageW(hwndTV, TVM_EDITLABELW, 0, (LPARAM)hItem);
519 }
520
521 static BOOL InitTreeViewItems(HWND hwndTV, LPWSTR pHostName)
522 {
523     TVINSERTSTRUCTW tvins;
524     HTREEITEM hRoot;
525     static WCHAR hkcr[] = {'H','K','E','Y','_','C','L','A','S','S','E','S','_','R','O','O','T',0},
526                  hkcu[] = {'H','K','E','Y','_','C','U','R','R','E','N','T','_','U','S','E','R',0},
527                  hklm[] = {'H','K','E','Y','_','L','O','C','A','L','_','M','A','C','H','I','N','E',0},
528                  hku[]  = {'H','K','E','Y','_','U','S','E','R','S',0},
529                  hkcc[] = {'H','K','E','Y','_','C','U','R','R','E','N','T','_','C','O','N','F','I','G',0},
530                  hkdd[] = {'H','K','E','Y','_','D','Y','N','_','D','A','T','A',0};
531
532     tvins.u.item.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_CHILDREN | TVIF_PARAM;
533     /* Set the text of the item.  */
534     tvins.u.item.pszText = pHostName;
535     tvins.u.item.cchTextMax = lstrlenW(pHostName);
536     /* Assume the item is not a parent item, so give it an image.  */
537     tvins.u.item.iImage = Image_Root;
538     tvins.u.item.iSelectedImage = Image_Root;
539     tvins.u.item.cChildren = 5;
540     /* Save the heading level in the item's application-defined data area.  */
541     tvins.u.item.lParam = 0;
542     tvins.hInsertAfter = TVI_FIRST;
543     tvins.hParent = TVI_ROOT;
544     /* Add the item to the tree view control.  */
545     if (!(hRoot = TreeView_InsertItemW(hwndTV, &tvins))) return FALSE;
546
547     if (!AddEntryToTree(hwndTV, hRoot, hkcr, HKEY_CLASSES_ROOT, 1)) return FALSE;
548     if (!AddEntryToTree(hwndTV, hRoot, hkcu, HKEY_CURRENT_USER, 1)) return FALSE;
549     if (!AddEntryToTree(hwndTV, hRoot, hklm, HKEY_LOCAL_MACHINE, 1)) return FALSE;
550     if (!AddEntryToTree(hwndTV, hRoot, hku, HKEY_USERS, 1)) return FALSE;
551     if (!AddEntryToTree(hwndTV, hRoot, hkcc, HKEY_CURRENT_CONFIG, 1)) return FALSE;
552     if (!AddEntryToTree(hwndTV, hRoot, hkdd, HKEY_DYN_DATA, 1)) return FALSE;
553
554     /* expand and select host name */
555     SendMessageW(hwndTV, TVM_EXPAND, TVE_EXPAND, (LPARAM)hRoot );
556     SendMessageW(hwndTV, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hRoot);
557     return TRUE;
558 }
559
560
561 /*
562  * InitTreeViewImageLists - creates an image list, adds three bitmaps
563  * to it, and associates the image list with a tree view control.
564  * Returns TRUE if successful, or FALSE otherwise.
565  * hwndTV - handle to the tree view control.
566  */
567 static BOOL InitTreeViewImageLists(HWND hwndTV)
568 {
569     HIMAGELIST himl;  /* handle to image list  */
570     HICON hico;       /* handle to icon  */
571
572     /* Create the image list.  */
573     if ((himl = ImageList_Create(CX_ICON, CY_ICON,
574                                  ILC_MASK, 0, NUM_ICONS)) == NULL)
575         return FALSE;
576
577     /* Add the open file, closed file, and document bitmaps.  */
578     hico = LoadIconW(hInst, MAKEINTRESOURCEW(IDI_OPEN_FILE));
579     Image_Open = ImageList_AddIcon(himl, hico);
580
581     hico = LoadIconW(hInst, MAKEINTRESOURCEW(IDI_CLOSED_FILE));
582     Image_Closed = ImageList_AddIcon(himl, hico);
583
584     hico = LoadIconW(hInst, MAKEINTRESOURCEW(IDI_ROOT));
585     Image_Root = ImageList_AddIcon(himl, hico);
586
587     /* Fail if not all of the images were added.  */
588     if (ImageList_GetImageCount(himl) < NUM_ICONS)
589     {
590       return FALSE;
591     }
592
593     /* Associate the image list with the tree view control.  */
594     SendMessageW(hwndTV, TVM_SETIMAGELIST, TVSIL_NORMAL, (LPARAM)himl);
595
596     return TRUE;
597 }
598
599 BOOL UpdateExpandingTree(HWND hwndTV, HTREEITEM hItem, int state)
600 {
601     DWORD dwCount, dwIndex, dwMaxSubKeyLen;
602     HKEY hRoot, hNewKey, hKey;
603     LPWSTR keyPath;
604     LPWSTR Name;
605     LONG errCode;
606     HCURSOR hcursorOld;
607     TVITEMW item;
608
609     static int expanding;
610     if (expanding) return FALSE;
611     if (state & TVIS_EXPANDEDONCE ) {
612         return TRUE;
613     }
614     expanding = TRUE;
615     hcursorOld = SetCursor(LoadCursorW(NULL, (LPCWSTR)IDC_WAIT));
616     SendMessageW(hwndTV, WM_SETREDRAW, FALSE, 0);
617
618     keyPath = GetItemPath(hwndTV, hItem, &hRoot);
619     if (!keyPath) goto done;
620
621     if (*keyPath) {
622         errCode = RegOpenKeyExW(hRoot, keyPath, 0, KEY_READ, &hNewKey);
623         if (errCode != ERROR_SUCCESS) goto done;
624     } else {
625         hNewKey = hRoot;
626     }
627
628     errCode = RegQueryInfoKeyW(hNewKey, 0, 0, 0, &dwCount, &dwMaxSubKeyLen, 0, 0, 0, 0, 0, 0);
629     if (errCode != ERROR_SUCCESS) goto done;
630     dwMaxSubKeyLen++; /* account for the \0 terminator */
631     Name = HeapAlloc(GetProcessHeap(), 0, dwMaxSubKeyLen * sizeof(WCHAR));
632     if (!Name) goto done;
633
634     for (dwIndex = 0; dwIndex < dwCount; dwIndex++) {
635         DWORD cName = dwMaxSubKeyLen, dwSubCount;
636
637         errCode = RegEnumKeyExW(hNewKey, dwIndex, Name, &cName, 0, 0, 0, 0);
638         if (errCode != ERROR_SUCCESS) continue;
639         errCode = RegOpenKeyExW(hNewKey, Name, 0, KEY_QUERY_VALUE, &hKey);
640         if (errCode == ERROR_SUCCESS) {
641             errCode = RegQueryInfoKeyW(hKey, 0, 0, 0, &dwSubCount, 0, 0, 0, 0, 0, 0, 0);
642             RegCloseKey(hKey);
643         }
644         if (errCode != ERROR_SUCCESS) dwSubCount = 0;
645         AddEntryToTree(hwndTV, hItem, Name, NULL, dwSubCount);
646     }
647     RegCloseKey(hNewKey);
648     HeapFree(GetProcessHeap(), 0, Name);
649
650 done:
651     item.mask = TVIF_STATE;
652     item.hItem = hItem;
653     item.stateMask = TVIS_EXPANDEDONCE;
654     item.state = TVIS_EXPANDEDONCE;
655     SendMessageW(hwndTV, TVM_SETITEMW, 0, (LPARAM)&item);
656     SendMessageW(hwndTV, WM_SETREDRAW, TRUE, 0);
657     SetCursor(hcursorOld);
658     expanding = FALSE;
659     HeapFree(GetProcessHeap(), 0, keyPath);
660
661     return TRUE;
662 }
663
664 BOOL OnTreeExpanding(HWND hwndTV, NMTREEVIEWW* pnmtv)
665 {
666     return UpdateExpandingTree(hwndTV, pnmtv->itemNew.hItem, pnmtv->itemNew.state);
667 }
668
669
670 /*
671  * CreateTreeView - creates a tree view control.
672  * Returns the handle to the new control if successful, or NULL otherwise.
673  * hwndParent - handle to the control's parent window.
674  */
675 HWND CreateTreeView(HWND hwndParent, LPWSTR pHostName, UINT id)
676 {
677     RECT rcClient;
678     HWND hwndTV;
679     WCHAR TreeView[] = {'T','r','e','e',' ','V','i','e','w',0};
680
681     /* Get the dimensions of the parent window's client area, and create the tree view control.  */
682     GetClientRect(hwndParent, &rcClient);
683     hwndTV = CreateWindowExW(WS_EX_CLIENTEDGE, WC_TREEVIEWW, TreeView,
684                             WS_VISIBLE | WS_CHILD | WS_TABSTOP | TVS_HASLINES | TVS_HASBUTTONS | TVS_LINESATROOT,
685                             0, 0, rcClient.right, rcClient.bottom,
686                             hwndParent, ULongToHandle(id), hInst, NULL);
687     SendMessageW(hwndTV, TVM_SETUNICODEFORMAT, TRUE, 0);
688     /* Initialize the image list, and add items to the control.  */
689     if (!InitTreeViewImageLists(hwndTV) || !InitTreeViewItems(hwndTV, pHostName)) {
690         DestroyWindow(hwndTV);
691         return NULL;
692     }
693     return hwndTV;
694 }