oleview: Mimick native treeview display of enumerations.
[wine] / programs / oleview / details.c
1 /*
2  * OleView (details.c)
3  *
4  * Copyright 2006 Piotr Caban
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 "main.h"
22
23 DETAILS details;
24 static const WCHAR wszAppID[] = { 'A','p','p','I','D','\0' };
25 static const WCHAR wszCLSID[] = { 'C','L','S','I','D','\0' };
26 static const WCHAR wszProgID[] = { 'P','r','o','g','I','D','\0' };
27 static const WCHAR wszProxyStubClsid32[] = 
28     { 'P','r','o','x','y','S','t','u','b','C','l','s','i','d','3','2','\0' };
29 static const WCHAR wszTypeLib[] = { 'T','y','p','e','L','i','b','\0' };
30
31 void CreateRegRec(HKEY hKey, HTREEITEM parent, WCHAR *wszKeyName, BOOL addings)
32 {
33     int i=0, j, retEnum;
34     HKEY hCurKey;
35     DWORD lenName, lenData, valType;
36     WCHAR wszName[MAX_LOAD_STRING];
37     WCHAR wszData[MAX_LOAD_STRING];
38     WCHAR wszTree[MAX_LOAD_STRING];
39     const WCHAR wszBinary[] = { '%','0','2','X',' ','\0' };
40     const WCHAR wszDots[] = { '.','.','.','\0' };
41     const WCHAR wszFormat1[] = { '%','s',' ','[','%','s',']',' ','=',' ','%','s','\0' };
42     const WCHAR wszFormat2[] = { '%','s',' ','=',' ','%','s','\0' };
43     TVINSERTSTRUCT tvis;
44     HTREEITEM addPlace = parent;
45
46     U(tvis).item.mask = TVIF_TEXT;
47     U(tvis).item.cchTextMax = MAX_LOAD_STRING;
48     U(tvis).item.pszText = wszTree;
49     tvis.hInsertAfter = (HTREEITEM)TVI_LAST;
50     tvis.hParent = parent;
51
52     while(TRUE)
53     {
54         lenName = sizeof(WCHAR[MAX_LOAD_STRING]);
55         lenData = sizeof(WCHAR[MAX_LOAD_STRING]);
56
57         retEnum = RegEnumValue(hKey, i, wszName, &lenName,
58                 NULL, &valType, (LPBYTE)wszData, &lenData);
59
60         if(retEnum != ERROR_SUCCESS)
61         {
62             if(!i && lstrlenW(wszKeyName) > 1)
63             {
64                 U(tvis).item.pszText = (LPWSTR)wszKeyName;
65                 addPlace = TreeView_InsertItem(details.hReg, &tvis);
66                 U(tvis).item.pszText = wszTree;
67             }
68             break;
69         }
70
71         if(valType == REG_BINARY)
72         {
73             WCHAR wszBuf[MAX_LOAD_STRING];
74
75             for(j=0; j<MAX_LOAD_STRING/3-1; j++)
76                 wsprintfW(&wszBuf[3*j], wszBinary, (int)((unsigned char)wszData[j]));
77             wszBuf[(lenData*3>=MAX_LOAD_STRING ? MAX_LOAD_STRING-1 : lenData*3)] = '\0';
78             lstrcpyW(wszData, wszBuf);
79             lstrcpyW(&wszData[MAX_LOAD_STRING-5], wszDots);
80         }
81
82         if(lenName) wsprintfW(wszTree, wszFormat1, wszKeyName, wszName, wszData);
83         else wsprintfW(wszTree, wszFormat2, wszKeyName, wszData);
84
85         addPlace = TreeView_InsertItem(details.hReg, &tvis);
86
87         if(addings && !memcmp(wszName, wszAppID, sizeof(WCHAR[6])))
88         {
89             lstrcpyW(wszTree, wszName);
90             memmove(&wszData[6], wszData, sizeof(WCHAR[MAX_LOAD_STRING-6]));
91             lstrcpyW(wszData, wszCLSID);
92             wszData[5] = '\\';
93
94             if(RegOpenKey(HKEY_CLASSES_ROOT, wszData, &hCurKey) != ERROR_SUCCESS)
95             {
96                 i++;
97                 continue;
98             }
99
100             tvis.hParent = TVI_ROOT;
101             tvis.hParent = TreeView_InsertItem(details.hReg, &tvis);
102
103             lenName = sizeof(WCHAR[MAX_LOAD_STRING]);
104
105             RegQueryValue(hCurKey, NULL, wszName, (LONG *)&lenName);
106             RegCloseKey(hCurKey);
107
108             wsprintfW(wszTree, wszFormat2, &wszData[6], wszName);
109
110             SendMessage(details.hReg, TVM_INSERTITEM, 0, (LPARAM)&tvis);
111             SendMessage(details.hReg, TVM_EXPAND, TVE_EXPAND, (LPARAM)tvis.hParent);
112
113             tvis.hParent = parent;
114         }
115         i++;
116     }
117
118     i=-1;
119     lenName = sizeof(WCHAR[MAX_LOAD_STRING]);
120
121     while(TRUE)
122     {
123         i++;
124
125         if(RegEnumKey(hKey, i, wszName, lenName) != ERROR_SUCCESS) break;
126
127         if(RegOpenKey(hKey, wszName, &hCurKey) != ERROR_SUCCESS) continue;
128
129         CreateRegRec(hCurKey, addPlace, wszName, addings);
130         SendMessage(details.hReg, TVM_EXPAND, TVE_EXPAND, (LPARAM)addPlace);
131
132         if(addings && !memcmp(wszName, wszProgID, sizeof(WCHAR[7])))
133         {
134             lenData = sizeof(WCHAR[MAX_LOAD_STRING]);
135
136             RegQueryValue(hCurKey, NULL, wszData, (LONG *)&lenData);
137             RegCloseKey(hCurKey);
138
139             if(RegOpenKey(HKEY_CLASSES_ROOT, wszData, &hCurKey) != ERROR_SUCCESS)
140                 continue;
141             CreateRegRec(hCurKey, TVI_ROOT, wszData, FALSE);
142         }
143         else if(addings && !memcmp(wszName, wszProxyStubClsid32, sizeof(WCHAR[17])))
144         {
145             lenData = sizeof(WCHAR[MAX_LOAD_STRING]);
146
147             RegQueryValue(hCurKey, NULL, wszData, (LONG *)&lenData);
148             RegCloseKey(hCurKey);
149
150             RegOpenKey(HKEY_CLASSES_ROOT, wszCLSID, &hCurKey);
151
152             lenName = sizeof(WCHAR[MAX_LOAD_STRING]);
153             RegQueryValue(hCurKey, NULL, wszName, (LONG *)&lenName);
154
155             tvis.hParent = TVI_ROOT;
156             wsprintfW(wszTree, wszFormat2, wszCLSID, wszName);
157             tvis.hParent = TreeView_InsertItem(details.hReg, &tvis);
158
159             RegCloseKey(hCurKey);
160
161             memmove(&wszData[6], wszData, sizeof(WCHAR[lenData]));
162             memcpy(wszData, wszCLSID, sizeof(WCHAR[6]));
163             wszData[5] = '\\';
164
165             RegOpenKey(HKEY_CLASSES_ROOT, wszData, &hCurKey);
166
167             CreateRegRec(hCurKey, tvis.hParent, &wszData[6], FALSE);
168
169             SendMessage(details.hReg, TVM_EXPAND, TVE_EXPAND, (LPARAM)tvis.hParent);
170             tvis.hParent = parent;
171         }
172         else if(addings && !memcmp(wszName, wszTypeLib, sizeof(WCHAR[8])))
173         {
174             lenData = sizeof(WCHAR[MAX_LOAD_STRING]);
175
176             RegQueryValue(hCurKey, NULL, wszData, (LONG *)&lenData);
177             RegCloseKey(hCurKey);
178
179             RegOpenKey(HKEY_CLASSES_ROOT, wszTypeLib, &hCurKey);
180
181             lenName = sizeof(WCHAR[MAX_LOAD_STRING]);
182             RegQueryValue(hCurKey, NULL, wszName, (LONG *)&lenName);
183
184             tvis.hParent = TVI_ROOT;
185             wsprintfW(wszTree, wszFormat2, wszTypeLib, wszName);
186             tvis.hParent = TreeView_InsertItem(details.hReg, &tvis);
187
188             RegCloseKey(hCurKey);
189
190             memmove(&wszData[8], wszData, sizeof(WCHAR[lenData]));
191             memcpy(wszData, wszTypeLib, sizeof(WCHAR[8]));
192             wszData[7] = '\\';
193             RegOpenKey(HKEY_CLASSES_ROOT, wszData, &hCurKey);
194
195             CreateRegRec(hCurKey, tvis.hParent, &wszData[8], FALSE);
196
197             SendMessage(details.hReg, TVM_EXPAND, TVE_EXPAND, (LPARAM)tvis.hParent);
198             tvis.hParent = parent;
199         }
200         RegCloseKey(hCurKey);
201     }
202 }
203
204 void CreateReg(WCHAR *buffer)
205 {
206     HKEY hKey;
207     DWORD lenBuffer=-1, lastLenBuffer, lenTree;
208     WCHAR *path;
209     WCHAR wszTree[MAX_LOAD_STRING];
210     TVINSERTSTRUCT tvis;
211     HTREEITEM addPlace = TVI_ROOT;
212
213     U(tvis).item.mask = TVIF_TEXT;
214     U(tvis).item.cchTextMax = MAX_LOAD_STRING;
215     U(tvis).item.pszText = wszTree;
216     tvis.hInsertAfter = (HTREEITEM)TVI_LAST;
217     tvis.hParent = TVI_ROOT;
218
219     path = buffer;
220     while(TRUE)
221     {
222         while(*path != '\\' && *path != '\0') path += 1;
223
224         if(*path == '\\')
225         {
226             *path = '\0';
227
228             if(RegOpenKey(HKEY_CLASSES_ROOT, (LPWSTR)buffer, &hKey) != ERROR_SUCCESS)
229                 return;
230
231             lastLenBuffer = lenBuffer+1;
232             lenBuffer = lstrlenW(buffer);
233             *path = '\\';
234             path += 1;
235
236             lenTree = sizeof(WCHAR[MAX_LOAD_STRING]);
237
238             if(RegQueryValue(hKey, NULL, wszTree, (LONG *)&lenTree) == ERROR_SUCCESS)
239             {
240                 memmove(&wszTree[lenBuffer-lastLenBuffer+3], wszTree,
241                         sizeof(WCHAR[lenTree]));
242                 memcpy(wszTree, &buffer[lastLenBuffer],
243                         sizeof(WCHAR[lenBuffer-lastLenBuffer]));
244
245                 if(lenTree == 1) wszTree[lenBuffer-lastLenBuffer] = '\0';
246                 else
247                 {
248                     wszTree[lenBuffer-lastLenBuffer] = ' ';
249                     wszTree[lenBuffer-lastLenBuffer+1] = '=';
250                     wszTree[lenBuffer-lastLenBuffer+2] = ' ';
251                 }
252
253                 addPlace = TreeView_InsertItem(details.hReg, &tvis);
254             }
255
256             tvis.hParent = addPlace;
257             RegCloseKey(hKey);
258         }
259         else break;
260     }
261
262     if(RegOpenKey(HKEY_CLASSES_ROOT, (LPWSTR)buffer, &hKey) != ERROR_SUCCESS) return;
263
264     CreateRegRec(hKey, addPlace, (LPWSTR)&buffer[lenBuffer+1], TRUE);
265
266     RegCloseKey(hKey);
267
268     SendMessage(details.hReg, TVM_EXPAND, TVE_EXPAND, (LPARAM)addPlace);
269     SendMessage(details.hReg, TVM_ENSUREVISIBLE, 0, (LPARAM)addPlace);
270 }
271
272 void RefreshDetails(HTREEITEM item)
273 {
274     TVITEM tvi;
275     WCHAR wszBuf[MAX_LOAD_STRING];
276     WCHAR wszStaticText[MAX_LOAD_STRING];
277     const WCHAR wszFormat[] = { '%','s','\n','%','s','\0' };
278     BOOL show;
279
280     memset(&tvi, 0, sizeof(TVITEM));
281     memset(&wszStaticText, 0, sizeof(WCHAR[MAX_LOAD_STRING]));
282     tvi.mask = TVIF_TEXT;
283     tvi.hItem = item;
284     tvi.pszText = wszBuf;
285     tvi.cchTextMax = MAX_LOAD_STRING;
286     SendMessage(globals.hTree, TVM_GETITEM, 0, (LPARAM)&tvi);
287
288     if(tvi.lParam)
289         wsprintfW(wszStaticText, wszFormat, tvi.pszText, ((ITEM_INFO *)tvi.lParam)->clsid);
290     else lstrcpyW(wszStaticText, tvi.pszText);
291
292     SetWindowText(details.hStatic, wszStaticText);
293
294     SendMessage(details.hTab, TCM_SETCURSEL, 0, 0);
295
296     if(tvi.lParam && ((ITEM_INFO *)tvi.lParam)->cFlag & SHOWALL)
297     {
298         if(TabCtrl_GetItemCount(details.hTab) == 1)
299         {
300             TCITEM tci;
301             memset(&tci, 0, sizeof(TCITEM));
302             tci.mask = TCIF_TEXT;
303             tci.pszText = wszBuf;
304             tci.cchTextMax = sizeof(WCHAR[MAX_LOAD_STRING]);
305
306             LoadString(globals.hMainInst, IDS_TAB_IMPL,
307                     wszBuf, sizeof(WCHAR[MAX_LOAD_STRING]));
308             SendMessage(details.hTab, TCM_INSERTITEM, 1, (LPARAM)&tci);
309
310             LoadString(globals.hMainInst, IDS_TAB_ACTIV,
311                     wszBuf, sizeof(WCHAR[MAX_LOAD_STRING]));
312             SendMessage(details.hTab, TCM_INSERTITEM, 2, (LPARAM)&tci);
313         }
314     }
315     else
316     {
317         SendMessage(details.hTab, TCM_DELETEITEM, 2, 0);
318         SendMessage(details.hTab, TCM_DELETEITEM, 1, 0);
319     }
320
321     show = CreateRegPath(item, wszBuf, MAX_LOAD_STRING);
322     ShowWindow(details.hTab, show ? SW_SHOW : SW_HIDE);
323
324     /* FIXME Next line deals with TreeView_EnsureVisible bug */
325     SendMessage(details.hReg, TVM_ENSUREVISIBLE, 0,
326             SendMessage(details.hReg, TVM_GETNEXTITEM, TVGN_CHILD, (LPARAM)TVI_ROOT));
327     SendMessage(details.hReg, TVM_DELETEITEM, 0, (LPARAM)TVI_ROOT);
328     if(show) CreateReg(wszBuf);
329 }
330
331 void CreateTabCtrl(HWND hWnd)
332 {
333     TCITEM tci;
334     WCHAR buffer[MAX_LOAD_STRING];
335
336     memset(&tci, 0, sizeof(TCITEM));
337     tci.mask = TCIF_TEXT;
338     tci.pszText = buffer;
339     tci.cchTextMax = sizeof(WCHAR[MAX_LOAD_STRING]);
340
341     details.hTab = CreateWindow(WC_TABCONTROL, NULL, WS_CHILD|WS_VISIBLE,
342             0, 0, 0, 0, hWnd, (HMENU)TAB_WINDOW, globals.hMainInst, NULL);
343     ShowWindow(details.hTab, SW_HIDE);
344
345     LoadString(globals.hMainInst, IDS_TAB_REG, buffer, sizeof(WCHAR[MAX_LOAD_STRING]));
346     SendMessage(details.hTab, TCM_INSERTITEM, 0, (LPARAM)&tci);
347
348     details.hReg = CreateWindowEx(WS_EX_CLIENTEDGE, WC_TREEVIEW, NULL,
349             WS_CHILD|WS_VISIBLE|TVS_HASLINES,
350             0, 0, 0, 0, details.hTab, NULL, globals.hMainInst, NULL);
351 }
352
353 LRESULT CALLBACK DetailsProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
354 {
355     int sel;
356
357     switch(uMsg)
358     {
359         case WM_CREATE:
360             {
361                 const WCHAR wszStatic[] = { 'S','t','a','t','i','c','\0' };
362
363                 details.hStatic = CreateWindow(wszStatic, NULL, WS_CHILD|WS_VISIBLE,
364                         0, 0, 0, 0, hWnd, NULL, globals.hMainInst, NULL);
365                 CreateTabCtrl(hWnd);
366             }
367             break;
368         case WM_SIZE:
369             MoveWindow(details.hStatic, 0, 0, LOWORD(lParam), 40, TRUE);
370             MoveWindow(details.hTab, 3, 40, LOWORD(lParam)-6, HIWORD(lParam)-43, TRUE);
371             MoveWindow(details.hReg, 10, 34, LOWORD(lParam)-26,
372                     HIWORD(lParam)-87, TRUE);
373             break;
374         case WM_NOTIFY:
375             if((int)wParam != TAB_WINDOW) break;
376             switch(((LPNMHDR)lParam)->code)
377             {
378                 case TCN_SELCHANGE:
379                     ShowWindow(details.hReg, SW_HIDE);
380                     sel = TabCtrl_GetCurSel(details.hTab);
381
382                     if(sel==0) ShowWindow(details.hReg, SW_SHOW);
383                     break;
384             }
385             break;
386         default:
387             return DefWindowProc(hWnd, uMsg, wParam, lParam);
388     }
389     return 0;
390 }
391
392 HWND CreateDetailsWindow(HINSTANCE hInst)
393 {
394     WNDCLASS wcd;
395     const WCHAR wszDetailsClass[] = { 'D','E','T','A','I','L','S','\0' };
396     
397     memset(&wcd, 0, sizeof(WNDCLASS));
398     wcd.lpfnWndProc = DetailsProc;
399     wcd.lpszClassName = wszDetailsClass;
400     wcd.hbrBackground = (HBRUSH)COLOR_WINDOW;
401
402     if(!RegisterClass(&wcd)) return NULL;
403
404     globals.hDetails = CreateWindowEx(WS_EX_CLIENTEDGE, wszDetailsClass, NULL,
405             WS_CHILD|WS_VISIBLE, 0, 0, 0, 0, globals.hPaneWnd, NULL, hInst, NULL);
406
407     return globals.hDetails;
408 }