Prevent memory leak and superfluous status notifications.
[wine] / programs / winecfg / libraries.c
1 /*
2  * WineCfg libraries tabsheet
3  *
4  * Copyright 2004 Robert van Herk
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
22 #define NONAMELESSUNION
23 #include <windows.h>
24 #include <commdlg.h>
25 #include <wine/debug.h>
26 #include <stdio.h>
27 #include "winecfg.h"
28 #include "resource.h"
29
30 WINE_DEFAULT_DEBUG_CHANNEL(winecfg);
31
32 typedef enum _DLGMODE
33 {
34         DLL,
35         APP,
36         GLOBAL,
37 } DLGMODE;
38
39 typedef enum _DLLMODE {
40         BUILTIN_NATIVE,
41         NATIVE_BUILTIN,
42         BUILTIN,
43         NATIVE,
44         DISABLE,
45         UNKNOWN /*Special value indicating an erronous DLL override mode*/
46 } DLLMODE;
47
48 static void removeSpaces(char* in, char* out)
49 {
50   int i,j;
51   j = 0;
52   for (i = 0; i < strlen(in); i++)
53   {
54     if (in[i] != ' ')
55     {
56       out[j] = in[i];
57       j++;
58     }
59   }
60   out[j] = 0;
61 }
62
63 static DLLMODE Str2DLLMode(char* c)
64 {
65   /*Parse a string into a DLLMode*/ 
66   char* d = HeapAlloc(GetProcessHeap(), 0, sizeof(c));
67   removeSpaces(c,d);
68   if (strcmp (d, "builtin,native") == 0) {
69     return BUILTIN_NATIVE;
70   } else
71   if (strcmp (d, "native,builtin") == 0) {
72     return NATIVE_BUILTIN;
73   } else
74   if (strcmp (d, "native") == 0){
75     return NATIVE;
76   } else
77   if (strcmp (d, "builtin") == 0) {
78     return BUILTIN;
79   } else
80   if (strcmp (d, "") == 0) {
81     return DISABLE;
82   } else
83     return UNKNOWN;
84 }
85
86 static char* DLLMode2Str(DLLMODE mode)
87 {
88   char* res;
89   switch (mode) {
90     case NATIVE:
91       res = "native";
92       break;
93     case BUILTIN:
94       res = "builtin";
95       break;
96     case NATIVE_BUILTIN:
97       res = "native, builtin";
98       break;
99     case BUILTIN_NATIVE:
100       res = "builtin, native";
101       break;
102     case DISABLE:
103       res = "";
104       break;
105     default:
106       res = "unknown";
107   }
108   return strdup(res);
109 }
110
111 typedef struct _DLLOVERRIDE
112 {
113         char* lpcKey;    /*The actual dll name*/
114         DLLMODE mode;
115 } DLLOVERRIDE, *LPDLLOVERRIDE;
116
117 static LPDLLOVERRIDE CreateDLLOverride(char* lpcKey)
118 {
119         LPDLLOVERRIDE out = HeapAlloc(GetProcessHeap(),0,sizeof(DLLOVERRIDE));
120         out->lpcKey = strdup (lpcKey);
121         return out;
122 }
123
124 static VOID FreeDLLOverride(LPDLLOVERRIDE ldo)
125 {
126         if (ldo->lpcKey)
127                 free(ldo->lpcKey);
128         HeapFree(GetProcessHeap(),0,ldo);
129 }
130
131 typedef struct _APPL
132 {
133         BOOL isGlobal;
134         char* lpcApplication;
135         char* lpcSection; /*Registry section*/
136 } APPL, *LPAPPL;
137
138 static LPAPPL CreateAppl(BOOL isGlobal, char* application, char* section)
139 {
140         LPAPPL out;
141         out = HeapAlloc(GetProcessHeap(),0,sizeof(APPL));
142         out->lpcApplication = strdup(application);
143         out->lpcSection = strdup(section);
144         out->isGlobal = isGlobal;
145         return out;
146 }
147
148 static VOID FreeAppl(LPAPPL lpAppl)
149 {
150         if (lpAppl->lpcApplication)
151                 free(lpAppl->lpcApplication); /* The strings were strdup-ped, so we use "free" */
152         if (lpAppl->lpcSection)
153                 free(lpAppl->lpcSection);
154         HeapFree(GetProcessHeap(),0,lpAppl);
155 }
156
157 typedef struct _ITEMTAG
158 {
159         LPAPPL lpAppl;
160         LPDLLOVERRIDE lpDo;
161 } ITEMTAG, *LPITEMTAG;
162
163 static LPITEMTAG CreateItemTag()
164 {
165         LPITEMTAG out;
166         out = HeapAlloc(GetProcessHeap(),0,sizeof(ITEMTAG));
167         out->lpAppl = 0;
168         out->lpDo = 0;
169         return out;
170 }
171
172 static VOID FreeItemTag(LPITEMTAG lpit)
173 {
174         if (lpit->lpAppl)
175                 FreeAppl(lpit->lpAppl);
176         if (lpit->lpDo)
177                 FreeDLLOverride(lpit->lpDo);
178         HeapFree(GetProcessHeap(),0,lpit);
179 }
180
181 static VOID UpdateDLLList(HWND hDlg, char* dll)
182 {
183         /*Add if it isn't already in*/
184         if (SendDlgItemMessage(hDlg, IDC_DLLLIST, CB_FINDSTRING, 1, (LPARAM) dll) == CB_ERR)
185                 SendDlgItemMessage(hDlg,IDC_DLLLIST,CB_ADDSTRING,0,(LPARAM) dll);
186 }
187
188 static VOID LoadLibrarySettings(LPAPPL appl /*DON'T FREE, treeview will own this*/, HWND hDlg, HWND hwndTV)
189 {
190         HKEY key;
191         int i;
192         DWORD size;
193         DWORD readSize;
194         char name [255];
195         char read [255];
196         LPITEMTAG lpIt;
197         TVINSERTSTRUCT tis;     
198         HTREEITEM hParent;
199         LPDLLOVERRIDE lpdo;
200         
201         WINE_TRACE("opening %s\n", appl->lpcSection);
202         if (RegOpenKey (configKey, appl->lpcSection, &key) == ERROR_SUCCESS)
203         {
204                 i = 0;
205                 size = 255;
206                 readSize = 255;
207                 
208                 lpIt = CreateItemTag();
209                 lpIt->lpAppl = appl;
210
211                 tis.hParent = NULL;
212                 tis.hInsertAfter = TVI_LAST;
213                 tis.u.item.mask = TVIF_TEXT | TVIF_PARAM;
214                 tis.u.item.pszText = appl->lpcApplication;
215                 tis.u.item.lParam = (LPARAM)lpIt;
216                 hParent = TreeView_InsertItem(hwndTV,&tis);
217                 tis.hParent = hParent;
218
219                 while (RegEnumValue(key, i, name, &size, NULL, NULL, read, &readSize) == ERROR_SUCCESS)
220                 {                           
221                         WINE_TRACE("Reading value %s, namely %s\n", name, read);
222                         
223                         lpIt = CreateItemTag();
224                         lpdo = CreateDLLOverride(name);
225                         lpIt->lpDo = lpdo;
226                         tis.u.item.lParam = (LPARAM)lpIt;
227                         tis.u.item.pszText = name;
228                         
229                         lpdo->mode = Str2DLLMode(read);
230                         
231                         TreeView_InsertItem(hwndTV,&tis);
232                         UpdateDLLList(hDlg, name);
233                         i ++; size = 255; readSize = 255;
234                 }
235                 RegCloseKey(key);
236         }
237 }
238
239 static VOID SetEnabledDLLControls(HWND dialog, DLGMODE dlgmode)
240 {
241         if (dlgmode == DLL) {
242                 enable(IDC_RAD_BUILTIN);
243                 enable(IDC_RAD_NATIVE);
244                 enable(IDC_RAD_BUILTIN_NATIVE);
245                 enable(IDC_RAD_NATIVE_BUILTIN);
246                 enable(IDC_RAD_DISABLE);
247                 enable(IDC_DLLS_REMOVEDLL);
248         } else {
249                 disable(IDC_RAD_BUILTIN);
250                 disable(IDC_RAD_NATIVE);
251                 disable(IDC_RAD_BUILTIN_NATIVE);
252                 disable(IDC_RAD_NATIVE_BUILTIN);
253                 disable(IDC_RAD_DISABLE);
254                 disable(IDC_DLLS_REMOVEDLL);
255         }
256
257         if (dlgmode == APP) {
258                 enable(IDC_DLLS_REMOVEAPP);
259         }
260         else {
261                 disable(IDC_DLLS_REMOVEAPP);
262         }
263 }
264
265 static VOID OnInitLibrariesDlg(HWND hDlg)
266 {
267         HWND hwndTV;
268         LPAPPL lpAppl;
269         HKEY applKey;
270         int i;
271         DWORD size;
272         char appl [255];
273         char lpcKey [255];
274         FILETIME ft;
275
276         hwndTV = GetDlgItem(hDlg,IDC_TREE_DLLS);
277         lpAppl = CreateAppl(TRUE,"Global DLL Overrides", "DllOverrides");
278         LoadLibrarySettings(lpAppl, hDlg, hwndTV);
279         
280         /*And now the application specific stuff:*/
281         if (RegOpenKey(configKey, "AppDefaults", &applKey) == ERROR_SUCCESS) {
282                 i = 0;
283                 size = 255;
284                 while (RegEnumKeyEx(applKey, i, appl, &size, NULL, NULL, NULL, &ft) == ERROR_SUCCESS)
285                 {
286                         sprintf(lpcKey, "AppDefaults\\%s\\DllOverrides", appl);
287                         lpAppl = CreateAppl(FALSE,appl, lpcKey);
288                         LoadLibrarySettings(lpAppl, hDlg, hwndTV);
289                         i++; size = 255;
290                 }
291                 RegCloseKey(applKey);
292         }
293
294         SetEnabledDLLControls(hDlg, GLOBAL);
295 }
296
297 static VOID OnTreeViewChangeItem(HWND hDlg, HWND hTV)
298 {
299         TVITEM ti;
300         LPITEMTAG lpit;
301         int buttonId;
302
303         ti.mask = TVIF_PARAM;
304         ti.hItem = TreeView_GetSelection(hTV);
305         if (TreeView_GetItem (hTV, &ti))
306         {
307                 lpit = (LPITEMTAG) ti.lParam;
308                 if (lpit->lpDo)
309                 {
310                         WINE_TRACE("%s\n", lpit->lpDo->lpcKey);
311                         buttonId = IDC_RAD_BUILTIN;
312                         switch (lpit->lpDo->mode)
313                         {
314                                 case NATIVE:
315                                         buttonId = IDC_RAD_NATIVE;
316                                         break;
317                                 case BUILTIN:
318                                         buttonId = IDC_RAD_BUILTIN;
319                                         break;
320                                 case NATIVE_BUILTIN:
321                                         buttonId = IDC_RAD_NATIVE_BUILTIN;
322                                         break;
323                                 case BUILTIN_NATIVE:
324                                         buttonId = IDC_RAD_BUILTIN_NATIVE;
325                                         break;
326                                 case DISABLE:
327                                         buttonId = IDC_RAD_DISABLE;
328                                         break;
329                                 case UNKNOWN:
330                                         buttonId = -1;
331                                         break;
332                         }
333                         CheckRadioButton(hDlg, IDC_RAD_BUILTIN, IDC_RAD_DISABLE, buttonId);
334                         SetEnabledDLLControls(hDlg, DLL);
335                 } else {
336                         if (lpit->lpAppl)
337                         {
338                                 if (lpit->lpAppl->isGlobal == TRUE)
339                                         SetEnabledDLLControls(hDlg, GLOBAL);
340                                 else
341                                         SetEnabledDLLControls(hDlg, APP);
342                         }
343                 }
344         }
345 }
346
347 static VOID SetDLLMode(HWND hDlg, DLLMODE mode)
348 {
349         HWND hTV;
350         TVITEM ti;
351         LPITEMTAG lpit;
352         char* cMode;
353         TVITEM tiPar;
354         LPITEMTAG lpitPar;
355
356         hTV = GetDlgItem(hDlg, IDC_TREE_DLLS);
357         ti.mask = TVIF_PARAM;
358         ti.hItem = TreeView_GetSelection(hTV);
359         if (TreeView_GetItem (hTV, &ti))
360         {
361                 lpit = (LPITEMTAG) ti.lParam;
362                 if (lpit->lpDo)
363                 {
364                         lpit->lpDo->mode = mode;
365                         cMode = DLLMode2Str (mode);
366                         /*Find parent, so we can read registry section*/
367                         tiPar.mask = TVIF_PARAM;
368                         tiPar.hItem = TreeView_GetParent(hTV, ti.hItem);
369                         if (TreeView_GetItem(hTV,&tiPar))
370                         {
371                                 lpitPar = (LPITEMTAG) tiPar.lParam;
372                                 if (lpitPar->lpAppl)
373                                 {
374                                         addTransaction(lpitPar->lpAppl->lpcSection, lpit->lpDo->lpcKey, ACTION_SET, cMode);
375                                 }
376                         }
377                         free(cMode);
378                 }
379         }
380 }
381
382 static VOID OnBuiltinClick(HWND hDlg)
383 {
384         SetDLLMode(hDlg, BUILTIN);
385 }
386
387 static VOID OnNativeClick(HWND hDlg)
388 {
389         SetDLLMode(hDlg, NATIVE);
390 }
391
392 static VOID OnBuiltinNativeClick(HWND hDlg)
393 {
394         SetDLLMode(hDlg, BUILTIN_NATIVE);
395 }
396
397 static VOID OnNativeBuiltinClick(HWND hDlg)
398 {
399         SetDLLMode(hDlg, NATIVE_BUILTIN);
400 }
401
402 static VOID OnDisableClick(HWND hDlg)
403 {
404         SetDLLMode(hDlg, DISABLE);
405 }
406
407 static VOID OnTreeViewDeleteItem(NMTREEVIEW* nmt)
408 {
409         FreeItemTag((LPITEMTAG)(nmt->itemOld.lParam));
410 }
411
412 static VOID OnAddDLLClick(HWND hDlg)
413 {
414         HWND hTV;
415         TVITEM ti;
416         LPITEMTAG lpit;
417         LPITEMTAG lpitNew;
418         TVITEM childti;
419         char dll [255];
420         BOOL doAdd;
421         TVINSERTSTRUCT tis;
422
423         hTV = GetDlgItem(hDlg, IDC_TREE_DLLS);
424         ti.mask = TVIF_PARAM;
425         ti.hItem = TreeView_GetSelection(hTV);
426         if (TreeView_GetItem (hTV, &ti))
427         {
428                 lpit = (LPITEMTAG) ti.lParam;
429                 if (lpit->lpDo) { /*Is this a DLL override (that is: a subitem), then find the parent*/
430                         ti.hItem = TreeView_GetParent(hTV, ti.hItem);
431                         if (TreeView_GetItem(hTV,&ti)) {
432                                 lpit = (LPITEMTAG) ti.lParam;
433                         } else return;
434                 }
435         } else return;
436         /*Now we should have an parent item*/
437         if (lpit->lpAppl)
438         {
439                 lpitNew = CreateItemTag();
440                 SendDlgItemMessage(hDlg,IDC_DLLLIST,WM_GETTEXT,(WPARAM)255, (LPARAM) dll);
441                 if (strlen(dll) > 0) {
442                         /*Is the dll already in the list? If so, don't do it*/
443                         doAdd = TRUE;
444                         childti.mask = TVIF_PARAM;
445                         if ((childti.hItem = TreeView_GetNextItem(hTV, ti.hItem, TVGN_CHILD))) {
446                                 /*Retrieved first child*/
447                                 while (TreeView_GetItem (hTV, &childti))
448                                 {
449                                         if (strcmp(((LPITEMTAG)childti.lParam)->lpDo->lpcKey,dll) == 0) {
450                                                 doAdd = FALSE;
451                                                 break;
452                                         }
453                                         childti.hItem = TreeView_GetNextItem(hTV, childti.hItem, TVGN_NEXT);
454                                 }
455                         }
456                         if (doAdd)
457                         {
458                                 lpitNew->lpDo = CreateDLLOverride(dll);
459                                 lpitNew->lpDo->mode = NATIVE;
460                                 tis.hInsertAfter = TVI_LAST;
461                                 tis.u.item.mask = TVIF_TEXT | TVIF_PARAM;
462                                 tis.u.item.pszText = dll;
463                                 tis.u.item.lParam = (LPARAM)lpitNew;
464                                 tis.hParent = ti.hItem;
465                                 TreeView_InsertItem(hTV,&tis);
466                                 UpdateDLLList(hDlg, dll);
467                                 addTransaction(lpit->lpAppl->lpcSection, dll, ACTION_SET, "native");
468                         } else MessageBox(hDlg, "A DLL with that name is already in this list...", "", MB_OK | MB_ICONINFORMATION);
469                 }
470         } else return;
471 }
472
473 static VOID OnRemoveDLLClick(HWND hDlg)
474 {
475         HWND hTV;
476         TVITEM ti;
477         LPITEMTAG lpit;
478         TVITEM tiPar;
479         LPITEMTAG lpitPar;
480
481         hTV = GetDlgItem(hDlg, IDC_TREE_DLLS);
482         ti.mask = TVIF_PARAM;
483         ti.hItem = TreeView_GetSelection(hTV);
484         if (TreeView_GetItem (hTV, &ti)) {
485                 lpit = (LPITEMTAG) ti.lParam;
486                 if (lpit->lpDo)
487                 {
488                         /*Find parent for section*/
489                         tiPar.mask = TVIF_PARAM;
490                         tiPar.hItem = TreeView_GetParent(hTV, ti.hItem);
491                         if (TreeView_GetItem(hTV,&tiPar))
492                         {
493                                 lpitPar = (LPITEMTAG) tiPar.lParam;
494                                 if (lpitPar->lpAppl)
495                                 {
496                                         addTransaction(lpitPar->lpAppl->lpcSection, lpit->lpDo->lpcKey, ACTION_REMOVE, NULL);
497                                         TreeView_DeleteItem(hTV,ti.hItem);
498                                 }
499                         }
500                 }
501         }
502 }
503
504 static VOID OnAddApplicationClick(HWND hDlg)
505 {
506         char szFileTitle [255];
507         char szFile [255];
508         char lpcKey [255];
509
510         TVINSERTSTRUCT tis;
511         LPITEMTAG lpit;
512         OPENFILENAME ofn = { sizeof(OPENFILENAME),
513                 0, /*hInst*/0, "Wine Programs (*.exe,*.exe.so)\0*.exe;*.exe.so\0", NULL, 0, 0, NULL,
514                 0, NULL, 0, NULL, NULL,
515                 OFN_SHOWHELP, 0, 0, NULL, 0, NULL };
516
517         ofn.lpstrFileTitle = szFileTitle;
518         ofn.lpstrFileTitle[0] = '\0';
519         ofn.nMaxFileTitle = sizeof(szFileTitle);
520         ofn.lpstrFile = szFile;
521         ofn.lpstrFile[0] = '\0';
522         ofn.nMaxFile = sizeof(szFile);
523
524         if (GetOpenFileName(&ofn))
525         {
526                 tis.hParent = NULL;
527                 tis.hInsertAfter = TVI_LAST;
528                 tis.u.item.mask = TVIF_TEXT | TVIF_PARAM;
529                 tis.u.item.pszText = szFileTitle;
530                 lpit = CreateItemTag();
531                 sprintf(lpcKey, "AppDefaults\\%s\\DllOverrides", szFileTitle);
532                 lpit->lpAppl = CreateAppl(FALSE,szFileTitle,lpcKey);
533                 tis.u.item.lParam = (LPARAM)lpit;
534                 TreeView_InsertItem(GetDlgItem(hDlg,IDC_TREE_DLLS), &tis);
535                 setConfigValue(lpcKey,NULL,NULL);
536         }
537 }
538
539 static VOID OnRemoveApplicationClick(HWND hDlg)
540 {
541         HWND hTV;
542         TVITEM ti;
543         LPITEMTAG lpit;
544
545         hTV = GetDlgItem(hDlg, IDC_TREE_DLLS);
546         ti.mask = TVIF_PARAM;
547         ti.hItem = TreeView_GetSelection(hTV);
548         if (TreeView_GetItem (hTV, &ti)) {
549                 lpit = (LPITEMTAG) ti.lParam;
550                 if (lpit->lpAppl)
551                 {
552                         addTransaction(lpit->lpAppl->lpcSection, NULL, ACTION_REMOVE, NULL);
553                         TreeView_DeleteItem(hTV,ti.hItem);
554                 }
555         }
556 }
557
558 INT_PTR CALLBACK
559 LibrariesDlgProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
560 {
561         switch (uMsg)
562         {
563         case WM_INITDIALOG:
564                 OnInitLibrariesDlg(hDlg);
565                 break;  
566         case WM_NOTIFY:
567                 switch (((LPNMHDR)lParam)->code) {
568                 case TVN_SELCHANGED: {
569                                 switch(LOWORD(wParam)) {
570                                 case IDC_TREE_DLLS:
571                                         OnTreeViewChangeItem(hDlg, GetDlgItem(hDlg,IDC_TREE_DLLS));
572                                         break;
573                                 }
574                         }
575                         break;
576                 case TVN_DELETEITEM:
577                         OnTreeViewDeleteItem ((LPNMTREEVIEW)lParam);
578                         break;
579                 }
580                 break;
581         case WM_COMMAND:
582                 switch(HIWORD(wParam)) {
583                 case BN_CLICKED:
584                         switch(LOWORD(wParam)) {
585                         case IDC_RAD_BUILTIN:
586                                 OnBuiltinClick(hDlg);
587                                 break;
588                         case IDC_RAD_NATIVE:
589                                 OnNativeClick(hDlg);
590                                 break;
591                         case IDC_RAD_BUILTIN_NATIVE:
592                                 OnBuiltinNativeClick(hDlg);
593                                 break;
594                         case IDC_RAD_NATIVE_BUILTIN:
595                                 OnNativeBuiltinClick(hDlg);
596                                 break;
597                         case IDC_RAD_DISABLE:
598                                 OnDisableClick(hDlg);
599                                 break;
600                         case IDC_DLLS_ADDAPP:
601                                 OnAddApplicationClick(hDlg);
602                                 break;
603                         case IDC_DLLS_REMOVEAPP:
604                                 OnRemoveApplicationClick(hDlg);
605                                 break;
606                         case IDC_DLLS_ADDDLL:
607                                 OnAddDLLClick(hDlg);
608                                 break;
609                         case IDC_DLLS_REMOVEDLL:
610                                 OnRemoveDLLClick(hDlg);
611                                 break;
612                         }
613                         break;
614                 }
615                 break;
616         }
617
618         return 0;
619 }