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