- merge audio headers
[reactos.git] / base / applications / regedit / listview.c
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
24 #define CX_ICON 16
25 #define CY_ICON 16
26 #define NUM_ICONS 2
27
28 int Image_String = 0;
29 int Image_Bin = 0;
30 INT iListViewSelect = -1;
31
32 typedef struct tagLINE_INFO
33 {
34 DWORD dwValType;
35 LPTSTR name;
36 void* val;
37 size_t val_len;
38 } LINE_INFO, *PLINE_INFO;
39
40 /*******************************************************************************
41 * Global and Local Variables:
42 */
43
44 static DWORD g_columnToSort = ~0UL;
45 static BOOL g_invertSort = FALSE;
46
47 #define MAX_LIST_COLUMNS (IDS_LIST_COLUMN_LAST - IDS_LIST_COLUMN_FIRST + 1)
48 static const int default_column_widths[MAX_LIST_COLUMNS] = { 200, 175, 400 };
49 static const int column_alignment[MAX_LIST_COLUMNS] = { LVCFMT_LEFT, LVCFMT_LEFT, LVCFMT_LEFT };
50
51 LPCTSTR GetValueName(HWND hwndLV, int iStartAt)
52 {
53 int item;
54 LVITEM LVItem;
55 PLINE_INFO lineinfo;
56
57 /*
58 if a new item is inserted, then no allocation,
59 otherwise the heap block will be lost!
60 */
61 item = ListView_GetNextItem(hwndLV, iStartAt, LVNI_SELECTED);
62 if (item == -1) return NULL;
63
64 /*
65 Should be always TRUE anyways
66 */
67 LVItem.iItem = item;
68 LVItem.iSubItem = 0;
69 LVItem.mask = LVIF_PARAM;
70 if (ListView_GetItem(hwndLV, &LVItem) == FALSE)
71 return NULL;
72
73 lineinfo = (PLINE_INFO)LVItem.lParam;
74 if (lineinfo == NULL)
75 return NULL;
76
77 return lineinfo->name;
78 }
79
80 VOID SetValueName(HWND hwndLV, LPCTSTR pszValueName)
81 {
82 INT i, c;
83 LV_FINDINFO fi;
84
85 c = ListView_GetItemCount(hwndLV);
86 for(i = 0; i < c; i++)
87 {
88 ListView_SetItemState(hwndLV, i, 0, LVIS_FOCUSED | LVIS_SELECTED);
89 }
90 if (pszValueName == NULL)
91 i = 0;
92 else
93 {
94 fi.flags = LVFI_STRING;
95 fi.psz = pszValueName;
96 i = ListView_FindItem(hwndLV, -1, &fi);
97 }
98 ListView_SetItemState(hwndLV, i, LVIS_FOCUSED | LVIS_SELECTED,
99 LVIS_FOCUSED | LVIS_SELECTED);
100 iListViewSelect = i;
101 }
102
103 BOOL IsDefaultValue(HWND hwndLV, int i)
104 {
105 PLINE_INFO lineinfo;
106 LVITEM Item;
107
108 Item.mask = LVIF_PARAM;
109 Item.iItem = i;
110 if(ListView_GetItem(hwndLV, &Item))
111 {
112 lineinfo = (PLINE_INFO)Item.lParam;
113 return lineinfo && (!lineinfo->name || !_tcscmp(lineinfo->name, _T("")));
114 }
115 return FALSE;
116 }
117
118 /*******************************************************************************
119 * Local module support methods
120 */
121 static void AddEntryToList(HWND hwndLV, LPTSTR Name, DWORD dwValType, void* ValBuf, DWORD dwCount, int Position, BOOL ValExists)
122 {
123 PLINE_INFO linfo;
124 LVITEM item;
125 int index;
126
127 linfo = (PLINE_INFO)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(LINE_INFO) + dwCount);
128 linfo->dwValType = dwValType;
129 linfo->val_len = dwCount;
130 if(dwCount > 0)
131 {
132 memcpy(&linfo[1], ValBuf, dwCount);
133 }
134 linfo->name = _tcsdup(Name);
135
136 item.mask = LVIF_TEXT | LVIF_PARAM | LVIF_IMAGE;
137 item.iItem = (Position == -1 ? 0: Position);
138 item.iSubItem = 0;
139 item.state = 0;
140 item.stateMask = 0;
141 item.pszText = Name;
142 item.cchTextMax = (int) _tcslen(item.pszText);
143 if (item.cchTextMax == 0)
144 item.pszText = LPSTR_TEXTCALLBACK;
145 item.iImage = 0;
146 item.lParam = (LPARAM)linfo;
147 switch(dwValType)
148 {
149 case REG_SZ:
150 case REG_EXPAND_SZ:
151 case REG_MULTI_SZ:
152 item.iImage = Image_String;
153 break;
154 default:
155 item.iImage = Image_Bin;
156 break;
157 }
158
159 /* item.lParam = (LPARAM)ValBuf; */
160 #if (_WIN32_IE >= 0x0300)
161 item.iIndent = 0;
162 #endif
163
164 index = ListView_InsertItem(hwndLV, &item);
165 if (index != -1) {
166 switch (dwValType) {
167 case REG_SZ:
168 case REG_EXPAND_SZ:
169 if(dwCount > 0)
170 {
171 ListView_SetItemText(hwndLV, index, 2, ValBuf);
172 }
173 else if(!ValExists)
174 {
175 TCHAR buffer[255];
176 /* load (value not set) string */
177 LoadString(hInst, IDS_VALUE_NOT_SET, buffer, sizeof(buffer)/sizeof(TCHAR));
178 ListView_SetItemText(hwndLV, index, 2, buffer);
179 }
180 break;
181 case REG_MULTI_SZ:
182 {
183 LPTSTR src, str;
184 if(dwCount >= 2)
185 {
186 src = (LPTSTR)ValBuf;
187 str = HeapAlloc(GetProcessHeap(), 0, dwCount);
188 if(str != NULL)
189 {
190 *str = _T('\0');
191 /* concatenate all srings */
192 while(*src != _T('\0'))
193 {
194 _tcscat(str, src);
195 _tcscat(str, _T(" "));
196 src += _tcslen(src) + 1;
197 }
198 ListView_SetItemText(hwndLV, index, 2, str);
199 HeapFree(GetProcessHeap(), 0, str);
200 }
201 else
202 ListView_SetItemText(hwndLV, index, 2, _T(""));
203 }
204 else
205 ListView_SetItemText(hwndLV, index, 2, _T(""));
206 }
207 break;
208 case REG_DWORD: {
209 TCHAR buf[200];
210 if(dwCount == sizeof(DWORD))
211 {
212 wsprintf(buf, _T("0x%08x (%u)"), *(DWORD*)ValBuf, *(DWORD*)ValBuf);
213 }
214 else
215 {
216 LoadString(hInst, IDS_INVALID_DWORD, buf, sizeof(buf)/sizeof(TCHAR));
217 }
218 ListView_SetItemText(hwndLV, index, 2, buf);
219 }
220 /* lpsRes = convertHexToDWORDStr(lpbData, dwLen); */
221 break;
222 default:
223 {
224 unsigned int i;
225 LPBYTE pData = (LPBYTE)ValBuf;
226 LPTSTR strBinary;
227 if(dwCount > 0)
228 {
229 strBinary = HeapAlloc(GetProcessHeap(), 0, (dwCount * sizeof(TCHAR) * 3) + sizeof(TCHAR));
230 for (i = 0; i < dwCount; i++)
231 {
232 wsprintf( strBinary + i*3, _T("%02X "), pData[i] );
233 }
234 strBinary[dwCount * 3] = 0;
235 ListView_SetItemText(hwndLV, index, 2, strBinary);
236 HeapFree(GetProcessHeap(), 0, strBinary);
237 }
238 else
239 {
240 TCHAR szText[128];
241 LoadString(hInst, IDS_BINARY_EMPTY, szText, sizeof(szText)/sizeof(TCHAR));
242 ListView_SetItemText(hwndLV, index, 2, szText);
243 }
244 }
245 break;
246 }
247 }
248 }
249
250 static BOOL CreateListColumns(HWND hWndListView)
251 {
252 TCHAR szText[50];
253 int index;
254 LV_COLUMN lvC;
255
256 /* Create columns. */
257 lvC.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
258 lvC.pszText = szText;
259
260 /* Load the column labels from the resource file. */
261 for (index = 0; index < MAX_LIST_COLUMNS; index++) {
262 lvC.iSubItem = index;
263 lvC.cx = default_column_widths[index];
264 lvC.fmt = column_alignment[index];
265 LoadString(hInst, IDS_LIST_COLUMN_FIRST + index, szText, sizeof(szText)/sizeof(TCHAR));
266 if (ListView_InsertColumn(hWndListView, index, &lvC) == -1) return FALSE;
267 }
268 return TRUE;
269 }
270
271 static BOOL InitListViewImageLists(HWND hwndLV)
272 {
273 HIMAGELIST himl; /* handle to image list */
274 HICON hico; /* handle to icon */
275
276 /* Create the image list. */
277 if ((himl = ImageList_Create(CX_ICON, CY_ICON,
278 ILC_MASK, 0, NUM_ICONS)) == NULL)
279 return FALSE;
280
281 hico = LoadIcon(hInst, MAKEINTRESOURCE(IDI_BIN));
282 Image_Bin = ImageList_AddIcon(himl, hico);
283
284 hico = LoadIcon(hInst, MAKEINTRESOURCE(IDI_STRING));
285 Image_String = ImageList_AddIcon(himl, hico);
286
287
288 /* Fail if not all of the images were added. */
289 if (ImageList_GetImageCount(himl) < NUM_ICONS)
290 {
291 return FALSE;
292 }
293
294 /* Associate the image list with the tree view control. */
295 (void)ListView_SetImageList(hwndLV, himl, LVSIL_SMALL);
296
297 return TRUE;
298 }
299
300 /* OnGetDispInfo - processes the LVN_GETDISPINFO notification message. */
301
302 static void OnGetDispInfo(NMLVDISPINFO* plvdi)
303 {
304 static TCHAR buffer[200];
305
306 plvdi->item.pszText = NULL;
307 plvdi->item.cchTextMax = 0;
308
309 switch (plvdi->item.iSubItem) {
310 case 0:
311 LoadString(hInst, IDS_DEFAULT_VALUE_NAME, buffer, sizeof(buffer)/sizeof(TCHAR));
312 plvdi->item.pszText = buffer;
313 break;
314 case 1:
315 switch (((LINE_INFO*)plvdi->item.lParam)->dwValType) {
316 case REG_NONE:
317 plvdi->item.pszText = _T("REG_NONE");
318 break;
319 case REG_SZ:
320 plvdi->item.pszText = _T("REG_SZ");
321 break;
322 case REG_EXPAND_SZ:
323 plvdi->item.pszText = _T("REG_EXPAND_SZ");
324 break;
325 case REG_BINARY:
326 plvdi->item.pszText = _T("REG_BINARY");
327 break;
328 case REG_DWORD: /* REG_DWORD_LITTLE_ENDIAN */
329 plvdi->item.pszText = _T("REG_DWORD");
330 break;
331 case REG_DWORD_BIG_ENDIAN:
332 plvdi->item.pszText = _T("REG_DWORD_BIG_ENDIAN");
333 break;
334 case REG_LINK:
335 plvdi->item.pszText = _T("REG_LINK");
336 break;
337 case REG_MULTI_SZ:
338 plvdi->item.pszText = _T("REG_MULTI_SZ");
339 break;
340 case REG_RESOURCE_LIST:
341 plvdi->item.pszText = _T("REG_RESOURCE_LIST");
342 break;
343 case REG_FULL_RESOURCE_DESCRIPTOR:
344 plvdi->item.pszText = _T("REG_FULL_RESOURCE_DESCRIPTOR");
345 break;
346 case REG_RESOURCE_REQUIREMENTS_LIST:
347 plvdi->item.pszText = _T("REG_RESOURCE_REQUIREMENTS_LIST");
348 break;
349 case REG_QWORD: /* REG_QWORD_LITTLE_ENDIAN */
350 plvdi->item.pszText = _T("REG_QWORD");
351 break;
352 default: {
353 TCHAR buf2[200];
354 LoadString(hInst, IDS_UNKNOWN_TYPE, buf2, sizeof(buf2)/sizeof(TCHAR));
355 wsprintf(buffer, buf2, ((LINE_INFO*)plvdi->item.lParam)->dwValType);
356 plvdi->item.pszText = buffer;
357 break;
358 }
359 }
360 break;
361 case 3:
362 plvdi->item.pszText = _T("");
363 break;
364 }
365 }
366
367 static int CALLBACK CompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
368 {
369 LINE_INFO*l, *r;
370 UNREFERENCED_PARAMETER(lParamSort);
371
372 l = (LINE_INFO*)lParam1;
373 r = (LINE_INFO*)lParam2;
374
375 if (g_columnToSort == ~0UL)
376 g_columnToSort = 0;
377
378 if (g_columnToSort == 1 && l->dwValType != r->dwValType)
379 return g_invertSort ? (int)r->dwValType - (int)l->dwValType : (int)l->dwValType - (int)r->dwValType;
380 if (g_columnToSort == 2) {
381 /* FIXME: Sort on value */
382 }
383 return g_invertSort ? _tcsicmp(r->name, l->name) : _tcsicmp(l->name, r->name);
384 }
385
386 BOOL ListWndNotifyProc(HWND hWnd, WPARAM wParam, LPARAM lParam, BOOL *Result)
387 {
388 NMLVDISPINFO* Info;
389 UNREFERENCED_PARAMETER(wParam);
390 *Result = TRUE;
391 switch (((LPNMHDR)lParam)->code) {
392 case LVN_GETDISPINFO:
393 OnGetDispInfo((NMLVDISPINFO*)lParam);
394 return TRUE;
395 case LVN_COLUMNCLICK:
396 if (g_columnToSort == (DWORD)((LPNMLISTVIEW)lParam)->iSubItem)
397 g_invertSort = !g_invertSort;
398 else {
399 g_columnToSort = ((LPNMLISTVIEW)lParam)->iSubItem;
400 g_invertSort = FALSE;
401 }
402
403 (void)ListView_SortItems(hWnd, CompareFunc, (WPARAM)hWnd);
404 return TRUE;
405 case NM_DBLCLK:
406 case NM_RETURN:
407 {
408 SendMessage(hFrameWnd, WM_COMMAND, MAKEWPARAM(ID_EDIT_MODIFY, 0), 0);
409 }
410 return TRUE;
411 case NM_SETFOCUS:
412 g_pChildWnd->nFocusPanel = 0;
413 break;
414 case LVN_BEGINLABELEDIT:
415 Info = (NMLVDISPINFO*)lParam;
416 if(Info)
417 {
418 PLINE_INFO lineinfo = (PLINE_INFO)Info->item.lParam;
419 if(!lineinfo->name || !_tcscmp(lineinfo->name, _T("")))
420 {
421 *Result = TRUE;
422 }
423 else
424 {
425 *Result = FALSE;
426 }
427 }
428 else
429 *Result = TRUE;
430 return TRUE;
431 case LVN_ENDLABELEDIT:
432 Info = (NMLVDISPINFO*)lParam;
433 if(Info && Info->item.pszText)
434 {
435 PLINE_INFO lineinfo = (PLINE_INFO)Info->item.lParam;
436 if(!lineinfo->name || !_tcscmp(lineinfo->name, _T("")))
437 {
438 *Result = FALSE;
439 }
440 else
441 {
442 if(_tcslen(Info->item.pszText) == 0)
443 {
444 TCHAR msg[128], caption[128];
445
446 LoadString(hInst, IDS_ERR_RENVAL_TOEMPTY, msg, sizeof(msg)/sizeof(TCHAR));
447 LoadString(hInst, IDS_ERR_RENVAL_CAPTION, caption, sizeof(caption)/sizeof(TCHAR));
448 MessageBox(0, msg, caption, 0);
449 *Result = TRUE;
450 }
451 else
452 {
453 HKEY hKeyRoot;
454 LPCTSTR keyPath;
455 LONG lResult;
456
457 keyPath = GetItemPath(g_pChildWnd->hTreeWnd, 0, &hKeyRoot);
458 lResult = RenameValue(hKeyRoot, keyPath, Info->item.pszText, lineinfo->name);
459 lineinfo->name = realloc(lineinfo->name, (_tcslen(Info->item.pszText)+1)*sizeof(TCHAR));
460 if (lineinfo->name != NULL)
461 _tcscpy(lineinfo->name, Info->item.pszText);
462
463 *Result = TRUE;
464 return (lResult == ERROR_SUCCESS);
465 }
466 }
467 }
468 else
469 *Result = TRUE;
470
471 return TRUE;
472 }
473 return FALSE;
474 }
475
476
477 HWND CreateListView(HWND hwndParent, HMENU id)
478 {
479 RECT rcClient;
480 HWND hwndLV;
481
482 /* Get the dimensions of the parent window's client area, and create the list view control. */
483 GetClientRect(hwndParent, &rcClient);
484 hwndLV = CreateWindowEx(WS_EX_CLIENTEDGE, WC_LISTVIEW, _T("List View"),
485 WS_VISIBLE | WS_CHILD | WS_TABSTOP | LVS_REPORT | LVS_EDITLABELS,
486 0, 0, rcClient.right, rcClient.bottom,
487 hwndParent, id, hInst, NULL);
488 if (!hwndLV) return NULL;
489
490 /* Initialize the image list, and add items to the control. */
491 if (!CreateListColumns(hwndLV)) goto fail;
492 if (!InitListViewImageLists(hwndLV)) goto fail;
493
494 return hwndLV;
495 fail:
496 DestroyWindow(hwndLV);
497 return NULL;
498 }
499
500 void DestroyListView(HWND hwndLV)
501 {
502 INT count, i;
503 LVITEM item;
504
505 count = ListView_GetItemCount(hwndLV);
506 for (i = 0; i < count; i++) {
507 item.mask = LVIF_PARAM;
508 item.iItem = i;
509 (void)ListView_GetItem(hwndLV, &item);
510 free(((LINE_INFO*)item.lParam)->name);
511 HeapFree(GetProcessHeap(), 0, (void*)item.lParam);
512 }
513
514 }
515
516 BOOL RefreshListView(HWND hwndLV, HKEY hKey, LPCTSTR keyPath)
517 {
518 DWORD max_sub_key_len;
519 DWORD max_val_name_len;
520 DWORD max_val_size;
521 DWORD val_count;
522 HKEY hNewKey;
523 LONG errCode;
524 INT i, c;
525 BOOL AddedDefault = FALSE;
526
527 if (!hwndLV) return FALSE;
528
529 (void)ListView_EditLabel(hwndLV, -1);
530
531 SendMessage(hwndLV, WM_SETREDRAW, FALSE, 0);
532 DestroyListView(hwndLV);
533
534 g_columnToSort = ~0UL;
535 (void)ListView_DeleteAllItems(hwndLV);
536
537 if(!hKey) return FALSE;
538
539 errCode = RegOpenKeyEx(hKey, keyPath, 0, KEY_READ, &hNewKey);
540 if (errCode != ERROR_SUCCESS) return FALSE;
541
542 /* get size information and resize the buffers if necessary */
543 errCode = RegQueryInfoKey(hNewKey, NULL, NULL, NULL, NULL, &max_sub_key_len, NULL,
544 &val_count, &max_val_name_len, &max_val_size, NULL, NULL);
545
546 if (errCode == ERROR_SUCCESS)
547 {
548 TCHAR* ValName = HeapAlloc(GetProcessHeap(), 0, ++max_val_name_len * sizeof(TCHAR));
549 DWORD dwValNameLen = max_val_name_len;
550 BYTE* ValBuf = HeapAlloc(GetProcessHeap(), 0, max_val_size + sizeof(TCHAR));
551 DWORD dwValSize = max_val_size;
552 DWORD dwIndex = 0L;
553 DWORD dwValType;
554 /* if (RegQueryValueEx(hNewKey, NULL, NULL, &dwValType, ValBuf, &dwValSize) == ERROR_SUCCESS) { */
555 /* AddEntryToList(hwndLV, _T("(Default)"), dwValType, ValBuf, dwValSize); */
556 /* } */
557 /* dwValSize = max_val_size; */
558 while (RegEnumValue(hNewKey, dwIndex, ValName, &dwValNameLen, NULL, &dwValType, ValBuf, &dwValSize) == ERROR_SUCCESS) {
559 /* Remove unwanted path from key name */
560 TCHAR *pLastBl = _tcsrchr(ValName, TEXT('\\'));
561 if (pLastBl != NULL)
562 ++pLastBl;
563 else
564 pLastBl = ValName;
565 /* Add a terminating 0 character. Usually this is only necessary for strings. */
566 ValBuf[dwValSize] = 0;
567 #ifdef UNICODE
568 ValBuf[dwValSize + 1] = 0;
569 #endif
570 AddEntryToList(hwndLV, pLastBl, dwValType, ValBuf, dwValSize, -1, TRUE);
571 dwValNameLen = max_val_name_len;
572 dwValSize = max_val_size;
573 dwValType = 0L;
574 ++dwIndex;
575 if(!_tcscmp(ValName, _T("")))
576 {
577 AddedDefault = TRUE;
578 }
579 }
580 HeapFree(GetProcessHeap(), 0, ValBuf);
581 HeapFree(GetProcessHeap(), 0, ValName);
582 }
583 if(!AddedDefault)
584 {
585 AddEntryToList(hwndLV, _T(""), REG_SZ, NULL, 0, 0, FALSE);
586 }
587 ListView_SortItems(hwndLV, CompareFunc, (WPARAM)hwndLV);
588 c = ListView_GetItemCount(hwndLV);
589 for(i = 0; i < c; i++)
590 {
591 ListView_SetItemState(hwndLV, i, 0, LVIS_FOCUSED | LVIS_SELECTED);
592 }
593 ListView_SetItemState(hwndLV, iListViewSelect,
594 LVIS_FOCUSED | LVIS_SELECTED,
595 LVIS_FOCUSED | LVIS_SELECTED);
596 RegCloseKey(hNewKey);
597 SendMessage(hwndLV, WM_SETREDRAW, TRUE, 0);
598
599 return TRUE;
600 }