Add audio tab with first pass at autodetection of audio driver.
[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,
41         NATIVE
42 } DLLMODE;
43
44 typedef struct _DLLOVERRIDE
45 {
46         char* lpcKey;    /*The actual dll name*/
47         DLLMODE mode;
48 } DLLOVERRIDE, *LPDLLOVERRIDE;
49
50 static LPDLLOVERRIDE CreateDLLOverride(char* lpcKey)
51 {
52         LPDLLOVERRIDE out = HeapAlloc(GetProcessHeap(),0,sizeof(DLLOVERRIDE));
53         out->lpcKey = strdup (lpcKey);
54         return out;
55 }
56
57 static VOID FreeDLLOverride(LPDLLOVERRIDE ldo)
58 {
59         if (ldo->lpcKey)
60                 free(ldo->lpcKey);
61         HeapFree(GetProcessHeap(),0,ldo);
62 }
63
64 typedef struct _APPL
65 {
66         BOOL isGlobal;
67         char* lpcApplication;
68         char* lpcSection; /*Registry section*/
69 } APPL, *LPAPPL;
70
71 static LPAPPL CreateAppl(BOOL isGlobal, char* application, char* section)
72 {
73         LPAPPL out;
74         out = HeapAlloc(GetProcessHeap(),0,sizeof(APPL));
75         out->lpcApplication = strdup(application);
76         out->lpcSection = strdup(section);
77         out->isGlobal = isGlobal;
78         return out;
79 }
80
81 static VOID FreeAppl(LPAPPL lpAppl)
82 {
83         if (lpAppl->lpcApplication)
84                 free(lpAppl->lpcApplication); /* The strings were strdup-ped, so we use "free" */
85         if (lpAppl->lpcSection)
86                 free(lpAppl->lpcSection);
87         HeapFree(GetProcessHeap(),0,lpAppl);
88 }
89
90 typedef struct _ITEMTAG
91 {
92         LPAPPL lpAppl;
93         LPDLLOVERRIDE lpDo;
94 } ITEMTAG, *LPITEMTAG;
95
96 static LPITEMTAG CreateItemTag()
97 {
98         LPITEMTAG out;
99         out = HeapAlloc(GetProcessHeap(),0,sizeof(ITEMTAG));
100         out->lpAppl = 0;
101         out->lpDo = 0;
102         return out;
103 }
104
105 static VOID FreeItemTag(LPITEMTAG lpit)
106 {
107         if (lpit->lpAppl)
108                 FreeAppl(lpit->lpAppl);
109         if (lpit->lpDo)
110                 FreeDLLOverride(lpit->lpDo);
111         HeapFree(GetProcessHeap(),0,lpit);
112 }
113
114 static VOID UpdateDLLList(HWND hDlg, char* dll)
115 {
116         /*Add if it isn't already in*/
117         if (SendDlgItemMessage(hDlg, IDC_DLLLIST, CB_FINDSTRING, 1, (LPARAM) dll) == CB_ERR)
118                 SendDlgItemMessage(hDlg,IDC_DLLLIST,CB_ADDSTRING,0,(LPARAM) dll);
119 }
120
121 static VOID LoadLibrarySettings(LPAPPL appl /*DON'T FREE, treeview will own this*/, HWND hDlg, HWND hwndTV)
122 {
123         HKEY key;
124         int i;
125         DWORD size;
126         DWORD readSize;
127         char name [255];
128         char read [255];
129         LPITEMTAG lpIt;
130         TVINSERTSTRUCT tis;     
131         HTREEITEM hParent;
132         LPDLLOVERRIDE lpdo;
133         
134         WINE_TRACE("opening %s\n", appl->lpcSection);
135         if (RegOpenKey (configKey, appl->lpcSection, &key) == ERROR_SUCCESS)
136         {
137                 i = 0;
138                 size = 255;
139                 readSize = 255;
140                 
141                 lpIt = CreateItemTag();
142                 lpIt->lpAppl = appl;
143
144                 tis.hParent = NULL;
145                 tis.hInsertAfter = TVI_LAST;
146                 tis.u.item.mask = TVIF_TEXT | TVIF_PARAM;
147                 tis.u.item.pszText = appl->lpcApplication;
148                 tis.u.item.lParam = (LPARAM)lpIt;
149                 hParent = TreeView_InsertItem(hwndTV,&tis);
150                 tis.hParent = hParent;
151
152                 while (RegEnumValue(key, i, name, &size, NULL, NULL, read, &readSize) == ERROR_SUCCESS)
153                 {                           
154                         WINE_TRACE("Reading value %s, namely %s\n", name, read);
155                         
156                         lpIt = CreateItemTag();
157                         lpdo = CreateDLLOverride(name);
158                         lpIt->lpDo = lpdo;
159                         tis.u.item.lParam = (LPARAM)lpIt;
160                         tis.u.item.pszText = name;
161                         if (strncmp (read, "built", 5) == 0)
162                                 lpdo->mode = BUILTIN;
163                         else
164                                 lpdo->mode = NATIVE;
165
166                         TreeView_InsertItem(hwndTV,&tis);
167                         UpdateDLLList(hDlg, name);
168                         i ++; size = 255; readSize = 255;
169                 }
170                 RegCloseKey(key);
171         }
172 }
173
174 static VOID SetEnabledDLLControls(HWND dialog, DLGMODE dlgmode)
175 {
176         if (dlgmode == DLL) {
177                 enable(IDC_RAD_BUILTIN);
178                 enable(IDC_RAD_NATIVE);
179                 enable(IDC_DLLS_REMOVEDLL);
180         } else {
181                 disable(IDC_RAD_BUILTIN);
182                 disable(IDC_RAD_NATIVE);
183                 disable(IDC_DLLS_REMOVEDLL);
184         }
185
186         if (dlgmode == APP) {
187                 enable(IDC_DLLS_REMOVEAPP);
188         }
189         else {
190                 disable(IDC_DLLS_REMOVEAPP);
191         }
192 }
193
194 static VOID OnInitLibrariesDlg(HWND hDlg)
195 {
196         HWND hwndTV;
197         LPAPPL lpAppl;
198         HKEY applKey;
199         int i;
200         DWORD size;
201         char appl [255];
202         char lpcKey [255];
203         FILETIME ft;
204
205         hwndTV = GetDlgItem(hDlg,IDC_TREE_DLLS);
206         lpAppl = CreateAppl(TRUE,"Global DLL Overrides", "DllOverrides");
207         LoadLibrarySettings(lpAppl, hDlg, hwndTV);
208         
209         /*And now the application specific stuff:*/
210         if (RegOpenKey(configKey, "AppDefaults", &applKey) == ERROR_SUCCESS) {
211                 i = 0;
212                 size = 255;
213                 while (RegEnumKeyEx(applKey, i, appl, &size, NULL, NULL, NULL, &ft) == ERROR_SUCCESS)
214                 {
215                         sprintf(lpcKey, "AppDefaults\\%s\\DllOverrides", appl);
216                         lpAppl = CreateAppl(FALSE,appl, lpcKey);
217                         LoadLibrarySettings(lpAppl, hDlg, hwndTV);
218                         i++; size = 255;
219                 }
220                 RegCloseKey(applKey);
221         }
222
223         SetEnabledDLLControls(hDlg, GLOBAL);
224 }
225
226 static VOID OnTreeViewChangeItem(HWND hDlg, HWND hTV)
227 {
228         TVITEM ti;
229         LPITEMTAG lpit;
230
231         ti.mask = TVIF_PARAM;
232         ti.hItem = TreeView_GetSelection(hTV);
233         if (TreeView_GetItem (hTV, &ti))
234         {
235                 lpit = (LPITEMTAG) ti.lParam;
236                 if (lpit->lpDo)
237                 {
238                         WINE_TRACE("%s\n", lpit->lpDo->lpcKey);
239                         if (lpit->lpDo->mode == BUILTIN) {
240                                 CheckRadioButton(hDlg, IDC_RAD_BUILTIN, IDC_RAD_NATIVE, IDC_RAD_BUILTIN);
241                         } else {
242                                 CheckRadioButton(hDlg, IDC_RAD_BUILTIN, IDC_RAD_NATIVE, IDC_RAD_NATIVE);
243                         }
244                         SetEnabledDLLControls(hDlg, DLL);
245                 } else {
246                         if (lpit->lpAppl)
247                         {
248                                 if (lpit->lpAppl->isGlobal == TRUE)
249                                         SetEnabledDLLControls(hDlg, GLOBAL);
250                                 else
251                                         SetEnabledDLLControls(hDlg, APP);
252                         }
253                 }
254         }
255 }
256
257 static VOID SetDLLMode(HWND hDlg, DLLMODE mode)
258 {
259         HWND hTV;
260         TVITEM ti;
261         LPITEMTAG lpit;
262         char* cMode;
263         TVITEM tiPar;
264         LPITEMTAG lpitPar;
265
266         hTV = GetDlgItem(hDlg, IDC_TREE_DLLS);
267         ti.mask = TVIF_PARAM;
268         ti.hItem = TreeView_GetSelection(hTV);
269         if (TreeView_GetItem (hTV, &ti))
270         {
271                 lpit = (LPITEMTAG) ti.lParam;
272                 if (lpit->lpDo)
273                 {
274                         lpit->lpDo->mode = mode;
275                         if (mode == NATIVE)
276                                 cMode = "native, builtin";
277                         else
278                                 cMode = "builtin, native";
279
280                         /*Find parent, so we can read registry section*/
281                         tiPar.mask = TVIF_PARAM;
282                         tiPar.hItem = TreeView_GetParent(hTV, ti.hItem);
283                         if (TreeView_GetItem(hTV,&tiPar))
284                         {
285                                 lpitPar = (LPITEMTAG) tiPar.lParam;
286                                 if (lpitPar->lpAppl)
287                                 {
288                                         addTransaction(lpitPar->lpAppl->lpcSection, lpit->lpDo->lpcKey, ACTION_SET, cMode);
289                                 }
290                         }
291                 }
292         }
293 }
294
295 static VOID OnBuiltinClick(HWND hDlg)
296 {
297         SetDLLMode(hDlg, BUILTIN);
298 }
299
300 static VOID OnNativeClick(HWND hDlg)
301 {
302         SetDLLMode(hDlg, NATIVE);
303 }
304
305 static VOID OnTreeViewDeleteItem(NMTREEVIEW* nmt)
306 {
307         FreeItemTag((LPITEMTAG)(nmt->itemOld.lParam));
308 }
309
310 static VOID OnAddDLLClick(HWND hDlg)
311 {
312         HWND hTV;
313         TVITEM ti;
314         LPITEMTAG lpit;
315         LPITEMTAG lpitNew;
316         TVITEM childti;
317         char dll [255];
318         BOOL doAdd;
319         TVINSERTSTRUCT tis;
320
321         hTV = GetDlgItem(hDlg, IDC_TREE_DLLS);
322         ti.mask = TVIF_PARAM;
323         ti.hItem = TreeView_GetSelection(hTV);
324         if (TreeView_GetItem (hTV, &ti))
325         {
326                 lpit = (LPITEMTAG) ti.lParam;
327                 if (lpit->lpDo) { /*Is this a DLL override (that is: a subitem), then find the parent*/
328                         ti.hItem = TreeView_GetParent(hTV, ti.hItem);
329                         if (TreeView_GetItem(hTV,&ti)) {
330                                 lpit = (LPITEMTAG) ti.lParam;
331                         } else return;
332                 }
333         } else return;
334         /*Now we should have an parent item*/
335         if (lpit->lpAppl)
336         {
337                 lpitNew = CreateItemTag();
338                 SendDlgItemMessage(hDlg,IDC_DLLLIST,WM_GETTEXT,(WPARAM)255, (LPARAM) dll);
339                 if (strlen(dll) > 0) {
340                         /*Is the dll already in the list? If so, don't do it*/
341                         doAdd = TRUE;
342                         childti.mask = TVIF_PARAM;
343                         if ((childti.hItem = TreeView_GetNextItem(hTV, ti.hItem, TVGN_CHILD))) {
344                                 /*Retrieved first child*/
345                                 while (TreeView_GetItem (hTV, &childti))
346                                 {
347                                         if (strcmp(((LPITEMTAG)childti.lParam)->lpDo->lpcKey,dll) == 0) {
348                                                 doAdd = FALSE;
349                                                 break;
350                                         }
351                                         childti.hItem = TreeView_GetNextItem(hTV, childti.hItem, TVGN_NEXT);
352                                 }
353                         }
354                         if (doAdd)
355                         {
356                                 lpitNew->lpDo = CreateDLLOverride(dll);
357                                 lpitNew->lpDo->mode = NATIVE;
358                                 tis.hInsertAfter = TVI_LAST;
359                                 tis.u.item.mask = TVIF_TEXT | TVIF_PARAM;
360                                 tis.u.item.pszText = dll;
361                                 tis.u.item.lParam = (LPARAM)lpitNew;
362                                 tis.hParent = ti.hItem;
363                                 TreeView_InsertItem(hTV,&tis);
364                                 UpdateDLLList(hDlg, dll);
365                                 addTransaction(lpit->lpAppl->lpcSection, dll, ACTION_SET, "native, builtin");
366                         } else MessageBox(hDlg, "A DLL with that name is already in this list...", "", MB_OK | MB_ICONINFORMATION);
367                 }
368         } else return;
369 }
370
371 static VOID OnRemoveDLLClick(HWND hDlg)
372 {
373         HWND hTV;
374         TVITEM ti;
375         LPITEMTAG lpit;
376         TVITEM tiPar;
377         LPITEMTAG lpitPar;
378
379         hTV = GetDlgItem(hDlg, IDC_TREE_DLLS);
380         ti.mask = TVIF_PARAM;
381         ti.hItem = TreeView_GetSelection(hTV);
382         if (TreeView_GetItem (hTV, &ti)) {
383                 lpit = (LPITEMTAG) ti.lParam;
384                 if (lpit->lpDo)
385                 {
386                         /*Find parent for section*/
387                         tiPar.mask = TVIF_PARAM;
388                         tiPar.hItem = TreeView_GetParent(hTV, ti.hItem);
389                         if (TreeView_GetItem(hTV,&tiPar))
390                         {
391                                 lpitPar = (LPITEMTAG) tiPar.lParam;
392                                 if (lpitPar->lpAppl)
393                                 {
394                                         addTransaction(lpitPar->lpAppl->lpcSection, lpit->lpDo->lpcKey, ACTION_REMOVE, NULL);
395                                         TreeView_DeleteItem(hTV,ti.hItem);
396                                 }
397                         }
398                 }
399         }
400 }
401
402 static VOID OnAddApplicationClick(HWND hDlg)
403 {
404         char szFileTitle [255];
405         char szFile [255];
406         char lpcKey [255];
407
408         TVINSERTSTRUCT tis;
409         LPITEMTAG lpit;
410         OPENFILENAME ofn = { sizeof(OPENFILENAME),
411                 0, /*hInst*/0, "Wine Programs (*.exe,*.exe.so)\0*.exe;*.exe.so\0", NULL, 0, 0, NULL,
412                 0, NULL, 0, NULL, NULL,
413                 OFN_SHOWHELP, 0, 0, NULL, 0, NULL };
414
415         ofn.lpstrFileTitle = szFileTitle;
416         ofn.lpstrFileTitle[0] = '\0';
417         ofn.nMaxFileTitle = sizeof(szFileTitle);
418         ofn.lpstrFile = szFile;
419         ofn.lpstrFile[0] = '\0';
420         ofn.nMaxFile = sizeof(szFile);
421
422         if (GetOpenFileName(&ofn))
423         {
424                 tis.hParent = NULL;
425                 tis.hInsertAfter = TVI_LAST;
426                 tis.u.item.mask = TVIF_TEXT | TVIF_PARAM;
427                 tis.u.item.pszText = szFileTitle;
428                 lpit = CreateItemTag();
429                 sprintf(lpcKey, "AppDefaults\\%s\\DllOverrides", szFileTitle);
430                 lpit->lpAppl = CreateAppl(FALSE,szFileTitle,lpcKey);
431                 tis.u.item.lParam = (LPARAM)lpit;
432                 TreeView_InsertItem(GetDlgItem(hDlg,IDC_TREE_DLLS), &tis);
433                 setConfigValue(lpcKey,NULL,NULL);
434         }
435 }
436
437 static VOID OnRemoveApplicationClick(HWND hDlg)
438 {
439         HWND hTV;
440         TVITEM ti;
441         LPITEMTAG lpit;
442
443         hTV = GetDlgItem(hDlg, IDC_TREE_DLLS);
444         ti.mask = TVIF_PARAM;
445         ti.hItem = TreeView_GetSelection(hTV);
446         if (TreeView_GetItem (hTV, &ti)) {
447                 lpit = (LPITEMTAG) ti.lParam;
448                 if (lpit->lpAppl)
449                 {
450                         addTransaction(lpit->lpAppl->lpcSection, NULL, ACTION_REMOVE, NULL);
451                         TreeView_DeleteItem(hTV,ti.hItem);
452                 }
453         }
454 }
455
456 INT_PTR CALLBACK
457 LibrariesDlgProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
458 {
459         switch (uMsg)
460         {
461         case WM_INITDIALOG:
462                 OnInitLibrariesDlg(hDlg);
463                 break;  
464         case WM_NOTIFY:
465                 switch (((LPNMHDR)lParam)->code) {
466                 case TVN_SELCHANGED: {
467                                 switch(LOWORD(wParam)) {
468                                 case IDC_TREE_DLLS:
469                                         OnTreeViewChangeItem(hDlg, GetDlgItem(hDlg,IDC_TREE_DLLS));
470                                         break;
471                                 }
472                         }
473                         break;
474                 case TVN_DELETEITEM:
475                         OnTreeViewDeleteItem ((LPNMTREEVIEW)lParam);
476                         break;
477                 }
478                 break;
479         case WM_COMMAND:
480                 switch(HIWORD(wParam)) {
481                 case BN_CLICKED:
482                         switch(LOWORD(wParam)) {
483                         case IDC_RAD_BUILTIN:
484                                 OnBuiltinClick(hDlg);
485                                 break;
486                         case IDC_RAD_NATIVE:
487                                 OnNativeClick(hDlg);
488                                 break;
489                         case IDC_DLLS_ADDAPP:
490                                 OnAddApplicationClick(hDlg);
491                                 break;
492                         case IDC_DLLS_REMOVEAPP:
493                                 OnRemoveApplicationClick(hDlg);
494                                 break;
495                         case IDC_DLLS_ADDDLL:
496                                 OnAddDLLClick(hDlg);
497                                 break;
498                         case IDC_DLLS_REMOVEDLL:
499                                 OnRemoveDLLClick(hDlg);
500                                 break;
501                         }
502                         break;
503                 }
504                 break;
505         }
506
507         return 0;
508 }