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