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