| /* | |
| * WineCfg libraries tabsheet | |
| * | |
| * Copyright 2004 Robert van Herk | |
| * | |
| * This library is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU Lesser General Public | |
| * License as published by the Free Software Foundation; either | |
| * version 2.1 of the License, or (at your option) any later version. | |
| * | |
| * This library is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| * Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; if not, write to the Free Software | |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
| * | |
| */ | |
| #define NONAMELESSUNION | |
| #include <windows.h> | |
| #include <commdlg.h> | |
| #include <wine/debug.h> | |
| #include <stdio.h> | |
| #include "winecfg.h" | |
| #include "resource.h" | |
| WINE_DEFAULT_DEBUG_CHANNEL(winecfg); | |
| typedef enum _DLGMODE | |
| { | |
| DLL, | |
| APP, | |
| GLOBAL, | |
| } DLGMODE; | |
| typedef enum _DLLMODE { | |
| BUILTIN_NATIVE, | |
| NATIVE_BUILTIN, | |
| BUILTIN, | |
| NATIVE, | |
| DISABLE, | |
| UNKNOWN /*Special value indicating an erronous DLL override mode*/ | |
| } DLLMODE; | |
| static void removeSpaces(char* in, char* out) | |
| { | |
| int i,j; | |
| j = 0; | |
| for (i = 0; i < strlen(in); i++) | |
| { | |
| if (in[i] != ' ') | |
| { | |
| out[j] = in[i]; | |
| j++; | |
| } | |
| } | |
| out[j] = 0; | |
| } | |
| static DLLMODE Str2DLLMode(char* c) | |
| { | |
| /*Parse a string into a DLLMode*/ | |
| char* d = HeapAlloc(GetProcessHeap(), 0, sizeof(c)); | |
| removeSpaces(c,d); | |
| if (strcmp (d, "builtin,native") == 0) { | |
| return BUILTIN_NATIVE; | |
| } else | |
| if (strcmp (d, "native,builtin") == 0) { | |
| return NATIVE_BUILTIN; | |
| } else | |
| if (strcmp (d, "native") == 0){ | |
| return NATIVE; | |
| } else | |
| if (strcmp (d, "builtin") == 0) { | |
| return BUILTIN; | |
| } else | |
| if (strcmp (d, "") == 0) { | |
| return DISABLE; | |
| } else | |
| return UNKNOWN; | |
| } | |
| static char* DLLMode2Str(DLLMODE mode) | |
| { | |
| char* res; | |
| switch (mode) { | |
| case NATIVE: | |
| res = "native"; | |
| break; | |
| case BUILTIN: | |
| res = "builtin"; | |
| break; | |
| case NATIVE_BUILTIN: | |
| res = "native, builtin"; | |
| break; | |
| case BUILTIN_NATIVE: | |
| res = "builtin, native"; | |
| break; | |
| case DISABLE: | |
| res = ""; | |
| break; | |
| default: | |
| res = "unknown"; | |
| } | |
| return strdup(res); | |
| } | |
| typedef struct _DLLOVERRIDE | |
| { | |
| char* lpcKey; /*The actual dll name*/ | |
| DLLMODE mode; | |
| } DLLOVERRIDE, *LPDLLOVERRIDE; | |
| static LPDLLOVERRIDE CreateDLLOverride(char* lpcKey) | |
| { | |
| LPDLLOVERRIDE out = HeapAlloc(GetProcessHeap(),0,sizeof(DLLOVERRIDE)); | |
| out->lpcKey = strdup (lpcKey); | |
| return out; | |
| } | |
| static VOID FreeDLLOverride(LPDLLOVERRIDE ldo) | |
| { | |
| if (ldo->lpcKey) | |
| free(ldo->lpcKey); | |
| HeapFree(GetProcessHeap(),0,ldo); | |
| } | |
| typedef struct _APPL | |
| { | |
| BOOL isGlobal; | |
| char* lpcApplication; | |
| char* lpcSection; /*Registry section*/ | |
| } APPL, *LPAPPL; | |
| static LPAPPL CreateAppl(BOOL isGlobal, char* application, char* section) | |
| { | |
| LPAPPL out; | |
| out = HeapAlloc(GetProcessHeap(),0,sizeof(APPL)); | |
| out->lpcApplication = strdup(application); | |
| out->lpcSection = strdup(section); | |
| out->isGlobal = isGlobal; | |
| return out; | |
| } | |
| static VOID FreeAppl(LPAPPL lpAppl) | |
| { | |
| if (lpAppl->lpcApplication) | |
| free(lpAppl->lpcApplication); /* The strings were strdup-ped, so we use "free" */ | |
| if (lpAppl->lpcSection) | |
| free(lpAppl->lpcSection); | |
| HeapFree(GetProcessHeap(),0,lpAppl); | |
| } | |
| typedef struct _ITEMTAG | |
| { | |
| LPAPPL lpAppl; | |
| LPDLLOVERRIDE lpDo; | |
| } ITEMTAG, *LPITEMTAG; | |
| static LPITEMTAG CreateItemTag() | |
| { | |
| LPITEMTAG out; | |
| out = HeapAlloc(GetProcessHeap(),0,sizeof(ITEMTAG)); | |
| out->lpAppl = 0; | |
| out->lpDo = 0; | |
| return out; | |
| } | |
| static VOID FreeItemTag(LPITEMTAG lpit) | |
| { | |
| if (lpit->lpAppl) | |
| FreeAppl(lpit->lpAppl); | |
| if (lpit->lpDo) | |
| FreeDLLOverride(lpit->lpDo); | |
| HeapFree(GetProcessHeap(),0,lpit); | |
| } | |
| static VOID UpdateDLLList(HWND hDlg, char* dll) | |
| { | |
| /*Add if it isn't already in*/ | |
| if (SendDlgItemMessage(hDlg, IDC_DLLLIST, CB_FINDSTRING, 1, (LPARAM) dll) == CB_ERR) | |
| SendDlgItemMessage(hDlg,IDC_DLLLIST,CB_ADDSTRING,0,(LPARAM) dll); | |
| } | |
| static VOID LoadLibrarySettings(LPAPPL appl /*DON'T FREE, treeview will own this*/, HWND hDlg, HWND hwndTV) | |
| { | |
| HKEY key; | |
| int i; | |
| DWORD size; | |
| DWORD readSize; | |
| char name [255]; | |
| char read [255]; | |
| LPITEMTAG lpIt; | |
| TVINSERTSTRUCT tis; | |
| HTREEITEM hParent; | |
| LPDLLOVERRIDE lpdo; | |
| WINE_TRACE("opening %s\n", appl->lpcSection); | |
| if (RegOpenKey (configKey, appl->lpcSection, &key) == ERROR_SUCCESS) | |
| { | |
| i = 0; | |
| size = 255; | |
| readSize = 255; | |
| lpIt = CreateItemTag(); | |
| lpIt->lpAppl = appl; | |
| tis.hParent = NULL; | |
| tis.hInsertAfter = TVI_LAST; | |
| tis.u.item.mask = TVIF_TEXT | TVIF_PARAM; | |
| tis.u.item.pszText = appl->lpcApplication; | |
| tis.u.item.lParam = (LPARAM)lpIt; | |
| hParent = TreeView_InsertItem(hwndTV,&tis); | |
| tis.hParent = hParent; | |
| while (RegEnumValue(key, i, name, &size, NULL, NULL, read, &readSize) == ERROR_SUCCESS) | |
| { | |
| WINE_TRACE("Reading value %s, namely %s\n", name, read); | |
| lpIt = CreateItemTag(); | |
| lpdo = CreateDLLOverride(name); | |
| lpIt->lpDo = lpdo; | |
| tis.u.item.lParam = (LPARAM)lpIt; | |
| tis.u.item.pszText = name; | |
| lpdo->mode = Str2DLLMode(read); | |
| TreeView_InsertItem(hwndTV,&tis); | |
| UpdateDLLList(hDlg, name); | |
| i ++; size = 255; readSize = 255; | |
| } | |
| RegCloseKey(key); | |
| } | |
| } | |
| static VOID SetEnabledDLLControls(HWND dialog, DLGMODE dlgmode) | |
| { | |
| if (dlgmode == DLL) { | |
| enable(IDC_RAD_BUILTIN); | |
| enable(IDC_RAD_NATIVE); | |
| enable(IDC_RAD_BUILTIN_NATIVE); | |
| enable(IDC_RAD_NATIVE_BUILTIN); | |
| enable(IDC_RAD_DISABLE); | |
| enable(IDC_DLLS_REMOVEDLL); | |
| } else { | |
| disable(IDC_RAD_BUILTIN); | |
| disable(IDC_RAD_NATIVE); | |
| disable(IDC_RAD_BUILTIN_NATIVE); | |
| disable(IDC_RAD_NATIVE_BUILTIN); | |
| disable(IDC_RAD_DISABLE); | |
| disable(IDC_DLLS_REMOVEDLL); | |
| } | |
| if (dlgmode == APP) { | |
| enable(IDC_DLLS_REMOVEAPP); | |
| } | |
| else { | |
| disable(IDC_DLLS_REMOVEAPP); | |
| } | |
| } | |
| static VOID OnInitLibrariesDlg(HWND hDlg) | |
| { | |
| HWND hwndTV; | |
| LPAPPL lpAppl; | |
| HKEY applKey; | |
| int i; | |
| DWORD size; | |
| char appl [255]; | |
| char lpcKey [255]; | |
| FILETIME ft; | |
| hwndTV = GetDlgItem(hDlg,IDC_TREE_DLLS); | |
| lpAppl = CreateAppl(TRUE,"Global DLL Overrides", "DllOverrides"); | |
| LoadLibrarySettings(lpAppl, hDlg, hwndTV); | |
| /*And now the application specific stuff:*/ | |
| if (RegOpenKey(configKey, "AppDefaults", &applKey) == ERROR_SUCCESS) { | |
| i = 0; | |
| size = 255; | |
| while (RegEnumKeyEx(applKey, i, appl, &size, NULL, NULL, NULL, &ft) == ERROR_SUCCESS) | |
| { | |
| sprintf(lpcKey, "AppDefaults\\%s\\DllOverrides", appl); | |
| lpAppl = CreateAppl(FALSE,appl, lpcKey); | |
| LoadLibrarySettings(lpAppl, hDlg, hwndTV); | |
| i++; size = 255; | |
| } | |
| RegCloseKey(applKey); | |
| } | |
| SetEnabledDLLControls(hDlg, GLOBAL); | |
| } | |
| static VOID OnTreeViewChangeItem(HWND hDlg, HWND hTV) | |
| { | |
| TVITEM ti; | |
| LPITEMTAG lpit; | |
| int buttonId; | |
| ti.mask = TVIF_PARAM; | |
| ti.hItem = TreeView_GetSelection(hTV); | |
| if (TreeView_GetItem (hTV, &ti)) | |
| { | |
| lpit = (LPITEMTAG) ti.lParam; | |
| if (lpit->lpDo) | |
| { | |
| WINE_TRACE("%s\n", lpit->lpDo->lpcKey); | |
| buttonId = IDC_RAD_BUILTIN; | |
| switch (lpit->lpDo->mode) | |
| { | |
| case NATIVE: | |
| buttonId = IDC_RAD_NATIVE; | |
| break; | |
| case BUILTIN: | |
| buttonId = IDC_RAD_BUILTIN; | |
| break; | |
| case NATIVE_BUILTIN: | |
| buttonId = IDC_RAD_NATIVE_BUILTIN; | |
| break; | |
| case BUILTIN_NATIVE: | |
| buttonId = IDC_RAD_BUILTIN_NATIVE; | |
| break; | |
| case DISABLE: | |
| buttonId = IDC_RAD_DISABLE; | |
| break; | |
| case UNKNOWN: | |
| buttonId = -1; | |
| break; | |
| } | |
| CheckRadioButton(hDlg, IDC_RAD_BUILTIN, IDC_RAD_DISABLE, buttonId); | |
| SetEnabledDLLControls(hDlg, DLL); | |
| } else { | |
| if (lpit->lpAppl) | |
| { | |
| if (lpit->lpAppl->isGlobal == TRUE) | |
| SetEnabledDLLControls(hDlg, GLOBAL); | |
| else | |
| SetEnabledDLLControls(hDlg, APP); | |
| } | |
| } | |
| } | |
| } | |
| static VOID SetDLLMode(HWND hDlg, DLLMODE mode) | |
| { | |
| HWND hTV; | |
| TVITEM ti; | |
| LPITEMTAG lpit; | |
| char* cMode; | |
| TVITEM tiPar; | |
| LPITEMTAG lpitPar; | |
| hTV = GetDlgItem(hDlg, IDC_TREE_DLLS); | |
| ti.mask = TVIF_PARAM; | |
| ti.hItem = TreeView_GetSelection(hTV); | |
| if (TreeView_GetItem (hTV, &ti)) | |
| { | |
| lpit = (LPITEMTAG) ti.lParam; | |
| if (lpit->lpDo) | |
| { | |
| lpit->lpDo->mode = mode; | |
| cMode = DLLMode2Str (mode); | |
| /*Find parent, so we can read registry section*/ | |
| tiPar.mask = TVIF_PARAM; | |
| tiPar.hItem = TreeView_GetParent(hTV, ti.hItem); | |
| if (TreeView_GetItem(hTV,&tiPar)) | |
| { | |
| lpitPar = (LPITEMTAG) tiPar.lParam; | |
| if (lpitPar->lpAppl) | |
| { | |
| addTransaction(lpitPar->lpAppl->lpcSection, lpit->lpDo->lpcKey, ACTION_SET, cMode); | |
| } | |
| } | |
| free(cMode); | |
| } | |
| } | |
| } | |
| static VOID OnBuiltinClick(HWND hDlg) | |
| { | |
| SetDLLMode(hDlg, BUILTIN); | |
| } | |
| static VOID OnNativeClick(HWND hDlg) | |
| { | |
| SetDLLMode(hDlg, NATIVE); | |
| } | |
| static VOID OnBuiltinNativeClick(HWND hDlg) | |
| { | |
| SetDLLMode(hDlg, BUILTIN_NATIVE); | |
| } | |
| static VOID OnNativeBuiltinClick(HWND hDlg) | |
| { | |
| SetDLLMode(hDlg, NATIVE_BUILTIN); | |
| } | |
| static VOID OnDisableClick(HWND hDlg) | |
| { | |
| SetDLLMode(hDlg, DISABLE); | |
| } | |
| static VOID OnTreeViewDeleteItem(NMTREEVIEW* nmt) | |
| { | |
| FreeItemTag((LPITEMTAG)(nmt->itemOld.lParam)); | |
| } | |
| static VOID OnAddDLLClick(HWND hDlg) | |
| { | |
| HWND hTV; | |
| TVITEM ti; | |
| LPITEMTAG lpit; | |
| LPITEMTAG lpitNew; | |
| TVITEM childti; | |
| char dll [255]; | |
| BOOL doAdd; | |
| TVINSERTSTRUCT tis; | |
| hTV = GetDlgItem(hDlg, IDC_TREE_DLLS); | |
| ti.mask = TVIF_PARAM; | |
| ti.hItem = TreeView_GetSelection(hTV); | |
| if (TreeView_GetItem (hTV, &ti)) | |
| { | |
| lpit = (LPITEMTAG) ti.lParam; | |
| if (lpit->lpDo) { /*Is this a DLL override (that is: a subitem), then find the parent*/ | |
| ti.hItem = TreeView_GetParent(hTV, ti.hItem); | |
| if (TreeView_GetItem(hTV,&ti)) { | |
| lpit = (LPITEMTAG) ti.lParam; | |
| } else return; | |
| } | |
| } else return; | |
| /*Now we should have an parent item*/ | |
| if (lpit->lpAppl) | |
| { | |
| lpitNew = CreateItemTag(); | |
| SendDlgItemMessage(hDlg,IDC_DLLLIST,WM_GETTEXT,(WPARAM)255, (LPARAM) dll); | |
| if (strlen(dll) > 0) { | |
| /*Is the dll already in the list? If so, don't do it*/ | |
| doAdd = TRUE; | |
| childti.mask = TVIF_PARAM; | |
| if ((childti.hItem = TreeView_GetNextItem(hTV, ti.hItem, TVGN_CHILD))) { | |
| /*Retrieved first child*/ | |
| while (TreeView_GetItem (hTV, &childti)) | |
| { | |
| if (strcmp(((LPITEMTAG)childti.lParam)->lpDo->lpcKey,dll) == 0) { | |
| doAdd = FALSE; | |
| break; | |
| } | |
| childti.hItem = TreeView_GetNextItem(hTV, childti.hItem, TVGN_NEXT); | |
| } | |
| } | |
| if (doAdd) | |
| { | |
| lpitNew->lpDo = CreateDLLOverride(dll); | |
| lpitNew->lpDo->mode = NATIVE; | |
| tis.hInsertAfter = TVI_LAST; | |
| tis.u.item.mask = TVIF_TEXT | TVIF_PARAM; | |
| tis.u.item.pszText = dll; | |
| tis.u.item.lParam = (LPARAM)lpitNew; | |
| tis.hParent = ti.hItem; | |
| TreeView_InsertItem(hTV,&tis); | |
| UpdateDLLList(hDlg, dll); | |
| addTransaction(lpit->lpAppl->lpcSection, dll, ACTION_SET, "native"); | |
| } else MessageBox(hDlg, "A DLL with that name is already in this list...", "", MB_OK | MB_ICONINFORMATION); | |
| } | |
| } else return; | |
| } | |
| static VOID OnRemoveDLLClick(HWND hDlg) | |
| { | |
| HWND hTV; | |
| TVITEM ti; | |
| LPITEMTAG lpit; | |
| TVITEM tiPar; | |
| LPITEMTAG lpitPar; | |
| hTV = GetDlgItem(hDlg, IDC_TREE_DLLS); | |
| ti.mask = TVIF_PARAM; | |
| ti.hItem = TreeView_GetSelection(hTV); | |
| if (TreeView_GetItem (hTV, &ti)) { | |
| lpit = (LPITEMTAG) ti.lParam; | |
| if (lpit->lpDo) | |
| { | |
| /*Find parent for section*/ | |
| tiPar.mask = TVIF_PARAM; | |
| tiPar.hItem = TreeView_GetParent(hTV, ti.hItem); | |
| if (TreeView_GetItem(hTV,&tiPar)) | |
| { | |
| lpitPar = (LPITEMTAG) tiPar.lParam; | |
| if (lpitPar->lpAppl) | |
| { | |
| addTransaction(lpitPar->lpAppl->lpcSection, lpit->lpDo->lpcKey, ACTION_REMOVE, NULL); | |
| TreeView_DeleteItem(hTV,ti.hItem); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| static VOID OnAddApplicationClick(HWND hDlg) | |
| { | |
| char szFileTitle [255]; | |
| char szFile [255]; | |
| char lpcKey [255]; | |
| TVINSERTSTRUCT tis; | |
| LPITEMTAG lpit; | |
| OPENFILENAME ofn = { sizeof(OPENFILENAME), | |
| 0, /*hInst*/0, "Wine Programs (*.exe,*.exe.so)\0*.exe;*.exe.so\0", NULL, 0, 0, NULL, | |
| 0, NULL, 0, NULL, NULL, | |
| OFN_SHOWHELP, 0, 0, NULL, 0, NULL }; | |
| ofn.lpstrFileTitle = szFileTitle; | |
| ofn.lpstrFileTitle[0] = '\0'; | |
| ofn.nMaxFileTitle = sizeof(szFileTitle); | |
| ofn.lpstrFile = szFile; | |
| ofn.lpstrFile[0] = '\0'; | |
| ofn.nMaxFile = sizeof(szFile); | |
| if (GetOpenFileName(&ofn)) | |
| { | |
| tis.hParent = NULL; | |
| tis.hInsertAfter = TVI_LAST; | |
| tis.u.item.mask = TVIF_TEXT | TVIF_PARAM; | |
| tis.u.item.pszText = szFileTitle; | |
| lpit = CreateItemTag(); | |
| sprintf(lpcKey, "AppDefaults\\%s\\DllOverrides", szFileTitle); | |
| lpit->lpAppl = CreateAppl(FALSE,szFileTitle,lpcKey); | |
| tis.u.item.lParam = (LPARAM)lpit; | |
| TreeView_InsertItem(GetDlgItem(hDlg,IDC_TREE_DLLS), &tis); | |
| setConfigValue(lpcKey,NULL,NULL); | |
| } | |
| } | |
| static VOID OnRemoveApplicationClick(HWND hDlg) | |
| { | |
| HWND hTV; | |
| TVITEM ti; | |
| LPITEMTAG lpit; | |
| hTV = GetDlgItem(hDlg, IDC_TREE_DLLS); | |
| ti.mask = TVIF_PARAM; | |
| ti.hItem = TreeView_GetSelection(hTV); | |
| if (TreeView_GetItem (hTV, &ti)) { | |
| lpit = (LPITEMTAG) ti.lParam; | |
| if (lpit->lpAppl) | |
| { | |
| addTransaction(lpit->lpAppl->lpcSection, NULL, ACTION_REMOVE, NULL); | |
| TreeView_DeleteItem(hTV,ti.hItem); | |
| } | |
| } | |
| } | |
| INT_PTR CALLBACK | |
| LibrariesDlgProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) | |
| { | |
| switch (uMsg) | |
| { | |
| case WM_INITDIALOG: | |
| OnInitLibrariesDlg(hDlg); | |
| break; | |
| case WM_NOTIFY: | |
| switch (((LPNMHDR)lParam)->code) { | |
| case TVN_SELCHANGED: { | |
| switch(LOWORD(wParam)) { | |
| case IDC_TREE_DLLS: | |
| OnTreeViewChangeItem(hDlg, GetDlgItem(hDlg,IDC_TREE_DLLS)); | |
| break; | |
| } | |
| } | |
| break; | |
| case TVN_DELETEITEM: | |
| OnTreeViewDeleteItem ((LPNMTREEVIEW)lParam); | |
| break; | |
| } | |
| break; | |
| case WM_COMMAND: | |
| switch(HIWORD(wParam)) { | |
| case BN_CLICKED: | |
| switch(LOWORD(wParam)) { | |
| case IDC_RAD_BUILTIN: | |
| OnBuiltinClick(hDlg); | |
| break; | |
| case IDC_RAD_NATIVE: | |
| OnNativeClick(hDlg); | |
| break; | |
| case IDC_RAD_BUILTIN_NATIVE: | |
| OnBuiltinNativeClick(hDlg); | |
| break; | |
| case IDC_RAD_NATIVE_BUILTIN: | |
| OnNativeBuiltinClick(hDlg); | |
| break; | |
| case IDC_RAD_DISABLE: | |
| OnDisableClick(hDlg); | |
| break; | |
| case IDC_DLLS_ADDAPP: | |
| OnAddApplicationClick(hDlg); | |
| break; | |
| case IDC_DLLS_REMOVEAPP: | |
| OnRemoveApplicationClick(hDlg); | |
| break; | |
| case IDC_DLLS_ADDDLL: | |
| OnAddDLLClick(hDlg); | |
| break; | |
| case IDC_DLLS_REMOVEDLL: | |
| OnRemoveDLLClick(hDlg); | |
| break; | |
| } | |
| break; | |
| } | |
| break; | |
| } | |
| return 0; | |
| } |