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