b781eb59ac0aefc4fe1d279551142a14c00cd973
[reactos.git] / reactos / 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 {
167 switch (dwValType)
168 {
169 case REG_SZ:
170 case REG_EXPAND_SZ:
171 if(dwCount > 0)
172 {
173 ListView_SetItemText(hwndLV, index, 2, ValBuf);
174 }
175 else if(!ValExists)
176 {
177 TCHAR buffer[255];
178 /* load (value not set) string */
179 LoadString(hInst, IDS_VALUE_NOT_SET, buffer, COUNT_OF(buffer));
180 ListView_SetItemText(hwndLV, index, 2, buffer);
181 }
182 break;
183 case REG_MULTI_SZ:
184 {
185 LPTSTR src, str;
186 if(dwCount >= 2)
187 {
188 src = (LPTSTR)ValBuf;
189 str = HeapAlloc(GetProcessHeap(), 0, dwCount + sizeof(TCHAR));
190 if(str != NULL)
191 {
192 *str = _T('\0');
193 /* concatenate all srings */
194 while(*src != _T('\0'))
195 {
196 _tcscat(str, src);
197 _tcscat(str, _T(" "));
198 src += _tcslen(src) + 1;
199 }
200 ListView_SetItemText(hwndLV, index, 2, str);
201 HeapFree(GetProcessHeap(), 0, str);
202 }
203 else
204 ListView_SetItemText(hwndLV, index, 2, _T(""));
205 }
206 else
207 ListView_SetItemText(hwndLV, index, 2, _T(""));
208 }
209 break;
210 case REG_DWORD:
211 {
212 TCHAR buf[200];
213 if(dwCount == sizeof(DWORD))
214 {
215 wsprintf(buf, _T("0x%08x (%u)"), *(DWORD*)ValBuf, *(DWORD*)ValBuf);
216 }
217 else
218 {
219 LoadString(hInst, IDS_INVALID_DWORD, buf, COUNT_OF(buf));
220 }
221 ListView_SetItemText(hwndLV, index, 2, buf);
222 }
223 /* lpsRes = convertHexToDWORDStr(lpbData, dwLen); */
224 break;
225 default:
226 {
227 unsigned int i;
228 LPBYTE pData = (LPBYTE)ValBuf;
229 LPTSTR strBinary;
230 if(dwCount > 0)
231 {
232 strBinary = HeapAlloc(GetProcessHeap(), 0, (dwCount * sizeof(TCHAR) * 3) + sizeof(TCHAR));
233 for (i = 0; i < dwCount; i++)
234 {
235 wsprintf( strBinary + i*3, _T("%02X "), pData[i] );
236 }
237 strBinary[dwCount * 3] = 0;
238 ListView_SetItemText(hwndLV, index, 2, strBinary);
239 HeapFree(GetProcessHeap(), 0, strBinary);
240 }
241 else
242 {
243 TCHAR szText[128];
244 LoadString(hInst, IDS_BINARY_EMPTY, szText, COUNT_OF(szText));
245 ListView_SetItemText(hwndLV, index, 2, szText);
246 }
247 }
248 break;
249 }
250 }
251 }
252
253 static BOOL CreateListColumns(HWND hWndListView)
254 {
255 TCHAR szText[50];
256 int index;
257 LV_COLUMN lvC;
258
259 /* Create columns. */
260 lvC.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
261 lvC.pszText = szText;
262
263 /* Load the column labels from the resource file. */
264 for (index = 0; index < MAX_LIST_COLUMNS; index++)
265 {
266 lvC.iSubItem = index;
267 lvC.cx = default_column_widths[index];
268 lvC.fmt = column_alignment[index];
269 LoadString(hInst, IDS_LIST_COLUMN_FIRST + index, szText, COUNT_OF(szText));
270 if (ListView_InsertColumn(hWndListView, index, &lvC) == -1) return FALSE;
271 }
272 return TRUE;
273 }
274
275 static BOOL InitListViewImageLists(HWND hwndLV)
276 {
277 HIMAGELIST himl; /* handle to image list */
278 HICON hico; /* handle to icon */
279
280 /* Create the image list. */
281 if ((himl = ImageList_Create(CX_ICON, CY_ICON,
282 ILC_MASK, 0, NUM_ICONS)) == NULL)
283 return FALSE;
284
285 hico = LoadIcon(hInst, MAKEINTRESOURCE(IDI_BIN));
286 Image_Bin = ImageList_AddIcon(himl, hico);
287
288 hico = LoadIcon(hInst, MAKEINTRESOURCE(IDI_STRING));
289 Image_String = ImageList_AddIcon(himl, hico);
290
291
292 /* Fail if not all of the images were added. */
293 if (ImageList_GetImageCount(himl) < NUM_ICONS)
294 {
295 return FALSE;
296 }
297
298 /* Associate the image list with the tree view control. */
299 (void)ListView_SetImageList(hwndLV, himl, LVSIL_SMALL);
300
301 return TRUE;
302 }
303
304 /* OnGetDispInfo - processes the LVN_GETDISPINFO notification message. */
305
306 static void OnGetDispInfo(NMLVDISPINFO* plvdi)
307 {
308 static TCHAR buffer[200];
309
310 plvdi->item.pszText = NULL;
311 plvdi->item.cchTextMax = 0;
312
313 switch (plvdi->item.iSubItem)
314 {
315 case 0:
316 LoadString(hInst, IDS_DEFAULT_VALUE_NAME, buffer, COUNT_OF(buffer));
317 plvdi->item.pszText = buffer;
318 break;
319 case 1:
320 switch (((LINE_INFO*)plvdi->item.lParam)->dwValType)
321 {
322 case REG_NONE:
323 plvdi->item.pszText = _T("REG_NONE");
324 break;
325 case REG_SZ:
326 plvdi->item.pszText = _T("REG_SZ");
327 break;
328 case REG_EXPAND_SZ:
329 plvdi->item.pszText = _T("REG_EXPAND_SZ");
330 break;
331 case REG_BINARY:
332 plvdi->item.pszText = _T("REG_BINARY");
333 break;
334 case REG_DWORD: /* REG_DWORD_LITTLE_ENDIAN */
335 plvdi->item.pszText = _T("REG_DWORD");
336 break;
337 case REG_DWORD_BIG_ENDIAN:
338 plvdi->item.pszText = _T("REG_DWORD_BIG_ENDIAN");
339 break;
340 case REG_LINK:
341 plvdi->item.pszText = _T("REG_LINK");
342 break;
343 case REG_MULTI_SZ:
344 plvdi->item.pszText = _T("REG_MULTI_SZ");
345 break;
346 case REG_RESOURCE_LIST:
347 plvdi->item.pszText = _T("REG_RESOURCE_LIST");
348 break;
349 case REG_FULL_RESOURCE_DESCRIPTOR:
350 plvdi->item.pszText = _T("REG_FULL_RESOURCE_DESCRIPTOR");
351 break;
352 case REG_RESOURCE_REQUIREMENTS_LIST:
353 plvdi->item.pszText = _T("REG_RESOURCE_REQUIREMENTS_LIST");
354 break;
355 case REG_QWORD: /* REG_QWORD_LITTLE_ENDIAN */
356 plvdi->item.pszText = _T("REG_QWORD");
357 break;
358 default:
359 {
360 TCHAR buf2[200];
361 LoadString(hInst, IDS_UNKNOWN_TYPE, buf2, COUNT_OF(buf2));
362 wsprintf(buffer, buf2, ((LINE_INFO*)plvdi->item.lParam)->dwValType);
363 plvdi->item.pszText = buffer;
364 break;
365 }
366 }
367 break;
368 case 3:
369 plvdi->item.pszText = _T("");
370 break;
371 }
372 }
373
374 static int CALLBACK CompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
375 {
376 LINE_INFO*l, *r;
377 UNREFERENCED_PARAMETER(lParamSort);
378
379 l = (LINE_INFO*)lParam1;
380 r = (LINE_INFO*)lParam2;
381
382 if (g_columnToSort == ~0UL)
383 g_columnToSort = 0;
384
385 if (g_columnToSort == 1 && l->dwValType != r->dwValType)
386 return g_invertSort ? (int)r->dwValType - (int)l->dwValType : (int)l->dwValType - (int)r->dwValType;
387 if (g_columnToSort == 2)
388 {
389 /* FIXME: Sort on value */
390 }
391 return g_invertSort ? _tcsicmp(r->name, l->name) : _tcsicmp(l->name, r->name);
392 }
393
394 BOOL ListWndNotifyProc(HWND hWnd, WPARAM wParam, LPARAM lParam, BOOL *Result)
395 {
396 NMLVDISPINFO* Info;
397 UNREFERENCED_PARAMETER(wParam);
398 *Result = TRUE;
399 switch (((LPNMHDR)lParam)->code)
400 {
401 case LVN_GETDISPINFO:
402 OnGetDispInfo((NMLVDISPINFO*)lParam);
403 return TRUE;
404 case LVN_COLUMNCLICK:
405 if (g_columnToSort == (DWORD)((LPNMLISTVIEW)lParam)->iSubItem)
406 g_invertSort = !g_invertSort;
407 else
408 {
409 g_columnToSort = ((LPNMLISTVIEW)lParam)->iSubItem;
410 g_invertSort = FALSE;
411 }
412
413 (void)ListView_SortItems(hWnd, CompareFunc, (WPARAM)hWnd);
414 return TRUE;
415 case NM_DBLCLK:
416 case NM_RETURN:
417 {
418 SendMessage(hFrameWnd, WM_COMMAND, MAKEWPARAM(ID_EDIT_MODIFY, 0), 0);
419 }
420 return TRUE;
421 case NM_SETFOCUS:
422 g_pChildWnd->nFocusPanel = 0;
423 break;
424 case LVN_BEGINLABELEDIT:
425 Info = (NMLVDISPINFO*)lParam;
426 if(Info)
427 {
428 PLINE_INFO lineinfo = (PLINE_INFO)Info->item.lParam;
429 if(!lineinfo->name || !_tcscmp(lineinfo->name, _T("")))
430 {
431 *Result = TRUE;
432 }
433 else
434 {
435 *Result = FALSE;
436 }
437 }
438 else
439 *Result = TRUE;
440 return TRUE;
441 case LVN_ENDLABELEDIT:
442 Info = (NMLVDISPINFO*)lParam;
443 if(Info && Info->item.pszText)
444 {
445 PLINE_INFO lineinfo = (PLINE_INFO)Info->item.lParam;
446 if(!lineinfo->name || !_tcscmp(lineinfo->name, _T("")))
447 {
448 *Result = FALSE;
449 }
450 else
451 {
452 if(_tcslen(Info->item.pszText) == 0)
453 {
454 TCHAR msg[128], caption[128];
455
456 LoadString(hInst, IDS_ERR_RENVAL_TOEMPTY, msg, COUNT_OF(msg));
457 LoadString(hInst, IDS_ERR_RENVAL_CAPTION, caption, COUNT_OF(caption));
458 MessageBox(0, msg, caption, 0);
459 *Result = TRUE;
460 }
461 else
462 {
463 HKEY hKeyRoot;
464 LPCTSTR keyPath;
465 LONG lResult;
466
467 keyPath = GetItemPath(g_pChildWnd->hTreeWnd, 0, &hKeyRoot);
468 lResult = RenameValue(hKeyRoot, keyPath, Info->item.pszText, lineinfo->name);
469 lineinfo->name = realloc(lineinfo->name, (_tcslen(Info->item.pszText)+1)*sizeof(TCHAR));
470 if (lineinfo->name != NULL)
471 _tcscpy(lineinfo->name, Info->item.pszText);
472
473 *Result = TRUE;
474 return (lResult == ERROR_SUCCESS);
475 }
476 }
477 }
478 else
479 *Result = TRUE;
480
481 return TRUE;
482 }
483 return FALSE;
484 }
485
486
487 HWND CreateListView(HWND hwndParent, HMENU id)
488 {
489 RECT rcClient;
490 HWND hwndLV;
491
492 /* Get the dimensions of the parent window's client area, and create the list view control. */
493 GetClientRect(hwndParent, &rcClient);
494 hwndLV = CreateWindowEx(WS_EX_CLIENTEDGE, WC_LISTVIEW, _T("List View"),
495 WS_VISIBLE | WS_CHILD | WS_TABSTOP | LVS_REPORT | LVS_EDITLABELS,
496 0, 0, rcClient.right, rcClient.bottom,
497 hwndParent, id, hInst, NULL);
498 if (!hwndLV) return NULL;
499
500 /* Initialize the image list, and add items to the control. */
501 if (!CreateListColumns(hwndLV)) goto fail;
502 if (!InitListViewImageLists(hwndLV)) goto fail;
503
504 return hwndLV;
505 fail:
506 DestroyWindow(hwndLV);
507 return NULL;
508 }
509
510 void DestroyListView(HWND hwndLV)
511 {
512 INT count, i;
513 LVITEM item;
514
515 count = ListView_GetItemCount(hwndLV);
516 for (i = 0; i < count; i++)
517 {
518 item.mask = LVIF_PARAM;
519 item.iItem = i;
520 (void)ListView_GetItem(hwndLV, &item);
521 free(((LINE_INFO*)item.lParam)->name);
522 HeapFree(GetProcessHeap(), 0, (void*)item.lParam);
523 }
524
525 }
526
527 BOOL RefreshListView(HWND hwndLV, HKEY hKey, LPCTSTR keyPath)
528 {
529 DWORD max_sub_key_len;
530 DWORD max_val_name_len;
531 DWORD max_val_size;
532 DWORD val_count;
533 HKEY hNewKey;
534 LONG errCode;
535 INT i, c;
536 BOOL AddedDefault = FALSE;
537
538 if (!hwndLV) return FALSE;
539
540 (void)ListView_EditLabel(hwndLV, -1);
541
542 SendMessage(hwndLV, WM_SETREDRAW, FALSE, 0);
543 DestroyListView(hwndLV);
544
545 g_columnToSort = ~0UL;
546 (void)ListView_DeleteAllItems(hwndLV);
547
548 if(!hKey) return FALSE;
549
550 errCode = RegOpenKeyEx(hKey, keyPath, 0, KEY_READ, &hNewKey);
551 if (errCode != ERROR_SUCCESS) return FALSE;
552
553 /* get size information and resize the buffers if necessary */
554 errCode = RegQueryInfoKey(hNewKey, NULL, NULL, NULL, NULL, &max_sub_key_len, NULL,
555 &val_count, &max_val_name_len, &max_val_size, NULL, NULL);
556
557 if (errCode == ERROR_SUCCESS)
558 {
559 TCHAR* ValName = HeapAlloc(GetProcessHeap(), 0, ++max_val_name_len * sizeof(TCHAR));
560 DWORD dwValNameLen = max_val_name_len;
561 BYTE* ValBuf = HeapAlloc(GetProcessHeap(), 0, max_val_size + sizeof(TCHAR));
562 DWORD dwValSize = max_val_size;
563 DWORD dwIndex = 0L;
564 DWORD dwValType;
565 /* if (RegQueryValueEx(hNewKey, NULL, NULL, &dwValType, ValBuf, &dwValSize) == ERROR_SUCCESS) { */
566 /* AddEntryToList(hwndLV, _T("(Default)"), dwValType, ValBuf, dwValSize); */
567 /* } */
568 /* dwValSize = max_val_size; */
569 while (RegEnumValue(hNewKey, dwIndex, ValName, &dwValNameLen, NULL, &dwValType, ValBuf, &dwValSize) == ERROR_SUCCESS)
570 {
571 /* Add a terminating 0 character. Usually this is only necessary for strings. */
572 ValBuf[dwValSize] = 0;
573 #ifdef UNICODE
574 ValBuf[dwValSize + 1] = 0;
575 #endif
576 AddEntryToList(hwndLV, ValName, dwValType, ValBuf, dwValSize, -1, TRUE);
577 dwValNameLen = max_val_name_len;
578 dwValSize = max_val_size;
579 dwValType = 0L;
580 ++dwIndex;
581 if(!_tcscmp(ValName, _T("")))
582 {
583 AddedDefault = TRUE;
584 }
585 }
586 HeapFree(GetProcessHeap(), 0, ValBuf);
587 HeapFree(GetProcessHeap(), 0, ValName);
588 }
589 if(!AddedDefault)
590 {
591 AddEntryToList(hwndLV, _T(""), REG_SZ, NULL, 0, 0, FALSE);
592 }
593 ListView_SortItems(hwndLV, CompareFunc, (WPARAM)hwndLV);
594 c = ListView_GetItemCount(hwndLV);
595 for(i = 0; i < c; i++)
596 {
597 ListView_SetItemState(hwndLV, i, 0, LVIS_FOCUSED | LVIS_SELECTED);
598 }
599 ListView_SetItemState(hwndLV, iListViewSelect,
600 LVIS_FOCUSED | LVIS_SELECTED,
601 LVIS_FOCUSED | LVIS_SELECTED);
602 RegCloseKey(hNewKey);
603 SendMessage(hwndLV, WM_SETREDRAW, TRUE, 0);
604
605 return TRUE;
606 }