Addition of control panel applet support to shell32. Still need to clean local and...
[reactos.git] / reactos / lib / shell32 / control / control.c
1 /*
2 * ReactOS shell32 - Control Panel
3 *
4 * control.c
5 *
6 * Copyright (C) 2002 Robert Dickenson <robd@reactos.org>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23 #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
24 #include <windows.h>
25 #include <cpl.h>
26 #include <commctrl.h>
27 #include <tchar.h>
28 #include <assert.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 //#include <memory.h>
33 //#include <process.h>
34
35 #include "control.h"
36 #include "framewnd.h"
37 #include "settings.h"
38
39 #include "shell32.h"
40 #include "trace.h"
41
42 //#define _USE_WINE_WND_
43
44 //WINE_DEFAULT_DEBUG_CHANNEL(shlctrl);
45
46 ////////////////////////////////////////////////////////////////////////////////
47 // Global Variables:
48 //
49
50 HINSTANCE hInst;
51 HWND hFrameWnd;
52 HWND hStatusBar;
53 HMENU hMenuFrame;
54
55 TCHAR szTitle[MAX_LOADSTRING];
56 TCHAR szWindowClass[MAX_LOADSTRING];
57
58
59 ////////////////////////////////////////////////////////////////////////////////
60
61 BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
62 {
63 RECT rect;
64 WNDCLASSEX wcFrame = {
65 sizeof(WNDCLASSEX),
66 CS_HREDRAW | CS_VREDRAW/*style*/,
67 FrameWndProc,
68 0/*cbClsExtra*/,
69 0/*cbWndExtra*/,
70 hInstance,
71 LoadIcon(hInstance, (LPCTSTR)MAKEINTRESOURCE(IDI_CONTROL)),
72 LoadCursor(0, IDC_ARROW),
73 0/*hbrBackground*/,
74 0/*lpszMenuName*/,
75 szWindowClass,
76 (HICON)LoadImage(hInstance, (LPCTSTR)MAKEINTRESOURCE(IDI_CONTROL), IMAGE_ICON,
77 GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_SHARED)
78 };
79 ATOM hFrameWndClass = RegisterClassEx(&wcFrame); // register frame window class
80
81 hMenuFrame = LoadMenu(hInstance, (LPCTSTR)MAKEINTRESOURCE(IDR_CONTROL_MENU));
82
83 // Initialize the Windows Common Controls DLL
84 InitCommonControls();
85
86 if (LoadSettings(&rect)) {
87 hFrameWnd = CreateWindowEx(0, (LPCTSTR)(int)hFrameWndClass, szTitle,
88 WS_OVERLAPPEDWINDOW | WS_EX_CLIENTEDGE,
89 rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,
90 NULL, hMenuFrame, hInstance, NULL/*lpParam*/);
91 } else {
92 hFrameWnd = CreateWindowEx(0, (LPCTSTR)(int)hFrameWndClass, szTitle,
93 WS_OVERLAPPEDWINDOW | WS_EX_CLIENTEDGE,
94 CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
95 NULL, hMenuFrame, hInstance, NULL/*lpParam*/);
96 }
97
98
99 if (!hFrameWnd) {
100 return FALSE;
101 }
102
103 // Create the status bar
104 hStatusBar = CreateStatusWindow(WS_VISIBLE|WS_CHILD|WS_CLIPSIBLINGS|SBT_NOBORDERS,
105 _T(""), hFrameWnd, STATUS_WINDOW);
106 if (hStatusBar) {
107 // Create the status bar panes
108 SetupStatusBar(hFrameWnd, FALSE);
109 CheckMenuItem(GetSubMenu(hMenuFrame, ID_VIEW_MENU), ID_VIEW_STATUSBAR, MF_BYCOMMAND|MF_CHECKED);
110 }
111 ShowWindow(hFrameWnd, nCmdShow);
112 UpdateWindow(hFrameWnd);
113 return TRUE;
114 }
115
116 ////////////////////////////////////////////////////////////////////////////////
117
118 void ExitInstance(void)
119 {
120 DestroyMenu(hMenuFrame);
121 }
122
123
124 int APIENTRY ControlMain(HINSTANCE hInstance,
125 HINSTANCE hPrevInstance,
126 LPCTSTR pCmdLine,
127 int nCmdShow)
128 {
129 MSG msg;
130 HACCEL hAccel;
131
132 // Initialize global strings
133 LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
134 LoadString(hInstance, IDC_CONTROL, szWindowClass, MAX_LOADSTRING);
135
136 // Store instance handle in our global variable
137 hInst = hInstance;
138
139 // Perform application initialization:
140 if (!InitInstance(hInstance, nCmdShow)) {
141 return FALSE;
142 }
143 hAccel = LoadAccelerators(hInstance, (LPCTSTR)IDC_CONTROL);
144
145 // Main message loop:
146 while (GetMessage(&msg, (HWND)NULL, 0, 0)) {
147 if (!TranslateAccelerator(msg.hwnd, hAccel, &msg)) {
148 TranslateMessage(&msg);
149 DispatchMessage(&msg);
150 }
151 }
152 ExitInstance();
153 return msg.wParam;
154 }
155
156 ////////////////////////////////////////////////////////////////////////////////
157
158 CPlApplet* Control_UnloadApplet(CPlApplet* applet)
159 {
160 unsigned i;
161 CPlApplet* next;
162
163 for (i = 0; i < applet->count; i++) {
164 if (!applet->info[i].dwSize) continue;
165 applet->proc(applet->hWnd, CPL_STOP, i, applet->info[i].lData);
166 }
167 if (applet->proc) applet->proc(applet->hWnd, CPL_EXIT, 0L, 0L);
168 FreeLibrary(applet->hModule);
169 next = applet->next;
170 HeapFree(GetProcessHeap(), 0, applet);
171 return next;
172 }
173
174 CPlApplet* Control_LoadApplet(HWND hWnd, LPCTSTR cmd, CPlApplet** pListHead)
175 {
176 CPlApplet* applet;
177 unsigned i;
178 CPLINFO info;
179
180 if (!(applet = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*applet))))
181 return applet;
182 applet->hWnd = hWnd;
183 if (!(applet->hModule = LoadLibrary(cmd))) {
184 TRACE(_T("Cannot load control panel applet %s\n"), cmd);
185 goto theError;
186 }
187 if (!(applet->proc = (APPLET_PROC)GetProcAddress(applet->hModule, "CPlApplet"))) {
188 // if (!(applet->proc = (APPLET_PROC)GetProcAddress(applet->hModule, "_CPlApplet@16"))) {
189 TRACE(_T("Not a valid control panel applet %s\n"), cmd);
190 goto theError;
191 }
192 if (!applet->proc(hWnd, CPL_INIT, 0L, 0L)) {
193 TRACE(_T("Init of applet has failed\n"));
194 goto theError;
195 }
196 if ((applet->count = applet->proc(hWnd, CPL_GETCOUNT, 0L, 0L)) == 0) {
197 TRACE(_T("No subprogram in applet\n"));
198 goto theError;
199 }
200 applet = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, applet,
201 // sizeof(*applet) + (applet->count - 1) * sizeof(NEWCPLINFOA));
202 sizeof(*applet) + (applet->count - 0) * sizeof(NEWCPLINFO));
203 for (i = 0; i < applet->count; i++) {
204 // applet->info[i].dwSize = sizeof(NEWCPLINFOA);
205 applet->info[i].dwSize = sizeof(NEWCPLINFO);
206 /* proc is supposed to return a null value upon success for
207 * CPL_INQUIRE and CPL_NEWINQUIRE
208 * However, real drivers don't seem to behave like this
209 * So, use introspection rather than return value
210 */
211 applet->info[i].hIcon = 0;
212 applet->proc(hWnd, CPL_NEWINQUIRE, i, (LPARAM)&applet->info[i]);
213 if (applet->info[i].hIcon == 0) {
214 applet->proc(hWnd, CPL_INQUIRE, i, (LPARAM)&info);
215 if (info.idIcon == 0 || info.idName == 0) {
216 TRACE(_T("Couldn't get info from sp %u\n"), i);
217 applet->info[i].dwSize = 0;
218 } else {
219 /* convert the old data into the new structure */
220 applet->info[i].dwFlags = 0;
221 applet->info[i].dwHelpContext = 0;
222 applet->info[i].lData = info.lData;
223 // applet->info[i].hIcon = LoadIcon(applet->hModule, (LPCTSTR)MAKEINTRESOURCEA(info.idIcon));
224 // applet->info[i].hIcon = LoadIcon(applet->hModule, (LPCTSTR)MAKEINTRESOURCE(info.idIcon));
225 applet->info[i].hIcon = LoadImage(applet->hModule, (LPCTSTR)info.idIcon, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);
226
227 LoadString(applet->hModule, info.idName, applet->info[i].szName, sizeof(applet->info[i].szName)/sizeof(TCHAR));
228 //LoadString(applet->hModule, info.idInfo, applet->info[i].szInfo, sizeof(applet->info[i].szInfo)/sizeof(TCHAR));
229 //applet->info[i].szHelpFile[0] = '\0';
230 LoadString(applet->hModule, info.idInfo, applet->info[i].szInfo, 192);
231 }
232 } else {
233 TRACE(_T("Using CPL_NEWINQUIRE data\n"));
234 }
235 }
236 applet->next = *pListHead;
237 *pListHead = applet;
238 return applet;
239 theError:
240 Control_UnloadApplet(applet);
241 return NULL;
242 }
243
244 void Control_DoLaunch(CPlApplet** pListHead, HWND hWnd, LPCTSTR cmd)
245 /* forms to parse:
246 * foo.cpl,@sp,str
247 * foo.cpl,@sp
248 * foo.cpl,,str
249 * foo.cpl @sp
250 * foo.cpl str
251 */
252 {
253 TCHAR* buffer;
254 TCHAR* beg = NULL;
255 TCHAR* end;
256 TCHAR ch;
257 unsigned sp = 0;
258 TCHAR* extraPmts = NULL;
259
260 buffer = HeapAlloc(GetProcessHeap(), 0, _tcslen(cmd) + 1);
261 if (!buffer) return;
262
263 end = _tcscpy(buffer, cmd);
264
265 for (;;) {
266 ch = *end;
267 if (ch == _T(' ') || ch == _T(',') || ch == _T('\0')) {
268 *end = _T('\0');
269 if (beg) {
270 if (*beg == _T('@')) {
271 sp = _ttoi(beg + 1);
272 } else if (*beg == _T('\0')) {
273 sp = 0;
274 } else {
275 extraPmts = beg;
276 }
277 }
278 if (ch == _T('\0')) break;
279 beg = end + 1;
280 if (ch == _T(' ')) while (end[1] == _T(' ')) end++;
281 }
282 end++;
283 }
284 #if 1
285 Control_LoadApplet(hWnd, buffer, pListHead);
286 if (*pListHead) {
287 CPlApplet* applet = *pListHead;
288 assert(applet && applet->next == NULL);
289 if (sp >= applet->count) {
290 TRACE(_T("Out of bounds (%u >= %u), setting to 0\n"), sp, applet->count);
291 sp = 0;
292 }
293 if (applet->info[sp].dwSize) {
294 if (!applet->proc(applet->hWnd, CPL_STARTWPARMS, sp, (LPARAM)extraPmts))
295 applet->proc(applet->hWnd, CPL_DBLCLK, sp, applet->info[sp].lData);
296 }
297 Control_UnloadApplet(applet);
298 }
299 #else
300
301 //static void Control_LaunchApplet(HWND hWnd, CPlEntry* pCPlEntry)
302
303 #endif
304 HeapFree(GetProcessHeap(), 0, buffer);
305 }
306
307 //int APIENTRY ControlMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPCTSTR lpCmdLine, int nCmdShow);
308
309 VOID Control_RunDLL(HWND hWnd, HINSTANCE hInst_unused, LPCTSTR lpCmdLine, DWORD nCmdShow)
310 {
311 CPanel panel;
312 // TRACE("(0x%08x, 0x%08lx, %s, 0x%08lx)\n", hWnd, (DWORD)hInst, debugstr_a(lpCmdLine), nCmdShow);
313
314 memset(&panel, 0, sizeof(panel));
315
316 if (!lpCmdLine || !*lpCmdLine) {
317 #ifdef _USE_WINE_WND_
318 Control_DoWindow(&panel, hWnd, hInst);
319 #else
320 ControlMain(hInst, NULL, lpCmdLine, nCmdShow);
321 #endif
322 } else {
323 Control_DoLaunch(&panel.first, hWnd, lpCmdLine);
324 }
325 }
326
327
328 ////////////////////////////////////////////////////////////////////////////////
329 #ifdef _USE_WINE_WND_
330
331 static void Control_WndProc_Create(HWND hWnd, const CREATESTRUCTA* cs)
332 {
333 CPanel* panel = (CPanel*)cs->lpCreateParams;
334
335 SetWindowLong(hWnd, 0, (LPARAM)panel);
336 panel->status = 0;
337 panel->hWnd = hWnd;
338 }
339
340 #define XICON 32
341 #define XSTEP 128
342 #define YICON 32
343 #define YSTEP 64
344
345 static BOOL Control_Localize(const CPanel* panel, unsigned cx, unsigned cy,
346 CPlApplet** papplet, unsigned* psp)
347 {
348 unsigned i, x = (XSTEP-XICON)/2, y = 0;
349 CPlApplet* applet;
350 RECT rc;
351
352 GetClientRect(panel->hWnd, &rc);
353 for (applet = panel->first; applet; applet = applet = applet->next) {
354 for (i = 0; i < applet->count; i++) {
355 if (!applet->info[i].dwSize) continue;
356 if (x + XSTEP >= rc.right - rc.left) {
357 x = (XSTEP-XICON)/2;
358 y += YSTEP;
359 }
360 if (cx >= x && cx < x + XICON && cy >= y && cy < y + YSTEP) {
361 *papplet = applet;
362 *psp = i;
363 return TRUE;
364 }
365 x += XSTEP;
366 }
367 }
368 return FALSE;
369 }
370
371 static LRESULT Control_WndProc_Paint(const CPanel* panel, WPARAM wParam)
372 {
373 HDC hdc;
374 PAINTSTRUCT ps;
375 RECT rc, txtRect;
376 unsigned i, x = 0, y = 0;
377 CPlApplet* applet;
378 HGDIOBJ hOldFont;
379
380 hdc = (wParam) ? (HDC)wParam : BeginPaint(panel->hWnd, &ps);
381 hOldFont = SelectObject(hdc, GetStockObject(ANSI_VAR_FONT));
382 GetClientRect(panel->hWnd, &rc);
383 for (applet = panel->first; applet; applet = applet = applet->next) {
384 for (i = 0; i < applet->count; i++) {
385 if (x + XSTEP >= rc.right - rc.left) {
386 x = 0;
387 y += YSTEP;
388 }
389 if (!applet->info[i].dwSize) continue;
390 DrawIcon(hdc, x + (XSTEP-XICON)/2, y, applet->info[i].hIcon);
391 txtRect.left = x;
392 txtRect.right = x + XSTEP;
393 txtRect.top = y + YICON;
394 txtRect.bottom = y + YSTEP;
395 DrawText(hdc, applet->info[i].szName, -1, &txtRect,
396 DT_CENTER | DT_VCENTER);
397 x += XSTEP;
398 }
399 }
400 SelectObject(hdc, hOldFont);
401 if (!wParam) EndPaint(panel->hWnd, &ps);
402 return 0;
403 }
404
405 static LRESULT Control_WndProc_LButton(CPanel* panel, LPARAM lParam, BOOL up)
406 {
407 unsigned i;
408 CPlApplet* applet;
409
410 if (Control_Localize(panel, LOWORD(lParam), HIWORD(lParam), &applet, &i)) {
411 if (up) {
412 if (panel->clkApplet == applet && panel->clkSP == i) {
413 applet->proc(applet->hWnd, CPL_DBLCLK, i, applet->info[i].lData);
414 }
415 } else {
416 panel->clkApplet = applet;
417 panel->clkSP = i;
418 }
419 }
420 return 0;
421 }
422
423 //static LRESULT WINAPI Control_WndProc(HWND hWnd, UINT wMsg,
424 static LRESULT __stdcall Control_WndProc(HWND hWnd, UINT wMsg,
425 WPARAM lParam1, LPARAM lParam2)
426 {
427 CPanel* panel = (CPanel*)GetWindowLongA(hWnd, 0);
428
429 if (panel || wMsg == WM_CREATE) {
430 switch (wMsg) {
431 case WM_CREATE:
432 Control_WndProc_Create(hWnd, (CREATESTRUCTA*)lParam2);
433 return 0;
434 case WM_DESTROY:
435 while ((panel->first = Control_UnloadApplet(panel->first)));
436 break;
437 case WM_PAINT:
438 return Control_WndProc_Paint(panel, lParam1);
439 case WM_LBUTTONUP:
440 return Control_WndProc_LButton(panel, lParam2, TRUE);
441 case WM_LBUTTONDOWN:
442 return Control_WndProc_LButton(panel, lParam2, FALSE);
443 /* EPP case WM_COMMAND: */
444 /* EPP return Control_WndProc_Command(mwi, lParam1, lParam2); */
445 }
446 }
447
448 return DefWindowProcA(hWnd, wMsg, lParam1, lParam2);
449 }
450
451 static void Control_DoInterface(CPanel* panel, HWND hWnd, HINSTANCE hInst)
452 {
453 WNDCLASS wc;
454 MSG msg;
455
456 wc.style = CS_HREDRAW|CS_VREDRAW;
457 wc.lpfnWndProc = Control_WndProc;
458 wc.cbClsExtra = 0;
459 wc.cbWndExtra = sizeof(CPlApplet*);
460 wc.hInstance = hInst;
461 wc.hIcon = 0;
462 wc.hCursor = 0;
463 wc.hbrBackground = GetStockObject(WHITE_BRUSH);
464 wc.lpszMenuName = NULL;
465 wc.lpszClassName = "Shell_Control_WndClass";
466
467 if (!RegisterClass(&wc)) return;
468
469 CreateWindowEx(0, wc.lpszClassName, "Wine Control Panel",
470 WS_OVERLAPPEDWINDOW | WS_VISIBLE,
471 CW_USEDEFAULT, CW_USEDEFAULT,
472 CW_USEDEFAULT, CW_USEDEFAULT,
473 hWnd, (HMENU)0, hInst, panel);
474 if (!panel->hWnd) return;
475 while (GetMessage(&msg, panel->hWnd, 0, 0)) {
476 TranslateMessage(&msg);
477 DispatchMessage(&msg);
478 if (!panel->first) break;
479 }
480 }
481
482 static void Control_DoWindow(CPanel* panel, HWND hWnd, HINSTANCE hInst)
483 {
484 HANDLE h;
485 WIN32_FIND_DATA fd;
486 TCHAR buffer[MAX_PATH];
487
488 /* TRACE: should grab path somewhere from configuration */
489 if ((h = FindFirstFile("c:\\winnt\\system32\\*.cpl", &fd)) != 0) {
490 do {
491 sprintf(buffer, "c:\\winnt\\system32\\%s", fd.cFileName);
492 if (!strstr(fd.cFileName, "powercfg")) {
493 Control_LoadApplet(hWnd, buffer, panel);
494 }
495 } while (FindNextFile(h, &fd));
496 FindClose(h);
497 }
498
499 if (panel->first) Control_DoInterface(panel, hWnd, hInst);
500 }
501
502 #endif // _USE_WINE_WND_
503
504 #if 0
505 /*************************************************************************
506 * Control_RunDLL [SHELL32.@]
507 *
508 */
509
510 VOID WINAPI
511 Control_RunDLL(HWND hWnd, HINSTANCE hInst_unused, LPCSTR lpCmdLine, DWORD nCmdShow)
512 {
513 // TRACE("(0x%08x, 0x%08lx, %s, 0x%08lx)\n", hWnd, (DWORD)hInst, debugstr_a(lpCmdLine), nCmdShow);
514 }
515
516 /*************************************************************************
517 * Control_FillCache_RunDLL [SHELL32.@]
518 *
519 */
520 HRESULT WINAPI
521 Control_FillCache_RunDLL(HWND hWnd, HANDLE hModule, DWORD w, DWORD x)
522 {
523 TRACE(_T("0x%04x 0x%04x 0x%04lx 0x%04lx stub\n"), hWnd, hModule, w, x);
524 return 0;
525 }
526
527 /*************************************************************************
528 * RunDLL_CallEntry16 [SHELL32.122]
529 * the name is probably wrong
530 */
531 HRESULT WINAPI
532 RunDLL_CallEntry16(DWORD v, DWORD w, DWORD x, DWORD y, DWORD z)
533 {
534 TRACE(_T("0x%04lx 0x%04lx 0x%04lx 0x%04lx 0x%04lx stub\n"),v,w,x,y,z);
535 return 0;
536 }
537
538 /*************************************************************************
539 * CallCPLEntry16 [SHELL32.166]
540 *
541 * called by desk.cpl on "Advanced" with:
542 * hMod("DeskCp16.Dll"), pFunc("CplApplet"), 0, 1, 0xc, 0
543 *
544 */
545 DWORD WINAPI
546 CallCPLEntry16(HMODULE hMod, FARPROC pFunc, DWORD dw3, DWORD dw4, DWORD dw5, DWORD dw6)
547 {
548 TRACE(_T("(%04x, %p, %08lx, %08lx, %08lx, %08lx): stub.\n"), hMod, pFunc, dw3, dw4, dw5, dw6);
549 return 0x0deadbee;
550 }
551 #endif