query: Add a stub implementation for LocateCatalogs.
[wine] / programs / 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 St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include <windows.h>
22 #include <commctrl.h>
23 #include <stdlib.h>
24 #include <tchar.h>
25 #include <stdio.h>
26
27 #include "main.h"
28
29 #include "wine/debug.h"
30 #include "wine/unicode.h"
31                                                                                                                              
32 WINE_DEFAULT_DEBUG_CHANNEL(regedit);
33
34 static INT Image_String;
35 static INT Image_Binary;
36
37 typedef struct tagLINE_INFO
38 {
39     DWORD dwValType;
40     LPTSTR name;
41     void* val;
42     size_t val_len;
43 } LINE_INFO;
44
45 /*******************************************************************************
46  * Global and Local Variables:
47  */
48
49 static WNDPROC g_orgListWndProc;
50 static DWORD g_columnToSort = ~0UL;
51 static BOOL  g_invertSort = FALSE;
52 static LPTSTR g_valueName;
53 static LPTSTR g_currentPath;
54 static HKEY g_currentRootKey;
55
56 #define MAX_LIST_COLUMNS (IDS_LIST_COLUMN_LAST - IDS_LIST_COLUMN_FIRST + 1)
57 static int default_column_widths[MAX_LIST_COLUMNS] = { 200, 175, 400 };
58 static int column_alignment[MAX_LIST_COLUMNS] = { LVCFMT_LEFT, LVCFMT_LEFT, LVCFMT_LEFT };
59
60 static LPTSTR get_item_text(HWND hwndLV, int item)
61 {
62     LPTSTR newStr, curStr;
63     unsigned int maxLen = 128;
64
65     curStr = HeapAlloc(GetProcessHeap(), 0, maxLen);
66     if (!curStr) return NULL;
67     if (item == 0) return NULL; /* first item is ALWAYS a default */
68     do {
69         ListView_GetItemText(hwndLV, item, 0, curStr, maxLen);
70         if (_tcslen(curStr) < maxLen - 1) return curStr;
71         newStr = HeapReAlloc(GetProcessHeap(), 0, curStr, maxLen * 2);
72         if (!newStr) break;
73         curStr = newStr;
74         maxLen *= 2;
75     } while (TRUE);
76     HeapFree(GetProcessHeap(), 0, curStr);
77     return NULL;
78 }
79
80 LPCTSTR GetValueName(HWND hwndLV)
81 {
82     INT item;
83
84     if (g_valueName != LPSTR_TEXTCALLBACK)
85         HeapFree(GetProcessHeap(), 0,  g_valueName);
86     g_valueName = NULL;
87
88     item = ListView_GetNextItem(hwndLV, -1, LVNI_FOCUSED);
89     if (item == -1) return NULL;
90
91     g_valueName = get_item_text(hwndLV, item);
92
93     return g_valueName;
94 }
95
96 /* convert '\0' separated string list into ',' separated string list */
97 static void MakeMULTISZDisplayable(LPTSTR multi)
98 {
99     do
100     {
101         for (; *multi; multi++)
102             ;
103         if (*(multi+1))
104         {
105             *multi = ',';
106             multi++;
107         }
108     } while (*multi);
109 }
110
111 /*******************************************************************************
112  * Local module support methods
113  */
114 static void AddEntryToList(HWND hwndLV, LPTSTR Name, DWORD dwValType, 
115     void* ValBuf, DWORD dwCount, BOOL bHighlight)
116 {
117     LINE_INFO* linfo;
118     LVITEM item;
119     int index;
120
121     linfo = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(LINE_INFO) + dwCount);
122     linfo->dwValType = dwValType;
123     linfo->val_len = dwCount;
124     memcpy(&linfo[1], ValBuf, dwCount);
125     
126     if (Name)
127         linfo->name = _tcsdup(Name);
128     else
129         linfo->name = NULL;
130
131     item.mask = LVIF_TEXT | LVIF_PARAM | LVIF_STATE | LVIF_IMAGE;
132     item.iItem = ListView_GetItemCount(hwndLV);/*idx;  */
133     item.iSubItem = 0;
134     item.state = 0;
135     item.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
136     item.pszText = Name ? Name : LPSTR_TEXTCALLBACK;
137     item.cchTextMax = Name ? _tcslen(item.pszText) : 0;
138     if (bHighlight) {
139         item.stateMask = item.state = LVIS_FOCUSED | LVIS_SELECTED;
140     }
141     switch (dwValType)
142     {
143     case REG_SZ:
144     case REG_EXPAND_SZ:
145     case REG_MULTI_SZ:
146         item.iImage = Image_String;
147         break;
148     default:
149         item.iImage = Image_Binary;
150         break;
151     }
152     item.lParam = (LPARAM)linfo;
153
154 #if (_WIN32_IE >= 0x0300)
155     item.iIndent = 0;
156 #endif
157
158     index = ListView_InsertItem(hwndLV, &item);
159     if (index != -1) {
160         /*        LPTSTR pszText = NULL; */
161         LPTSTR pszText = _T("(cannot display value)");
162         switch (dwValType) {
163         case REG_SZ:
164         case REG_EXPAND_SZ:
165             if (ValBuf) {
166                 ListView_SetItemText(hwndLV, index, 2, ValBuf);
167             } else {
168                 TCHAR textT[] = {'(','n','o','t',' ','s','e','t',')',0};
169                 ListView_SetItemText(hwndLV, index, 2, textT);
170             }
171             break;
172         case REG_DWORD: {
173                 TCHAR buf[64];
174                 wsprintf(buf, _T("0x%08X (%d)"), *(DWORD*)ValBuf, *(DWORD*)ValBuf);
175                 ListView_SetItemText(hwndLV, index, 2, buf);
176             }
177             /*            lpsRes = convertHexToDWORDStr(lpbData, dwLen); */
178             break;
179         case REG_BINARY: {
180                 unsigned int i;
181                 LPBYTE pData = (LPBYTE)ValBuf;
182                 LPTSTR strBinary = HeapAlloc(GetProcessHeap(), 0, dwCount * sizeof(TCHAR) * 3 + 1);
183                 for (i = 0; i < dwCount; i++)
184                     wsprintf( strBinary + i*3, _T("%02X "), pData[i] );
185                 strBinary[dwCount * 3] = 0;
186                 ListView_SetItemText(hwndLV, index, 2, strBinary);
187                 HeapFree(GetProcessHeap(), 0, strBinary);
188             }
189             break;
190         case REG_MULTI_SZ:
191             MakeMULTISZDisplayable(ValBuf);
192             ListView_SetItemText(hwndLV, index, 2, ValBuf);
193             break;
194         default:
195             /*            lpsRes = convertHexToHexCSV(lpbData, dwLen); */
196             ListView_SetItemText(hwndLV, index, 2, pszText);
197             break;
198         }
199     }
200 }
201
202 static BOOL InitListViewImageList(HWND hWndListView)
203 {
204     HIMAGELIST himl;
205     HICON hicon;
206     INT cx = GetSystemMetrics(SM_CXSMICON);
207     INT cy = GetSystemMetrics(SM_CYSMICON);
208
209     himl = ImageList_Create(cx, cy, ILC_MASK, 0, 2);
210     if (!himl)
211         return FALSE;
212
213     hicon = LoadImage(hInst, MAKEINTRESOURCE(IDI_STRING),
214         IMAGE_ICON, cx, cy, LR_DEFAULTCOLOR);
215     Image_String = ImageList_AddIcon(himl, hicon);
216
217     hicon = LoadImage(hInst, MAKEINTRESOURCE(IDI_BIN),
218         IMAGE_ICON, cx, cy, LR_DEFAULTCOLOR);
219     Image_Binary = ImageList_AddIcon(himl, hicon);
220
221     SendMessage( hWndListView, LVM_SETIMAGELIST, LVSIL_SMALL, (LPARAM) himl );
222
223     /* fail if some of the icons failed to load */
224     if (ImageList_GetImageCount(himl) < 2)
225         return FALSE;
226
227     return TRUE;
228 }
229
230 static BOOL CreateListColumns(HWND hWndListView)
231 {
232     TCHAR szText[50];
233     int index;
234     LV_COLUMN lvC;
235
236     /* Create columns. */
237     lvC.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
238     lvC.pszText = szText;
239
240     /* Load the column labels from the resource file. */
241     for (index = 0; index < MAX_LIST_COLUMNS; index++) {
242         lvC.iSubItem = index;
243         lvC.cx = default_column_widths[index];
244         lvC.fmt = column_alignment[index];
245         LoadString(hInst, IDS_LIST_COLUMN_FIRST + index, szText, sizeof(szText)/sizeof(TCHAR));
246         if (ListView_InsertColumn(hWndListView, index, &lvC) == -1) return FALSE;
247     }
248     return TRUE;
249 }
250
251 /* OnGetDispInfo - processes the LVN_GETDISPINFO notification message.  */
252
253 static void OnGetDispInfo(NMLVDISPINFO* plvdi)
254 {
255     static TCHAR buffer[200];
256     static TCHAR reg_szT[]               = {'R','E','G','_','S','Z',0},
257                  reg_expand_szT[]        = {'R','E','G','_','E','X','P','A','N','D','_','S','Z',0},
258                  reg_binaryT[]           = {'R','E','G','_','B','I','N','A','R','Y',0},
259                  reg_dwordT[]            = {'R','E','G','_','D','W','O','R','D',0},
260                  reg_dword_big_endianT[] = {'R','E','G','_','D','W','O','R','D','_',
261                                             'B','I','G','_','E','N','D','I','A','N',0},
262                  reg_multi_szT[]         = {'R','E','G','_','M','U','L','T','I','_','S','Z',0},
263                  reg_linkT[]             = {'R','E','G','_','L','I','N','K',0},
264                  reg_resource_listT[]    = {'R','E','G','_','R','E','S','O','U','R','C','E','_','L','I','S','T',0},
265                  reg_noneT[]             = {'R','E','G','_','N','O','N','E',0},
266                  not_setT[]              = {'(','v','a','l','u','e',' ','n','o','t',' ','s','e','t',')',0},
267                  emptyT[]                = {0};
268     static const TCHAR unknownT[] = {'u','n','k','n','o','w','n','(','%','d',')',0};
269
270     plvdi->item.pszText = NULL;
271     plvdi->item.cchTextMax = 0;
272
273     switch (plvdi->item.iSubItem) {
274     case 0:
275         plvdi->item.pszText = (LPSTR)g_pszDefaultValueName;
276         break;
277     case 1:
278         switch (((LINE_INFO*)plvdi->item.lParam)->dwValType) {
279         case REG_SZ:
280             plvdi->item.pszText = reg_szT;
281             break;
282         case REG_EXPAND_SZ:
283             plvdi->item.pszText = reg_expand_szT;
284             break;
285         case REG_BINARY:
286             plvdi->item.pszText = reg_binaryT;
287             break;
288         case REG_DWORD:
289             plvdi->item.pszText = reg_dwordT;
290             break;
291         case REG_DWORD_BIG_ENDIAN:
292             plvdi->item.pszText = reg_dword_big_endianT;
293             break;
294         case REG_MULTI_SZ:
295             plvdi->item.pszText = reg_multi_szT;
296             break;
297         case REG_LINK:
298             plvdi->item.pszText = reg_linkT;
299             break;
300         case REG_RESOURCE_LIST:
301             plvdi->item.pszText = reg_resource_listT;
302             break;
303         case REG_NONE:
304             plvdi->item.pszText = reg_noneT;
305             break;
306         default:
307             wsprintf(buffer, unknownT, plvdi->item.lParam);
308             plvdi->item.pszText = buffer;
309             break;
310         }
311         break;
312     case 2:
313         plvdi->item.pszText = not_setT;
314         break;
315     case 3:
316         plvdi->item.pszText = emptyT;
317         break;
318     }
319 }
320
321 static int CALLBACK CompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
322 {
323     LINE_INFO*l, *r;
324     l = (LINE_INFO*)lParam1;
325     r = (LINE_INFO*)lParam2;
326     if (!l->name) return -1;
327     if (!r->name) return +1;
328         
329     if (g_columnToSort == ~0UL) 
330         g_columnToSort = 0;
331     
332     if (g_columnToSort == 1 && l->dwValType != r->dwValType)
333         return g_invertSort ? (int)r->dwValType - (int)l->dwValType : (int)l->dwValType - (int)r->dwValType;
334     if (g_columnToSort == 2) {
335         /* FIXME: Sort on value */
336     }
337     return g_invertSort ? _tcscmp(r->name, l->name) : _tcscmp(l->name, r->name);
338 }
339
340 HWND StartValueRename(HWND hwndLV)
341 {
342     int item;
343
344     item = ListView_GetNextItem(hwndLV, -1, LVNI_FOCUSED | LVNI_SELECTED);
345     if (item < 1) { /* cannot rename default key */
346         MessageBeep(MB_ICONHAND);
347         return 0;
348     }
349     return ListView_EditLabel(hwndLV, item);
350 }
351
352 static BOOL _CmdWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
353 {
354     switch (LOWORD(wParam)) {
355         /*    case ID_FILE_OPEN: */
356         /*        break; */
357     default:
358         return FALSE;
359     }
360     return TRUE;
361 }
362
363 static LRESULT CALLBACK ListWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
364 {
365     switch (message) {
366     case WM_COMMAND:
367         if (!_CmdWndProc(hWnd, message, wParam, lParam)) {
368             return CallWindowProc(g_orgListWndProc, hWnd, message, wParam, lParam);
369         }
370         break;
371     case WM_NOTIFY_REFLECT:
372         switch (((LPNMHDR)lParam)->code) {
373         
374         case LVN_BEGINLABELEDIT:
375             if (!((NMLVDISPINFO *)lParam)->item.iItem)
376                 return 1;
377             return 0;
378         case LVN_GETDISPINFO:
379             OnGetDispInfo((NMLVDISPINFO*)lParam);
380             break;
381         case LVN_COLUMNCLICK:
382             if (g_columnToSort == ((LPNMLISTVIEW)lParam)->iSubItem)
383                 g_invertSort = !g_invertSort;
384             else {
385                 g_columnToSort = ((LPNMLISTVIEW)lParam)->iSubItem;
386                 g_invertSort = FALSE;
387             }
388                     
389             SendMessage(hWnd, LVM_SORTITEMS, (WPARAM)hWnd, (LPARAM)CompareFunc);
390             break;
391         case LVN_ENDLABELEDIT: {
392                 LPNMLVDISPINFO dispInfo = (LPNMLVDISPINFO)lParam;
393                 LPTSTR valueName = get_item_text(hWnd, dispInfo->item.iItem);
394                 LONG ret;
395                 if (!valueName) return -1; /* cannot rename a default value */
396                 ret = RenameValue(hWnd, g_currentRootKey, g_currentPath, valueName, dispInfo->item.pszText);
397                 if (ret)
398                     RefreshListView(hWnd, g_currentRootKey, g_currentPath, dispInfo->item.pszText);
399                 HeapFree(GetProcessHeap(), 0, valueName);
400                 return 0;
401             }
402         case NM_RETURN: {
403                 int cnt = ListView_GetNextItem(hWnd, -1, LVNI_FOCUSED | LVNI_SELECTED);
404                 if (cnt != -1)
405                     SendMessage(hFrameWnd, WM_COMMAND, ID_EDIT_MODIFY, 0);
406             }
407             break;
408         case NM_DBLCLK: {
409                 NMITEMACTIVATE* nmitem = (LPNMITEMACTIVATE)lParam;
410                 LVHITTESTINFO info;
411
412                 /* if (nmitem->hdr.hwndFrom != hWnd) break; unnecessary because of WM_NOTIFY_REFLECT */
413                 /*            if (nmitem->hdr.idFrom != IDW_LISTVIEW) break;  */
414                 /*            if (nmitem->hdr.code != ???) break;  */
415 #ifdef _MSC_VER
416                 switch (nmitem->uKeyFlags) {
417                 case LVKF_ALT:     /*  The ALT key is pressed.   */
418                     /* properties dialog box ? */
419                     break;
420                 case LVKF_CONTROL: /*  The CTRL key is pressed. */
421                     /* run dialog box for providing parameters... */
422                     break;
423                 case LVKF_SHIFT:   /*  The SHIFT key is pressed.    */
424                     break;
425                 }
426 #endif
427                 info.pt.x = nmitem->ptAction.x;
428                 info.pt.y = nmitem->ptAction.y;
429                 if (ListView_HitTest(hWnd, &info) != -1) {
430                     ListView_SetItemState(hWnd, -1, 0, LVIS_FOCUSED|LVIS_SELECTED);
431                     ListView_SetItemState(hWnd, info.iItem, LVIS_FOCUSED|LVIS_SELECTED,
432                         LVIS_FOCUSED|LVIS_SELECTED);
433                     SendMessage(hFrameWnd, WM_COMMAND, ID_EDIT_MODIFY, 0);
434                 }
435             }
436             break;
437
438         default:
439             return 0; /* shouldn't call default ! */
440         }
441         break;
442     case WM_CONTEXTMENU: {
443         int cnt = ListView_GetNextItem(hWnd, -1, LVNI_SELECTED);
444         TrackPopupMenu(GetSubMenu(hPopupMenus, cnt == -1 ? PM_NEW : PM_MODIFYVALUE),
445                        TPM_RIGHTBUTTON, (short)LOWORD(lParam), (short)HIWORD(lParam),
446                        0, hFrameWnd, NULL);
447         break;
448     }
449     default:
450         return CallWindowProc(g_orgListWndProc, hWnd, message, wParam, lParam);
451         break;
452     }
453     return 0;
454 }
455
456
457 HWND CreateListView(HWND hwndParent, int id)
458 {
459     RECT rcClient;
460     HWND hwndLV;
461
462     /* Get the dimensions of the parent window's client area, and create the list view control.  */
463     GetClientRect(hwndParent, &rcClient);
464     hwndLV = CreateWindowEx(WS_EX_CLIENTEDGE, WC_LISTVIEW, _T("List View"),
465                             WS_VISIBLE | WS_CHILD | WS_TABSTOP | LVS_REPORT | LVS_EDITLABELS,
466                             0, 0, rcClient.right, rcClient.bottom,
467                             hwndParent, (HMENU)id, hInst, NULL);
468     if (!hwndLV) return NULL;
469     SendMessage(hwndLV, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, LVS_EX_FULLROWSELECT);
470
471     /* Initialize the image list */
472     if (!InitListViewImageList(hwndLV)) goto fail;
473     if (!CreateListColumns(hwndLV)) goto fail;
474     g_orgListWndProc = (WNDPROC) SetWindowLongPtr(hwndLV, GWLP_WNDPROC, (LPARAM)ListWndProc);
475     return hwndLV;
476 fail:
477     DestroyWindow(hwndLV);
478     return NULL;
479 }
480
481 BOOL RefreshListView(HWND hwndLV, HKEY hKeyRoot, LPCTSTR keyPath, LPCTSTR highlightValue)
482 {
483     BOOL result = FALSE;
484     DWORD max_sub_key_len;
485     DWORD max_val_name_len, valNameLen;
486     DWORD max_val_size, valSize;
487     DWORD val_count, index, valType;
488     TCHAR* valName = 0;
489     BYTE* valBuf = 0;
490     HKEY hKey = 0;
491     LONG errCode;
492     INT count, i;
493     LVITEM item;
494
495     if (!hwndLV) return FALSE;
496
497     SendMessage(hwndLV, WM_SETREDRAW, FALSE, 0);
498
499     errCode = RegOpenKeyEx(hKeyRoot, keyPath, 0, KEY_READ, &hKey);
500     if (errCode != ERROR_SUCCESS) goto done;
501
502     count = ListView_GetItemCount(hwndLV);
503     for (i = 0; i < count; i++) {
504         item.mask = LVIF_PARAM;
505         item.iItem = i;
506         SendMessage( hwndLV, LVM_GETITEM, 0, (LPARAM)&item );
507         free(((LINE_INFO*)item.lParam)->name);
508         HeapFree(GetProcessHeap(), 0, (void*)item.lParam);
509     }
510     g_columnToSort = ~0UL;
511     SendMessage( hwndLV, LVM_DELETEALLITEMS, 0, 0L );
512
513     /* get size information and resize the buffers if necessary */
514     errCode = RegQueryInfoKey(hKey, NULL, NULL, NULL, NULL, &max_sub_key_len, NULL, 
515                               &val_count, &max_val_name_len, &max_val_size, NULL, NULL);
516     if (errCode != ERROR_SUCCESS) goto done;
517
518     /* account for the terminator char */
519     max_val_name_len++;
520     max_val_size++;
521
522     valName = HeapAlloc(GetProcessHeap(), 0, max_val_name_len * sizeof(TCHAR));
523     valBuf = HeapAlloc(GetProcessHeap(), 0, max_val_size);
524     if (RegQueryValueEx(hKey, NULL, NULL, &valType, valBuf, &valSize) == ERROR_FILE_NOT_FOUND) { 
525         AddEntryToList(hwndLV, NULL, REG_SZ, NULL, 0, !highlightValue);
526     }
527     /*dwValSize = max_val_size; */
528     for(index = 0; index < val_count; index++) {
529         BOOL bSelected = (valName == highlightValue); /* NOT a bug, we check for double NULL here */
530         valNameLen = max_val_name_len;
531         valSize = max_val_size;
532         valType = 0;
533         errCode = RegEnumValue(hKey, index, valName, &valNameLen, NULL, &valType, valBuf, &valSize);
534         if (errCode != ERROR_SUCCESS) goto done;
535         valBuf[valSize] = 0;
536         if (valName && highlightValue && !_tcscmp(valName, highlightValue))
537             bSelected = TRUE;
538         AddEntryToList(hwndLV, valName[0] ? valName : NULL, valType, valBuf, valSize, bSelected);
539     }
540     SendMessage(hwndLV, LVM_SORTITEMS, (WPARAM)hwndLV, (LPARAM)CompareFunc);
541
542     g_currentRootKey = hKeyRoot;
543     if (keyPath != g_currentPath) {
544         HeapFree(GetProcessHeap(), 0, g_currentPath);
545         g_currentPath = HeapAlloc(GetProcessHeap(), 0, (lstrlen(keyPath) + 1) * sizeof(TCHAR));
546         if (!g_currentPath) goto done;
547         lstrcpy(g_currentPath, keyPath);
548     }
549
550     result = TRUE;
551
552 done:
553     HeapFree(GetProcessHeap(), 0, valBuf);
554     HeapFree(GetProcessHeap(), 0, valName);
555     SendMessage(hwndLV, WM_SETREDRAW, TRUE, 0);
556     if (hKey) RegCloseKey(hKey);
557
558     return result;
559 }