shell32/tests: Use the available ARRAY_SIZE() macro.
[wine.git] / programs / regedit / treeview.c
blobd5a67677547af59f3d48336bd860372664392e51
1 /*
2 * Regedit treeview
4 * Copyright (C) 2002 Robert Dickenson <robd@reactos.org>
5 * Copyright (C) 2008 Alexander N. Sørnes <alex@thehandofagony.com>
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.
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.
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
22 #define WIN32_LEAN_AND_MEAN /* Exclude rarely-used stuff from Windows headers */
24 #define NONAMELESSUNION
26 #include <windows.h>
27 #include <commctrl.h>
28 #include <stdlib.h>
29 #include <wine/debug.h>
30 #include <shlwapi.h>
32 #include "wine/heap.h"
33 #include "main.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(regedit);
37 /* Global variables and constants */
38 /* Image_Open, Image_Closed, and Image_Root - integer variables for indexes of the images. */
39 /* CX_BITMAP and CY_BITMAP - width and height of an icon. */
40 /* NUM_BITMAPS - number of bitmaps to add to the image list. */
41 int Image_Open;
42 int Image_Closed;
43 int Image_Root;
45 #define NUM_ICONS 3
47 static BOOL UpdateExpandingTree(HWND hwndTV, HTREEITEM hItem, int state);
49 static BOOL get_item_path(HWND hwndTV, HTREEITEM hItem, HKEY* phKey, LPWSTR* pKeyPath, int* pPathLen, int* pMaxChars)
51 TVITEMW item;
52 int maxChars, chars;
53 HTREEITEM hParent;
55 item.mask = TVIF_PARAM;
56 item.hItem = hItem;
57 if (!TreeView_GetItemW(hwndTV, &item)) return FALSE;
59 if (item.lParam) {
60 /* found root key with valid key value */
61 *phKey = (HKEY)item.lParam;
62 return TRUE;
65 hParent = (HTREEITEM)SendMessageW(hwndTV, TVM_GETNEXTITEM, TVGN_PARENT, (LPARAM)hItem);
66 if(!get_item_path(hwndTV, hParent, phKey, pKeyPath, pPathLen, pMaxChars)) return FALSE;
67 if (*pPathLen) {
68 (*pKeyPath)[*pPathLen] = '\\';
69 ++(*pPathLen);
72 do {
73 item.mask = TVIF_TEXT;
74 item.hItem = hItem;
75 item.pszText = *pKeyPath + *pPathLen;
76 item.cchTextMax = maxChars = *pMaxChars - *pPathLen;
77 if (!TreeView_GetItemW(hwndTV, &item)) return FALSE;
78 chars = lstrlenW(item.pszText);
79 if (chars < maxChars - 1) {
80 *pPathLen += chars;
81 break;
84 *pMaxChars *= 2;
85 *pKeyPath = heap_xrealloc(*pKeyPath, *pMaxChars);
86 } while(TRUE);
88 return TRUE;
91 LPWSTR GetItemPath(HWND hwndTV, HTREEITEM hItem, HKEY* phRootKey)
93 int pathLen = 0, maxLen = 1024;
94 WCHAR *pathBuffer;
96 if (!hItem) {
97 hItem = (HTREEITEM)SendMessageW(hwndTV, TVM_GETNEXTITEM, TVGN_CARET, 0);
98 if (!hItem) return NULL;
101 pathBuffer = heap_xalloc(maxLen * sizeof(WCHAR));
102 if (!pathBuffer) return NULL;
103 *pathBuffer = 0;
104 if (!get_item_path(hwndTV, hItem, phRootKey, &pathBuffer, &pathLen, &maxLen)) return NULL;
105 return pathBuffer;
108 static LPWSTR get_path_component(LPCWSTR *lplpKeyName) {
109 LPCWSTR lpPos = *lplpKeyName;
110 LPWSTR lpResult = NULL;
111 int len;
112 if (!lpPos)
113 return NULL;
114 while(*lpPos && *lpPos != '\\')
115 lpPos++;
116 if (*lpPos && lpPos == *lplpKeyName)
117 return NULL;
119 len = lpPos+1-(*lplpKeyName);
120 lpResult = heap_xalloc(len * sizeof(WCHAR));
122 lstrcpynW(lpResult, *lplpKeyName, len);
123 *lplpKeyName = *lpPos ? lpPos+1 : NULL;
124 return lpResult;
127 HTREEITEM FindPathInTree(HWND hwndTV, LPCWSTR lpKeyName) {
128 TVITEMEXW tvi;
129 WCHAR buf[261]; /* tree view has 260 character limitation on item name */
130 HTREEITEM hRoot, hItem, hOldItem;
131 BOOL valid_path;
133 buf[260] = '\0';
134 hRoot = (HTREEITEM)SendMessageW(hwndTV, TVM_GETNEXTITEM, TVGN_ROOT, 0);
135 hItem = hRoot;
136 SendMessageW(hwndTV, TVM_EXPAND, TVE_EXPAND, (LPARAM)hItem );
137 hItem = (HTREEITEM)SendMessageW(hwndTV, TVM_GETNEXTITEM, TVGN_CHILD, (LPARAM)hItem);
138 hOldItem = hItem;
139 valid_path = FALSE;
140 while(1) {
141 LPWSTR lpItemName = get_path_component(&lpKeyName);
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 valid_path = TRUE;
152 SendMessageW(hwndTV, TVM_EXPAND, TVE_EXPAND, (LPARAM)hItem );
153 if (!lpKeyName)
155 heap_free(lpItemName);
156 return hItem;
158 hOldItem = hItem;
159 hItem = (HTREEITEM)SendMessageW(hwndTV, TVM_GETNEXTITEM, TVGN_CHILD, (LPARAM)hItem);
160 break;
162 hItem = (HTREEITEM)SendMessageW(hwndTV, TVM_GETNEXTITEM, TVGN_NEXT, (LPARAM)hItem);
164 heap_free(lpItemName);
165 if (!hItem)
166 return valid_path ? hOldItem : hRoot;
168 else
169 return valid_path ? hItem : hRoot;
173 BOOL DeleteNode(HWND hwndTV, HTREEITEM hItem)
175 if (!hItem) hItem = (HTREEITEM)SendMessageW(hwndTV, TVM_GETNEXTITEM, TVGN_CARET, 0);
176 if (!hItem) return FALSE;
177 return (BOOL)SendMessageW(hwndTV, TVM_DELETEITEM, 0, (LPARAM)hItem);
180 /* Add an entry to the tree. Only give hKey for root nodes (HKEY_ constants) */
181 static HTREEITEM AddEntryToTree(HWND hwndTV, HTREEITEM hParent, LPWSTR label, HKEY hKey, DWORD dwChildren)
183 TVINSERTSTRUCTW tvins;
185 if (hKey) {
186 if (RegQueryInfoKeyW(hKey, 0, 0, 0, &dwChildren, 0, 0, 0, 0, 0, 0, 0) != ERROR_SUCCESS) {
187 dwChildren = 0;
191 tvins.u.item.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_CHILDREN | TVIF_PARAM;
192 tvins.u.item.pszText = label;
193 tvins.u.item.cchTextMax = lstrlenW(label);
194 tvins.u.item.iImage = Image_Closed;
195 tvins.u.item.iSelectedImage = Image_Open;
196 tvins.u.item.cChildren = dwChildren;
197 tvins.u.item.lParam = (LPARAM)hKey;
198 tvins.hInsertAfter = hKey ? TVI_LAST : TVI_SORT;
199 tvins.hParent = hParent;
201 return TreeView_InsertItemW(hwndTV, &tvins);
204 static BOOL match_string(LPCWSTR sstring1, LPCWSTR sstring2, int mode)
206 if (mode & SEARCH_WHOLE)
207 return !lstrcmpiW(sstring1, sstring2);
208 else
209 return NULL != StrStrIW(sstring1, sstring2);
212 static BOOL match_item(HWND hwndTV, HTREEITEM hItem, LPCWSTR sstring, int mode, int *row)
214 TVITEMW item;
215 WCHAR keyname[KEY_MAX_LEN];
216 item.mask = TVIF_TEXT;
217 item.hItem = hItem;
218 item.pszText = keyname;
219 item.cchTextMax = KEY_MAX_LEN;
220 if (!TreeView_GetItemW(hwndTV, &item)) return FALSE;
221 if ((mode & SEARCH_KEYS) && match_string(keyname, sstring, mode)) {
222 *row = -1;
223 return TRUE;
226 if (mode & (SEARCH_VALUES | SEARCH_CONTENT)) {
227 int i, adjust;
228 WCHAR *valName, *KeyPath;
229 HKEY hKey, hRoot;
230 DWORD lenName, lenNameMax, lenValueMax;
231 WCHAR *buffer = NULL;
233 KeyPath = GetItemPath(hwndTV, hItem, &hRoot);
235 if (!KeyPath || !hRoot)
236 return FALSE;
238 if (RegOpenKeyExW(hRoot, KeyPath, 0, KEY_READ, &hKey) != ERROR_SUCCESS) {
239 heap_free(KeyPath);
240 return FALSE;
243 heap_free(KeyPath);
245 if (ERROR_SUCCESS != RegQueryInfoKeyW(hKey, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &lenNameMax, &lenValueMax, NULL, NULL))
246 return FALSE;
248 lenName = ++lenNameMax;
249 valName = heap_xalloc(lenName * sizeof(WCHAR));
251 adjust = 0;
253 /* RegEnumValue won't return empty default value, so fake it when dealing with *row,
254 which corresponds to list view rows, not value ids */
255 if (ERROR_SUCCESS == RegEnumValueW(hKey, 0, valName, &lenName, NULL, NULL, NULL, NULL) && *valName)
256 adjust = 1;
258 i = (*row)-adjust;
259 if (i < 0) i = 0;
260 while(1) {
261 DWORD lenValue = 0, type = 0;
262 lenName = lenNameMax;
264 if (ERROR_SUCCESS != RegEnumValueW(hKey,
265 i, valName, &lenName, NULL, &type, NULL, NULL))
266 break;
268 if (mode & SEARCH_VALUES) {
269 if (match_string(valName, sstring, mode)) {
270 heap_free(valName);
271 heap_free(buffer);
272 RegCloseKey(hKey);
273 *row = i+adjust;
274 return TRUE;
278 if ((mode & SEARCH_CONTENT) && (type == REG_EXPAND_SZ || type == REG_SZ)) {
279 if (!buffer)
280 buffer = heap_xalloc(lenValueMax);
282 lenName = lenNameMax;
283 lenValue = lenValueMax;
284 if (ERROR_SUCCESS != RegEnumValueW(hKey, i, valName, &lenName, NULL, &type, (LPBYTE)buffer, &lenValue))
285 break;
286 if (match_string(buffer, sstring, mode)) {
287 heap_free(valName);
288 heap_free(buffer);
289 RegCloseKey(hKey);
290 *row = i+adjust;
291 return TRUE;
295 i++;
297 heap_free(valName);
298 heap_free(buffer);
299 RegCloseKey(hKey);
301 return FALSE;
304 HTREEITEM FindNext(HWND hwndTV, HTREEITEM hItem, LPCWSTR sstring, int mode, int *row)
306 HTREEITEM hTry, hLast;
308 hLast = hItem;
309 (*row)++;
310 if (match_item(hwndTV, hLast, sstring, mode & ~SEARCH_KEYS, row)) {
311 return hLast;
313 *row = 0;
315 while(hLast) {
316 /* first look in subtree */
317 /* no children? maybe we haven't loaded them yet? */
318 if (!SendMessageW(hwndTV, TVM_GETNEXTITEM, TVGN_CHILD, (LPARAM)hLast)) {
319 UINT state = (UINT)SendMessageW(hwndTV, TVM_GETITEMSTATE, (WPARAM)hLast, -1);
320 UpdateExpandingTree(hwndTV, hLast, state);
322 hTry = (HTREEITEM)SendMessageW(hwndTV, TVM_GETNEXTITEM, TVGN_CHILD, (LPARAM)hLast);
323 if (hTry) {
324 if (match_item(hwndTV, hTry, sstring, mode, row))
325 return hTry;
326 hLast = hTry;
327 continue;
329 /* no more children, maybe there are any siblings? */
330 hTry = (HTREEITEM)SendMessageW(hwndTV, TVM_GETNEXTITEM, TVGN_NEXT, (LPARAM)hLast);
331 if (hTry) {
332 if (match_item(hwndTV, hTry, sstring, mode, row))
333 return hTry;
334 hLast = hTry;
335 continue;
337 /* no more siblings, look at the next siblings in parent(s) */
338 hLast = (HTREEITEM)SendMessageW(hwndTV, TVM_GETNEXTITEM, TVGN_PARENT, (LPARAM)hLast);
339 if (!hLast)
340 return NULL;
341 while (hLast && (hTry = (HTREEITEM)SendMessageW(hwndTV, TVM_GETNEXTITEM, TVGN_NEXT, (LPARAM)hLast)) == NULL) {
342 hLast = (HTREEITEM)SendMessageW(hwndTV, TVM_GETNEXTITEM, TVGN_PARENT, (LPARAM)hLast);
344 if (match_item(hwndTV, hTry, sstring, mode, row))
345 return hTry;
346 hLast = hTry;
348 return NULL;
351 static BOOL RefreshTreeItem(HWND hwndTV, HTREEITEM hItem)
353 HKEY hRoot, hKey, hSubKey;
354 HTREEITEM childItem;
355 LPWSTR KeyPath;
356 DWORD dwCount, dwIndex, dwMaxSubKeyLen;
357 LPWSTR Name;
358 TVITEMW tvItem;
360 hRoot = NULL;
361 KeyPath = GetItemPath(hwndTV, hItem, &hRoot);
363 if (!KeyPath || !hRoot)
364 return FALSE;
366 if (*KeyPath) {
367 if (RegOpenKeyExW(hRoot, KeyPath, 0, KEY_READ, &hKey) != ERROR_SUCCESS) {
368 WINE_TRACE("RegOpenKeyEx failed, %s was probably removed.\n", wine_dbgstr_w(KeyPath));
369 return FALSE;
371 } else {
372 hKey = hRoot;
374 heap_free(KeyPath);
376 if (RegQueryInfoKeyW(hKey, 0, 0, 0, &dwCount, &dwMaxSubKeyLen, 0, 0, 0, 0, 0, 0) != ERROR_SUCCESS) {
377 return FALSE;
380 /* Set the number of children again */
381 tvItem.mask = TVIF_CHILDREN;
382 tvItem.hItem = hItem;
383 tvItem.cChildren = dwCount;
384 if (!TreeView_SetItemW(hwndTV, &tvItem)) {
385 return FALSE;
388 /* We don't have to bother with the rest if it's not expanded. */
389 if (SendMessageW(hwndTV, TVM_GETITEMSTATE, (WPARAM)hItem, TVIS_EXPANDED) == 0) {
390 RegCloseKey(hKey);
391 return TRUE;
394 dwMaxSubKeyLen++; /* account for the \0 terminator */
395 Name = heap_xalloc(dwMaxSubKeyLen * sizeof(WCHAR));
397 tvItem.cchTextMax = dwMaxSubKeyLen;
398 tvItem.pszText = heap_xalloc(dwMaxSubKeyLen * sizeof(WCHAR));
400 /* Now go through all the children in the registry, and check if any have to be added. */
401 for (dwIndex = 0; dwIndex < dwCount; dwIndex++) {
402 DWORD cName = dwMaxSubKeyLen, dwSubCount;
403 BOOL found;
405 found = FALSE;
406 if (RegEnumKeyExW(hKey, dwIndex, Name, &cName, 0, 0, 0, NULL) != ERROR_SUCCESS) {
407 continue;
410 /* Find the number of children of the node. */
411 dwSubCount = 0;
412 if (RegOpenKeyExW(hKey, Name, 0, KEY_QUERY_VALUE, &hSubKey) == ERROR_SUCCESS) {
413 if (RegQueryInfoKeyW(hSubKey, 0, 0, 0, &dwSubCount, 0, 0, 0, 0, 0, 0, 0) != ERROR_SUCCESS) {
414 dwSubCount = 0;
416 RegCloseKey(hSubKey);
419 /* Check if the node is already in there. */
420 for (childItem = (HTREEITEM)SendMessageW(hwndTV, TVM_GETNEXTITEM, TVGN_CHILD, (LPARAM)hItem); childItem;
421 childItem = (HTREEITEM)SendMessageW(hwndTV, TVM_GETNEXTITEM, TVGN_NEXT, (LPARAM)childItem)) {
422 tvItem.mask = TVIF_TEXT;
423 tvItem.hItem = childItem;
424 if (!TreeView_GetItemW(hwndTV, &tvItem)) {
425 heap_free(Name);
426 heap_free(tvItem.pszText);
427 return FALSE;
430 if (!lstrcmpiW(tvItem.pszText, Name)) {
431 found = TRUE;
432 break;
436 if (found == FALSE) {
437 WINE_TRACE("New subkey %s\n", wine_dbgstr_w(Name));
438 AddEntryToTree(hwndTV, hItem, Name, NULL, dwSubCount);
441 heap_free(Name);
442 heap_free(tvItem.pszText);
443 RegCloseKey(hKey);
445 /* Now go through all the children in the tree, and check if any have to be removed. */
446 childItem = (HTREEITEM)SendMessageW(hwndTV, TVM_GETNEXTITEM, TVGN_CHILD, (LPARAM)hItem);
447 while (childItem) {
448 HTREEITEM nextItem = (HTREEITEM)SendMessageW(hwndTV, TVM_GETNEXTITEM, TVGN_NEXT, (LPARAM)childItem);
449 if (RefreshTreeItem(hwndTV, childItem) == FALSE) {
450 SendMessageW(hwndTV, TVM_DELETEITEM, 0, (LPARAM)childItem);
452 childItem = nextItem;
455 return TRUE;
458 static void treeview_sort_item(HWND hWnd, HTREEITEM item)
460 HTREEITEM child = (HTREEITEM)SendMessageW(hWnd, TVM_GETNEXTITEM, TVGN_CHILD, (LPARAM)item);
462 while (child != NULL) {
463 treeview_sort_item(hWnd, child);
464 child = (HTREEITEM)SendMessageW(hWnd, TVM_GETNEXTITEM, TVGN_NEXT, (LPARAM)child);
466 SendMessageW(hWnd, TVM_SORTCHILDREN, 0, (LPARAM)item);
469 BOOL RefreshTreeView(HWND hwndTV)
471 HTREEITEM hItem;
472 HTREEITEM hSelectedItem;
473 HCURSOR hcursorOld;
474 HTREEITEM hRoot;
476 WINE_TRACE("\n");
477 hSelectedItem = (HTREEITEM)SendMessageW(hwndTV, TVM_GETNEXTITEM, TVGN_CARET, 0);
478 hcursorOld = SetCursor(LoadCursorW(NULL, (LPCWSTR)IDC_WAIT));
479 SendMessageW(hwndTV, WM_SETREDRAW, FALSE, 0);
481 hRoot = (HTREEITEM)SendMessageW(hwndTV, TVM_GETNEXTITEM, TVGN_ROOT, 0);
482 hItem = (HTREEITEM)SendMessageW(hwndTV, TVM_GETNEXTITEM, TVGN_CHILD, (LPARAM)hRoot);
483 while (hItem) {
484 RefreshTreeItem(hwndTV, hItem);
485 treeview_sort_item(hwndTV, hItem);
486 hItem = (HTREEITEM)SendMessageW(hwndTV, TVM_GETNEXTITEM, TVGN_NEXT, (LPARAM)hItem);
489 SendMessageW(hwndTV, WM_SETREDRAW, TRUE, 0);
490 InvalidateRect(hwndTV, NULL, FALSE);
491 SetCursor(hcursorOld);
493 /* We reselect the currently selected node, this will prompt a refresh of the listview. */
494 SendMessageW(hwndTV, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hSelectedItem);
495 return TRUE;
498 HTREEITEM InsertNode(HWND hwndTV, HTREEITEM hItem, LPWSTR name)
500 WCHAR buf[MAX_NEW_KEY_LEN];
501 HTREEITEM hNewItem = 0;
502 TVITEMEXW item;
504 if (!hItem) hItem = (HTREEITEM)SendMessageW(hwndTV, TVM_GETNEXTITEM, TVGN_CARET, 0);
505 if (!hItem) return FALSE;
506 if (SendMessageW(hwndTV, TVM_GETITEMSTATE, (WPARAM)hItem, TVIS_EXPANDEDONCE) & TVIS_EXPANDEDONCE) {
507 hNewItem = AddEntryToTree(hwndTV, hItem, name, 0, 0);
508 } else {
509 item.mask = TVIF_CHILDREN | TVIF_HANDLE;
510 item.hItem = hItem;
511 if (!TreeView_GetItemW(hwndTV, &item)) return FALSE;
512 item.cChildren = 1;
513 if (!TreeView_SetItemW(hwndTV, &item)) return FALSE;
515 SendMessageW(hwndTV, TVM_EXPAND, TVE_EXPAND, (LPARAM)hItem );
516 if (!hNewItem) {
517 for(hNewItem = (HTREEITEM)SendMessageW(hwndTV, TVM_GETNEXTITEM, TVGN_CHILD, (LPARAM)hItem); hNewItem;
518 hNewItem = (HTREEITEM)SendMessageW(hwndTV, TVM_GETNEXTITEM, TVGN_NEXT, (LPARAM)hNewItem)) {
519 item.mask = TVIF_HANDLE | TVIF_TEXT;
520 item.hItem = hNewItem;
521 item.pszText = buf;
522 item.cchTextMax = ARRAY_SIZE(buf);
523 if (!TreeView_GetItemW(hwndTV, &item)) continue;
524 if (lstrcmpW(name, item.pszText) == 0) break;
527 if (hNewItem)
528 SendMessageW(hwndTV, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hNewItem);
530 return hNewItem;
533 HWND StartKeyRename(HWND hwndTV)
535 HTREEITEM hItem;
537 if(!(hItem = (HTREEITEM)SendMessageW(hwndTV, TVM_GETNEXTITEM, TVGN_CARET, 0))) return 0;
538 SetWindowLongPtrW(hwndTV, GWLP_USERDATA, 1);
539 return (HWND)SendMessageW(hwndTV, TVM_EDITLABELW, 0, (LPARAM)hItem);
542 static BOOL InitTreeViewItems(HWND hwndTV, LPWSTR pHostName)
544 TVINSERTSTRUCTW tvins;
545 HTREEITEM hRoot;
546 static WCHAR hkcr[] = {'H','K','E','Y','_','C','L','A','S','S','E','S','_','R','O','O','T',0},
547 hkcu[] = {'H','K','E','Y','_','C','U','R','R','E','N','T','_','U','S','E','R',0},
548 hklm[] = {'H','K','E','Y','_','L','O','C','A','L','_','M','A','C','H','I','N','E',0},
549 hku[] = {'H','K','E','Y','_','U','S','E','R','S',0},
550 hkcc[] = {'H','K','E','Y','_','C','U','R','R','E','N','T','_','C','O','N','F','I','G',0},
551 hkdd[] = {'H','K','E','Y','_','D','Y','N','_','D','A','T','A',0};
553 tvins.u.item.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_CHILDREN | TVIF_PARAM;
554 /* Set the text of the item. */
555 tvins.u.item.pszText = pHostName;
556 tvins.u.item.cchTextMax = lstrlenW(pHostName);
557 /* Assume the item is not a parent item, so give it an image. */
558 tvins.u.item.iImage = Image_Root;
559 tvins.u.item.iSelectedImage = Image_Root;
560 tvins.u.item.cChildren = 5;
561 /* Save the heading level in the item's application-defined data area. */
562 tvins.u.item.lParam = 0;
563 tvins.hInsertAfter = TVI_FIRST;
564 tvins.hParent = TVI_ROOT;
565 /* Add the item to the tree view control. */
566 if (!(hRoot = TreeView_InsertItemW(hwndTV, &tvins))) return FALSE;
568 if (!AddEntryToTree(hwndTV, hRoot, hkcr, HKEY_CLASSES_ROOT, 1)) return FALSE;
569 if (!AddEntryToTree(hwndTV, hRoot, hkcu, HKEY_CURRENT_USER, 1)) return FALSE;
570 if (!AddEntryToTree(hwndTV, hRoot, hklm, HKEY_LOCAL_MACHINE, 1)) return FALSE;
571 if (!AddEntryToTree(hwndTV, hRoot, hku, HKEY_USERS, 1)) return FALSE;
572 if (!AddEntryToTree(hwndTV, hRoot, hkcc, HKEY_CURRENT_CONFIG, 1)) return FALSE;
573 if (!AddEntryToTree(hwndTV, hRoot, hkdd, HKEY_DYN_DATA, 1)) return FALSE;
575 /* expand and select host name */
576 SendMessageW(hwndTV, TVM_EXPAND, TVE_EXPAND, (LPARAM)hRoot );
577 SendMessageW(hwndTV, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hRoot);
578 return TRUE;
583 * InitTreeViewImageLists - creates an image list, adds three bitmaps
584 * to it, and associates the image list with a tree view control.
585 * Returns TRUE if successful, or FALSE otherwise.
586 * hwndTV - handle to the tree view control.
588 static BOOL InitTreeViewImageLists(HWND hwndTV)
590 HIMAGELIST himl; /* handle to image list */
591 HICON hico; /* handle to icon */
592 INT cx = GetSystemMetrics(SM_CXSMICON);
593 INT cy = GetSystemMetrics(SM_CYSMICON);
595 /* Create the image list. */
596 if ((himl = ImageList_Create(cx, cy, ILC_MASK, 0, NUM_ICONS)) == NULL)
597 return FALSE;
599 /* Add the open file, closed file, and document bitmaps. */
600 hico = LoadIconW(hInst, MAKEINTRESOURCEW(IDI_OPEN_FILE));
601 Image_Open = ImageList_AddIcon(himl, hico);
603 hico = LoadIconW(hInst, MAKEINTRESOURCEW(IDI_CLOSED_FILE));
604 Image_Closed = ImageList_AddIcon(himl, hico);
606 hico = LoadIconW(hInst, MAKEINTRESOURCEW(IDI_ROOT));
607 Image_Root = ImageList_AddIcon(himl, hico);
609 /* Fail if not all of the images were added. */
610 if (ImageList_GetImageCount(himl) < NUM_ICONS)
612 return FALSE;
615 /* Associate the image list with the tree view control. */
616 SendMessageW(hwndTV, TVM_SETIMAGELIST, TVSIL_NORMAL, (LPARAM)himl);
618 return TRUE;
621 BOOL UpdateExpandingTree(HWND hwndTV, HTREEITEM hItem, int state)
623 DWORD dwCount, dwIndex, dwMaxSubKeyLen;
624 HKEY hRoot, hNewKey, hKey;
625 LPWSTR keyPath;
626 LPWSTR Name;
627 LONG errCode;
628 HCURSOR hcursorOld;
629 TVITEMW item;
631 static int expanding;
632 if (expanding) return FALSE;
633 if (state & TVIS_EXPANDEDONCE ) {
634 return TRUE;
636 expanding = TRUE;
637 hcursorOld = SetCursor(LoadCursorW(NULL, (LPCWSTR)IDC_WAIT));
638 SendMessageW(hwndTV, WM_SETREDRAW, FALSE, 0);
640 keyPath = GetItemPath(hwndTV, hItem, &hRoot);
641 if (!keyPath) goto done;
643 if (*keyPath) {
644 errCode = RegOpenKeyExW(hRoot, keyPath, 0, KEY_READ, &hNewKey);
645 if (errCode != ERROR_SUCCESS) goto done;
646 } else {
647 hNewKey = hRoot;
650 errCode = RegQueryInfoKeyW(hNewKey, 0, 0, 0, &dwCount, &dwMaxSubKeyLen, 0, 0, 0, 0, 0, 0);
651 if (errCode != ERROR_SUCCESS) goto done;
652 dwMaxSubKeyLen++; /* account for the \0 terminator */
653 Name = heap_xalloc(dwMaxSubKeyLen * sizeof(WCHAR));
655 for (dwIndex = 0; dwIndex < dwCount; dwIndex++) {
656 DWORD cName = dwMaxSubKeyLen, dwSubCount;
658 errCode = RegEnumKeyExW(hNewKey, dwIndex, Name, &cName, 0, 0, 0, 0);
659 if (errCode != ERROR_SUCCESS) continue;
660 errCode = RegOpenKeyExW(hNewKey, Name, 0, KEY_QUERY_VALUE, &hKey);
661 if (errCode == ERROR_SUCCESS) {
662 errCode = RegQueryInfoKeyW(hKey, 0, 0, 0, &dwSubCount, 0, 0, 0, 0, 0, 0, 0);
663 RegCloseKey(hKey);
665 if (errCode != ERROR_SUCCESS) dwSubCount = 0;
666 AddEntryToTree(hwndTV, hItem, Name, NULL, dwSubCount);
668 RegCloseKey(hNewKey);
669 heap_free(Name);
671 done:
672 item.mask = TVIF_STATE;
673 item.hItem = hItem;
674 item.stateMask = TVIS_EXPANDEDONCE;
675 item.state = TVIS_EXPANDEDONCE;
676 SendMessageW(hwndTV, TVM_SETITEMW, 0, (LPARAM)&item);
677 SendMessageW(hwndTV, WM_SETREDRAW, TRUE, 0);
678 SetCursor(hcursorOld);
679 expanding = FALSE;
680 heap_free(keyPath);
682 return TRUE;
685 BOOL OnTreeExpanding(HWND hwndTV, NMTREEVIEWW* pnmtv)
687 return UpdateExpandingTree(hwndTV, pnmtv->itemNew.hItem, pnmtv->itemNew.state);
692 * CreateTreeView - creates a tree view control.
693 * Returns the handle to the new control if successful, or NULL otherwise.
694 * hwndParent - handle to the control's parent window.
696 HWND CreateTreeView(HWND hwndParent, LPWSTR pHostName, UINT id)
698 RECT rcClient;
699 HWND hwndTV;
700 WCHAR TreeView[] = {'T','r','e','e',' ','V','i','e','w',0};
702 /* Get the dimensions of the parent window's client area, and create the tree view control. */
703 GetClientRect(hwndParent, &rcClient);
704 hwndTV = CreateWindowExW(WS_EX_CLIENTEDGE, WC_TREEVIEWW, TreeView,
705 WS_VISIBLE | WS_CHILD | WS_TABSTOP | TVS_HASLINES | TVS_HASBUTTONS |
706 TVS_LINESATROOT | TVS_EDITLABELS | TVS_SHOWSELALWAYS,
707 0, 0, rcClient.right, rcClient.bottom,
708 hwndParent, ULongToHandle(id), hInst, NULL);
709 SendMessageW(hwndTV, TVM_SETUNICODEFORMAT, TRUE, 0);
710 /* Initialize the image list, and add items to the control. */
711 if (!InitTreeViewImageLists(hwndTV) || !InitTreeViewItems(hwndTV, pHostName)) {
712 DestroyWindow(hwndTV);
713 return NULL;
715 return hwndTV;