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