Implement LBS_COMBOBOX, and make use of it.
[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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
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>
28
29 #include "main.h"
30
31 #include "wine/debug.h"
32 #include "wine/unicode.h"
33                                                                                                                              
34 WINE_DEFAULT_DEBUG_CHANNEL(regedit);
35                                                                                                                              
36 typedef struct tagLINE_INFO
37 {
38     DWORD dwValType;
39     LPTSTR name;
40     void* val;
41     size_t val_len;
42 } LINE_INFO;
43
44 /*******************************************************************************
45  * Global and Local Variables:
46  */
47
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;
54
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 };
58
59 LPTSTR get_item_text(HWND hwndLV, int item)
60 {
61     LPTSTR newStr, curStr;
62     unsigned int maxLen = 128;
63
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;
77 }
78
79 LPCTSTR GetValueName(HWND hwndLV)
80 {
81     INT item;
82
83     if (g_valueName && g_valueName != LPSTR_TEXTCALLBACK)
84         HeapFree(GetProcessHeap(), 0,  g_valueName);
85     g_valueName = NULL;
86
87     item = ListView_GetNextItem(hwndLV, -1, LVNI_FOCUSED);
88     if (item == -1) return NULL;
89
90     g_valueName = get_item_text(hwndLV, item);
91
92     return g_valueName;
93 }
94
95 /*******************************************************************************
96  * Local module support methods
97  */
98 static void AddEntryToList(HWND hwndLV, LPTSTR Name, DWORD dwValType, 
99     void* ValBuf, DWORD dwCount, BOOL bHighlight)
100 {
101     LINE_INFO* linfo;
102     LVITEM item;
103     int index;
104
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);
109     
110     if (Name)
111         linfo->name = _tcsdup(Name);
112     else
113         linfo->name = NULL;
114
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;
124     }
125     item.iImage = 0;
126     item.lParam = (LPARAM)linfo;
127
128     /*    item.lParam = (LPARAM)ValBuf; */
129 #if (_WIN32_IE >= 0x0300)
130     item.iIndent = 0;
131 #endif
132
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)");
144             }
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);
150             }
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);
162             }
163             break;
164         default:
165             /*            lpsRes = convertHexToHexCSV(lpbData, dwLen); */
166             ListView_SetItemText(hwndLV, index, 2, pszText);
167             break;
168         }
169     }
170 }
171
172 static BOOL CreateListColumns(HWND hWndListView)
173 {
174     TCHAR szText[50];
175     int index;
176     LV_COLUMN lvC;
177
178     /* Create columns. */
179     lvC.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
180     lvC.pszText = szText;
181
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;
189     }
190     return TRUE;
191 }
192
193 /* OnGetDispInfo - processes the LVN_GETDISPINFO notification message.  */
194
195 static void OnGetDispInfo(NMLVDISPINFO* plvdi)
196 {
197     static TCHAR buffer[200];
198
199     plvdi->item.pszText = NULL;
200     plvdi->item.cchTextMax = 0;
201
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;
239         }
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;
247     }
248 }
249
250 static int CALLBACK CompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
251 {
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;
257         
258     if (g_columnToSort == ~0UL) 
259         g_columnToSort = 0;
260     
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 */
265     }
266     return g_invertSort ? _tcscmp(r->name, l->name) : _tcscmp(l->name, r->name);
267 }
268
269 HWND StartValueRename(HWND hwndLV)
270 {
271     int item;
272
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;
277     }
278     return ListView_EditLabel(hwndLV, item);
279 }
280
281 static BOOL _CmdWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
282 {
283     switch (LOWORD(wParam)) {
284         /*    case ID_FILE_OPEN: */
285         /*        break; */
286     default:
287         return FALSE;
288     }
289     return TRUE;
290 }
291
292 static LRESULT CALLBACK ListWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
293 {
294     switch (message) {
295     case WM_COMMAND:
296         if (!_CmdWndProc(hWnd, message, wParam, lParam)) {
297             return CallWindowProc(g_orgListWndProc, hWnd, message, wParam, lParam);
298         }
299         break;
300     case WM_NOTIFY_REFLECT:
301         switch (((LPNMHDR)lParam)->code) {
302         
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;
316             }
317                     
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;
330             }
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);
335             }
336             break;
337         case NM_DBLCLK: {
338                 NMITEMACTIVATE* nmitem = (LPNMITEMACTIVATE)lParam;
339                 LVHITTESTINFO info;
340
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;
354                 }
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);
363                 }
364             }
365             break;
366
367         default:
368             return 0; /* shouldn't call default ! */
369         }
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;
377     }
378     default:
379         return CallWindowProc(g_orgListWndProc, hWnd, message, wParam, lParam);
380         break;
381     }
382     return 0;
383 }
384
385
386 HWND CreateListView(HWND hwndParent, int id)
387 {
388     RECT rcClient;
389     HWND hwndLV;
390
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);
399
400     /* Initialize the image list, and add items to the control.  */
401     /*
402     if (!InitListViewImageLists(hwndLV)) goto fail;
403     if (!InitListViewItems(hwndLV, szName)) goto fail;
404     */
405     if (!CreateListColumns(hwndLV)) goto fail;
406     g_orgListWndProc = SubclassWindow(hwndLV, ListWndProc);
407     return hwndLV;
408 fail:
409     DestroyWindow(hwndLV);
410     return NULL;
411 }
412
413 BOOL RefreshListView(HWND hwndLV, HKEY hKeyRoot, LPCTSTR keyPath, LPCTSTR highlightValue)
414 {
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;
426
427     if (!hwndLV) return FALSE;
428
429     SendMessage(hwndLV, WM_SETREDRAW, FALSE, 0);
430
431     errCode = RegOpenKeyEx(hKeyRoot, keyPath, 0, KEY_READ, &hKey);
432     if (errCode != ERROR_SUCCESS) goto done;
433
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);
441     }
442     g_columnToSort = ~0UL;
443     ListView_DeleteAllItems(hwndLV);
444
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;
449
450     /* account for the terminator char */
451     max_val_name_len++;
452     max_val_size++;
453
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);
458     }
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);
471     }
472     ListView_SortItems(hwndLV, CompareFunc, hwndLV); 
473
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);
480     }
481
482     result = TRUE;
483
484 done:
485     HeapFree(GetProcessHeap(), 0, valBuf);
486     HeapFree(GetProcessHeap(), 0, valName);
487     SendMessage(hwndLV, WM_SETREDRAW, TRUE, 0);
488     if (hKey) RegCloseKey(hKey);
489
490     return result;
491 }