Revert an unwanted change from r66575.
[reactos.git] / reactos / base / shell / shell32 / wine / control.c
1 /* Control Panel management
2 *
3 * Copyright 2001 Eric Pouech
4 * Copyright 2008 Owen Rudge
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 */
20
21 #include <assert.h>
22
23 #define WIN32_NO_STATUS
24 #define _INC_WINDOWS
25
26 #include <windef.h>
27 #include <winbase.h>
28 #define NO_SHLWAPI_REG
29 #include <shlwapi.h>
30 #include <wine/debug.h>
31
32 #include "cpanel.h"
33
34 WINE_DEFAULT_DEBUG_CHANNEL(shlctrl);
35
36 CPlApplet* Control_UnloadApplet(CPlApplet* applet)
37 {
38 unsigned i;
39 CPlApplet* next;
40
41 for (i = 0; i < applet->count; i++)
42 {
43 if (!applet->info[i].dwSize)
44 continue;
45 applet->proc(applet->hWnd, CPL_STOP, i, applet->info[i].lData);
46 }
47 if (applet->proc)
48 applet->proc(applet->hWnd, CPL_EXIT, 0L, 0L);
49
50 FreeLibrary(applet->hModule);
51 next = applet->next;
52 HeapFree(GetProcessHeap(), 0, applet->cmd);
53 HeapFree(GetProcessHeap(), 0, applet);
54 return next;
55 }
56
57 CPlApplet* Control_LoadApplet(HWND hWnd, LPCWSTR cmd, CPanel* panel)
58 {
59 CPlApplet* applet;
60 DWORD len;
61 unsigned i;
62 CPLINFO info;
63 NEWCPLINFOW newinfo;
64
65 if (!(applet = (CPlApplet *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*applet))))
66 return applet;
67
68 len = ExpandEnvironmentStringsW(cmd, NULL, 0);
69 if (len > 0)
70 {
71 if (!(applet->cmd = HeapAlloc(GetProcessHeap(), 0, (len+1) * sizeof(WCHAR))))
72 {
73 WARN("Cannot allocate memory for applet path\n");
74 goto theError;
75 }
76 ExpandEnvironmentStringsW(cmd, applet->cmd, len+1);
77 }
78 else
79 {
80 WARN("Cannot expand applet path\n");
81 goto theError;
82 }
83
84 applet->hWnd = hWnd;
85
86 if (!(applet->hModule = LoadLibraryW(applet->cmd)))
87 {
88 WARN("Cannot load control panel applet %s\n", debugstr_w(applet->cmd));
89 goto theError;
90 }
91 if (!(applet->proc = (APPLET_PROC)GetProcAddress(applet->hModule, "CPlApplet")))
92 {
93 WARN("Not a valid control panel applet %s\n", debugstr_w(applet->cmd));
94 goto theError;
95 }
96 if (!applet->proc(hWnd, CPL_INIT, 0L, 0L))
97 {
98 WARN("Init of applet has failed\n");
99 goto theError;
100 }
101 if ((applet->count = applet->proc(hWnd, CPL_GETCOUNT, 0L, 0L)) == 0)
102 {
103 WARN("No subprogram in applet\n");
104 goto theError;
105 }
106
107 applet = (CPlApplet *)HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, applet,
108 sizeof(*applet) + (applet->count - 1) * sizeof(NEWCPLINFOW));
109
110 for (i = 0; i < applet->count; i++)
111 {
112 ZeroMemory(&newinfo, sizeof(newinfo));
113 newinfo.dwSize = sizeof(NEWCPLINFOW);
114 applet->info[i].dwSize = sizeof(NEWCPLINFOW);
115 /* proc is supposed to return a null value upon success for
116 * CPL_INQUIRE and CPL_NEWINQUIRE
117 * However, real drivers don't seem to behave like this
118 * So, use introspection rather than return value
119 */
120 applet->proc(hWnd, CPL_NEWINQUIRE, i, (LPARAM)&newinfo);
121 if (newinfo.hIcon == 0)
122 {
123 applet->proc(hWnd, CPL_INQUIRE, i, (LPARAM)&info);
124 if (info.idIcon == 0 || info.idName == 0)
125 {
126 WARN("Couldn't get info from sp %u\n", i);
127 applet->info[i].dwSize = 0;
128 }
129 else
130 {
131 /* convert the old data into the new structure */
132 applet->info[i].dwFlags = 0;
133 applet->info[i].dwHelpContext = 0;
134 applet->info[i].lData = info.lData;
135 applet->info[i].hIcon = LoadIconW(applet->hModule,
136 MAKEINTRESOURCEW(info.idIcon));
137 LoadStringW(applet->hModule, info.idName,
138 applet->info[i].szName, sizeof(applet->info[i].szName) / sizeof(WCHAR));
139 LoadStringW(applet->hModule, info.idInfo,
140 applet->info[i].szInfo, sizeof(applet->info[i].szInfo) / sizeof(WCHAR));
141 applet->info[i].szHelpFile[0] = '\0';
142 }
143 }
144 else
145 {
146 CopyMemory(&applet->info[i], &newinfo, newinfo.dwSize);
147 if (newinfo.dwSize != sizeof(NEWCPLINFOW))
148 {
149 applet->info[i].dwSize = sizeof(NEWCPLINFOW);
150 lstrcpyW(applet->info[i].szName, newinfo.szName);
151 lstrcpyW(applet->info[i].szInfo, newinfo.szInfo);
152 lstrcpyW(applet->info[i].szHelpFile, newinfo.szHelpFile);
153 }
154 }
155 }
156
157 applet->next = panel->first;
158 panel->first = applet;
159
160 return applet;
161
162 theError:
163 Control_UnloadApplet(applet);
164 return NULL;
165 }
166
167 static void Control_WndProc_Create(HWND hWnd, const CREATESTRUCTW* cs)
168 {
169 CPanel* panel = (CPanel*)cs->lpCreateParams;
170
171 SetWindowLongPtrW(hWnd, 0, (LONG_PTR)panel);
172 panel->status = 0;
173 panel->hWnd = hWnd;
174 }
175
176 #define XICON 32
177 #define XSTEP 128
178 #define YICON 32
179 #define YSTEP 64
180
181 static BOOL Control_Localize(const CPanel* panel, int cx, int cy,
182 CPlApplet** papplet, unsigned* psp)
183 {
184 unsigned int i;
185 int x = (XSTEP-XICON)/2, y = 0;
186 CPlApplet* applet;
187 RECT rc;
188
189 GetClientRect(panel->hWnd, &rc);
190 for (applet = panel->first; applet; applet = applet->next)
191 {
192 for (i = 0; i < applet->count; i++)
193 {
194 if (!applet->info[i].dwSize)
195 continue;
196 if (x + XSTEP >= rc.right - rc.left)
197 {
198 x = (XSTEP-XICON)/2;
199 y += YSTEP;
200 }
201 if (cx >= x && cx < x + XICON && cy >= y && cy < y + YSTEP)
202 {
203 *papplet = applet;
204 *psp = i;
205 return TRUE;
206 }
207 x += XSTEP;
208 }
209 }
210 return FALSE;
211 }
212
213 static LRESULT Control_WndProc_Paint(const CPanel* panel, WPARAM wParam)
214 {
215 HDC hdc;
216 PAINTSTRUCT ps;
217 RECT rc, txtRect;
218 unsigned int i;
219 int x = 0, y = 0;
220 CPlApplet* applet;
221 HGDIOBJ hOldFont;
222
223 hdc = (wParam) ? (HDC)wParam : BeginPaint(panel->hWnd, &ps);
224 hOldFont = SelectObject(hdc, GetStockObject(ANSI_VAR_FONT));
225 GetClientRect(panel->hWnd, &rc);
226
227 for (applet = panel->first; applet; applet = applet->next)
228 {
229 for (i = 0; i < applet->count; i++)
230 {
231 if (x + XSTEP >= rc.right - rc.left)
232 {
233 x = 0;
234 y += YSTEP;
235 }
236 if (!applet->info[i].dwSize)
237 continue;
238 DrawIcon(hdc, x + (XSTEP-XICON)/2, y, applet->info[i].hIcon);
239 txtRect.left = x;
240 txtRect.right = x + XSTEP;
241 txtRect.top = y + YICON;
242 txtRect.bottom = y + YSTEP;
243 DrawTextW(hdc, applet->info[i].szName, -1, &txtRect,
244 DT_CENTER | DT_VCENTER);
245 x += XSTEP;
246 }
247 }
248
249 SelectObject(hdc, hOldFont);
250 if (!wParam)
251 EndPaint(panel->hWnd, &ps);
252
253 return 0;
254 }
255
256 static LRESULT Control_WndProc_LButton(CPanel* panel, LPARAM lParam, BOOL up)
257 {
258 unsigned i;
259 CPlApplet* applet;
260
261 if (Control_Localize(panel, (short)LOWORD(lParam), (short)HIWORD(lParam), &applet, &i))
262 {
263 if (up)
264 {
265 if (panel->clkApplet == applet && panel->clkSP == i)
266 {
267 applet->proc(applet->hWnd, CPL_DBLCLK, i, applet->info[i].lData);
268 }
269 }
270 else
271 {
272 panel->clkApplet = applet;
273 panel->clkSP = i;
274 }
275 }
276 return 0;
277 }
278
279 static LRESULT WINAPI Control_WndProc(HWND hWnd, UINT wMsg,
280 WPARAM lParam1, LPARAM lParam2)
281 {
282 CPanel* panel = (CPanel*)GetWindowLongPtrW(hWnd, 0);
283
284 if (panel || wMsg == WM_CREATE)
285 {
286 switch (wMsg)
287 {
288 case WM_CREATE:
289 Control_WndProc_Create(hWnd, (CREATESTRUCTW*)lParam2);
290 return 0;
291 case WM_DESTROY:
292 {
293 CPlApplet *applet = panel->first;
294 while (applet)
295 applet = Control_UnloadApplet(applet);
296
297 PostQuitMessage(0);
298 break;
299 }
300 case WM_PAINT:
301 return Control_WndProc_Paint(panel, lParam1);
302 case WM_LBUTTONUP:
303 return Control_WndProc_LButton(panel, lParam2, TRUE);
304 case WM_LBUTTONDOWN:
305 return Control_WndProc_LButton(panel, lParam2, FALSE);
306 /* EPP case WM_COMMAND: */
307 /* EPP return Control_WndProc_Command(mwi, lParam1, lParam2); */
308 }
309 }
310
311 return DefWindowProcW(hWnd, wMsg, lParam1, lParam2);
312 }
313
314 static void Control_DoInterface(CPanel* panel, HWND hWnd, HINSTANCE hInst)
315 {
316 WNDCLASSW wc;
317 MSG msg;
318 const WCHAR* appName = L"ReactOS Control Panel";
319 wc.style = CS_HREDRAW|CS_VREDRAW;
320 wc.lpfnWndProc = Control_WndProc;
321 wc.cbClsExtra = 0;
322 wc.cbWndExtra = sizeof(CPlApplet*);
323 wc.hInstance = hInst;
324 wc.hIcon = 0;
325 wc.hCursor = 0;
326 wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
327 wc.lpszMenuName = NULL;
328 wc.lpszClassName = L"Shell_Control_WndClass";
329
330 if (!RegisterClassW(&wc)) return;
331
332 CreateWindowExW(0, wc.lpszClassName, appName,
333 WS_OVERLAPPEDWINDOW | WS_VISIBLE,
334 CW_USEDEFAULT, CW_USEDEFAULT,
335 CW_USEDEFAULT, CW_USEDEFAULT,
336 hWnd, NULL, hInst, panel);
337
338 if (!panel->hWnd)
339 return;
340
341 if (!panel->first)
342 {
343 /* FIXME appName & message should be localized */
344 MessageBoxW(panel->hWnd, L"Cannot load any applets", appName, MB_OK);
345 return;
346 }
347
348 while (GetMessageW(&msg, panel->hWnd, 0, 0))
349 {
350 TranslateMessage(&msg);
351 DispatchMessageW(&msg);
352 }
353 }
354
355 static void Control_DoWindow(CPanel *panel, HWND hWnd, HINSTANCE hInst)
356 {
357 HANDLE hFind;
358 WIN32_FIND_DATAW wfd;
359 WCHAR wszPath[MAX_PATH];
360 WCHAR *Ptr = wszPath;
361
362 Ptr += GetSystemDirectoryW(wszPath, MAX_PATH);
363 *Ptr++ = '\\';
364 wcscpy(Ptr, L"*.cpl");
365
366 hFind = FindFirstFileW(wszPath, &wfd);
367 if (hFind != INVALID_HANDLE_VALUE)
368 {
369 do
370 {
371 wcscpy(Ptr, wfd.cFileName);
372 Control_LoadApplet(hWnd, wszPath, panel);
373 } while (FindNextFileW(hFind, &wfd));
374 FindClose(hFind);
375 }
376
377 Control_DoInterface(panel, hWnd, hInst);
378 }
379
380 static void Control_DoLaunch(CPanel *pPanel, HWND hWnd, LPCWSTR pwszCmd)
381 {
382 HANDLE hMutex;
383
384 /* Make a pwszCmd copy so we can modify it */
385 LPWSTR pwszCmdCopy = _wcsdup(pwszCmd);
386
387 LPWSTR pwszPath = pwszCmdCopy, pwszArg = NULL, pwszArg2 = NULL;
388 if (!pwszCmdCopy)
389 return;
390
391 /* Path can be quoted */
392 if (pwszPath[0] == L'"')
393 {
394 ++pwszPath;
395 pwszArg = wcschr(pwszPath, L'"');
396 if (pwszArg)
397 *(pwszArg++) = '\0';
398 }
399 else
400 pwszArg = pwszCmdCopy;
401
402 /* First argument starts after space or ','. Note: we ignore characters between '"' and ',' or ' '. */
403 if (pwszArg)
404 pwszArg = wcspbrk(pwszArg, L" ,");
405 if (pwszArg)
406 {
407 /* NULL terminate path and find first character of arg */
408 *(pwszArg++) = L'\0';
409 if (pwszArg[0] == L'"')
410 {
411 ++pwszArg;
412 pwszArg2 = wcschr(pwszArg, L'"');
413 if (pwszArg2)
414 *(pwszArg2++) = L'\0';
415 } else
416 pwszArg2 = pwszArg;
417
418 /* Second argument always starts with ','. Note: we ignore characters between '"' and ','. */
419 if (pwszArg2)
420 pwszArg2 = wcschr(pwszArg2, L',');
421 }
422
423 TRACE("Launch %ls, arg %ls, arg2 %ls\n", pwszPath, pwszArg, pwszArg2);
424
425 /* Create a mutex to disallow running multiple instances */
426 hMutex = CreateMutexW(NULL, TRUE, PathFindFileNameW(pwszPath));
427 if (!hMutex || GetLastError() == ERROR_ALREADY_EXISTS)
428 {
429 TRACE("Next instance disallowed\n");
430 if (hMutex)
431 CloseHandle(hMutex);
432 return;
433 }
434
435 /* Load applet cpl */
436 TRACE("Load applet %ls\n", pwszPath);
437 Control_LoadApplet(hWnd, pwszPath, pPanel);
438 if (pPanel->first)
439 {
440 INT i = 0;
441 /* First pPanel applet is the new one */
442 CPlApplet *pApplet = pPanel->first;
443 assert(pApplet && pApplet->next == NULL);
444 TRACE("pApplet->count %d\n", pApplet->count);
445
446 /* Note: if there is only one applet, first argument is ignored */
447 if (pApplet->count > 1 && pwszArg && pwszArg[0])
448 {
449 /* If arg begins with '@', number specifies applet index */
450 if (pwszArg[0] == L'@')
451 i = _wtoi(pwszArg + 1);
452 else
453 {
454 /* Otherwise it's applet name */
455 for (i = 0; i < (INT)pApplet->count; ++i)
456 if (!wcscmp(pwszArg, pApplet->info[i].szName))
457 break;
458 }
459 }
460
461 if (i >= 0 && i < (INT)pApplet->count && pApplet->info[i].dwSize)
462 {
463 /* Start the applet */
464 TRACE("Starting applet %d\n", i);
465 if (!pApplet->proc(pApplet->hWnd, CPL_STARTWPARMSW, i, (LPARAM)pwszArg))
466 pApplet->proc(pApplet->hWnd, CPL_DBLCLK, i, pApplet->info[i].lData);
467 } else
468 ERR("Applet not found: %ls\n", pwszArg ? pwszArg : L"NULL");
469
470 Control_UnloadApplet(pApplet);
471 }
472 else
473 ERR("Failed to load applet %ls\n", pwszPath);
474
475 ReleaseMutex(hMutex);
476 CloseHandle(hMutex);
477 free(pwszCmdCopy);
478 }
479
480 /*************************************************************************
481 * Control_RunDLLW [SHELL32.@]
482 *
483 */
484 void WINAPI Control_RunDLLW(HWND hWnd, HINSTANCE hInst, LPCWSTR cmd, DWORD nCmdShow)
485 {
486 CPanel Panel;
487
488 TRACE("(%p, %p, %s, 0x%08x)\n",
489 hWnd, hInst, debugstr_w(cmd), nCmdShow);
490
491 memset(&Panel, 0, sizeof(Panel));
492
493 if (!cmd || !*cmd)
494 {
495 TRACE("[shell32, Control_RunDLLW] Calling Control_DoWindow\n");
496 Control_DoWindow(&Panel, hWnd, hInst);
497 }
498 else
499 {
500 TRACE("[shell32, Control_RunDLLW] Calling Control_DoLaunch\n");
501 Control_DoLaunch(&Panel, hWnd, cmd);
502 }
503 }
504
505 /*************************************************************************
506 * Control_RunDLLA [SHELL32.@]
507 *
508 */
509 void WINAPI Control_RunDLLA(HWND hWnd, HINSTANCE hInst, LPCSTR cmd, DWORD nCmdShow)
510 {
511 DWORD len = MultiByteToWideChar(CP_ACP, 0, cmd, -1, NULL, 0 );
512 LPWSTR wszCmd = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
513 if (wszCmd && MultiByteToWideChar(CP_ACP, 0, cmd, -1, wszCmd, len ))
514 {
515 Control_RunDLLW(hWnd, hInst, wszCmd, nCmdShow);
516 }
517 HeapFree(GetProcessHeap(), 0, wszCmd);
518 }
519
520 /*************************************************************************
521 * Control_FillCache_RunDLLW [SHELL32.@]
522 *
523 */
524 HRESULT WINAPI Control_FillCache_RunDLLW(HWND hWnd, HANDLE hModule, DWORD w, DWORD x)
525 {
526 FIXME("%p %p 0x%08x 0x%08x stub\n", hWnd, hModule, w, x);
527 return S_OK;
528 }
529
530 /*************************************************************************
531 * Control_FillCache_RunDLLA [SHELL32.@]
532 *
533 */
534 HRESULT WINAPI Control_FillCache_RunDLLA(HWND hWnd, HANDLE hModule, DWORD w, DWORD x)
535 {
536 return Control_FillCache_RunDLLW(hWnd, hModule, w, x);
537 }
538
539
540 /*************************************************************************
541 * RunDll_CallEntry16 [SHELL32.122]
542 * the name is OK (when written with Dll, and not DLL as in Wine!)
543 */
544 void WINAPI RunDll_CallEntry16( DWORD proc, HWND hwnd, HINSTANCE inst,
545 LPCSTR cmdline, INT cmdshow )
546 {
547 #if !defined(__CYGWIN__) && !defined (__MINGW32__) && !defined(_MSC_VER)
548 WORD args[5];
549 SEGPTR cmdline_seg;
550
551 TRACE( "proc %x hwnd %p inst %p cmdline %s cmdshow %d\n",
552 proc, hwnd, inst, debugstr_a(cmdline), cmdshow );
553
554 cmdline_seg = MapLS( cmdline );
555 args[4] = HWND_16(hwnd);
556 args[3] = MapHModuleLS(inst);
557 args[2] = SELECTOROF(cmdline_seg);
558 args[1] = OFFSETOF(cmdline_seg);
559 args[0] = cmdshow;
560 WOWCallback16Ex( proc, WCB16_PASCAL, sizeof(args), args, NULL );
561 UnMapLS( cmdline_seg );
562 #else
563 FIXME( "proc %lx hwnd %p inst %p cmdline %s cmdshow %d\n",
564 proc, hwnd, inst, debugstr_a(cmdline), cmdshow );
565 #endif
566 }
567
568 /*************************************************************************
569 * CallCPLEntry16 [SHELL32.166]
570 *
571 * called by desk.cpl on "Advanced" with:
572 * hMod("DeskCp16.Dll"), pFunc("CplApplet"), 0, 1, 0xc, 0
573 *
574 */
575 LRESULT WINAPI CallCPLEntry16(HINSTANCE hMod, FARPROC pFunc, HWND dw3, UINT dw4, LPARAM dw5, LPARAM dw6)
576 {
577 FIXME("(%p, %p, %08x, %08x, %08x, %08x): stub.\n", hMod, pFunc, dw3, dw4, dw5, dw6);
578 return 0x0deadbee;
579 }