Remove most string size limitations.
[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 typedef struct tagLINE_INFO
32 {
33     DWORD dwValType;
34     LPTSTR name;
35     void* val;
36     size_t val_len;
37 } LINE_INFO;
38
39 /*******************************************************************************
40  * Global and Local Variables:
41  */
42
43 static WNDPROC g_orgListWndProc;
44 static DWORD g_columnToSort = ~0UL;
45 static BOOL  g_invertSort = FALSE;
46 static LPTSTR g_valueName;
47
48 #define MAX_LIST_COLUMNS (IDS_LIST_COLUMN_LAST - IDS_LIST_COLUMN_FIRST + 1)
49 static int default_column_widths[MAX_LIST_COLUMNS] = { 200, 175, 400 };
50 static int column_alignment[MAX_LIST_COLUMNS] = { LVCFMT_LEFT, LVCFMT_LEFT, LVCFMT_LEFT };
51
52 LPCTSTR GetValueName(HWND hwndLV)
53 {
54     int item, len, maxLen;
55     LPTSTR newStr;
56
57     if (!g_valueName) g_valueName = HeapAlloc(GetProcessHeap(), 0, 1024);
58     if (!g_valueName) return NULL;
59     *g_valueName = 0;
60     maxLen = HeapSize(GetProcessHeap(), 0, g_valueName);
61     if (maxLen == (SIZE_T) - 1) return NULL;
62
63     item = ListView_GetNextItem(hwndLV, -1, LVNI_FOCUSED);
64     if (item == -1) return NULL;
65     do {
66         ListView_GetItemText(hwndLV, item, 0, g_valueName, maxLen);
67         len = _tcslen(g_valueName);
68         if (len < maxLen - 1) break;
69         newStr = HeapReAlloc(GetProcessHeap(), 0, g_valueName, maxLen * 2);
70         if (!newStr) return NULL;
71         g_valueName = newStr;
72         maxLen *= 2;
73     } while (TRUE);
74
75     return g_valueName;
76 }
77
78 /*******************************************************************************
79  * Local module support methods
80  */
81 static void AddEntryToList(HWND hwndLV, LPTSTR Name, DWORD dwValType, void* ValBuf, DWORD dwCount)
82 {
83     LINE_INFO* linfo;
84     LVITEM item;
85     int index;
86
87     linfo = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(LINE_INFO) + dwCount);
88     linfo->dwValType = dwValType;
89     linfo->val_len = dwCount;
90     memcpy(&linfo[1], ValBuf, dwCount);
91     linfo->name = _tcsdup(Name);
92
93     item.mask = LVIF_TEXT | LVIF_PARAM;
94     item.iItem = 0;/*idx;  */
95     item.iSubItem = 0;
96     item.state = 0;
97     item.stateMask = 0;
98     item.pszText = Name;
99     item.cchTextMax = _tcslen(item.pszText);
100     if (item.cchTextMax == 0)
101         item.pszText = LPSTR_TEXTCALLBACK;
102     item.iImage = 0;
103     item.lParam = (LPARAM)linfo;
104
105     /*    item.lParam = (LPARAM)ValBuf; */
106 #if (_WIN32_IE >= 0x0300)
107     item.iIndent = 0;
108 #endif
109
110     index = ListView_InsertItem(hwndLV, &item);
111     if (index != -1) {
112         /*        LPTSTR pszText = NULL; */
113         LPTSTR pszText = _T("value");
114         switch (dwValType) {
115         case REG_SZ:
116         case REG_EXPAND_SZ:
117             ListView_SetItemText(hwndLV, index, 2, ValBuf);
118             break;
119         case REG_DWORD: {
120                 TCHAR buf[64];
121                 wsprintf(buf, _T("0x%08X (%d)"), *(DWORD*)ValBuf, *(DWORD*)ValBuf);
122                 ListView_SetItemText(hwndLV, index, 2, buf);
123             }
124             /*            lpsRes = convertHexToDWORDStr(lpbData, dwLen); */
125             break;
126         case REG_BINARY: {
127                 unsigned int i;
128                 LPBYTE pData = (LPBYTE)ValBuf;
129                 LPTSTR strBinary = HeapAlloc(GetProcessHeap(), 0, dwCount * sizeof(TCHAR) * 3 + 1);
130                 for (i = 0; i < dwCount; i++)
131                     wsprintf( strBinary + i*3, _T("%02X "), pData[i] );
132                 strBinary[dwCount * 3] = 0;
133                 ListView_SetItemText(hwndLV, index, 2, strBinary);
134                 HeapFree(GetProcessHeap(), 0, strBinary);
135             }
136             break;
137         default:
138             /*            lpsRes = convertHexToHexCSV(lpbData, dwLen); */
139             ListView_SetItemText(hwndLV, index, 2, pszText);
140             break;
141         }
142     }
143 }
144
145 static BOOL CreateListColumns(HWND hWndListView)
146 {
147     TCHAR szText[50];
148     int index;
149     LV_COLUMN lvC;
150
151     /* Create columns. */
152     lvC.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
153     lvC.pszText = szText;
154
155     /* Load the column labels from the resource file. */
156     for (index = 0; index < MAX_LIST_COLUMNS; index++) {
157         lvC.iSubItem = index;
158         lvC.cx = default_column_widths[index];
159         lvC.fmt = column_alignment[index];
160         LoadString(hInst, IDS_LIST_COLUMN_FIRST + index, szText, sizeof(szText)/sizeof(TCHAR));
161         if (ListView_InsertColumn(hWndListView, index, &lvC) == -1) return FALSE;
162     }
163     return TRUE;
164 }
165
166 /* OnGetDispInfo - processes the LVN_GETDISPINFO notification message.  */
167
168 static void OnGetDispInfo(NMLVDISPINFO* plvdi)
169 {
170     static TCHAR buffer[200];
171
172     plvdi->item.pszText = NULL;
173     plvdi->item.cchTextMax = 0;
174
175     switch (plvdi->item.iSubItem) {
176     case 0:
177         plvdi->item.pszText = _T("(Default)");
178         break;
179     case 1:
180         switch (((LINE_INFO*)plvdi->item.lParam)->dwValType) {
181         case REG_SZ:
182             plvdi->item.pszText = _T("REG_SZ");
183             break;
184         case REG_EXPAND_SZ:
185             plvdi->item.pszText = _T("REG_EXPAND_SZ");
186             break;
187         case REG_BINARY:
188             plvdi->item.pszText = _T("REG_BINARY");
189             break;
190         case REG_DWORD:
191             plvdi->item.pszText = _T("REG_DWORD");
192             break;
193         case REG_DWORD_BIG_ENDIAN:
194             plvdi->item.pszText = _T("REG_DWORD_BIG_ENDIAN");
195             break;
196         case REG_MULTI_SZ:
197             plvdi->item.pszText = _T("REG_MULTI_SZ");
198             break;
199         case REG_LINK:
200             plvdi->item.pszText = _T("REG_LINK");
201             break;
202         case REG_RESOURCE_LIST:
203             plvdi->item.pszText = _T("REG_RESOURCE_LIST");
204             break;
205         case REG_NONE:
206             plvdi->item.pszText = _T("REG_NONE");
207             break;
208         default:
209             wsprintf(buffer, _T("unknown(%d)"), plvdi->item.lParam);
210             plvdi->item.pszText = buffer;
211             break;
212         }
213         break;
214     case 2:
215         plvdi->item.pszText = _T("(value not set)");
216         break;
217     case 3:
218         plvdi->item.pszText = _T("");
219         break;
220     }
221 }
222
223 static int CALLBACK CompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
224 {
225     LINE_INFO*l, *r;
226     l = (LINE_INFO*)lParam1;
227     r = (LINE_INFO*)lParam2;
228         
229     if (g_columnToSort == ~0UL) 
230         g_columnToSort = 0;
231     
232     if (g_columnToSort == 1 && l->dwValType != r->dwValType)
233         return g_invertSort ? (int)r->dwValType - (int)l->dwValType : (int)l->dwValType - (int)r->dwValType;
234     if (g_columnToSort == 2) {
235         /* FIXME: Sort on value */
236     }
237     return g_invertSort ? _tcscmp(r->name, l->name) : _tcscmp(l->name, r->name);
238 }
239
240 static void ListViewPopUpMenu(HWND hWnd, POINT pt)
241 {}
242
243 static BOOL _CmdWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
244 {
245     switch (LOWORD(wParam)) {
246         /*    case ID_FILE_OPEN: */
247         /*        break; */
248     default:
249         return FALSE;
250     }
251     return TRUE;
252 }
253
254 static LRESULT CALLBACK ListWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
255 {
256     switch (message) {
257     case WM_COMMAND:
258         if (!_CmdWndProc(hWnd, message, wParam, lParam)) {
259             return CallWindowProc(g_orgListWndProc, hWnd, message, wParam, lParam);
260         }
261         break;
262     case WM_NOTIFY:
263         switch (((LPNMHDR)lParam)->code) {
264         case LVN_GETDISPINFO:
265             OnGetDispInfo((NMLVDISPINFO*)lParam);
266             break;
267         case LVN_COLUMNCLICK:
268             if (g_columnToSort == ((LPNMLISTVIEW)lParam)->iSubItem)
269                 g_invertSort = !g_invertSort;
270             else {
271                 g_columnToSort = ((LPNMLISTVIEW)lParam)->iSubItem;
272                 g_invertSort = FALSE;
273             }
274                     
275             ListView_SortItems(hWnd, CompareFunc, hWnd);
276             break;
277         case NM_DBLCLK: {
278                 NMITEMACTIVATE* nmitem = (LPNMITEMACTIVATE)lParam;
279                 LVHITTESTINFO info;
280
281                 if (nmitem->hdr.hwndFrom != hWnd) break;
282                 /*            if (nmitem->hdr.idFrom != IDW_LISTVIEW) break;  */
283                 /*            if (nmitem->hdr.code != ???) break;  */
284 #ifdef _MSC_VER
285                 switch (nmitem->uKeyFlags) {
286                 case LVKF_ALT:     /*  The ALT key is pressed.   */
287                     /* properties dialog box ? */
288                     break;
289                 case LVKF_CONTROL: /*  The CTRL key is pressed. */
290                     /* run dialog box for providing parameters... */
291                     break;
292                 case LVKF_SHIFT:   /*  The SHIFT key is pressed.    */
293                     break;
294                 }
295 #endif
296                 info.pt.x = nmitem->ptAction.x;
297                 info.pt.y = nmitem->ptAction.y;
298                 if (ListView_HitTest(hWnd, &info) != -1) {
299                     LVITEM item;
300                     item.mask = LVIF_PARAM;
301                     item.iItem = info.iItem;
302                     if (ListView_GetItem(hWnd, &item)) {}
303                 }
304             }
305             break;
306
307         case NM_RCLICK: {
308                 int idx;
309                 LV_HITTESTINFO lvH;
310                 NM_LISTVIEW* pNm = (NM_LISTVIEW*)lParam;
311                 lvH.pt.x = pNm->ptAction.x;
312                 lvH.pt.y = pNm->ptAction.y;
313                 idx = ListView_HitTest(hWnd, &lvH);
314                 if (idx != -1) {
315                     POINT pt;
316                     GetCursorPos(&pt);
317                     ListViewPopUpMenu(hWnd, pt);
318                     return idx;
319                 }
320             }
321             break;
322
323         default:
324             return CallWindowProc(g_orgListWndProc, hWnd, message, wParam, lParam);
325         }
326         break;
327     case WM_KEYDOWN:
328         if (wParam == VK_TAB) {
329             /*TODO: SetFocus(Globals.hDriveBar) */
330             /*SetFocus(child->nFocusPanel? child->left.hWnd: child->right.hWnd); */
331         }
332         /* fall thru... */
333     default:
334         return CallWindowProc(g_orgListWndProc, hWnd, message, wParam, lParam);
335         break;
336     }
337     return 0;
338 }
339
340
341 HWND CreateListView(HWND hwndParent, int id)
342 {
343     RECT rcClient;
344     HWND hwndLV;
345
346     /* Get the dimensions of the parent window's client area, and create the list view control.  */
347     GetClientRect(hwndParent, &rcClient);
348     hwndLV = CreateWindowEx(WS_EX_CLIENTEDGE, WC_LISTVIEW, _T("List View"),
349                             WS_VISIBLE | WS_CHILD | LVS_REPORT,
350                             0, 0, rcClient.right, rcClient.bottom,
351                             hwndParent, (HMENU)id, hInst, NULL);
352     if (!hwndLV) return NULL;
353     ListView_SetExtendedListViewStyle(hwndLV,  LVS_EX_FULLROWSELECT);
354
355     /* Initialize the image list, and add items to the control.  */
356     /*
357     if (!InitListViewImageLists(hwndLV)) goto fail;
358     if (!InitListViewItems(hwndLV, szName)) goto fail;
359     */
360     if (!CreateListColumns(hwndLV)) goto fail;
361     g_orgListWndProc = SubclassWindow(hwndLV, ListWndProc);
362     return hwndLV;
363 fail:
364     DestroyWindow(hwndLV);
365     return NULL;
366 }
367
368 BOOL RefreshListView(HWND hwndLV, HKEY hKey, LPCTSTR keyPath)
369 {
370     DWORD max_sub_key_len;
371     DWORD max_val_name_len;
372     DWORD max_val_size;
373     DWORD val_count;
374     HKEY hNewKey;
375     LONG errCode;
376     INT count, i;
377     LVITEM item;
378
379     if (!hwndLV) return FALSE;
380
381     SendMessage(hwndLV, WM_SETREDRAW, FALSE, 0);
382     count = ListView_GetItemCount(hwndLV);
383     for (i = 0; i < count; i++) {
384         item.mask = LVIF_PARAM;
385         item.iItem = i;
386         ListView_GetItem(hwndLV, &item);
387         free(((LINE_INFO*)item.lParam)->name);
388         HeapFree(GetProcessHeap(), 0, (void*)item.lParam);
389     }
390     g_columnToSort = ~0UL;
391     ListView_DeleteAllItems(hwndLV);
392
393     errCode = RegOpenKeyEx(hKey, keyPath, 0, KEY_READ, &hNewKey);
394     if (errCode != ERROR_SUCCESS) return FALSE;
395
396     /* get size information and resize the buffers if necessary */
397     errCode = RegQueryInfoKey(hNewKey, NULL, NULL, NULL, NULL, &max_sub_key_len, NULL, 
398                               &val_count, &max_val_name_len, &max_val_size, NULL, NULL);
399
400     #define BUF_HEAD_SPACE 2 /* FIXME: check why this is required with ROS ??? */
401
402     if (errCode == ERROR_SUCCESS) {
403         TCHAR* ValName = HeapAlloc(GetProcessHeap(), 0, ++max_val_name_len * sizeof(TCHAR) + BUF_HEAD_SPACE);
404         DWORD dwValNameLen = max_val_name_len;
405         BYTE* ValBuf = HeapAlloc(GetProcessHeap(), 0, ++max_val_size/* + BUF_HEAD_SPACE*/);
406         DWORD dwValSize = max_val_size;
407         DWORD dwIndex = 0L;
408         DWORD dwValType;
409         /*                if (RegQueryValueEx(hNewKey, NULL, NULL, &dwValType, ValBuf, &dwValSize) == ERROR_SUCCESS) { */
410         /*                    AddEntryToList(hwndLV, _T("(Default)"), dwValType, ValBuf, dwValSize); */
411         /*                } */
412         /*                dwValSize = max_val_size; */
413         while (RegEnumValue(hNewKey, dwIndex, ValName, &dwValNameLen, NULL, &dwValType, ValBuf, &dwValSize) == ERROR_SUCCESS) {
414             ValBuf[dwValSize] = 0;
415             AddEntryToList(hwndLV, ValName, dwValType, ValBuf, dwValSize);
416             dwValNameLen = max_val_name_len;
417             dwValSize = max_val_size;
418             dwValType = 0L;
419             ++dwIndex;
420         }
421         HeapFree(GetProcessHeap(), 0, ValBuf);
422         HeapFree(GetProcessHeap(), 0, ValName);
423     }
424     ListView_SortItems(hwndLV, CompareFunc, hwndLV);
425     RegCloseKey(hNewKey);
426     SendMessage(hwndLV, WM_SETREDRAW, TRUE, 0);
427
428     return TRUE;
429 }