1 /*
2  * Regedit listviews
3  *
4  * Copyright (C) 2002 Robert Dickenson <robd@reactos.org>
5  * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later)
6  */
7 
8 #include "regedit.h"
9 
10 #define CX_ICON            16
11 #define CY_ICON            16
12 #define LISTVIEW_NUM_ICONS 2
13 
14 int Image_String;
15 int Image_Bin;
16 INT iListViewSelect = -1;
17 
18 typedef struct tagLINE_INFO
19 {
20     DWORD dwValType;
21     LPWSTR name;
22     void* val;
23     size_t val_len;
24 } LINE_INFO, *PLINE_INFO;
25 
26 typedef struct tagSORT_INFO
27 {
28     INT  iSortingColumn;
29     BOOL bSortAscending;
30 } SORT_INFO, *PSORT_INFO;
31 
32 /*******************************************************************************
33  * Global and Local Variables:
34  */
35 
36 static INT g_iSortedColumn;
37 
38 #define MAX_LIST_COLUMNS (IDS_LIST_COLUMN_LAST - IDS_LIST_COLUMN_FIRST + 1)
39 static const int default_column_widths[MAX_LIST_COLUMNS] = { 35, 25, 40 };  /* in percents */
40 static const int column_alignment[MAX_LIST_COLUMNS] = { LVCFMT_LEFT, LVCFMT_LEFT, LVCFMT_LEFT };
41 
42 WCHAR *GetValueName(HWND hwndLV, int iStartAt)
43 {
44     int item;
45     LVITEMW LVItem;
46     PLINE_INFO lineinfo;
47 
48     /*
49        if a new item is inserted, then no allocation,
50        otherwise the heap block will be lost!
51     */
52     item = ListView_GetNextItem(hwndLV, iStartAt, LVNI_SELECTED);
53     if (item == -1) return NULL;
54 
55     /*
56         Should be always TRUE anyways
57     */
58     LVItem.iItem = item;
59     LVItem.iSubItem = 0;
60     LVItem.mask = LVIF_PARAM;
61     if (ListView_GetItem(hwndLV, &LVItem) == FALSE)
62         return NULL;
63 
64     lineinfo = (PLINE_INFO)LVItem.lParam;
65     if (lineinfo == NULL)
66         return NULL;
67 
68     return lineinfo->name;
69 }
70 
71 VOID SetValueName(HWND hwndLV, LPCWSTR pszValueName)
72 {
73     INT i, c;
74     LVFINDINFOW fi;
75 
76     c = ListView_GetItemCount(hwndLV);
77     for(i = 0; i < c; i++)
78     {
79         ListView_SetItemState(hwndLV, i, 0, LVIS_FOCUSED | LVIS_SELECTED);
80     }
81     if (pszValueName == NULL || pszValueName[0] == 0)
82         i = 0;
83     else
84     {
85         fi.flags    = LVFI_STRING;
86         fi.psz      = pszValueName;
87         i = ListView_FindItem(hwndLV, -1, &fi);
88     }
89     ListView_SetItemState(hwndLV, i, LVIS_FOCUSED | LVIS_SELECTED,
90                           LVIS_FOCUSED | LVIS_SELECTED);
91     ListView_EnsureVisible(hwndLV, i, FALSE);
92     iListViewSelect = i;
93 }
94 
95 BOOL IsDefaultValue(HWND hwndLV, int i)
96 {
97     PLINE_INFO lineinfo;
98     LVITEMW Item;
99 
100     Item.mask = LVIF_PARAM;
101     Item.iItem = i;
102     if(ListView_GetItem(hwndLV, &Item))
103     {
104         lineinfo = (PLINE_INFO)Item.lParam;
105         return lineinfo && (!lineinfo->name || !wcscmp(lineinfo->name, L""));
106     }
107     return FALSE;
108 }
109 
110 /*******************************************************************************
111  * Local module support methods
112  */
113 static void AddEntryToList(HWND hwndLV, LPWSTR Name, DWORD dwValType, void* ValBuf, DWORD dwCount, int Position, BOOL ValExists)
114 {
115     PLINE_INFO linfo;
116     LVITEMW item;
117     int index;
118 
119     linfo = (PLINE_INFO)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(LINE_INFO) + dwCount);
120     linfo->dwValType = dwValType;
121     linfo->val_len = dwCount;
122     if (dwCount > 0)
123     {
124         memcpy(&linfo[1], ValBuf, dwCount);
125         linfo->val = &linfo[1];
126     }
127     linfo->name = _wcsdup(Name);
128 
129     item.mask = LVIF_TEXT | LVIF_PARAM | LVIF_IMAGE;
130     item.iItem = (Position == -1 ? 0: Position);
131     item.iSubItem = 0;
132     item.state = 0;
133     item.stateMask = 0;
134     item.pszText = Name;
135     item.cchTextMax = (int)wcslen(item.pszText);
136     if (item.cchTextMax == 0)
137         item.pszText = LPSTR_TEXTCALLBACK;
138     item.iImage = 0;
139     item.lParam = (LPARAM)linfo;
140     switch(dwValType)
141     {
142         case REG_SZ:
143         case REG_EXPAND_SZ:
144         case REG_MULTI_SZ:
145             item.iImage = Image_String;
146             break;
147         default:
148             item.iImage = Image_Bin;
149             break;
150     }
151 
152     /*    item.lParam = (LPARAM)ValBuf; */
153 #if (_WIN32_IE >= 0x0300)
154     item.iIndent = 0;
155 #endif
156 
157     index = ListView_InsertItem(hwndLV, &item);
158     if (index != -1)
159     {
160         switch (dwValType)
161         {
162         case REG_SZ:
163         case REG_EXPAND_SZ:
164             if(dwCount > 0)
165             {
166                 ListView_SetItemText(hwndLV, index, 2, ValBuf);
167             }
168             else if(!ValExists)
169             {
170                 WCHAR buffer[255];
171                 /* load (value not set) string */
172                 LoadStringW(hInst, IDS_VALUE_NOT_SET, buffer, ARRAY_SIZE(buffer));
173                 ListView_SetItemText(hwndLV, index, 2, buffer);
174             }
175             break;
176         case REG_MULTI_SZ:
177         {
178             LPWSTR src, str;
179             if(dwCount >= 2)
180             {
181                 src = (LPWSTR)ValBuf;
182                 str = HeapAlloc(GetProcessHeap(), 0, dwCount + sizeof(WCHAR));
183                 if(str != NULL)
184                 {
185                     *str = L'\0';
186                     /* concatenate all srings */
187                     while(*src != L'\0')
188                     {
189                         wcscat(str, src);
190                         wcscat(str, L" ");
191                         src += wcslen(src) + 1;
192                     }
193                     ListView_SetItemText(hwndLV, index, 2, str);
194                     HeapFree(GetProcessHeap(), 0, str);
195                 }
196                 else
197                     ListView_SetItemText(hwndLV, index, 2, L"");
198             }
199             else
200                 ListView_SetItemText(hwndLV, index, 2, L"");
201         }
202         break;
203         case REG_DWORD:
204         {
205             WCHAR buf[200];
206             if(dwCount == sizeof(DWORD))
207             {
208                 wsprintf(buf, L"0x%08x (%u)", *(DWORD*)ValBuf, *(DWORD*)ValBuf);
209             }
210             else
211             {
212                 LoadStringW(hInst, IDS_INVALID_DWORD, buf, ARRAY_SIZE(buf));
213             }
214             ListView_SetItemText(hwndLV, index, 2, buf);
215         }
216         /*            lpsRes = convertHexToDWORDStr(lpbData, dwLen); */
217         break;
218         default: /* REG_BINARY, REG_NONE etc. */
219         {
220             unsigned int i;
221             LPBYTE pData = (LPBYTE)ValBuf;
222             LPWSTR strBinary;
223             if(dwCount > 0)
224             {
225                 strBinary = HeapAlloc(GetProcessHeap(), 0, (dwCount * sizeof(WCHAR) * 3) + sizeof(WCHAR));
226                 for (i = 0; i < dwCount; i++)
227                 {
228                     wsprintf( strBinary + i*3, L"%02X ", pData[i] );
229                 }
230                 strBinary[dwCount * 3] = 0;
231                 ListView_SetItemText(hwndLV, index, 2, strBinary);
232                 HeapFree(GetProcessHeap(), 0, strBinary);
233             }
234             else
235             {
236                 WCHAR szText[128];
237                 LoadStringW(hInst, IDS_BINARY_EMPTY, szText, ARRAY_SIZE(szText));
238                 ListView_SetItemText(hwndLV, index, 2, szText);
239             }
240         }
241         break;
242         }
243     }
244 }
245 
246 static BOOL CreateListColumns(HWND hWndListView, INT cxTotal)
247 {
248     WCHAR szText[50];
249     int index;
250     LVCOLUMN lvC;
251 
252     /* Create columns. */
253     lvC.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
254     lvC.pszText = szText;
255 
256     /* Load the column labels from the resource file. */
257     for (index = 0; index < MAX_LIST_COLUMNS; index++)
258     {
259         lvC.iSubItem = index;
260         lvC.cx = (cxTotal * default_column_widths[index]) / 100;
261         lvC.fmt = column_alignment[index];
262         LoadStringW(hInst, IDS_LIST_COLUMN_FIRST + index, szText, ARRAY_SIZE(szText));
263         if (ListView_InsertColumn(hWndListView, index, &lvC) == -1) return FALSE;
264     }
265     return TRUE;
266 }
267 
268 static BOOL InitListViewImageLists(HWND hwndLV)
269 {
270     HIMAGELIST himl;  /* handle to image list  */
271     HICON hico;       /* handle to icon  */
272 
273     /* Create the image list.  */
274     if ((himl = ImageList_Create(CX_ICON, CY_ICON,
275                                  ILC_MASK, 0, LISTVIEW_NUM_ICONS)) == NULL)
276     {
277         return FALSE;
278     }
279 
280     hico = LoadIconW(hInst, MAKEINTRESOURCEW(IDI_BIN));
281     Image_Bin = ImageList_AddIcon(himl, hico);
282 
283     hico = LoadIconW(hInst, MAKEINTRESOURCEW(IDI_STRING));
284     Image_String = ImageList_AddIcon(himl, hico);
285 
286     /* Fail if not all of the images were added.  */
287     if (ImageList_GetImageCount(himl) < LISTVIEW_NUM_ICONS)
288     {
289         return FALSE;
290     }
291 
292     /* Associate the image list with the tree view control.  */
293     (void)ListView_SetImageList(hwndLV, himl, LVSIL_SMALL);
294 
295     return TRUE;
296 }
297 
298 /* OnGetDispInfo - processes the LVN_GETDISPINFO notification message.  */
299 
300 static void OnGetDispInfo(NMLVDISPINFO* plvdi)
301 {
302     static WCHAR buffer[200];
303 
304     plvdi->item.pszText = NULL;
305     plvdi->item.cchTextMax = 0;
306 
307     switch (plvdi->item.iSubItem)
308     {
309     case 0:
310         LoadStringW(hInst, IDS_DEFAULT_VALUE_NAME, buffer, ARRAY_SIZE(buffer));
311         plvdi->item.pszText = buffer;
312         break;
313     case 1:
314         switch (((LINE_INFO*)plvdi->item.lParam)->dwValType)
315         {
316             case REG_NONE:
317                 plvdi->item.pszText = L"REG_NONE";
318                 break;
319             case REG_SZ:
320                 plvdi->item.pszText = L"REG_SZ";
321                 break;
322             case REG_EXPAND_SZ:
323                 plvdi->item.pszText = L"REG_EXPAND_SZ";
324                 break;
325             case REG_BINARY:
326                 plvdi->item.pszText = L"REG_BINARY";
327                 break;
328             case REG_DWORD: /* REG_DWORD_LITTLE_ENDIAN */
329                 plvdi->item.pszText = L"REG_DWORD";
330                 break;
331             case REG_DWORD_BIG_ENDIAN:
332                 plvdi->item.pszText = L"REG_DWORD_BIG_ENDIAN";
333                 break;
334             case REG_LINK:
335                 plvdi->item.pszText = L"REG_LINK";
336                 break;
337             case REG_MULTI_SZ:
338                 plvdi->item.pszText = L"REG_MULTI_SZ";
339                 break;
340             case REG_RESOURCE_LIST:
341                 plvdi->item.pszText = L"REG_RESOURCE_LIST";
342                 break;
343             case REG_FULL_RESOURCE_DESCRIPTOR:
344                 plvdi->item.pszText = L"REG_FULL_RESOURCE_DESCRIPTOR";
345                 break;
346             case REG_RESOURCE_REQUIREMENTS_LIST:
347                 plvdi->item.pszText = L"REG_RESOURCE_REQUIREMENTS_LIST";
348                 break;
349             case REG_QWORD: /* REG_QWORD_LITTLE_ENDIAN */
350                 plvdi->item.pszText = L"REG_QWORD";
351                 break;
352             default:
353             {
354                 WCHAR buf2[200];
355                 LoadStringW(hInst, IDS_UNKNOWN_TYPE, buf2, ARRAY_SIZE(buf2));
356                 wsprintf(buffer, buf2, ((LINE_INFO*)plvdi->item.lParam)->dwValType);
357                 plvdi->item.pszText = buffer;
358                 break;
359             }
360         }
361         break;
362     case 3:
363         plvdi->item.pszText = L"";
364         break;
365     }
366 }
367 
368 static int CALLBACK CompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
369 {
370     PSORT_INFO pSortInfo = (PSORT_INFO)lParamSort;
371     LINE_INFO *l, *r;
372     DWORD dw1, dw2;
373     DWORDLONG qw1, qw2;
374 
375     l = (LINE_INFO*)lParam1;
376     r = (LINE_INFO*)lParam2;
377 
378     if (pSortInfo->iSortingColumn == 1 && l->dwValType != r->dwValType)
379     {
380         /* Sort by type */
381         if (pSortInfo->bSortAscending)
382             return ((int)l->dwValType - (int)r->dwValType);
383         else
384             return ((int)r->dwValType - (int)l->dwValType);
385     }
386     if (pSortInfo->iSortingColumn == 2)
387     {
388         /* Sort by value */
389         if (l->dwValType != r->dwValType)
390         {
391             if (pSortInfo->bSortAscending)
392                 return ((int)l->dwValType - (int)r->dwValType);
393             else
394                 return ((int)r->dwValType - (int)l->dwValType);
395         }
396 
397         if (l->val == NULL && r->val == NULL)
398             return 0;
399 
400         if (pSortInfo->bSortAscending)
401         {
402             if (l->val == NULL)
403                 return -1;
404             if (r->val == NULL)
405                 return 1;
406         }
407         else
408         {
409             if (l->val == NULL)
410                 return 1;
411             if (r->val == NULL)
412                 return -1;
413         }
414 
415         switch(l->dwValType)
416         {
417             case REG_DWORD:
418             {
419                 dw1 = *(DWORD*)l->val;
420                 dw2 = *(DWORD*)r->val;
421                 if (pSortInfo->bSortAscending)
422                     // return (dw1 > dw2 ? 1 : -1);
423                     return ((int)dw1 - (int)dw2);
424                 else
425                     // return (dw1 > dw2 ? -1 : 1);
426                     return ((int)dw2 - (int)dw1);
427             }
428 
429             case REG_QWORD: /* REG_QWORD_LITTLE_ENDIAN */
430             {
431                 qw1 = *(DWORDLONG*)l->val;
432                 qw2 = *(DWORDLONG*)r->val;
433                 if (pSortInfo->bSortAscending)
434                     // return (qw1 > qw2 ? 1 : -1);
435                     return ((int)qw1 - (int)qw2);
436                 else
437                     // return (qw1 > qw2 ? -1 : 1);
438                     return ((int)qw2 - (int)qw1);
439             }
440 
441             default:
442             {
443                 INT nCompare = 0;
444 
445                 if (pSortInfo->bSortAscending)
446                 {
447                     nCompare = memcmp(l->val, r->val, min(l->val_len, r->val_len));
448                     if (nCompare == 0)
449                         nCompare = l->val_len - r->val_len;
450                 }
451                 else
452                 {
453                     nCompare = memcmp(r->val, l->val, min(r->val_len, l->val_len));
454                     if (nCompare == 0)
455                         nCompare = r->val_len - l->val_len;
456                 }
457 
458                 return nCompare;
459             }
460         }
461     }
462 
463     /* Sort by name */
464     return (pSortInfo->bSortAscending ? StrCmpLogicalW(l->name, r->name) : StrCmpLogicalW(r->name, l->name));
465 }
466 
467 static BOOL ListView_Sort(HWND hListView, int iSortingColumn, int iSortedColumn)
468 {
469     if (!(GetWindowLongPtr(hListView, GWL_STYLE) & LVS_NOSORTHEADER) &&
470          (iSortingColumn >= 0) )
471     {
472         BOOL bSortAscending;
473         SORT_INFO SortInfo;
474 
475         HWND hHeader = ListView_GetHeader(hListView);
476         HDITEM hColumn = {0};
477 
478         /* If we are sorting according to another column, uninitialize the old one */
479         if ( (iSortedColumn >= 0) && (iSortingColumn != iSortedColumn) )
480         {
481             hColumn.mask = HDI_FORMAT;
482             Header_GetItem(hHeader, iSortedColumn, &hColumn);
483             hColumn.fmt &= ~(HDF_SORTUP | HDF_SORTDOWN);
484             Header_SetItem(hHeader, iSortedColumn, &hColumn);
485         }
486 
487         /* Get the sorting state of the new column */
488         hColumn.mask = HDI_FORMAT;
489         Header_GetItem(hHeader, iSortingColumn, &hColumn);
490 
491         /*
492          * Check whether we are sorting the list because the user clicked
493          * on a column, or because we are refreshing the list:
494          *
495          * iSortedColumn >= 0 - User clicked on a column; holds the
496          *                      old sorting column index.
497          * iSortedColumn  < 0 - List being refreshed.
498          */
499         if (iSortedColumn >= 0)
500         {
501             /* Invert the sorting direction */
502             bSortAscending = ((hColumn.fmt & HDF_SORTUP) == 0);
503         }
504         else
505         {
506             /*
507              * If the sorting state of the column is uninitialized,
508              * initialize it by default to ascending sorting.
509              */
510             if ((hColumn.fmt & (HDF_SORTUP | HDF_SORTDOWN)) == 0)
511                 hColumn.fmt |= HDF_SORTUP;
512 
513             /* Keep the same sorting direction */
514             bSortAscending = ((hColumn.fmt & HDF_SORTUP) != 0);
515         }
516 
517         /* Set the new column sorting state */
518         hColumn.fmt &= ~(bSortAscending ? HDF_SORTDOWN : HDF_SORTUP  );
519         hColumn.fmt |=  (bSortAscending ? HDF_SORTUP   : HDF_SORTDOWN);
520         Header_SetItem(hHeader, iSortingColumn, &hColumn);
521 
522         /* Sort the list */
523         SortInfo.iSortingColumn = iSortingColumn;
524         SortInfo.bSortAscending = bSortAscending;
525         return ListView_SortItems(hListView, CompareFunc, (LPARAM)&SortInfo);
526     }
527     else
528         return TRUE;
529 }
530 
531 BOOL ListWndNotifyProc(HWND hWnd, WPARAM wParam, LPARAM lParam, BOOL *Result)
532 {
533     NMLVDISPINFO* Info;
534     int iSortingColumn;
535     UNREFERENCED_PARAMETER(wParam);
536     *Result = TRUE;
537     switch (((LPNMHDR)lParam)->code)
538     {
539     case LVN_GETDISPINFO:
540         OnGetDispInfo((NMLVDISPINFO*)lParam);
541         return TRUE;
542     case LVN_COLUMNCLICK:
543         iSortingColumn = ((LPNMLISTVIEW)lParam)->iSubItem;
544         (void)ListView_Sort(hWnd, iSortingColumn, g_iSortedColumn);
545         g_iSortedColumn = iSortingColumn;
546         return TRUE;
547     case NM_DBLCLK:
548     case NM_RETURN:
549     {
550         SendMessageW(hFrameWnd, WM_COMMAND, MAKEWPARAM(ID_EDIT_MODIFY, 0), 0);
551     }
552     return TRUE;
553     case NM_SETFOCUS:
554         g_pChildWnd->nFocusPanel = 1;
555         break;
556     case LVN_BEGINLABELEDIT:
557         Info = (NMLVDISPINFO*)lParam;
558         if(Info)
559         {
560             PLINE_INFO lineinfo = (PLINE_INFO)Info->item.lParam;
561             if(!lineinfo->name || !wcscmp(lineinfo->name, L""))
562             {
563                 *Result = TRUE;
564             }
565             else
566             {
567                 *Result = FALSE;
568             }
569         }
570         else
571             *Result = TRUE;
572         return TRUE;
573     case LVN_ENDLABELEDIT:
574         Info = (NMLVDISPINFO*)lParam;
575         if(Info && Info->item.pszText)
576         {
577             PLINE_INFO lineinfo = (PLINE_INFO)Info->item.lParam;
578             if(!lineinfo->name || !wcscmp(lineinfo->name, L""))
579             {
580                 *Result = FALSE;
581             }
582             else
583             {
584                 if(wcslen(Info->item.pszText) == 0)
585                 {
586                     WCHAR msg[128], caption[128];
587 
588                     LoadStringW(hInst, IDS_ERR_RENVAL_TOEMPTY, msg, ARRAY_SIZE(msg));
589                     LoadStringW(hInst, IDS_ERR_RENVAL_CAPTION, caption, ARRAY_SIZE(caption));
590                     MessageBoxW(0, msg, caption, 0);
591                     *Result = TRUE;
592                 }
593                 else
594                 {
595                     HKEY hKeyRoot;
596                     LPCWSTR keyPath;
597                     LONG lResult;
598 
599                     keyPath = GetItemPath(g_pChildWnd->hTreeWnd, 0, &hKeyRoot);
600                     lResult = RenameValue(hKeyRoot, keyPath, Info->item.pszText, lineinfo->name);
601                     lineinfo->name = realloc(lineinfo->name, (wcslen(Info->item.pszText)+1)*sizeof(WCHAR));
602                     if (lineinfo->name != NULL)
603                         wcscpy(lineinfo->name, Info->item.pszText);
604 
605                     *Result = TRUE;
606                     return (lResult == ERROR_SUCCESS);
607                 }
608             }
609         }
610         else
611             *Result = TRUE;
612 
613         return TRUE;
614     }
615     return FALSE;
616 }
617 
618 HWND CreateListView(HWND hwndParent, HMENU id, INT cx)
619 {
620     RECT rcClient;
621     HWND hwndLV;
622 
623     /* Get the dimensions of the parent window's client area, and create the list view control. */
624     GetClientRect(hwndParent, &rcClient);
625     hwndLV = CreateWindowExW(WS_EX_CLIENTEDGE, WC_LISTVIEW, L"List View",
626                              WS_VISIBLE | WS_CHILD | WS_TABSTOP | LVS_REPORT | LVS_EDITLABELS | LVS_SHOWSELALWAYS,
627                              0, 0, rcClient.right, rcClient.bottom,
628                              hwndParent, id, hInst, NULL);
629     if (!hwndLV) return NULL;
630 
631     /* Initialize the image list, and add items to the control. */
632     if (!CreateListColumns(hwndLV, cx)) goto fail;
633     if (!InitListViewImageLists(hwndLV)) goto fail;
634 
635     return hwndLV;
636 fail:
637     DestroyWindow(hwndLV);
638     return NULL;
639 }
640 
641 void DestroyListView(HWND hwndLV)
642 {
643     INT count, i;
644     LVITEMW item;
645 
646     count = ListView_GetItemCount(hwndLV);
647     for (i = 0; i < count; i++)
648     {
649         item.mask = LVIF_PARAM;
650         item.iItem = i;
651         (void)ListView_GetItem(hwndLV, &item);
652         free(((LINE_INFO*)item.lParam)->name);
653         HeapFree(GetProcessHeap(), 0, (void*)item.lParam);
654     }
655 }
656 
657 BOOL RefreshListView(HWND hwndLV, HKEY hKey, LPCWSTR keyPath, BOOL bSelectNone)
658 {
659     DWORD max_sub_key_len;
660     DWORD max_val_name_len;
661     DWORD max_val_size;
662     DWORD val_count;
663     HKEY hNewKey;
664     LONG errCode;
665     INT i, c;
666     BOOL AddedDefault = FALSE;
667 
668     if (!hwndLV) return FALSE;
669 
670     (void)ListView_EditLabel(hwndLV, -1);
671 
672     SendMessageW(hwndLV, WM_SETREDRAW, FALSE, 0);
673     DestroyListView(hwndLV);
674 
675     (void)ListView_DeleteAllItems(hwndLV);
676 
677     if(!hKey) return FALSE;
678 
679     errCode = RegOpenKeyExW(hKey, keyPath, 0, KEY_READ, &hNewKey);
680     if (errCode != ERROR_SUCCESS) return FALSE;
681 
682     /* get size information and resize the buffers if necessary */
683     errCode = RegQueryInfoKeyW(hNewKey, NULL, NULL, NULL, NULL, &max_sub_key_len, NULL,
684                                &val_count, &max_val_name_len, &max_val_size, NULL, NULL);
685 
686     if (errCode == ERROR_SUCCESS)
687     {
688         WCHAR* ValName = HeapAlloc(GetProcessHeap(), 0, ++max_val_name_len * sizeof(WCHAR));
689         DWORD dwValNameLen = max_val_name_len;
690         BYTE* ValBuf = HeapAlloc(GetProcessHeap(), 0, max_val_size + sizeof(WCHAR));
691         DWORD dwValSize = max_val_size;
692         DWORD dwIndex = 0L;
693         DWORD dwValType;
694         /*                if (RegQueryValueExW(hNewKey, NULL, NULL, &dwValType, ValBuf, &dwValSize) == ERROR_SUCCESS) { */
695         /*                    AddEntryToList(hwndLV, L"(Default)", dwValType, ValBuf, dwValSize); */
696         /*                } */
697         /*                dwValSize = max_val_size; */
698         while (RegEnumValueW(hNewKey, dwIndex, ValName, &dwValNameLen, NULL, &dwValType, ValBuf, &dwValSize) == ERROR_SUCCESS)
699         {
700             /* Add a terminating 0 character. Usually this is only necessary for strings. */
701             ValBuf[dwValSize] = ValBuf[dwValSize + 1] = 0;
702 
703             AddEntryToList(hwndLV, ValName, dwValType, ValBuf, dwValSize, -1, TRUE);
704             dwValNameLen = max_val_name_len;
705             dwValSize = max_val_size;
706             dwValType = 0L;
707             ++dwIndex;
708             if(!wcscmp(ValName, L""))
709             {
710                 AddedDefault = TRUE;
711             }
712         }
713         HeapFree(GetProcessHeap(), 0, ValBuf);
714         HeapFree(GetProcessHeap(), 0, ValName);
715     }
716     RegCloseKey(hNewKey);
717 
718     if(!AddedDefault)
719     {
720         AddEntryToList(hwndLV, L"", REG_SZ, NULL, 0, 0, FALSE);
721     }
722     c = ListView_GetItemCount(hwndLV);
723     for(i = 0; i < c; i++)
724     {
725         ListView_SetItemState(hwndLV, i, 0, LVIS_FOCUSED | LVIS_SELECTED);
726     }
727 
728     if (bSelectNone)
729         iListViewSelect = -1;
730     ListView_SetItemState(hwndLV, iListViewSelect,
731                           LVIS_FOCUSED | LVIS_SELECTED,
732                           LVIS_FOCUSED | LVIS_SELECTED);
733     (void)ListView_Sort(hwndLV, g_iSortedColumn, -1);
734     SendMessageW(hwndLV, WM_SETREDRAW, TRUE, 0);
735 
736     return TRUE;
737 }
738