Commit | Line | Data |
---|---|---|
0799c1a7 AJ |
1 | /* Control Panel management |
2 | * | |
3 | * Copyright 2001 Eric Pouech | |
4 | * | |
5 | * This library is free software; you can redistribute it and/or | |
6 | * modify it under the terms of the GNU Lesser General Public | |
7 | * License as published by the Free Software Foundation; either | |
8 | * version 2.1 of the License, or (at your option) any later version. | |
9 | * | |
10 | * This library is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 | * Lesser General Public License for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU Lesser General Public | |
16 | * License along with this library; if not, write to the Free Software | |
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
18 | */ | |
92b23186 EP |
19 | |
20 | #include <assert.h> | |
21 | #include <stdio.h> | |
22 | #include <stdlib.h> | |
23 | #include <string.h> | |
24 | ||
25 | #include "winbase.h" | |
26 | #include "wingdi.h" | |
27 | #include "winuser.h" | |
0799c1a7 | 28 | #include "wine/debug.h" |
92b23186 EP |
29 | #include "cpl.h" |
30 | ||
0799c1a7 | 31 | WINE_DEFAULT_DEBUG_CHANNEL(shlctrl); |
92b23186 EP |
32 | |
33 | typedef struct CPlApplet { | |
34 | struct CPlApplet* next; /* linked list */ | |
35 | HWND hWnd; | |
36 | unsigned count; /* number of subprograms */ | |
37 | HMODULE hModule; /* module of loaded applet */ | |
38 | APPLET_PROC proc; /* entry point address */ | |
9a624916 | 39 | NEWCPLINFOA info[1]; /* array of count information. |
92b23186 EP |
40 | * dwSize field is 0 if entry is invalid */ |
41 | } CPlApplet; | |
42 | ||
43 | typedef struct CPanel { | |
44 | CPlApplet* first; /* linked list */ | |
45 | HWND hWnd; | |
46 | unsigned status; | |
47 | CPlApplet* clkApplet; | |
48 | unsigned clkSP; | |
49 | } CPanel; | |
50 | ||
51 | static CPlApplet* Control_UnloadApplet(CPlApplet* applet) | |
52 | { | |
53 | unsigned i; | |
54 | CPlApplet* next; | |
55 | ||
56 | for (i = 0; i < applet->count; i++) { | |
57 | if (!applet->info[i].dwSize) continue; | |
58 | applet->proc(applet->hWnd, CPL_STOP, i, applet->info[i].lData); | |
59 | } | |
60 | if (applet->proc) applet->proc(applet->hWnd, CPL_EXIT, 0L, 0L); | |
61 | FreeLibrary(applet->hModule); | |
62 | next = applet->next; | |
63 | HeapFree(GetProcessHeap(), 0, applet); | |
64 | return next; | |
65 | } | |
66 | ||
67 | static CPlApplet* Control_LoadApplet(HWND hWnd, LPCSTR cmd, CPanel* panel) | |
68 | { | |
69 | CPlApplet* applet; | |
70 | unsigned i; | |
71 | CPLINFO info; | |
72 | ||
73 | if (!(applet = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*applet)))) | |
74 | return applet; | |
75 | ||
76 | applet->hWnd = hWnd; | |
77 | ||
78 | if (!(applet->hModule = LoadLibraryA(cmd))) { | |
79 | WARN("Cannot load control panel applet %s\n", cmd); | |
80 | goto theError; | |
81 | } | |
82 | if (!(applet->proc = (APPLET_PROC)GetProcAddress(applet->hModule, "CPlApplet"))) { | |
83 | WARN("Not a valid control panel applet %s\n", cmd); | |
84 | goto theError; | |
85 | } | |
86 | if (!applet->proc(hWnd, CPL_INIT, 0L, 0L)) { | |
87 | WARN("Init of applet has failed\n"); | |
88 | goto theError; | |
89 | } | |
90 | if ((applet->count = applet->proc(hWnd, CPL_GETCOUNT, 0L, 0L)) == 0) { | |
91 | WARN("No subprogram in applet\n"); | |
92 | goto theError; | |
93 | } | |
94 | ||
9a624916 | 95 | applet = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, applet, |
92b23186 | 96 | sizeof(*applet) + (applet->count - 1) * sizeof(NEWCPLINFOA)); |
9a624916 | 97 | |
92b23186 EP |
98 | for (i = 0; i < applet->count; i++) { |
99 | applet->info[i].dwSize = sizeof(NEWCPLINFOA); | |
100 | /* proc is supposed to return a null value upon success for | |
101 | * CPL_INQUIRE and CPL_NEWINQUIRE | |
102 | * However, real drivers don't seem to behave like this | |
103 | * So, use introspection rather than return value | |
104 | */ | |
105 | applet->proc(hWnd, CPL_NEWINQUIRE, i, (LPARAM)&applet->info[i]); | |
106 | if (applet->info[i].hIcon == 0) { | |
107 | applet->proc(hWnd, CPL_INQUIRE, i, (LPARAM)&info); | |
108 | if (info.idIcon == 0 || info.idName == 0) { | |
109 | WARN("Couldn't get info from sp %u\n", i); | |
110 | applet->info[i].dwSize = 0; | |
111 | } else { | |
112 | /* convert the old data into the new structure */ | |
113 | applet->info[i].dwFlags = 0; | |
114 | applet->info[i].dwHelpContext = 0; | |
115 | applet->info[i].lData = info.lData; | |
9a624916 | 116 | applet->info[i].hIcon = LoadIconA(applet->hModule, |
92b23186 | 117 | MAKEINTRESOURCEA(info.idIcon)); |
9a624916 | 118 | LoadStringA(applet->hModule, info.idName, |
92b23186 | 119 | applet->info[i].szName, sizeof(applet->info[i].szName)); |
9a624916 | 120 | LoadStringA(applet->hModule, info.idInfo, |
92b23186 EP |
121 | applet->info[i].szInfo, sizeof(applet->info[i].szInfo)); |
122 | applet->info[i].szHelpFile[0] = '\0'; | |
123 | } | |
124 | } | |
125 | } | |
126 | ||
127 | applet->next = panel->first; | |
128 | panel->first = applet; | |
129 | ||
130 | return applet; | |
131 | ||
132 | theError: | |
133 | Control_UnloadApplet(applet); | |
134 | return NULL; | |
135 | } | |
136 | ||
137 | static void Control_WndProc_Create(HWND hWnd, const CREATESTRUCTA* cs) | |
138 | { | |
139 | CPanel* panel = (CPanel*)cs->lpCreateParams; | |
140 | ||
141 | SetWindowLongA(hWnd, 0, (LPARAM)panel); | |
142 | panel->status = 0; | |
143 | panel->hWnd = hWnd; | |
144 | } | |
145 | ||
146 | #define XICON 32 | |
147 | #define XSTEP 128 | |
148 | #define YICON 32 | |
149 | #define YSTEP 64 | |
150 | ||
151 | static BOOL Control_Localize(const CPanel* panel, unsigned cx, unsigned cy, | |
152 | CPlApplet** papplet, unsigned* psp) | |
153 | { | |
154 | unsigned i, x = (XSTEP-XICON)/2, y = 0; | |
155 | CPlApplet* applet; | |
156 | RECT rc; | |
9a624916 | 157 | |
92b23186 EP |
158 | GetClientRect(panel->hWnd, &rc); |
159 | for (applet = panel->first; applet; applet = applet = applet->next) { | |
160 | for (i = 0; i < applet->count; i++) { | |
161 | if (!applet->info[i].dwSize) continue; | |
162 | if (x + XSTEP >= rc.right - rc.left) { | |
163 | x = (XSTEP-XICON)/2; | |
164 | y += YSTEP; | |
165 | } | |
166 | if (cx >= x && cx < x + XICON && cy >= y && cy < y + YSTEP) { | |
167 | *papplet = applet; | |
168 | *psp = i; | |
169 | return TRUE; | |
170 | } | |
171 | x += XSTEP; | |
172 | } | |
173 | } | |
174 | return FALSE; | |
175 | } | |
176 | ||
177 | static LRESULT Control_WndProc_Paint(const CPanel* panel, WPARAM wParam) | |
178 | { | |
179 | HDC hdc; | |
180 | PAINTSTRUCT ps; | |
181 | RECT rc, txtRect; | |
182 | unsigned i, x = 0, y = 0; | |
183 | CPlApplet* applet; | |
184 | HGDIOBJ hOldFont; | |
185 | ||
186 | hdc = (wParam) ? (HDC)wParam : BeginPaint(panel->hWnd, &ps); | |
187 | hOldFont = SelectObject(hdc, GetStockObject(ANSI_VAR_FONT)); | |
188 | GetClientRect(panel->hWnd, &rc); | |
189 | for (applet = panel->first; applet; applet = applet = applet->next) { | |
190 | for (i = 0; i < applet->count; i++) { | |
191 | if (x + XSTEP >= rc.right - rc.left) { | |
192 | x = 0; | |
193 | y += YSTEP; | |
194 | } | |
195 | if (!applet->info[i].dwSize) continue; | |
196 | DrawIcon(hdc, x + (XSTEP-XICON)/2, y, applet->info[i].hIcon); | |
197 | txtRect.left = x; | |
198 | txtRect.right = x + XSTEP; | |
199 | txtRect.top = y + YICON; | |
200 | txtRect.bottom = y + YSTEP; | |
9a624916 | 201 | DrawTextA(hdc, applet->info[i].szName, -1, &txtRect, |
92b23186 EP |
202 | DT_CENTER | DT_VCENTER); |
203 | x += XSTEP; | |
204 | } | |
205 | } | |
206 | SelectObject(hdc, hOldFont); | |
207 | if (!wParam) EndPaint(panel->hWnd, &ps); | |
208 | return 0; | |
209 | } | |
210 | ||
211 | static LRESULT Control_WndProc_LButton(CPanel* panel, LPARAM lParam, BOOL up) | |
212 | { | |
213 | unsigned i; | |
214 | CPlApplet* applet; | |
9a624916 | 215 | |
92b23186 EP |
216 | if (Control_Localize(panel, LOWORD(lParam), HIWORD(lParam), &applet, &i)) { |
217 | if (up) { | |
218 | if (panel->clkApplet == applet && panel->clkSP == i) { | |
219 | applet->proc(applet->hWnd, CPL_DBLCLK, i, applet->info[i].lData); | |
220 | } | |
221 | } else { | |
222 | panel->clkApplet = applet; | |
223 | panel->clkSP = i; | |
224 | } | |
225 | } | |
226 | return 0; | |
227 | } | |
228 | ||
9a624916 | 229 | static LRESULT WINAPI Control_WndProc(HWND hWnd, UINT wMsg, |
92b23186 EP |
230 | WPARAM lParam1, LPARAM lParam2) |
231 | { | |
232 | CPanel* panel = (CPanel*)GetWindowLongA(hWnd, 0); | |
233 | ||
234 | if (panel || wMsg == WM_CREATE) { | |
235 | switch (wMsg) { | |
236 | case WM_CREATE: | |
237 | Control_WndProc_Create(hWnd, (CREATESTRUCTA*)lParam2); | |
238 | return 0; | |
239 | case WM_DESTROY: | |
240 | while ((panel->first = Control_UnloadApplet(panel->first))); | |
241 | break; | |
242 | case WM_PAINT: | |
243 | return Control_WndProc_Paint(panel, lParam1); | |
244 | case WM_LBUTTONUP: | |
245 | return Control_WndProc_LButton(panel, lParam2, TRUE); | |
246 | case WM_LBUTTONDOWN: | |
247 | return Control_WndProc_LButton(panel, lParam2, FALSE); | |
248 | /* EPP case WM_COMMAND: */ | |
249 | /* EPP return Control_WndProc_Command(mwi, lParam1, lParam2); */ | |
250 | } | |
251 | } | |
252 | ||
253 | return DefWindowProcA(hWnd, wMsg, lParam1, lParam2); | |
254 | } | |
255 | ||
256 | static void Control_DoInterface(CPanel* panel, HWND hWnd, HINSTANCE hInst) | |
257 | { | |
258 | WNDCLASSA wc; | |
259 | MSG msg; | |
260 | ||
261 | wc.style = CS_HREDRAW|CS_VREDRAW; | |
262 | wc.lpfnWndProc = Control_WndProc; | |
263 | wc.cbClsExtra = 0; | |
264 | wc.cbWndExtra = sizeof(CPlApplet*); | |
265 | wc.hInstance = hInst; | |
266 | wc.hIcon = 0; | |
267 | wc.hCursor = 0; | |
268 | wc.hbrBackground = GetStockObject(WHITE_BRUSH); | |
269 | wc.lpszMenuName = NULL; | |
270 | wc.lpszClassName = "Shell_Control_WndClass"; | |
271 | ||
272 | if (!RegisterClassA(&wc)) return; | |
273 | ||
9a624916 VB |
274 | CreateWindowExA(0, wc.lpszClassName, "Wine Control Panel", |
275 | WS_OVERLAPPEDWINDOW | WS_VISIBLE, | |
276 | CW_USEDEFAULT, CW_USEDEFAULT, | |
277 | CW_USEDEFAULT, CW_USEDEFAULT, | |
92b23186 EP |
278 | hWnd, (HMENU)0, hInst, panel); |
279 | if (!panel->hWnd) return; | |
280 | while (GetMessageA(&msg, panel->hWnd, 0, 0)) { | |
281 | TranslateMessage(&msg); | |
282 | DispatchMessageA(&msg); | |
283 | if (!panel->first) break; | |
284 | } | |
285 | } | |
286 | ||
287 | static void Control_DoWindow(CPanel* panel, HWND hWnd, HINSTANCE hInst) | |
288 | { | |
289 | HANDLE h; | |
290 | WIN32_FIND_DATAA fd; | |
291 | char buffer[MAX_PATH]; | |
292 | ||
293 | /* FIXME: should grab path somewhere from configuration */ | |
294 | if ((h = FindFirstFileA("c:\\windows\\system\\*.cpl", &fd)) != 0) { | |
295 | do { | |
296 | sprintf(buffer, "c:\\windows\\system\\%s", fd.cFileName); | |
297 | Control_LoadApplet(hWnd, buffer, panel); | |
298 | } while (FindNextFileA(h, &fd)); | |
299 | FindClose(h); | |
300 | } | |
301 | ||
302 | if (panel->first) Control_DoInterface(panel, hWnd, hInst); | |
303 | } | |
304 | ||
305 | static void Control_DoLaunch(CPanel* panel, HWND hWnd, LPCSTR cmd) | |
306 | /* forms to parse: | |
307 | * foo.cpl,@sp,str | |
308 | * foo.cpl,@sp | |
309 | * foo.cpl,,str | |
310 | * foo.cpl @sp | |
311 | * foo.cpl str | |
312 | */ | |
313 | { | |
314 | char* buffer; | |
315 | char* beg = NULL; | |
316 | char* end; | |
317 | char ch; | |
318 | unsigned sp = 0; | |
319 | char* extraPmts = NULL; | |
320 | ||
321 | buffer = HeapAlloc(GetProcessHeap(), 0, strlen(cmd) + 1); | |
322 | if (!buffer) return; | |
323 | ||
324 | end = strcpy(buffer, cmd); | |
325 | ||
326 | for (;;) { | |
327 | ch = *end; | |
328 | if (ch == ' ' || ch == ',' || ch == '\0') { | |
329 | *end = '\0'; | |
330 | if (beg) { | |
331 | if (*beg == '@') { | |
332 | sp = atoi(beg + 1); | |
333 | } else if (*beg == '\0') { | |
334 | sp = 0; | |
335 | } else { | |
336 | extraPmts = beg; | |
337 | } | |
338 | } | |
339 | if (ch == '\0') break; | |
340 | beg = end + 1; | |
341 | if (ch == ' ') while (end[1] == ' ') end++; | |
342 | } | |
343 | end++; | |
344 | } | |
345 | Control_LoadApplet(hWnd, buffer, panel); | |
346 | ||
347 | if (panel->first) { | |
348 | CPlApplet* applet = panel->first; | |
349 | ||
350 | assert(applet && applet->next == NULL); | |
351 | if (sp >= applet->count) { | |
352 | WARN("Out of bounds (%u >= %u), setting to 0\n", sp, applet->count); | |
353 | sp = 0; | |
354 | } | |
355 | if (applet->info[sp].dwSize) { | |
356 | if (!applet->proc(applet->hWnd, CPL_STARTWPARMSA, sp, (LPARAM)extraPmts)) | |
357 | applet->proc(applet->hWnd, CPL_DBLCLK, sp, applet->info[sp].lData); | |
358 | } | |
359 | Control_UnloadApplet(applet); | |
360 | } | |
361 | HeapFree(GetProcessHeap(), 0, buffer); | |
362 | } | |
363 | ||
364 | /************************************************************************* | |
8b216b3d | 365 | * Control_RunDLL [SHELL32.@] |
92b23186 EP |
366 | * |
367 | */ | |
368 | void WINAPI Control_RunDLL(HWND hWnd, HINSTANCE hInst, LPCSTR cmd, DWORD nCmdShow) | |
369 | { | |
370 | CPanel panel; | |
371 | ||
9a624916 | 372 | TRACE("(0x%08x, 0x%08lx, %s, 0x%08lx)\n", |
92b23186 EP |
373 | hWnd, (DWORD)hInst, debugstr_a(cmd), nCmdShow); |
374 | ||
375 | memset(&panel, 0, sizeof(panel)); | |
376 | ||
377 | if (!cmd || !*cmd) { | |
378 | Control_DoWindow(&panel, hWnd, hInst); | |
379 | } else { | |
380 | Control_DoLaunch(&panel, hWnd, cmd); | |
381 | } | |
382 | } | |
383 | ||
384 | /************************************************************************* | |
8b216b3d | 385 | * Control_FillCache_RunDLL [SHELL32.@] |
92b23186 EP |
386 | * |
387 | */ | |
388 | HRESULT WINAPI Control_FillCache_RunDLL(HWND hWnd, HANDLE hModule, DWORD w, DWORD x) | |
389 | { | |
390 | FIXME("0x%04x 0x%04x 0x%04lx 0x%04lx stub\n",hWnd, hModule, w, x); | |
391 | return 0; | |
392 | } | |
393 | ||
394 | /************************************************************************* | |
395 | * RunDLL_CallEntry16 [SHELL32.122] | |
69249675 | 396 | * the name is probably wrong |
92b23186 EP |
397 | */ |
398 | HRESULT WINAPI RunDLL_CallEntry16(DWORD v, DWORD w, DWORD x, DWORD y, DWORD z) | |
399 | { | |
400 | FIXME("0x%04lx 0x%04lx 0x%04lx 0x%04lx 0x%04lx stub\n",v,w,x,y,z); | |
401 | return 0; | |
402 | } | |
69249675 | 403 | |
19d66cc1 PS |
404 | /************************************************************************* |
405 | * CallCPLEntry16 [SHELL32.166] | |
406 | * | |
69249675 AM |
407 | * called by desk.cpl on "Advanced" with: |
408 | * hMod("DeskCp16.Dll"), pFunc("CplApplet"), 0, 1, 0xc, 0 | |
409 | * | |
410 | */ | |
411 | DWORD WINAPI CallCPLEntry16(HMODULE hMod, FARPROC pFunc, DWORD dw3, DWORD dw4, DWORD dw5, DWORD dw6) | |
412 | { | |
413 | FIXME("(%04x, %p, %08lx, %08lx, %08lx, %08lx): stub.\n", hMod, pFunc, dw3, dw4, dw5, dw6); | |
414 | return 0x0deadbee; | |
415 | } |