|  | /* Control Panel management | 
|  | * | 
|  | * Copyright 2001 Eric Pouech | 
|  | * | 
|  | * 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 | 
|  | */ | 
|  |  | 
|  | #include <assert.h> | 
|  | #include <stdarg.h> | 
|  | #include <stdio.h> | 
|  | #include <stdlib.h> | 
|  | #include <string.h> | 
|  |  | 
|  | #include "windef.h" | 
|  | #include "winbase.h" | 
|  | #include "wingdi.h" | 
|  | #include "winuser.h" | 
|  | #include "wine/winbase16.h" | 
|  | #include "wownt32.h" | 
|  | #include "wine/debug.h" | 
|  | #include "cpl.h" | 
|  | #include "wine/unicode.h" | 
|  |  | 
|  | #define NO_SHLWAPI_REG | 
|  | #include "shlwapi.h" | 
|  |  | 
|  | #include "cpanel.h" | 
|  |  | 
|  | WINE_DEFAULT_DEBUG_CHANNEL(shlctrl); | 
|  |  | 
|  | CPlApplet*	Control_UnloadApplet(CPlApplet* applet) | 
|  | { | 
|  | unsigned	i; | 
|  | CPlApplet*	next; | 
|  |  | 
|  | for (i = 0; i < applet->count; i++) { | 
|  | if (!applet->info[i].dwSize) continue; | 
|  | applet->proc(applet->hWnd, CPL_STOP, i, applet->info[i].lData); | 
|  | } | 
|  | if (applet->proc) applet->proc(applet->hWnd, CPL_EXIT, 0L, 0L); | 
|  | FreeLibrary(applet->hModule); | 
|  | next = applet->next; | 
|  | HeapFree(GetProcessHeap(), 0, applet); | 
|  | return next; | 
|  | } | 
|  |  | 
|  | CPlApplet*	Control_LoadApplet(HWND hWnd, LPCWSTR cmd, CPanel* panel) | 
|  | { | 
|  | CPlApplet*	applet; | 
|  | unsigned 	i; | 
|  | CPLINFO	info; | 
|  | NEWCPLINFOW newinfo; | 
|  |  | 
|  | if (!(applet = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*applet)))) | 
|  | return applet; | 
|  |  | 
|  | applet->hWnd = hWnd; | 
|  |  | 
|  | if (!(applet->hModule = LoadLibraryW(cmd))) { | 
|  | WARN("Cannot load control panel applet %s\n", debugstr_w(cmd)); | 
|  | goto theError; | 
|  | } | 
|  | if (!(applet->proc = (APPLET_PROC)GetProcAddress(applet->hModule, "CPlApplet"))) { | 
|  | WARN("Not a valid control panel applet %s\n", debugstr_w(cmd)); | 
|  | goto theError; | 
|  | } | 
|  | if (!applet->proc(hWnd, CPL_INIT, 0L, 0L)) { | 
|  | WARN("Init of applet has failed\n"); | 
|  | goto theError; | 
|  | } | 
|  | if ((applet->count = applet->proc(hWnd, CPL_GETCOUNT, 0L, 0L)) == 0) { | 
|  | WARN("No subprogram in applet\n"); | 
|  | goto theError; | 
|  | } | 
|  |  | 
|  | applet = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, applet, | 
|  | sizeof(*applet) + (applet->count - 1) * sizeof(NEWCPLINFOW)); | 
|  |  | 
|  | for (i = 0; i < applet->count; i++) { | 
|  | ZeroMemory(&newinfo, sizeof(newinfo)); | 
|  | newinfo.dwSize = sizeof(NEWCPLINFOA); | 
|  | applet->info[i].dwSize = sizeof(NEWCPLINFOW); | 
|  | /* proc is supposed to return a null value upon success for | 
|  | * CPL_INQUIRE and CPL_NEWINQUIRE | 
|  | * However, real drivers don't seem to behave like this | 
|  | * So, use introspection rather than return value | 
|  | */ | 
|  | applet->proc(hWnd, CPL_NEWINQUIRE, i, (LPARAM)&newinfo); | 
|  | if (newinfo.hIcon == 0) { | 
|  | applet->proc(hWnd, CPL_INQUIRE, i, (LPARAM)&info); | 
|  | if (info.idIcon == 0 || info.idName == 0) { | 
|  | WARN("Couldn't get info from sp %u\n", i); | 
|  | applet->info[i].dwSize = 0; | 
|  | } else { | 
|  | /* convert the old data into the new structure */ | 
|  | applet->info[i].dwFlags = 0; | 
|  | applet->info[i].dwHelpContext = 0; | 
|  | applet->info[i].lData = info.lData; | 
|  | applet->info[i].hIcon = LoadIconW(applet->hModule, | 
|  | MAKEINTRESOURCEW(info.idIcon)); | 
|  | LoadStringW(applet->hModule, info.idName, | 
|  | applet->info[i].szName, sizeof(applet->info[i].szName) / sizeof(WCHAR)); | 
|  | LoadStringW(applet->hModule, info.idInfo, | 
|  | applet->info[i].szInfo, sizeof(applet->info[i].szInfo) / sizeof(WCHAR)); | 
|  | applet->info[i].szHelpFile[0] = '\0'; | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | CopyMemory(&applet->info[i], &newinfo, newinfo.dwSize); | 
|  | if (newinfo.dwSize != sizeof(NEWCPLINFOW)) | 
|  | { | 
|  | applet->info[i].dwSize = sizeof(NEWCPLINFOW); | 
|  | MultiByteToWideChar(CP_ACP, 0, ((LPNEWCPLINFOA)&newinfo)->szName, | 
|  | sizeof(((LPNEWCPLINFOA)&newinfo)->szName) / sizeof(CHAR), | 
|  | applet->info[i].szName, | 
|  | sizeof(applet->info[i].szName) / sizeof(WCHAR)); | 
|  | MultiByteToWideChar(CP_ACP, 0, ((LPNEWCPLINFOA)&newinfo)->szInfo, | 
|  | sizeof(((LPNEWCPLINFOA)&newinfo)->szInfo) / sizeof(CHAR), | 
|  | applet->info[i].szInfo, | 
|  | sizeof(applet->info[i].szInfo) / sizeof(WCHAR)); | 
|  | MultiByteToWideChar(CP_ACP, 0, ((LPNEWCPLINFOA)&newinfo)->szHelpFile, | 
|  | sizeof(((LPNEWCPLINFOA)&newinfo)->szHelpFile) / sizeof(CHAR), | 
|  | applet->info[i].szHelpFile, | 
|  | sizeof(applet->info[i].szHelpFile) / sizeof(WCHAR)); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | applet->next = panel->first; | 
|  | panel->first = applet; | 
|  |  | 
|  | return applet; | 
|  |  | 
|  | theError: | 
|  | Control_UnloadApplet(applet); | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | static void 	 Control_WndProc_Create(HWND hWnd, const CREATESTRUCTA* cs) | 
|  | { | 
|  | CPanel*	panel = (CPanel*)cs->lpCreateParams; | 
|  |  | 
|  | SetWindowLongPtrA(hWnd, 0, (LONG_PTR)panel); | 
|  | panel->status = 0; | 
|  | panel->hWnd = hWnd; | 
|  | } | 
|  |  | 
|  | #define	XICON	32 | 
|  | #define XSTEP	128 | 
|  | #define	YICON	32 | 
|  | #define YSTEP	64 | 
|  |  | 
|  | static BOOL	Control_Localize(const CPanel* panel, unsigned cx, unsigned cy, | 
|  | CPlApplet** papplet, unsigned* psp) | 
|  | { | 
|  | unsigned	i, x = (XSTEP-XICON)/2, y = 0; | 
|  | CPlApplet*	applet; | 
|  | RECT	rc; | 
|  |  | 
|  | GetClientRect(panel->hWnd, &rc); | 
|  | for (applet = panel->first; applet; applet = applet->next) { | 
|  | for (i = 0; i < applet->count; i++) { | 
|  | if (!applet->info[i].dwSize) continue; | 
|  | if (x + XSTEP >= rc.right - rc.left) { | 
|  | x = (XSTEP-XICON)/2; | 
|  | y += YSTEP; | 
|  | } | 
|  | if (cx >= x && cx < x + XICON && cy >= y && cy < y + YSTEP) { | 
|  | *papplet = applet; | 
|  | *psp = i; | 
|  | return TRUE; | 
|  | } | 
|  | x += XSTEP; | 
|  | } | 
|  | } | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | static LRESULT Control_WndProc_Paint(const CPanel* panel, WPARAM wParam) | 
|  | { | 
|  | HDC		hdc; | 
|  | PAINTSTRUCT	ps; | 
|  | RECT	rc, txtRect; | 
|  | unsigned	i, x = 0, y = 0; | 
|  | CPlApplet*	applet; | 
|  | HGDIOBJ	hOldFont; | 
|  |  | 
|  | hdc = (wParam) ? (HDC)wParam : BeginPaint(panel->hWnd, &ps); | 
|  | hOldFont = SelectObject(hdc, GetStockObject(ANSI_VAR_FONT)); | 
|  | GetClientRect(panel->hWnd, &rc); | 
|  | for (applet = panel->first; applet; applet = applet->next) { | 
|  | for (i = 0; i < applet->count; i++) { | 
|  | if (x + XSTEP >= rc.right - rc.left) { | 
|  | x = 0; | 
|  | y += YSTEP; | 
|  | } | 
|  | if (!applet->info[i].dwSize) continue; | 
|  | DrawIcon(hdc, x + (XSTEP-XICON)/2, y, applet->info[i].hIcon); | 
|  | txtRect.left = x; | 
|  | txtRect.right = x + XSTEP; | 
|  | txtRect.top = y + YICON; | 
|  | txtRect.bottom = y + YSTEP; | 
|  | DrawTextW(hdc, applet->info[i].szName, -1, &txtRect, | 
|  | DT_CENTER | DT_VCENTER); | 
|  | x += XSTEP; | 
|  | } | 
|  | } | 
|  | SelectObject(hdc, hOldFont); | 
|  | if (!wParam) EndPaint(panel->hWnd, &ps); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static LRESULT Control_WndProc_LButton(CPanel* panel, LPARAM lParam, BOOL up) | 
|  | { | 
|  | unsigned	i; | 
|  | CPlApplet*	applet; | 
|  |  | 
|  | if (Control_Localize(panel, LOWORD(lParam), HIWORD(lParam), &applet, &i)) { | 
|  | if (up) { | 
|  | if (panel->clkApplet == applet && panel->clkSP == i) { | 
|  | applet->proc(applet->hWnd, CPL_DBLCLK, i, applet->info[i].lData); | 
|  | } | 
|  | } else { | 
|  | panel->clkApplet = applet; | 
|  | panel->clkSP = i; | 
|  | } | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static LRESULT WINAPI	Control_WndProc(HWND hWnd, UINT wMsg, | 
|  | WPARAM lParam1, LPARAM lParam2) | 
|  | { | 
|  | CPanel*	panel = (CPanel*)GetWindowLongPtrA(hWnd, 0); | 
|  |  | 
|  | if (panel || wMsg == WM_CREATE) { | 
|  | switch (wMsg) { | 
|  | case WM_CREATE: | 
|  | Control_WndProc_Create(hWnd, (CREATESTRUCTA*)lParam2); | 
|  | return 0; | 
|  | case WM_DESTROY: | 
|  | { | 
|  | CPlApplet*	applet = panel->first; | 
|  | while (applet) | 
|  | applet = Control_UnloadApplet(applet); | 
|  | } | 
|  | PostQuitMessage(0); | 
|  | break; | 
|  | case WM_PAINT: | 
|  | return Control_WndProc_Paint(panel, lParam1); | 
|  | case WM_LBUTTONUP: | 
|  | return Control_WndProc_LButton(panel, lParam2, TRUE); | 
|  | case WM_LBUTTONDOWN: | 
|  | return Control_WndProc_LButton(panel, lParam2, FALSE); | 
|  | /* EPP       case WM_COMMAND: */ | 
|  | /* EPP 	 return Control_WndProc_Command(mwi, lParam1, lParam2); */ | 
|  | } | 
|  | } | 
|  |  | 
|  | return DefWindowProcA(hWnd, wMsg, lParam1, lParam2); | 
|  | } | 
|  |  | 
|  | static void    Control_DoInterface(CPanel* panel, HWND hWnd, HINSTANCE hInst) | 
|  | { | 
|  | WNDCLASSA	wc; | 
|  | MSG		msg; | 
|  | const CHAR* appName = "Wine Control Panel"; | 
|  | wc.style = CS_HREDRAW|CS_VREDRAW; | 
|  | wc.lpfnWndProc = Control_WndProc; | 
|  | wc.cbClsExtra = 0; | 
|  | wc.cbWndExtra = sizeof(CPlApplet*); | 
|  | wc.hInstance = hInst; | 
|  | wc.hIcon = 0; | 
|  | wc.hCursor = 0; | 
|  | wc.hbrBackground = GetStockObject(WHITE_BRUSH); | 
|  | wc.lpszMenuName = NULL; | 
|  | wc.lpszClassName = "Shell_Control_WndClass"; | 
|  |  | 
|  | if (!RegisterClassA(&wc)) return; | 
|  |  | 
|  | CreateWindowExA(0, wc.lpszClassName, appName, | 
|  | WS_OVERLAPPEDWINDOW | WS_VISIBLE, | 
|  | CW_USEDEFAULT, CW_USEDEFAULT, | 
|  | CW_USEDEFAULT, CW_USEDEFAULT, | 
|  | hWnd, NULL, hInst, panel); | 
|  | if (!panel->hWnd) return; | 
|  |  | 
|  | if (!panel->first) { | 
|  | /* FIXME appName & message should be localized  */ | 
|  | MessageBoxA(panel->hWnd, "Cannot load any applets", appName, MB_OK); | 
|  | return; | 
|  | } | 
|  |  | 
|  | while (GetMessageA(&msg, panel->hWnd, 0, 0)) { | 
|  | TranslateMessage(&msg); | 
|  | DispatchMessageA(&msg); | 
|  | } | 
|  | } | 
|  |  | 
|  | static	void	Control_DoWindow(CPanel* panel, HWND hWnd, HINSTANCE hInst) | 
|  | { | 
|  | HANDLE		h; | 
|  | WIN32_FIND_DATAW	fd; | 
|  | WCHAR		buffer[MAX_PATH]; | 
|  | static const WCHAR wszAllCpl[] = {'*','.','c','p','l',0}; | 
|  | WCHAR *p; | 
|  |  | 
|  | GetSystemDirectoryW( buffer, MAX_PATH ); | 
|  | p = buffer + strlenW(buffer); | 
|  | *p++ = '\\'; | 
|  | lstrcpyW(p, wszAllCpl); | 
|  |  | 
|  | if ((h = FindFirstFileW(buffer, &fd)) != INVALID_HANDLE_VALUE) { | 
|  | do { | 
|  | lstrcpyW(p, fd.cFileName); | 
|  | Control_LoadApplet(hWnd, buffer, panel); | 
|  | } while (FindNextFileW(h, &fd)); | 
|  | FindClose(h); | 
|  | } | 
|  |  | 
|  | Control_DoInterface(panel, hWnd, hInst); | 
|  | } | 
|  |  | 
|  | static	void	Control_DoLaunch(CPanel* panel, HWND hWnd, LPCWSTR wszCmd) | 
|  | /* forms to parse: | 
|  | *	foo.cpl,@sp,str | 
|  | *	foo.cpl,@sp | 
|  | *	foo.cpl,,str | 
|  | *	foo.cpl @sp | 
|  | *	foo.cpl str | 
|  | *   "a path\foo.cpl" | 
|  | */ | 
|  | { | 
|  | LPWSTR	buffer; | 
|  | LPWSTR	beg = NULL; | 
|  | LPWSTR	end; | 
|  | WCHAR	ch; | 
|  | LPWSTR       ptr; | 
|  | unsigned 	sp = 0; | 
|  | LPWSTR	extraPmts = NULL; | 
|  | int        quoted = 0; | 
|  |  | 
|  | buffer = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(wszCmd) + 1) * sizeof(*wszCmd)); | 
|  | if (!buffer) return; | 
|  |  | 
|  | end = lstrcpyW(buffer, wszCmd); | 
|  |  | 
|  | for (;;) { | 
|  | ch = *end; | 
|  | if (ch == '"') quoted = !quoted; | 
|  | if (!quoted && (ch == ' ' || ch == ',' || ch == '\0')) { | 
|  | *end = '\0'; | 
|  | if (beg) { | 
|  | if (*beg == '@') { | 
|  | sp = atoiW(beg + 1); | 
|  | } else if (*beg == '\0') { | 
|  | sp = 0; | 
|  | } else { | 
|  | extraPmts = beg; | 
|  | } | 
|  | } | 
|  | if (ch == '\0') break; | 
|  | beg = end + 1; | 
|  | if (ch == ' ') while (end[1] == ' ') end++; | 
|  | } | 
|  | end++; | 
|  | } | 
|  | while ((ptr = StrChrW(buffer, '"'))) | 
|  | memmove(ptr, ptr+1, lstrlenW(ptr)*sizeof(WCHAR)); | 
|  |  | 
|  | TRACE("cmd %s, extra %s, sp %d\n", debugstr_w(buffer), debugstr_w(extraPmts), sp); | 
|  |  | 
|  | Control_LoadApplet(hWnd, buffer, panel); | 
|  |  | 
|  | if (panel->first) { | 
|  | CPlApplet* applet = panel->first; | 
|  |  | 
|  | assert(applet && applet->next == NULL); | 
|  | if (sp >= applet->count) { | 
|  | WARN("Out of bounds (%u >= %u), setting to 0\n", sp, applet->count); | 
|  | sp = 0; | 
|  | } | 
|  | if (applet->info[sp].dwSize) { | 
|  | if (!applet->proc(applet->hWnd, CPL_STARTWPARMSA, sp, (LPARAM)extraPmts)) | 
|  | applet->proc(applet->hWnd, CPL_DBLCLK, sp, applet->info[sp].lData); | 
|  | } | 
|  | Control_UnloadApplet(applet); | 
|  | } | 
|  | HeapFree(GetProcessHeap(), 0, buffer); | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * Control_RunDLLW			[SHELL32.@] | 
|  | * | 
|  | */ | 
|  | void WINAPI Control_RunDLLW(HWND hWnd, HINSTANCE hInst, LPCWSTR cmd, DWORD nCmdShow) | 
|  | { | 
|  | CPanel	panel; | 
|  |  | 
|  | TRACE("(%p, %p, %s, 0x%08lx)\n", | 
|  | hWnd, hInst, debugstr_w(cmd), nCmdShow); | 
|  |  | 
|  | memset(&panel, 0, sizeof(panel)); | 
|  |  | 
|  | if (!cmd || !*cmd) { | 
|  | Control_DoWindow(&panel, hWnd, hInst); | 
|  | } else { | 
|  | Control_DoLaunch(&panel, hWnd, cmd); | 
|  | } | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * Control_RunDLLA			[SHELL32.@] | 
|  | * | 
|  | */ | 
|  | void WINAPI Control_RunDLLA(HWND hWnd, HINSTANCE hInst, LPCSTR cmd, DWORD nCmdShow) | 
|  | { | 
|  | DWORD len = MultiByteToWideChar(CP_ACP, 0, cmd, -1, NULL, 0 ); | 
|  | LPWSTR wszCmd = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); | 
|  | if (wszCmd && MultiByteToWideChar(CP_ACP, 0, cmd, -1, wszCmd, len )) | 
|  | { | 
|  | Control_RunDLLW(hWnd, hInst, wszCmd, nCmdShow); | 
|  | } | 
|  | HeapFree(GetProcessHeap(), 0, wszCmd); | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * Control_FillCache_RunDLLW			[SHELL32.@] | 
|  | * | 
|  | */ | 
|  | HRESULT WINAPI Control_FillCache_RunDLLW(HWND hWnd, HANDLE hModule, DWORD w, DWORD x) | 
|  | { | 
|  | FIXME("%p %p 0x%08lx 0x%08lx stub\n", hWnd, hModule, w, x); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * Control_FillCache_RunDLLA			[SHELL32.@] | 
|  | * | 
|  | */ | 
|  | HRESULT WINAPI Control_FillCache_RunDLLA(HWND hWnd, HANDLE hModule, DWORD w, DWORD x) | 
|  | { | 
|  | return Control_FillCache_RunDLLW(hWnd, hModule, w, x); | 
|  | } | 
|  |  | 
|  |  | 
|  | /************************************************************************* | 
|  | * RunDLL_CallEntry16				[SHELL32.122] | 
|  | * the name is probably wrong | 
|  | */ | 
|  | void WINAPI RunDLL_CallEntry16( DWORD proc, HWND hwnd, HINSTANCE inst, | 
|  | LPCSTR cmdline, INT cmdshow ) | 
|  | { | 
|  | WORD args[5]; | 
|  | SEGPTR cmdline_seg; | 
|  |  | 
|  | TRACE( "proc %lx hwnd %p inst %p cmdline %s cmdshow %d\n", | 
|  | proc, hwnd, inst, debugstr_a(cmdline), cmdshow ); | 
|  |  | 
|  | cmdline_seg = MapLS( cmdline ); | 
|  | args[4] = HWND_16(hwnd); | 
|  | args[3] = MapHModuleLS(inst); | 
|  | args[2] = SELECTOROF(cmdline_seg); | 
|  | args[1] = OFFSETOF(cmdline_seg); | 
|  | args[0] = cmdshow; | 
|  | WOWCallback16Ex( proc, WCB16_PASCAL, sizeof(args), args, NULL ); | 
|  | UnMapLS( cmdline_seg ); | 
|  | } | 
|  |  | 
|  | /************************************************************************* | 
|  | * CallCPLEntry16				[SHELL32.166] | 
|  | * | 
|  | * called by desk.cpl on "Advanced" with: | 
|  | * hMod("DeskCp16.Dll"), pFunc("CplApplet"), 0, 1, 0xc, 0 | 
|  | * | 
|  | */ | 
|  | DWORD WINAPI CallCPLEntry16(HMODULE hMod, FARPROC pFunc, DWORD dw3, DWORD dw4, DWORD dw5, DWORD dw6) | 
|  | { | 
|  | FIXME("(%p, %p, %08lx, %08lx, %08lx, %08lx): stub.\n", hMod, pFunc, dw3, dw4, dw5, dw6); | 
|  | return 0x0deadbee; | 
|  | } |