:pserver:cvsanon@mok.lvcm.com:/CVS/ReactOS reactos
[reactos.git] / 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