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