Log the last error too if the tests fail without default printer.
[wine/multimedia.git] / programs / regedit / listview.c
blob0a273acceb53ec82a236ce46bb538abf13ee61e7
1 /*
2 * Regedit listviews
4 * Copyright (C) 2002 Robert Dickenson <robd@reactos.org>
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.
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.
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
21 #include <windows.h>
22 #include <windowsx.h>
23 #include <commctrl.h>
24 #include <stdlib.h>
25 #include <tchar.h>
26 #include <process.h>
27 #include <stdio.h>
29 #include "main.h"
31 #include "wine/debug.h"
32 #include "wine/unicode.h"
34 WINE_DEFAULT_DEBUG_CHANNEL(regedit);
36 typedef struct tagLINE_INFO
38 DWORD dwValType;
39 LPTSTR name;
40 void* val;
41 size_t val_len;
42 } LINE_INFO;
44 /*******************************************************************************
45 * Global and Local Variables:
48 static WNDPROC g_orgListWndProc;
49 static DWORD g_columnToSort = ~0UL;
50 static BOOL g_invertSort = FALSE;
51 static LPTSTR g_valueName;
52 static LPTSTR g_currentPath;
53 static HKEY g_currentRootKey;
55 #define MAX_LIST_COLUMNS (IDS_LIST_COLUMN_LAST - IDS_LIST_COLUMN_FIRST + 1)
56 static int default_column_widths[MAX_LIST_COLUMNS] = { 200, 175, 400 };
57 static int column_alignment[MAX_LIST_COLUMNS] = { LVCFMT_LEFT, LVCFMT_LEFT, LVCFMT_LEFT };
59 LPTSTR get_item_text(HWND hwndLV, int item)
61 LPTSTR newStr, curStr;
62 int maxLen = 128;
64 curStr = HeapAlloc(GetProcessHeap(), 0, maxLen);
65 if (!curStr) return NULL;
66 if (item == 0) return NULL; /* first item is ALWAYS a default */
67 do {
68 ListView_GetItemText(hwndLV, item, 0, curStr, maxLen);
69 if (_tcslen(curStr) < maxLen - 1) return curStr;
70 newStr = HeapReAlloc(GetProcessHeap(), 0, curStr, maxLen * 2);
71 if (!newStr) break;
72 curStr = newStr;
73 maxLen *= 2;
74 } while (TRUE);
75 HeapFree(GetProcessHeap(), 0, curStr);
76 return NULL;
79 LPCTSTR GetValueName(HWND hwndLV)
81 INT item;
83 if (g_valueName && g_valueName != LPSTR_TEXTCALLBACK)
84 HeapFree(GetProcessHeap(), 0, g_valueName);
85 g_valueName = NULL;
87 item = ListView_GetNextItem(hwndLV, -1, LVNI_FOCUSED);
88 if (item == -1) return NULL;
90 g_valueName = get_item_text(hwndLV, item);
92 return g_valueName;
95 /*******************************************************************************
96 * Local module support methods
98 static void AddEntryToList(HWND hwndLV, LPTSTR Name, DWORD dwValType,
99 void* ValBuf, DWORD dwCount, BOOL bHighlight)
101 LINE_INFO* linfo;
102 LVITEM item;
103 int index;
105 linfo = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(LINE_INFO) + dwCount);
106 linfo->dwValType = dwValType;
107 linfo->val_len = dwCount;
108 memcpy(&linfo[1], ValBuf, dwCount);
110 if (Name)
111 linfo->name = _tcsdup(Name);
112 else
113 linfo->name = NULL;
115 item.mask = LVIF_TEXT | LVIF_PARAM | LVIF_STATE;
116 item.iItem = ListView_GetItemCount(hwndLV);/*idx; */
117 item.iSubItem = 0;
118 item.state = 0;
119 item.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
120 item.pszText = Name ? Name : LPSTR_TEXTCALLBACK;
121 item.cchTextMax = Name ? _tcslen(item.pszText) : 0;
122 if (bHighlight) {
123 item.stateMask = item.state = LVIS_FOCUSED | LVIS_SELECTED;
125 item.iImage = 0;
126 item.lParam = (LPARAM)linfo;
128 /* item.lParam = (LPARAM)ValBuf; */
129 #if (_WIN32_IE >= 0x0300)
130 item.iIndent = 0;
131 #endif
133 index = ListView_InsertItem(hwndLV, &item);
134 if (index != -1) {
135 /* LPTSTR pszText = NULL; */
136 LPTSTR pszText = _T("value");
137 switch (dwValType) {
138 case REG_SZ:
139 case REG_EXPAND_SZ:
140 if (ValBuf) {
141 ListView_SetItemText(hwndLV, index, 2, ValBuf);
142 } else {
143 ListView_SetItemText(hwndLV, index, 2, "(not set)");
145 break;
146 case REG_DWORD: {
147 TCHAR buf[64];
148 wsprintf(buf, _T("0x%08X (%d)"), *(DWORD*)ValBuf, *(DWORD*)ValBuf);
149 ListView_SetItemText(hwndLV, index, 2, buf);
151 /* lpsRes = convertHexToDWORDStr(lpbData, dwLen); */
152 break;
153 case REG_BINARY: {
154 unsigned int i;
155 LPBYTE pData = (LPBYTE)ValBuf;
156 LPTSTR strBinary = HeapAlloc(GetProcessHeap(), 0, dwCount * sizeof(TCHAR) * 3 + 1);
157 for (i = 0; i < dwCount; i++)
158 wsprintf( strBinary + i*3, _T("%02X "), pData[i] );
159 strBinary[dwCount * 3] = 0;
160 ListView_SetItemText(hwndLV, index, 2, strBinary);
161 HeapFree(GetProcessHeap(), 0, strBinary);
163 break;
164 default:
165 /* lpsRes = convertHexToHexCSV(lpbData, dwLen); */
166 ListView_SetItemText(hwndLV, index, 2, pszText);
167 break;
172 static BOOL CreateListColumns(HWND hWndListView)
174 TCHAR szText[50];
175 int index;
176 LV_COLUMN lvC;
178 /* Create columns. */
179 lvC.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
180 lvC.pszText = szText;
182 /* Load the column labels from the resource file. */
183 for (index = 0; index < MAX_LIST_COLUMNS; index++) {
184 lvC.iSubItem = index;
185 lvC.cx = default_column_widths[index];
186 lvC.fmt = column_alignment[index];
187 LoadString(hInst, IDS_LIST_COLUMN_FIRST + index, szText, sizeof(szText)/sizeof(TCHAR));
188 if (ListView_InsertColumn(hWndListView, index, &lvC) == -1) return FALSE;
190 return TRUE;
193 /* OnGetDispInfo - processes the LVN_GETDISPINFO notification message. */
195 static void OnGetDispInfo(NMLVDISPINFO* plvdi)
197 static TCHAR buffer[200];
199 plvdi->item.pszText = NULL;
200 plvdi->item.cchTextMax = 0;
202 switch (plvdi->item.iSubItem) {
203 case 0:
204 plvdi->item.pszText = (LPSTR)g_pszDefaultValueName;
205 break;
206 case 1:
207 switch (((LINE_INFO*)plvdi->item.lParam)->dwValType) {
208 case REG_SZ:
209 plvdi->item.pszText = _T("REG_SZ");
210 break;
211 case REG_EXPAND_SZ:
212 plvdi->item.pszText = _T("REG_EXPAND_SZ");
213 break;
214 case REG_BINARY:
215 plvdi->item.pszText = _T("REG_BINARY");
216 break;
217 case REG_DWORD:
218 plvdi->item.pszText = _T("REG_DWORD");
219 break;
220 case REG_DWORD_BIG_ENDIAN:
221 plvdi->item.pszText = _T("REG_DWORD_BIG_ENDIAN");
222 break;
223 case REG_MULTI_SZ:
224 plvdi->item.pszText = _T("REG_MULTI_SZ");
225 break;
226 case REG_LINK:
227 plvdi->item.pszText = _T("REG_LINK");
228 break;
229 case REG_RESOURCE_LIST:
230 plvdi->item.pszText = _T("REG_RESOURCE_LIST");
231 break;
232 case REG_NONE:
233 plvdi->item.pszText = _T("REG_NONE");
234 break;
235 default:
236 wsprintf(buffer, _T("unknown(%d)"), plvdi->item.lParam);
237 plvdi->item.pszText = buffer;
238 break;
240 break;
241 case 2:
242 plvdi->item.pszText = _T("(value not set)");
243 break;
244 case 3:
245 plvdi->item.pszText = _T("");
246 break;
250 static int CALLBACK CompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
252 LINE_INFO*l, *r;
253 l = (LINE_INFO*)lParam1;
254 r = (LINE_INFO*)lParam2;
255 if (!l->name) return -1;
256 if (!r->name) return +1;
258 if (g_columnToSort == ~0UL)
259 g_columnToSort = 0;
261 if (g_columnToSort == 1 && l->dwValType != r->dwValType)
262 return g_invertSort ? (int)r->dwValType - (int)l->dwValType : (int)l->dwValType - (int)r->dwValType;
263 if (g_columnToSort == 2) {
264 /* FIXME: Sort on value */
266 return g_invertSort ? _tcscmp(r->name, l->name) : _tcscmp(l->name, r->name);
269 HWND StartValueRename(HWND hwndLV)
271 int item;
273 item = ListView_GetNextItem(hwndLV, -1, LVNI_FOCUSED | LVNI_SELECTED);
274 if (item < 1) { /* cannot rename default key */
275 MessageBeep(MB_ICONHAND);
276 return 0;
278 return ListView_EditLabel(hwndLV, item);
281 static BOOL _CmdWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
283 switch (LOWORD(wParam)) {
284 /* case ID_FILE_OPEN: */
285 /* break; */
286 default:
287 return FALSE;
289 return TRUE;
292 static LRESULT CALLBACK ListWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
294 switch (message) {
295 case WM_COMMAND:
296 if (!_CmdWndProc(hWnd, message, wParam, lParam)) {
297 return CallWindowProc(g_orgListWndProc, hWnd, message, wParam, lParam);
299 break;
300 case WM_NOTIFY_REFLECT:
301 switch (((LPNMHDR)lParam)->code) {
303 case LVN_BEGINLABELEDIT:
304 if (!((NMLVDISPINFO *)lParam)->item.iItem)
305 return 1;
306 return 0;
307 case LVN_GETDISPINFO:
308 OnGetDispInfo((NMLVDISPINFO*)lParam);
309 break;
310 case LVN_COLUMNCLICK:
311 if (g_columnToSort == ((LPNMLISTVIEW)lParam)->iSubItem)
312 g_invertSort = !g_invertSort;
313 else {
314 g_columnToSort = ((LPNMLISTVIEW)lParam)->iSubItem;
315 g_invertSort = FALSE;
318 ListView_SortItems(hWnd, CompareFunc, hWnd);
319 break;
320 case LVN_ENDLABELEDIT: {
321 LPNMLVDISPINFO dispInfo = (LPNMLVDISPINFO)lParam;
322 LPTSTR valueName = get_item_text(hWnd, dispInfo->item.iItem);
323 LONG ret;
324 if (!valueName) return -1; /* cannot rename a default value */
325 ret = RenameValue(hWnd, g_currentRootKey, g_currentPath, valueName, dispInfo->item.pszText);
326 if (ret)
327 RefreshListView(hWnd, g_currentRootKey, g_currentPath, dispInfo->item.pszText);
328 HeapFree(GetProcessHeap(), 0, valueName);
329 return 0;
331 case NM_RETURN: {
332 int cnt = ListView_GetNextItem(hWnd, -1, LVNI_FOCUSED | LVNI_SELECTED);
333 if (cnt != -1)
334 SendMessage(hFrameWnd, WM_COMMAND, ID_EDIT_MODIFY, 0);
336 break;
337 case NM_DBLCLK: {
338 NMITEMACTIVATE* nmitem = (LPNMITEMACTIVATE)lParam;
339 LVHITTESTINFO info;
341 /* if (nmitem->hdr.hwndFrom != hWnd) break; unnecessary because of WM_NOTIFY_REFLECT */
342 /* if (nmitem->hdr.idFrom != IDW_LISTVIEW) break; */
343 /* if (nmitem->hdr.code != ???) break; */
344 #ifdef _MSC_VER
345 switch (nmitem->uKeyFlags) {
346 case LVKF_ALT: /* The ALT key is pressed. */
347 /* properties dialog box ? */
348 break;
349 case LVKF_CONTROL: /* The CTRL key is pressed. */
350 /* run dialog box for providing parameters... */
351 break;
352 case LVKF_SHIFT: /* The SHIFT key is pressed. */
353 break;
355 #endif
356 info.pt.x = nmitem->ptAction.x;
357 info.pt.y = nmitem->ptAction.y;
358 if (ListView_HitTest(hWnd, &info) != -1) {
359 ListView_SetItemState(hWnd, -1, 0, LVIS_FOCUSED|LVIS_SELECTED);
360 ListView_SetItemState(hWnd, info.iItem, LVIS_FOCUSED|LVIS_SELECTED,
361 LVIS_FOCUSED|LVIS_SELECTED);
362 SendMessage(hFrameWnd, WM_COMMAND, ID_EDIT_MODIFY, 0);
365 break;
367 default:
368 return 0; /* shouldn't call default ! */
370 break;
371 case WM_CONTEXTMENU: {
372 POINTS pt = MAKEPOINTS(lParam);
373 int cnt = ListView_GetNextItem(hWnd, -1, LVNI_SELECTED);
374 TrackPopupMenu(GetSubMenu(hPopupMenus, cnt == -1 ? PM_NEW : PM_MODIFYVALUE),
375 TPM_RIGHTBUTTON, pt.x, pt.y, 0, hFrameWnd, NULL);
376 break;
378 default:
379 return CallWindowProc(g_orgListWndProc, hWnd, message, wParam, lParam);
380 break;
382 return 0;
386 HWND CreateListView(HWND hwndParent, int id)
388 RECT rcClient;
389 HWND hwndLV;
391 /* Get the dimensions of the parent window's client area, and create the list view control. */
392 GetClientRect(hwndParent, &rcClient);
393 hwndLV = CreateWindowEx(WS_EX_CLIENTEDGE, WC_LISTVIEW, _T("List View"),
394 WS_VISIBLE | WS_CHILD | WS_TABSTOP | LVS_REPORT | LVS_EDITLABELS,
395 0, 0, rcClient.right, rcClient.bottom,
396 hwndParent, (HMENU)id, hInst, NULL);
397 if (!hwndLV) return NULL;
398 ListView_SetExtendedListViewStyle(hwndLV, LVS_EX_FULLROWSELECT);
400 /* Initialize the image list, and add items to the control. */
402 if (!InitListViewImageLists(hwndLV)) goto fail;
403 if (!InitListViewItems(hwndLV, szName)) goto fail;
405 if (!CreateListColumns(hwndLV)) goto fail;
406 g_orgListWndProc = SubclassWindow(hwndLV, ListWndProc);
407 return hwndLV;
408 fail:
409 DestroyWindow(hwndLV);
410 return NULL;
413 BOOL RefreshListView(HWND hwndLV, HKEY hKeyRoot, LPCTSTR keyPath, LPCTSTR highlightValue)
415 BOOL result = FALSE;
416 DWORD max_sub_key_len;
417 DWORD max_val_name_len, valNameLen;
418 DWORD max_val_size, valSize;
419 DWORD val_count, index, valType;
420 TCHAR* valName = 0;
421 BYTE* valBuf = 0;
422 HKEY hKey = 0;
423 LONG errCode;
424 INT count, i;
425 LVITEM item;
427 if (!hwndLV) return FALSE;
429 SendMessage(hwndLV, WM_SETREDRAW, FALSE, 0);
431 errCode = RegOpenKeyEx(hKeyRoot, keyPath, 0, KEY_READ, &hKey);
432 if (errCode != ERROR_SUCCESS) goto done;
434 count = ListView_GetItemCount(hwndLV);
435 for (i = 0; i < count; i++) {
436 item.mask = LVIF_PARAM;
437 item.iItem = i;
438 ListView_GetItem(hwndLV, &item);
439 free(((LINE_INFO*)item.lParam)->name);
440 HeapFree(GetProcessHeap(), 0, (void*)item.lParam);
442 g_columnToSort = ~0UL;
443 ListView_DeleteAllItems(hwndLV);
445 /* get size information and resize the buffers if necessary */
446 errCode = RegQueryInfoKey(hKey, NULL, NULL, NULL, NULL, &max_sub_key_len, NULL,
447 &val_count, &max_val_name_len, &max_val_size, NULL, NULL);
448 if (errCode != ERROR_SUCCESS) goto done;
450 /* account for the terminator char */
451 max_val_name_len++;
452 max_val_size++;
454 valName = HeapAlloc(GetProcessHeap(), 0, max_val_name_len * sizeof(TCHAR));
455 valBuf = HeapAlloc(GetProcessHeap(), 0, max_val_size);
456 if (RegQueryValueEx(hKey, NULL, NULL, &valType, valBuf, &valSize) == ERROR_FILE_NOT_FOUND) {
457 AddEntryToList(hwndLV, NULL, REG_SZ, NULL, 0, !highlightValue);
459 /*dwValSize = max_val_size; */
460 for(index = 0; index < val_count; index++) {
461 BOOL bSelected = (valName == highlightValue); /* NOT a bug, we check for double NULL here */
462 valNameLen = max_val_name_len;
463 valSize = max_val_size;
464 valType = 0;
465 errCode = RegEnumValue(hKey, index, valName, &valNameLen, NULL, &valType, valBuf, &valSize);
466 if (errCode != ERROR_SUCCESS) goto done;
467 valBuf[valSize] = 0;
468 if (valName && highlightValue && !_tcscmp(valName, highlightValue))
469 bSelected = TRUE;
470 AddEntryToList(hwndLV, valName[0] ? valName : NULL, valType, valBuf, valSize, bSelected);
472 ListView_SortItems(hwndLV, CompareFunc, hwndLV);
474 g_currentRootKey = hKeyRoot;
475 if (keyPath != g_currentPath) {
476 HeapFree(GetProcessHeap(), 0, g_currentPath);
477 g_currentPath = HeapAlloc(GetProcessHeap(), 0, (lstrlen(keyPath) + 1) * sizeof(TCHAR));
478 if (!g_currentPath) goto done;
479 lstrcpy(g_currentPath, keyPath);
482 result = TRUE;
484 done:
485 HeapFree(GetProcessHeap(), 0, valBuf);
486 HeapFree(GetProcessHeap(), 0, valName);
487 SendMessage(hwndLV, WM_SETREDRAW, TRUE, 0);
488 if (hKey) RegCloseKey(hKey);
490 return result;