/* 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 #include #include #include #include "winbase.h" #include "wingdi.h" #include "winuser.h" #include "wine/debug.h" #include "cpl.h" WINE_DEFAULT_DEBUG_CHANNEL(shlctrl); typedef struct CPlApplet { struct CPlApplet* next; /* linked list */ HWND hWnd; unsigned count; /* number of subprograms */ HMODULE hModule; /* module of loaded applet */ APPLET_PROC proc; /* entry point address */ NEWCPLINFOA info[1]; /* array of count information. * dwSize field is 0 if entry is invalid */ } CPlApplet; typedef struct CPanel { CPlApplet* first; /* linked list */ HWND hWnd; unsigned status; CPlApplet* clkApplet; unsigned clkSP; } CPanel; static 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; } static CPlApplet* Control_LoadApplet(HWND hWnd, LPCSTR cmd, CPanel* panel) { CPlApplet* applet; unsigned i; CPLINFO info; if (!(applet = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*applet)))) return applet; applet->hWnd = hWnd; if (!(applet->hModule = LoadLibraryA(cmd))) { WARN("Cannot load control panel applet %s\n", cmd); goto theError; } if (!(applet->proc = (APPLET_PROC)GetProcAddress(applet->hModule, "CPlApplet"))) { WARN("Not a valid control panel applet %s\n", 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(NEWCPLINFOA)); for (i = 0; i < applet->count; i++) { applet->info[i].dwSize = sizeof(NEWCPLINFOA); /* 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)&applet->info[i]); if (applet->info[i].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 = LoadIconA(applet->hModule, MAKEINTRESOURCEA(info.idIcon)); LoadStringA(applet->hModule, info.idName, applet->info[i].szName, sizeof(applet->info[i].szName)); LoadStringA(applet->hModule, info.idInfo, applet->info[i].szInfo, sizeof(applet->info[i].szInfo)); applet->info[i].szHelpFile[0] = '\0'; } } } 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; SetWindowLongA(hWnd, 0, (LPARAM)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 = 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 = 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; DrawTextA(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*)GetWindowLongA(hWnd, 0); if (panel || wMsg == WM_CREATE) { switch (wMsg) { case WM_CREATE: Control_WndProc_Create(hWnd, (CREATESTRUCTA*)lParam2); return 0; case WM_DESTROY: while ((panel->first = Control_UnloadApplet(panel->first))); 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; 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, "Wine Control Panel", WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, hWnd, NULL, hInst, panel); if (!panel->hWnd) return; while (GetMessageA(&msg, panel->hWnd, 0, 0)) { TranslateMessage(&msg); DispatchMessageA(&msg); if (!panel->first) break; } } static void Control_DoWindow(CPanel* panel, HWND hWnd, HINSTANCE hInst) { HANDLE h; WIN32_FIND_DATAA fd; char buffer[MAX_PATH]; /* FIXME: should grab path somewhere from configuration */ if ((h = FindFirstFileA("c:\\windows\\system\\*.cpl", &fd)) != 0) { do { sprintf(buffer, "c:\\windows\\system\\%s", fd.cFileName); Control_LoadApplet(hWnd, buffer, panel); } while (FindNextFileA(h, &fd)); FindClose(h); } if (panel->first) Control_DoInterface(panel, hWnd, hInst); } static void Control_DoLaunch(CPanel* panel, HWND hWnd, LPCSTR cmd) /* forms to parse: * foo.cpl,@sp,str * foo.cpl,@sp * foo.cpl,,str * foo.cpl @sp * foo.cpl str * "a path\foo.cpl" */ { char* buffer; char* beg = NULL; char* end; char ch; char* ptr; unsigned sp = 0; char* extraPmts = NULL; int quoted = 0; buffer = HeapAlloc(GetProcessHeap(), 0, strlen(cmd) + 1); if (!buffer) return; end = strcpy(buffer, cmd); for (;;) { ch = *end; if (ch == '"') quoted = !quoted; if (!quoted && (ch == ' ' || ch == ',' || ch == '\0')) { *end = '\0'; if (beg) { if (*beg == '@') { sp = atoi(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 = strchr(buffer, (int) '"'))) memmove(ptr, ptr+1, strlen(ptr)); TRACE("cmd %s, extra %s, sp %d\n", buffer, debugstr_a(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_RunDLL [SHELL32.@] * */ void WINAPI Control_RunDLL(HWND hWnd, HINSTANCE hInst, LPCSTR cmd, DWORD nCmdShow) { CPanel panel; TRACE("(%p, 0x%08lx, %s, 0x%08lx)\n", hWnd, (DWORD)hInst, debugstr_a(cmd), nCmdShow); memset(&panel, 0, sizeof(panel)); if (!cmd || !*cmd) { Control_DoWindow(&panel, hWnd, hInst); } else { Control_DoLaunch(&panel, hWnd, cmd); } } /************************************************************************* * Control_FillCache_RunDLL [SHELL32.@] * */ HRESULT WINAPI Control_FillCache_RunDLL(HWND hWnd, HANDLE hModule, DWORD w, DWORD x) { FIXME("%p %p 0x%04lx 0x%04lx stub\n", hWnd, hModule, w, x); return 0; } /************************************************************************* * RunDLL_CallEntry16 [SHELL32.122] * the name is probably wrong */ HRESULT WINAPI RunDLL_CallEntry16(DWORD v, DWORD w, DWORD x, DWORD y, DWORD z) { FIXME("0x%04lx 0x%04lx 0x%04lx 0x%04lx 0x%04lx stub\n",v,w,x,y,z); return 0; } /************************************************************************* * 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; }