Added Finnish resources.
[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 <stdio.h>
27
28 #include "main.h"
29
30 #include "wine/debug.h"
31 #include "wine/unicode.h"
32                                                                                                                              
33 WINE_DEFAULT_DEBUG_CHANNEL(regedit);
34
35 static INT Image_String;
36 static INT Image_Binary;
37
38 typedef struct tagLINE_INFO
39 {
40     DWORD dwValType;
41     LPTSTR name;
42     void* val;
43     size_t val_len;
44 } LINE_INFO;
45
46 /*******************************************************************************
47  * Global and Local Variables:
48  */
49
50 static WNDPROC g_orgListWndProc;
51 static DWORD g_columnToSort = ~0UL;
52 static BOOL  g_invertSort = FALSE;
53 static LPTSTR g_valueName;
54 static LPTSTR g_currentPath;
55 static HKEY g_currentRootKey;
56
57 #define MAX_LIST_COLUMNS (IDS_LIST_COLUMN_LAST - IDS_LIST_COLUMN_FIRST + 1)
58 static int default_column_widths[MAX_LIST_COLUMNS] = { 200, 175, 400 };
59 static int column_alignment[MAX_LIST_COLUMNS] = { LVCFMT_LEFT, LVCFMT_LEFT, LVCFMT_LEFT };
60
61 static LPTSTR get_item_text(HWND hwndLV, int item)
62 {
63     LPTSTR newStr, curStr;
64     unsigned int maxLen = 128;
65
66     curStr = HeapAlloc(GetProcessHeap(), 0, maxLen);
67     if (!curStr) return NULL;
68     if (item == 0) return NULL; /* first item is ALWAYS a default */
69     do {
70         ListView_GetItemText(hwndLV, item, 0, curStr, maxLen);
71         if (_tcslen(curStr) < maxLen - 1) return curStr;
72         newStr = HeapReAlloc(GetProcessHeap(), 0, curStr, maxLen * 2);
73         if (!newStr) break;
74         curStr = newStr;
75         maxLen *= 2;
76     } while (TRUE);
77     HeapFree(GetProcessHeap(), 0, curStr);
78     return NULL;
79 }
80
81 LPCTSTR GetValueName(HWND hwndLV)
82 {
83     INT item;
84
85     if (g_valueName && g_valueName != LPSTR_TEXTCALLBACK)
86         HeapFree(GetProcessHeap(), 0,  g_valueName);
87     g_valueName = NULL;
88
89     item = ListView_GetNextItem(hwndLV, -1, LVNI_FOCUSED);
90     if (item == -1) return NULL;
91
92     g_valueName = get_item_text(hwndLV, item);
93
94     return g_valueName;
95 }
96
97 /* convert '\0' separated string list into ',' separated string list */
98 static void MakeMULTISZDisplayable(LPTSTR multi)
99 {
100     do
101     {
102         for (; *multi; multi++)
103             ;
104         if (*(multi+1))
105         {
106             *multi = ',';
107             multi++;
108         }
109     } while (*multi);
110 }
111
112 /*******************************************************************************
113  * Local module support methods
114  */
115 static void AddEntryToList(HWND hwndLV, LPTSTR Name, DWORD dwValType, 
116     void* ValBuf, DWORD dwCount, BOOL bHighlight)
117 {
118     LINE_INFO* linfo;
119     LVITEM item;
120     int index;
121
122     linfo = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(LINE_INFO) + dwCount);
123     linfo->dwValType = dwValType;
124     linfo->val_len = dwCount;
125     memcpy(&linfo[1], ValBuf, dwCount);
126     
127     if (Name)
128         linfo->name = _tcsdup(Name);
129     else
130         linfo->name = NULL;
131
132     item.mask = LVIF_TEXT | LVIF_PARAM | LVIF_STATE | LVIF_IMAGE;
133     item.iItem = ListView_GetItemCount(hwndLV);/*idx;  */
134     item.iSubItem = 0;
135     item.state = 0;
136     item.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
137     item.pszText = Name ? Name : LPSTR_TEXTCALLBACK;
138     item.cchTextMax = Name ? _tcslen(item.pszText) : 0;
139     if (bHighlight) {
140         item.stateMask = item.state = LVIS_FOCUSED | LVIS_SELECTED;
141     }
142     switch (dwValType)
143     {
144     case REG_SZ:
145     case REG_EXPAND_SZ:
146     case REG_MULTI_SZ:
147         item.iImage = Image_String;
148         break;
149     default:
150         item.iImage = Image_Binary;
151         break;
152     }
153     item.lParam = (LPARAM)linfo;
154
155 #if (_WIN32_IE >= 0x0300)
156     item.iIndent = 0;
157 #endif
158
159     index = ListView_InsertItem(hwndLV, &item);
160     if (index != -1) {
161         /*        LPTSTR pszText = NULL; */
162         LPTSTR pszText = _T("(cannot display value)");
163         switch (dwValType) {
164         case REG_SZ:
165         case REG_EXPAND_SZ:
166             if (ValBuf) {
167                 ListView_SetItemText(hwndLV, index, 2, ValBuf);
168             } else {
169                 ListView_SetItemText(hwndLV, index, 2, "(not set)");
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     ListView_SetImageList(hWndListView, himl, LVSIL_SMALL);
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
257     plvdi->item.pszText = NULL;
258     plvdi->item.cchTextMax = 0;
259
260     switch (plvdi->item.iSubItem) {
261     case 0:
262         plvdi->item.pszText = (LPSTR)g_pszDefaultValueName;
263         break;
264     case 1:
265         switch (((LINE_INFO*)plvdi->item.lParam)->dwValType) {
266         case REG_SZ:
267             plvdi->item.pszText = _T("REG_SZ");
268             break;
269         case REG_EXPAND_SZ:
270             plvdi->item.pszText = _T("REG_EXPAND_SZ");
271             break;
272         case REG_BINARY:
273             plvdi->item.pszText = _T("REG_BINARY");
274             break;
275         case REG_DWORD:
276             plvdi->item.pszText = _T("REG_DWORD");
277             break;
278         case REG_DWORD_BIG_ENDIAN:
279             plvdi->item.pszText = _T("REG_DWORD_BIG_ENDIAN");
280             break;
281         case REG_MULTI_SZ:
282             plvdi->item.pszText = _T("REG_MULTI_SZ");
283             break;
284         case REG_LINK:
285             plvdi->item.pszText = _T("REG_LINK");
286             break;
287         case REG_RESOURCE_LIST:
288             plvdi->item.pszText = _T("REG_RESOURCE_LIST");
289             break;
290         case REG_NONE:
291             plvdi->item.pszText = _T("REG_NONE");
292             break;
293         default:
294             wsprintf(buffer, _T("unknown(%d)"), plvdi->item.lParam);
295             plvdi->item.pszText = buffer;
296             break;
297         }
298         break;
299     case 2:
300         plvdi->item.pszText = _T("(value not set)");
301         break;
302     case 3:
303         plvdi->item.pszText = _T("");
304         break;
305     }
306 }
307
308 static int CALLBACK CompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
309 {
310     LINE_INFO*l, *r;
311     l = (LINE_INFO*)lParam1;
312     r = (LINE_INFO*)lParam2;
313     if (!l->name) return -1;
314     if (!r->name) return +1;
315         
316     if (g_columnToSort == ~0UL) 
317         g_columnToSort = 0;
318     
319     if (g_columnToSort == 1 && l->dwValType != r->dwValType)
320         return g_invertSort ? (int)r->dwValType - (int)l->dwValType : (int)l->dwValType - (int)r->dwValType;
321     if (g_columnToSort == 2) {
322         /* FIXME: Sort on value */
323     }
324     return g_invertSort ? _tcscmp(r->name, l->name) : _tcscmp(l->name, r->name);
325 }
326
327 HWND StartValueRename(HWND hwndLV)
328 {
329     int item;
330
331     item = ListView_GetNextItem(hwndLV, -1, LVNI_FOCUSED | LVNI_SELECTED);
332     if (item < 1) { /* cannot rename default key */
333         MessageBeep(MB_ICONHAND);
334         return 0;
335     }
336     return ListView_EditLabel(hwndLV, item);
337 }
338
339 static BOOL _CmdWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
340 {
341     switch (LOWORD(wParam)) {
342         /*    case ID_FILE_OPEN: */
343         /*        break; */
344     default:
345         return FALSE;
346     }
347     return TRUE;
348 }
349
350 static LRESULT CALLBACK ListWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
351 {
352     switch (message) {
353     case WM_COMMAND:
354         if (!_CmdWndProc(hWnd, message, wParam, lParam)) {
355             return CallWindowProc(g_orgListWndProc, hWnd, message, wParam, lParam);
356         }
357         break;
358     case WM_NOTIFY_REFLECT:
359         switch (((LPNMHDR)lParam)->code) {
360         
361         case LVN_BEGINLABELEDIT:
362             if (!((NMLVDISPINFO *)lParam)->item.iItem)
363                 return 1;
364             return 0;
365         case LVN_GETDISPINFO:
366             OnGetDispInfo((NMLVDISPINFO*)lParam);
367             break;
368         case LVN_COLUMNCLICK:
369             if (g_columnToSort == ((LPNMLISTVIEW)lParam)->iSubItem)
370                 g_invertSort = !g_invertSort;
371             else {
372                 g_columnToSort = ((LPNMLISTVIEW)lParam)->iSubItem;
373                 g_invertSort = FALSE;
374             }
375                     
376             ListView_SortItems(hWnd, CompareFunc, hWnd);
377             break;
378         case LVN_ENDLABELEDIT: {
379                 LPNMLVDISPINFO dispInfo = (LPNMLVDISPINFO)lParam;
380                 LPTSTR valueName = get_item_text(hWnd, dispInfo->item.iItem);
381                 LONG ret;
382                 if (!valueName) return -1; /* cannot rename a default value */
383                 ret = RenameValue(hWnd, g_currentRootKey, g_currentPath, valueName, dispInfo->item.pszText);
384                 if (ret)
385                     RefreshListView(hWnd, g_currentRootKey, g_currentPath, dispInfo->item.pszText);
386                 HeapFree(GetProcessHeap(), 0, valueName);
387                 return 0;
388             }
389         case NM_RETURN: {
390                 int cnt = ListView_GetNextItem(hWnd, -1, LVNI_FOCUSED | LVNI_SELECTED);
391                 if (cnt != -1)
392                     SendMessage(hFrameWnd, WM_COMMAND, ID_EDIT_MODIFY, 0);
393             }
394             break;
395         case NM_DBLCLK: {
396                 NMITEMACTIVATE* nmitem = (LPNMITEMACTIVATE)lParam;
397                 LVHITTESTINFO info;
398
399                 /* if (nmitem->hdr.hwndFrom != hWnd) break; unnecessary because of WM_NOTIFY_REFLECT */
400                 /*            if (nmitem->hdr.idFrom != IDW_LISTVIEW) break;  */
401                 /*            if (nmitem->hdr.code != ???) break;  */
402 #ifdef _MSC_VER
403                 switch (nmitem->uKeyFlags) {
404                 case LVKF_ALT:     /*  The ALT key is pressed.   */
405                     /* properties dialog box ? */
406                     break;
407                 case LVKF_CONTROL: /*  The CTRL key is pressed. */
408                     /* run dialog box for providing parameters... */
409                     break;
410                 case LVKF_SHIFT:   /*  The SHIFT key is pressed.    */
411                     break;
412                 }
413 #endif
414                 info.pt.x = nmitem->ptAction.x;
415                 info.pt.y = nmitem->ptAction.y;
416                 if (ListView_HitTest(hWnd, &info) != -1) {
417                     ListView_SetItemState(hWnd, -1, 0, LVIS_FOCUSED|LVIS_SELECTED);
418                     ListView_SetItemState(hWnd, info.iItem, LVIS_FOCUSED|LVIS_SELECTED,
419                         LVIS_FOCUSED|LVIS_SELECTED);
420                     SendMessage(hFrameWnd, WM_COMMAND, ID_EDIT_MODIFY, 0);
421                 }
422             }
423             break;
424
425         default:
426             return 0; /* shouldn't call default ! */
427         }
428         break;
429     case WM_CONTEXTMENU: {
430         int cnt = ListView_GetNextItem(hWnd, -1, LVNI_SELECTED);
431         TrackPopupMenu(GetSubMenu(hPopupMenus, cnt == -1 ? PM_NEW : PM_MODIFYVALUE),
432                        TPM_RIGHTBUTTON, (short)LOWORD(lParam), (short)HIWORD(lParam),
433                        0, hFrameWnd, NULL);
434         break;
435     }
436     default:
437         return CallWindowProc(g_orgListWndProc, hWnd, message, wParam, lParam);
438         break;
439     }
440     return 0;
441 }
442
443
444 HWND CreateListView(HWND hwndParent, int id)
445 {
446     RECT rcClient;
447     HWND hwndLV;
448
449     /* Get the dimensions of the parent window's client area, and create the list view control.  */
450     GetClientRect(hwndParent, &rcClient);
451     hwndLV = CreateWindowEx(WS_EX_CLIENTEDGE, WC_LISTVIEW, _T("List View"),
452                             WS_VISIBLE | WS_CHILD | WS_TABSTOP | LVS_REPORT | LVS_EDITLABELS,
453                             0, 0, rcClient.right, rcClient.bottom,
454                             hwndParent, (HMENU)id, hInst, NULL);
455     if (!hwndLV) return NULL;
456     ListView_SetExtendedListViewStyle(hwndLV,  LVS_EX_FULLROWSELECT);
457
458     /* Initialize the image list */
459     if (!InitListViewImageList(hwndLV)) goto fail;
460     if (!CreateListColumns(hwndLV)) goto fail;
461     g_orgListWndProc = SubclassWindow(hwndLV, ListWndProc);
462     return hwndLV;
463 fail:
464     DestroyWindow(hwndLV);
465     return NULL;
466 }
467
468 BOOL RefreshListView(HWND hwndLV, HKEY hKeyRoot, LPCTSTR keyPath, LPCTSTR highlightValue)
469 {
470     BOOL result = FALSE;
471     DWORD max_sub_key_len;
472     DWORD max_val_name_len, valNameLen;
473     DWORD max_val_size, valSize;
474     DWORD val_count, index, valType;
475     TCHAR* valName = 0;
476     BYTE* valBuf = 0;
477     HKEY hKey = 0;
478     LONG errCode;
479     INT count, i;
480     LVITEM item;
481
482     if (!hwndLV) return FALSE;
483
484     SendMessage(hwndLV, WM_SETREDRAW, FALSE, 0);
485
486     errCode = RegOpenKeyEx(hKeyRoot, keyPath, 0, KEY_READ, &hKey);
487     if (errCode != ERROR_SUCCESS) goto done;
488
489     count = ListView_GetItemCount(hwndLV);
490     for (i = 0; i < count; i++) {
491         item.mask = LVIF_PARAM;
492         item.iItem = i;
493         ListView_GetItem(hwndLV, &item);
494         free(((LINE_INFO*)item.lParam)->name);
495         HeapFree(GetProcessHeap(), 0, (void*)item.lParam);
496     }
497     g_columnToSort = ~0UL;
498     ListView_DeleteAllItems(hwndLV);
499
500     /* get size information and resize the buffers if necessary */
501     errCode = RegQueryInfoKey(hKey, NULL, NULL, NULL, NULL, &max_sub_key_len, NULL, 
502                               &val_count, &max_val_name_len, &max_val_size, NULL, NULL);
503     if (errCode != ERROR_SUCCESS) goto done;
504
505     /* account for the terminator char */
506     max_val_name_len++;
507     max_val_size++;
508
509     valName = HeapAlloc(GetProcessHeap(), 0, max_val_name_len * sizeof(TCHAR));
510     valBuf = HeapAlloc(GetProcessHeap(), 0, max_val_size);
511     if (RegQueryValueEx(hKey, NULL, NULL, &valType, valBuf, &valSize) == ERROR_FILE_NOT_FOUND) { 
512         AddEntryToList(hwndLV, NULL, REG_SZ, NULL, 0, !highlightValue);
513     }
514     /*dwValSize = max_val_size; */
515     for(index = 0; index < val_count; index++) {
516         BOOL bSelected = (valName == highlightValue); /* NOT a bug, we check for double NULL here */
517         valNameLen = max_val_name_len;
518         valSize = max_val_size;
519         valType = 0;
520         errCode = RegEnumValue(hKey, index, valName, &valNameLen, NULL, &valType, valBuf, &valSize);
521         if (errCode != ERROR_SUCCESS) goto done;
522         valBuf[valSize] = 0;
523         if (valName && highlightValue && !_tcscmp(valName, highlightValue))
524             bSelected = TRUE;
525         AddEntryToList(hwndLV, valName[0] ? valName : NULL, valType, valBuf, valSize, bSelected);
526     }
527     ListView_SortItems(hwndLV, CompareFunc, hwndLV); 
528
529     g_currentRootKey = hKeyRoot;
530     if (keyPath != g_currentPath) {
531         HeapFree(GetProcessHeap(), 0, g_currentPath);
532         g_currentPath = HeapAlloc(GetProcessHeap(), 0, (lstrlen(keyPath) + 1) * sizeof(TCHAR));
533         if (!g_currentPath) goto done;
534         lstrcpy(g_currentPath, keyPath);
535     }
536
537     result = TRUE;
538
539 done:
540     HeapFree(GetProcessHeap(), 0, valBuf);
541     HeapFree(GetProcessHeap(), 0, valName);
542     SendMessage(hwndLV, WM_SETREDRAW, TRUE, 0);
543     if (hKey) RegCloseKey(hKey);
544
545     return result;
546 }