Do not break quoted strings in Control_DoLaunch.
[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
31 WINE_DEFAULT_DEBUG_CHANNEL(shlctrl);
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 */
39     NEWCPLINFOA         info[1];        /* array of count information.
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
95     applet = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, applet,
96                          sizeof(*applet) + (applet->count - 1) * sizeof(NEWCPLINFOA));
97
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;
116                applet->info[i].hIcon = LoadIconA(applet->hModule,
117                                                  MAKEINTRESOURCEA(info.idIcon));
118                LoadStringA(applet->hModule, info.idName,
119                            applet->info[i].szName, sizeof(applet->info[i].szName));
120                LoadStringA(applet->hModule, info.idInfo,
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;
157
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;
201             DrawTextA(hdc, applet->info[i].szName, -1, &txtRect,
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;
215
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
229 static LRESULT WINAPI   Control_WndProc(HWND hWnd, UINT wMsg,
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
274     CreateWindowExA(0, wc.lpszClassName, "Wine Control Panel",
275                     WS_OVERLAPPEDWINDOW | WS_VISIBLE,
276                     CW_USEDEFAULT, CW_USEDEFAULT,
277                     CW_USEDEFAULT, CW_USEDEFAULT,
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     int        quoted = 0;
321
322     buffer = HeapAlloc(GetProcessHeap(), 0, strlen(cmd) + 1);
323     if (!buffer) return;
324
325     end = strcpy(buffer, cmd);
326
327     for (;;) {
328         ch = *end;
329         if (ch == '"') quoted = !quoted;
330         if (!quoted && (ch == ' ' || ch == ',' || ch == '\0')) {
331             *end = '\0';
332             if (beg) {
333                 if (*beg == '@') {
334                     sp = atoi(beg + 1);
335                 } else if (*beg == '\0') {
336                     sp = 0;
337                 } else {
338                     extraPmts = beg;
339                 }
340             }
341             if (ch == '\0') break;
342             beg = end + 1;
343             if (ch == ' ') while (end[1] == ' ') end++;
344         }
345         end++;
346     }
347
348     TRACE("cmd %s, extra %s, sp %d\n", buffer, debugstr_a(extraPmts), sp);
349
350     Control_LoadApplet(hWnd, buffer, panel);
351
352     if (panel->first) {
353        CPlApplet* applet = panel->first;
354
355        assert(applet && applet->next == NULL);
356        if (sp >= applet->count) {
357           WARN("Out of bounds (%u >= %u), setting to 0\n", sp, applet->count);
358           sp = 0;
359        }
360        if (applet->info[sp].dwSize) {
361           if (!applet->proc(applet->hWnd, CPL_STARTWPARMSA, sp, (LPARAM)extraPmts))
362              applet->proc(applet->hWnd, CPL_DBLCLK, sp, applet->info[sp].lData);
363        }
364        Control_UnloadApplet(applet);
365     }
366     HeapFree(GetProcessHeap(), 0, buffer);
367 }
368
369 /*************************************************************************
370  * Control_RunDLL                       [SHELL32.@]
371  *
372  */
373 void WINAPI Control_RunDLL(HWND hWnd, HINSTANCE hInst, LPCSTR cmd, DWORD nCmdShow)
374 {
375     CPanel      panel;
376
377     TRACE("(0x%08x, 0x%08lx, %s, 0x%08lx)\n",
378           hWnd, (DWORD)hInst, debugstr_a(cmd), nCmdShow);
379
380     memset(&panel, 0, sizeof(panel));
381
382     if (!cmd || !*cmd) {
383         Control_DoWindow(&panel, hWnd, hInst);
384     } else {
385         Control_DoLaunch(&panel, hWnd, cmd);
386     }
387 }
388
389 /*************************************************************************
390  * Control_FillCache_RunDLL                     [SHELL32.@]
391  *
392  */
393 HRESULT WINAPI Control_FillCache_RunDLL(HWND hWnd, HANDLE hModule, DWORD w, DWORD x)
394 {
395     FIXME("0x%04x 0x%04x 0x%04lx 0x%04lx stub\n",hWnd, hModule, w, x);
396     return 0;
397 }
398
399 /*************************************************************************
400  * RunDLL_CallEntry16                           [SHELL32.122]
401  * the name is probably wrong
402  */
403 HRESULT WINAPI RunDLL_CallEntry16(DWORD v, DWORD w, DWORD x, DWORD y, DWORD z)
404 {
405     FIXME("0x%04lx 0x%04lx 0x%04lx 0x%04lx 0x%04lx stub\n",v,w,x,y,z);
406     return 0;
407 }
408
409 /*************************************************************************
410  * CallCPLEntry16                               [SHELL32.166]
411  *
412  * called by desk.cpl on "Advanced" with:
413  * hMod("DeskCp16.Dll"), pFunc("CplApplet"), 0, 1, 0xc, 0
414  *
415  */
416 DWORD WINAPI CallCPLEntry16(HMODULE hMod, FARPROC pFunc, DWORD dw3, DWORD dw4, DWORD dw5, DWORD dw6)
417 {
418     FIXME("(%04x, %p, %08lx, %08lx, %08lx, %08lx): stub.\n", hMod, pFunc, dw3, dw4, dw5, dw6);
419     return 0x0deadbee;
420 }