Remove unused options -a and -C.
[wine] / dlls / shell32 / control.c
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  */
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"
28 #include "wine/debug.h"
29 #include "cpl.h"
30 #include "wine/unicode.h"
31
32 #define NO_SHLWAPI_REG
33 #include "shlwapi.h"
34
35 WINE_DEFAULT_DEBUG_CHANNEL(shlctrl);
36
37 typedef struct CPlApplet {
38     struct CPlApplet*   next;           /* linked list */
39     HWND                hWnd;
40     unsigned            count;          /* number of subprograms */
41     HMODULE             hModule;        /* module of loaded applet */
42     APPLET_PROC         proc;           /* entry point address */
43     NEWCPLINFOW         info[1];        /* array of count information.
44                                          * dwSize field is 0 if entry is invalid */
45 } CPlApplet;
46
47 typedef struct CPanel {
48     CPlApplet*          first;          /* linked list */
49     HWND                hWnd;
50     unsigned            status;
51     CPlApplet*          clkApplet;
52     unsigned            clkSP;
53 } CPanel;
54
55 static  CPlApplet*      Control_UnloadApplet(CPlApplet* applet)
56 {
57     unsigned    i;
58     CPlApplet*  next;
59
60     for (i = 0; i < applet->count; i++) {
61         if (!applet->info[i].dwSize) continue;
62         applet->proc(applet->hWnd, CPL_STOP, i, applet->info[i].lData);
63     }
64     if (applet->proc) applet->proc(applet->hWnd, CPL_EXIT, 0L, 0L);
65     FreeLibrary(applet->hModule);
66     next = applet->next;
67     HeapFree(GetProcessHeap(), 0, applet);
68     return next;
69 }
70
71 static CPlApplet*       Control_LoadApplet(HWND hWnd, LPCWSTR cmd, CPanel* panel)
72 {
73     CPlApplet*  applet;
74     unsigned    i;
75     CPLINFO     info;
76     NEWCPLINFOW newinfo;
77
78     if (!(applet = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*applet))))
79        return applet;
80
81     applet->hWnd = hWnd;
82
83     if (!(applet->hModule = LoadLibraryW(cmd))) {
84         WARN("Cannot load control panel applet %s\n", debugstr_w(cmd));
85         goto theError;
86     }
87     if (!(applet->proc = (APPLET_PROC)GetProcAddress(applet->hModule, "CPlApplet"))) {
88         WARN("Not a valid control panel applet %s\n", debugstr_w(cmd));
89         goto theError;
90     }
91     if (!applet->proc(hWnd, CPL_INIT, 0L, 0L)) {
92         WARN("Init of applet has failed\n");
93         goto theError;
94     }
95     if ((applet->count = applet->proc(hWnd, CPL_GETCOUNT, 0L, 0L)) == 0) {
96         WARN("No subprogram in applet\n");
97         goto theError;
98     }
99
100     applet = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, applet,
101                          sizeof(*applet) + (applet->count - 1) * sizeof(NEWCPLINFOW));
102
103     for (i = 0; i < applet->count; i++) {
104        ZeroMemory(&newinfo, sizeof(newinfo));
105        newinfo.dwSize = sizeof(NEWCPLINFOA);
106        applet->info[i].dwSize = sizeof(NEWCPLINFOW);
107        /* proc is supposed to return a null value upon success for
108         * CPL_INQUIRE and CPL_NEWINQUIRE
109         * However, real drivers don't seem to behave like this
110         * So, use introspection rather than return value
111         */
112        applet->proc(hWnd, CPL_NEWINQUIRE, i, (LPARAM)&newinfo);
113        if (newinfo.hIcon == 0) {
114            applet->proc(hWnd, CPL_INQUIRE, i, (LPARAM)&info);
115            if (info.idIcon == 0 || info.idName == 0) {
116                WARN("Couldn't get info from sp %u\n", i);
117                applet->info[i].dwSize = 0;
118            } else {
119                /* convert the old data into the new structure */
120                applet->info[i].dwFlags = 0;
121                applet->info[i].dwHelpContext = 0;
122                applet->info[i].lData = info.lData;
123                applet->info[i].hIcon = LoadIconW(applet->hModule,
124                                                  MAKEINTRESOURCEW(info.idIcon));
125                LoadStringW(applet->hModule, info.idName,
126                            applet->info[i].szName, sizeof(applet->info[i].szName) / sizeof(WCHAR));
127                LoadStringW(applet->hModule, info.idInfo,
128                            applet->info[i].szInfo, sizeof(applet->info[i].szInfo) / sizeof(WCHAR));
129                applet->info[i].szHelpFile[0] = '\0';
130            }
131        }
132        else
133        {
134            CopyMemory(&applet->info[i], &newinfo, newinfo.dwSize);
135            if (newinfo.dwSize != sizeof(NEWCPLINFOW))
136            {
137                applet->info[i].dwSize = sizeof(NEWCPLINFOW);
138                MultiByteToWideChar(CP_ACP, 0, ((LPNEWCPLINFOA)&newinfo)->szName,
139                                    sizeof(((LPNEWCPLINFOA)&newinfo)->szName) / sizeof(CHAR),
140                                    applet->info[i].szName,
141                                    sizeof(applet->info[i].szName) / sizeof(WCHAR));
142                MultiByteToWideChar(CP_ACP, 0, ((LPNEWCPLINFOA)&newinfo)->szInfo,
143                                    sizeof(((LPNEWCPLINFOA)&newinfo)->szInfo) / sizeof(CHAR),
144                                    applet->info[i].szInfo,
145                                    sizeof(applet->info[i].szInfo) / sizeof(WCHAR));
146                MultiByteToWideChar(CP_ACP, 0, ((LPNEWCPLINFOA)&newinfo)->szHelpFile,
147                                    sizeof(((LPNEWCPLINFOA)&newinfo)->szHelpFile) / sizeof(CHAR),
148                                    applet->info[i].szHelpFile,
149                                    sizeof(applet->info[i].szHelpFile) / sizeof(WCHAR));
150            }
151        }
152     }
153
154     applet->next = panel->first;
155     panel->first = applet;
156
157     return applet;
158
159  theError:
160     Control_UnloadApplet(applet);
161     return NULL;
162 }
163
164 static void      Control_WndProc_Create(HWND hWnd, const CREATESTRUCTA* cs)
165 {
166    CPanel*      panel = (CPanel*)cs->lpCreateParams;
167
168    SetWindowLongA(hWnd, 0, (LPARAM)panel);
169    panel->status = 0;
170    panel->hWnd = hWnd;
171 }
172
173 #define XICON   32
174 #define XSTEP   128
175 #define YICON   32
176 #define YSTEP   64
177
178 static BOOL     Control_Localize(const CPanel* panel, unsigned cx, unsigned cy,
179                                  CPlApplet** papplet, unsigned* psp)
180 {
181     unsigned    i, x = (XSTEP-XICON)/2, y = 0;
182     CPlApplet*  applet;
183     RECT        rc;
184
185     GetClientRect(panel->hWnd, &rc);
186     for (applet = panel->first; applet; applet = applet = applet->next) {
187         for (i = 0; i < applet->count; i++) {
188             if (!applet->info[i].dwSize) continue;
189             if (x + XSTEP >= rc.right - rc.left) {
190                 x = (XSTEP-XICON)/2;
191                 y += YSTEP;
192             }
193             if (cx >= x && cx < x + XICON && cy >= y && cy < y + YSTEP) {
194                 *papplet = applet;
195                 *psp = i;
196                 return TRUE;
197             }
198             x += XSTEP;
199         }
200     }
201     return FALSE;
202 }
203
204 static LRESULT Control_WndProc_Paint(const CPanel* panel, WPARAM wParam)
205 {
206     HDC         hdc;
207     PAINTSTRUCT ps;
208     RECT        rc, txtRect;
209     unsigned    i, x = 0, y = 0;
210     CPlApplet*  applet;
211     HGDIOBJ     hOldFont;
212
213     hdc = (wParam) ? (HDC)wParam : BeginPaint(panel->hWnd, &ps);
214     hOldFont = SelectObject(hdc, GetStockObject(ANSI_VAR_FONT));
215     GetClientRect(panel->hWnd, &rc);
216     for (applet = panel->first; applet; applet = applet = applet->next) {
217         for (i = 0; i < applet->count; i++) {
218             if (x + XSTEP >= rc.right - rc.left) {
219                 x = 0;
220                 y += YSTEP;
221             }
222             if (!applet->info[i].dwSize) continue;
223             DrawIcon(hdc, x + (XSTEP-XICON)/2, y, applet->info[i].hIcon);
224             txtRect.left = x;
225             txtRect.right = x + XSTEP;
226             txtRect.top = y + YICON;
227             txtRect.bottom = y + YSTEP;
228             DrawTextW(hdc, applet->info[i].szName, -1, &txtRect,
229                       DT_CENTER | DT_VCENTER);
230             x += XSTEP;
231         }
232     }
233     SelectObject(hdc, hOldFont);
234     if (!wParam) EndPaint(panel->hWnd, &ps);
235     return 0;
236 }
237
238 static LRESULT Control_WndProc_LButton(CPanel* panel, LPARAM lParam, BOOL up)
239 {
240     unsigned    i;
241     CPlApplet*  applet;
242
243     if (Control_Localize(panel, LOWORD(lParam), HIWORD(lParam), &applet, &i)) {
244        if (up) {
245            if (panel->clkApplet == applet && panel->clkSP == i) {
246                applet->proc(applet->hWnd, CPL_DBLCLK, i, applet->info[i].lData);
247            }
248        } else {
249            panel->clkApplet = applet;
250            panel->clkSP = i;
251        }
252     }
253     return 0;
254 }
255
256 static LRESULT WINAPI   Control_WndProc(HWND hWnd, UINT wMsg,
257                                         WPARAM lParam1, LPARAM lParam2)
258 {
259    CPanel*      panel = (CPanel*)GetWindowLongA(hWnd, 0);
260
261    if (panel || wMsg == WM_CREATE) {
262       switch (wMsg) {
263       case WM_CREATE:
264          Control_WndProc_Create(hWnd, (CREATESTRUCTA*)lParam2);
265          return 0;
266       case WM_DESTROY:
267          while ((panel->first = Control_UnloadApplet(panel->first)));
268          break;
269       case WM_PAINT:
270          return Control_WndProc_Paint(panel, lParam1);
271       case WM_LBUTTONUP:
272          return Control_WndProc_LButton(panel, lParam2, TRUE);
273       case WM_LBUTTONDOWN:
274          return Control_WndProc_LButton(panel, lParam2, FALSE);
275 /* EPP       case WM_COMMAND: */
276 /* EPP   return Control_WndProc_Command(mwi, lParam1, lParam2); */
277       }
278    }
279
280    return DefWindowProcA(hWnd, wMsg, lParam1, lParam2);
281 }
282
283 static void    Control_DoInterface(CPanel* panel, HWND hWnd, HINSTANCE hInst)
284 {
285     WNDCLASSA   wc;
286     MSG         msg;
287
288     wc.style = CS_HREDRAW|CS_VREDRAW;
289     wc.lpfnWndProc = Control_WndProc;
290     wc.cbClsExtra = 0;
291     wc.cbWndExtra = sizeof(CPlApplet*);
292     wc.hInstance = hInst;
293     wc.hIcon = 0;
294     wc.hCursor = 0;
295     wc.hbrBackground = GetStockObject(WHITE_BRUSH);
296     wc.lpszMenuName = NULL;
297     wc.lpszClassName = "Shell_Control_WndClass";
298
299     if (!RegisterClassA(&wc)) return;
300
301     CreateWindowExA(0, wc.lpszClassName, "Wine Control Panel",
302                     WS_OVERLAPPEDWINDOW | WS_VISIBLE,
303                     CW_USEDEFAULT, CW_USEDEFAULT,
304                     CW_USEDEFAULT, CW_USEDEFAULT,
305                     hWnd, NULL, hInst, panel);
306     if (!panel->hWnd) return;
307     while (GetMessageA(&msg, panel->hWnd, 0, 0)) {
308         TranslateMessage(&msg);
309         DispatchMessageA(&msg);
310         if (!panel->first) break;
311     }
312 }
313
314 static  void    Control_DoWindow(CPanel* panel, HWND hWnd, HINSTANCE hInst)
315 {
316     HANDLE              h;
317     WIN32_FIND_DATAW    fd;
318     WCHAR               buffer[MAX_PATH];
319     static const WCHAR wszAllCpl[] = {'*','.','c','p','l',0};
320     WCHAR *p;
321
322     GetSystemDirectoryW( buffer, MAX_PATH );
323     p = buffer + strlenW(buffer);
324     *p++ = '\\';
325     lstrcpyW(p, wszAllCpl);
326
327     if ((h = FindFirstFileW(buffer, &fd)) != 0) {
328         do {
329            lstrcpyW(p, fd.cFileName);
330            Control_LoadApplet(hWnd, buffer, panel);
331         } while (FindNextFileW(h, &fd));
332         FindClose(h);
333     }
334
335     if (panel->first) Control_DoInterface(panel, hWnd, hInst);
336 }
337
338 static  void    Control_DoLaunch(CPanel* panel, HWND hWnd, LPCWSTR wszCmd)
339    /* forms to parse:
340     *   foo.cpl,@sp,str
341     *   foo.cpl,@sp
342     *   foo.cpl,,str
343     *   foo.cpl @sp
344     *   foo.cpl str
345     *   "a path\foo.cpl"
346     */
347 {
348     LPWSTR      buffer;
349     LPWSTR      beg = NULL;
350     LPWSTR      end;
351     WCHAR       ch;
352     LPWSTR       ptr;
353     unsigned    sp = 0;
354     LPWSTR      extraPmts = NULL;
355     int        quoted = 0;
356
357     buffer = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(wszCmd) + 1) * sizeof(*wszCmd));
358     if (!buffer) return;
359
360     end = lstrcpyW(buffer, wszCmd);
361
362     for (;;) {
363         ch = *end;
364         if (ch == '"') quoted = !quoted;
365         if (!quoted && (ch == ' ' || ch == ',' || ch == '\0')) {
366             *end = '\0';
367             if (beg) {
368                 if (*beg == '@') {
369                     sp = atoiW(beg + 1);
370                 } else if (*beg == '\0') {
371                     sp = 0;
372                 } else {
373                     extraPmts = beg;
374                 }
375             }
376             if (ch == '\0') break;
377             beg = end + 1;
378             if (ch == ' ') while (end[1] == ' ') end++;
379         }
380         end++;
381     }
382     while ((ptr = StrChrW(buffer, '"')))
383         memmove(ptr, ptr+1, lstrlenW(ptr));
384
385     TRACE("cmd %s, extra %s, sp %d\n", debugstr_w(buffer), debugstr_w(extraPmts), sp);
386
387     Control_LoadApplet(hWnd, buffer, panel);
388
389     if (panel->first) {
390        CPlApplet* applet = panel->first;
391
392        assert(applet && applet->next == NULL);
393        if (sp >= applet->count) {
394           WARN("Out of bounds (%u >= %u), setting to 0\n", sp, applet->count);
395           sp = 0;
396        }
397        if (applet->info[sp].dwSize) {
398           if (!applet->proc(applet->hWnd, CPL_STARTWPARMSA, sp, (LPARAM)extraPmts))
399              applet->proc(applet->hWnd, CPL_DBLCLK, sp, applet->info[sp].lData);
400        }
401        Control_UnloadApplet(applet);
402     }
403     HeapFree(GetProcessHeap(), 0, buffer);
404 }
405
406 /*************************************************************************
407  * Control_RunDLLW                      [SHELL32.@]
408  *
409  */
410 void WINAPI Control_RunDLLW(HWND hWnd, HINSTANCE hInst, LPCWSTR cmd, DWORD nCmdShow)
411 {
412     CPanel      panel;
413
414     TRACE("(%p, %p, %s, 0x%08lx)\n",
415           hWnd, hInst, debugstr_w(cmd), nCmdShow);
416
417     memset(&panel, 0, sizeof(panel));
418
419     if (!cmd || !*cmd) {
420         Control_DoWindow(&panel, hWnd, hInst);
421     } else {
422         Control_DoLaunch(&panel, hWnd, cmd);
423     }
424 }
425
426 /*************************************************************************
427  * Control_RunDLLA                      [SHELL32.@]
428  *
429  */
430 void WINAPI Control_RunDLLA(HWND hWnd, HINSTANCE hInst, LPCSTR cmd, DWORD nCmdShow)
431 {
432     DWORD len = MultiByteToWideChar(CP_ACP, 0, cmd, -1, NULL, 0 );
433     LPWSTR wszCmd = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
434     if (wszCmd && MultiByteToWideChar(CP_ACP, 0, cmd, -1, wszCmd, len ))
435     {
436         Control_RunDLLW(hWnd, hInst, wszCmd, nCmdShow);
437     }
438     HeapFree(GetProcessHeap(), 0, wszCmd);
439 }
440
441 /*************************************************************************
442  * Control_FillCache_RunDLL                     [SHELL32.@]
443  *
444  */
445 HRESULT WINAPI Control_FillCache_RunDLL(HWND hWnd, HANDLE hModule, DWORD w, DWORD x)
446 {
447     FIXME("%p %p 0x%04lx 0x%04lx stub\n", hWnd, hModule, w, x);
448     return 0;
449 }
450
451 /*************************************************************************
452  * RunDLL_CallEntry16                           [SHELL32.122]
453  * the name is probably wrong
454  */
455 HRESULT WINAPI RunDLL_CallEntry16(DWORD v, DWORD w, DWORD x, DWORD y, DWORD z)
456 {
457     FIXME("0x%04lx 0x%04lx 0x%04lx 0x%04lx 0x%04lx stub\n",v,w,x,y,z);
458     return 0;
459 }
460
461 /*************************************************************************
462  * CallCPLEntry16                               [SHELL32.166]
463  *
464  * called by desk.cpl on "Advanced" with:
465  * hMod("DeskCp16.Dll"), pFunc("CplApplet"), 0, 1, 0xc, 0
466  *
467  */
468 DWORD WINAPI CallCPLEntry16(HMODULE hMod, FARPROC pFunc, DWORD dw3, DWORD dw4, DWORD dw5, DWORD dw6)
469 {
470     FIXME("(%p, %p, %08lx, %08lx, %08lx, %08lx): stub.\n", hMod, pFunc, dw3, dw4, dw5, dw6);
471     return 0x0deadbee;
472 }