dinput: Move SetEventNotification and associated event into base class.
[wine] / programs / clock / main.c
1 /*
2  * Clock
3  *
4  * Copyright 1998 Marcel Baur <mbaur@g26.ethz.ch>
5  *
6  * Clock is partially based on
7  * - Program Manager by Ulrich Schmied
8  * - rolex.c by Jim Peterson
9  *
10  *
11  * This library is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public
13  * License as published by the Free Software Foundation; either
14  * version 2.1 of the License, or (at your option) any later version.
15  *
16  * This library is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public
22  * License along with this library; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
24  */
25
26 #include <stdio.h>
27
28 #include "windows.h"
29 #include "commdlg.h"
30
31 #include "main.h"
32 #include "winclock.h"
33
34 #define INITIAL_WINDOW_SIZE 200
35 #define TIMER_ID 1
36
37 CLOCK_GLOBALS Globals;
38
39 static VOID WineLicense(HWND Wnd)
40 {
41   char cap[20], text[1024];
42   LoadString(Globals.hInstance, IDS_LICENSE, text, sizeof text);
43   LoadString(Globals.hInstance, IDS_LICENSE_CAPTION, cap, sizeof cap);
44   MessageBox(Wnd, text, cap, MB_ICONINFORMATION | MB_OK);
45 }
46
47 static VOID WineWarranty(HWND Wnd)
48 {
49   char cap[20], text[1024];
50   LoadString(Globals.hInstance, IDS_WARRANTY, text, sizeof text);
51   LoadString(Globals.hInstance, IDS_WARRANTY_CAPTION, cap, sizeof cap);
52   MessageBox(Wnd, text, cap, MB_ICONEXCLAMATION | MB_OK);
53 }
54
55 static VOID CLOCK_UpdateMenuCheckmarks(VOID)
56 {
57     HMENU hPropertiesMenu;
58     hPropertiesMenu = GetSubMenu(Globals.hMainMenu, 0);
59     if (!hPropertiesMenu)
60         return;
61
62     if(Globals.bAnalog) {
63
64         /* analog clock */
65         CheckMenuRadioItem(hPropertiesMenu, IDM_ANALOG, IDM_DIGITAL, IDM_ANALOG, MF_CHECKED);
66         EnableMenuItem(hPropertiesMenu, IDM_FONT, MF_GRAYED);
67     }
68     else
69     {
70         /* digital clock */
71         CheckMenuRadioItem(hPropertiesMenu, IDM_ANALOG, IDM_DIGITAL, IDM_DIGITAL, MF_CHECKED);
72         EnableMenuItem(hPropertiesMenu, IDM_FONT, 0);
73     }
74
75     CheckMenuItem(hPropertiesMenu, IDM_NOTITLE, (Globals.bWithoutTitle ? MF_CHECKED : MF_UNCHECKED));
76
77     CheckMenuItem(hPropertiesMenu, IDM_ONTOP, (Globals.bAlwaysOnTop ? MF_CHECKED : MF_UNCHECKED));
78     CheckMenuItem(hPropertiesMenu, IDM_SECONDS, (Globals.bSeconds ? MF_CHECKED : MF_UNCHECKED));
79     CheckMenuItem(hPropertiesMenu, IDM_DATE, (Globals.bDate ? MF_CHECKED : MF_UNCHECKED));
80 }
81
82 static VOID CLOCK_UpdateWindowCaption(VOID)
83 {
84     CHAR szCaption[MAX_STRING_LEN];
85     int chars = 0;
86
87     /* Set frame caption */
88     if (Globals.bDate) {
89         chars = GetDateFormat(LOCALE_USER_DEFAULT, DATE_LONGDATE, NULL, NULL,
90                               szCaption, sizeof(szCaption));
91         if (chars) {
92             --chars;
93             szCaption[chars++] = ' ';
94             szCaption[chars++] = '-';
95             szCaption[chars++] = ' ';
96             szCaption[chars] = '\0';
97         }
98     }
99     LoadString(0, IDS_CLOCK, szCaption + chars, sizeof(szCaption) - chars);
100     SetWindowText(Globals.hMainWnd, szCaption);
101 }
102
103 /***********************************************************************
104  *
105  *           CLOCK_ResetTimer
106  */
107 static BOOL CLOCK_ResetTimer(void)
108 {
109     UINT period; /* milliseconds */
110
111     KillTimer(Globals.hMainWnd, TIMER_ID);
112
113     if (Globals.bSeconds)
114         if (Globals.bAnalog)
115             period = 50;
116         else
117             period = 500;
118     else
119         period = 1000;
120
121     if (!SetTimer (Globals.hMainWnd, TIMER_ID, period, NULL)) {
122         CHAR szApp[MAX_STRING_LEN];
123         LoadString(Globals.hInstance, IDS_CLOCK, szApp, sizeof(szApp));
124         MessageBox(0, "No available timers", szApp, MB_ICONEXCLAMATION | MB_OK);
125         return FALSE;
126     }
127     return TRUE;
128 }
129
130 /***********************************************************************
131  *
132  *           CLOCK_ResetFont
133  */
134 static VOID CLOCK_ResetFont(VOID)
135 {
136     HFONT newfont;
137     HDC dc = GetDC(Globals.hMainWnd);
138     newfont = SizeFont(dc, Globals.MaxX, Globals.MaxY, Globals.bSeconds, &Globals.logfont);
139     if (newfont) {
140         DeleteObject(Globals.hFont);
141         Globals.hFont = newfont;
142     }
143         
144     ReleaseDC(Globals.hMainWnd, dc);
145 }
146
147
148 /***********************************************************************
149  *
150  *           CLOCK_ChooseFont
151  */
152 static VOID CLOCK_ChooseFont(VOID)
153 {
154     LOGFONT lf;
155     CHOOSEFONT cf;
156     memset(&cf, 0, sizeof(cf));
157     lf = Globals.logfont;
158     cf.lStructSize = sizeof(cf);
159     cf.hwndOwner = Globals.hMainWnd;
160     cf.lpLogFont = &lf;
161     cf.Flags = CF_SCREENFONTS | CF_INITTOLOGFONTSTRUCT;
162     if (ChooseFont(&cf)) {
163         Globals.logfont = lf;
164         CLOCK_ResetFont();
165     }
166 }
167
168 /***********************************************************************
169  *
170  *           CLOCK_ToggleTitle
171  */
172 static VOID CLOCK_ToggleTitle(VOID)
173 {
174     /* Also shows/hides the menu */
175     LONG style = GetWindowLong(Globals.hMainWnd, GWL_STYLE);
176     if ((Globals.bWithoutTitle = !Globals.bWithoutTitle)) {
177         style = (style & ~WS_OVERLAPPEDWINDOW) | WS_POPUP|WS_THICKFRAME;
178         SetMenu(Globals.hMainWnd, 0);
179     }
180     else {
181         style = (style & ~(WS_POPUP|WS_THICKFRAME)) | WS_OVERLAPPEDWINDOW;
182         SetMenu(Globals.hMainWnd, Globals.hMainMenu);
183     }
184     SetWindowLong(Globals.hMainWnd, GWL_STYLE, style);
185     SetWindowPos(Globals.hMainWnd, 0,0,0,0,0, 
186                  SWP_DRAWFRAME|SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER);
187     
188     CLOCK_UpdateMenuCheckmarks();
189     CLOCK_UpdateWindowCaption();
190 }
191
192 /***********************************************************************
193  *
194  *           CLOCK_ToggleOnTop
195  */
196 static VOID CLOCK_ToggleOnTop(VOID)
197 {
198     if ((Globals.bAlwaysOnTop = !Globals.bAlwaysOnTop)) {
199         SetWindowPos(Globals.hMainWnd, HWND_TOPMOST, 0,0,0,0,
200                      SWP_NOMOVE|SWP_NOSIZE);
201     }
202     else {
203         SetWindowPos(Globals.hMainWnd, HWND_NOTOPMOST, 0,0,0,0,
204                      SWP_NOMOVE|SWP_NOSIZE);
205     }
206     CLOCK_UpdateMenuCheckmarks();
207 }
208 /***********************************************************************
209  *
210  *           CLOCK_MenuCommand
211  *
212  *  All handling of main menu events
213  */
214
215 static int CLOCK_MenuCommand (WPARAM wParam)
216 {
217     CHAR szApp[MAX_STRING_LEN];
218     CHAR szAppRelease[MAX_STRING_LEN];
219     switch (wParam) {
220         /* switch to analog */
221         case IDM_ANALOG: {
222             Globals.bAnalog = TRUE;
223             CLOCK_UpdateMenuCheckmarks();
224             CLOCK_ResetTimer();
225             InvalidateRect(Globals.hMainWnd, NULL, FALSE);
226             break;
227         }
228             /* switch to digital */
229         case IDM_DIGITAL: {
230             Globals.bAnalog = FALSE;
231             CLOCK_UpdateMenuCheckmarks();
232             CLOCK_ResetTimer();
233             CLOCK_ResetFont();
234             InvalidateRect(Globals.hMainWnd, NULL, FALSE);
235             break;
236         }
237             /* change font */
238         case IDM_FONT: {
239             CLOCK_ChooseFont();
240             break;
241         }
242             /* hide title bar */
243         case IDM_NOTITLE: {
244             CLOCK_ToggleTitle();
245             break;
246         }
247             /* always on top */
248         case IDM_ONTOP: {
249             CLOCK_ToggleOnTop();
250             break;
251         }
252             /* show or hide seconds */
253         case IDM_SECONDS: {
254             Globals.bSeconds = !Globals.bSeconds;
255             CLOCK_UpdateMenuCheckmarks();
256             CLOCK_ResetTimer();
257             if (!Globals.bAnalog)
258                 CLOCK_ResetFont();
259             InvalidateRect(Globals.hMainWnd, NULL, FALSE);
260             break;
261         }
262             /* show or hide date */
263         case IDM_DATE: {
264             Globals.bDate = !Globals.bDate;
265             CLOCK_UpdateMenuCheckmarks();
266             CLOCK_UpdateWindowCaption();
267             break;
268         }
269             /* show license */
270         case IDM_LICENSE: {
271             WineLicense(Globals.hMainWnd);
272             break;
273         }
274             /* show warranties */
275         case IDM_NOWARRANTY: {
276             WineWarranty(Globals.hMainWnd);
277             break;
278         }
279             /* show "about" box */
280         case IDM_ABOUT: {
281             LoadString(Globals.hInstance, IDS_CLOCK, szApp, sizeof(szApp));
282             lstrcpy(szAppRelease,szApp);
283             ShellAbout(Globals.hMainWnd, szApp, szAppRelease, 0);
284             break;
285         }
286     }
287     return 0;
288 }
289
290 /***********************************************************************
291  *
292  *           CLOCK_Paint
293  */
294 static VOID CLOCK_Paint(HWND hWnd)
295 {
296     PAINTSTRUCT ps;
297     HDC dcMem, dc;
298     HBITMAP bmMem, bmOld;
299
300     dc = BeginPaint(hWnd, &ps);
301
302     /* Use an offscreen dc to avoid flicker */
303     dcMem = CreateCompatibleDC(dc);
304     bmMem = CreateCompatibleBitmap(dc, ps.rcPaint.right - ps.rcPaint.left,
305                                     ps.rcPaint.bottom - ps.rcPaint.top);
306
307     bmOld = SelectObject(dcMem, bmMem);
308
309     SetViewportOrgEx(dcMem, -ps.rcPaint.left, -ps.rcPaint.top, NULL);
310     /* Erase the background */
311     FillRect(dcMem, &ps.rcPaint, GetStockObject(LTGRAY_BRUSH));
312
313     if(Globals.bAnalog)
314         AnalogClock(dcMem, Globals.MaxX, Globals.MaxY, Globals.bSeconds);
315     else
316         DigitalClock(dcMem, Globals.MaxX, Globals.MaxY, Globals.bSeconds, Globals.hFont);
317
318     /* Blit the changes to the screen */
319     BitBlt(dc, 
320            ps.rcPaint.left, ps.rcPaint.top,
321            ps.rcPaint.right - ps.rcPaint.left, ps.rcPaint.bottom - ps.rcPaint.top,
322            dcMem,
323            ps.rcPaint.left, ps.rcPaint.top,
324            SRCCOPY);
325
326     SelectObject(dcMem, bmOld);
327     DeleteObject(bmMem);
328     DeleteDC(dcMem);
329     
330     EndPaint(hWnd, &ps);
331 }
332
333 /***********************************************************************
334  *
335  *           CLOCK_WndProc
336  */
337
338 static LRESULT WINAPI CLOCK_WndProc (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
339 {
340     switch (msg) {
341         /* L button drag moves the window */
342         case WM_NCHITTEST: {
343             LRESULT ret = DefWindowProc (hWnd, msg, wParam, lParam);
344             if (ret == HTCLIENT)
345                 ret = HTCAPTION;
346             return ret;
347         }
348
349         case WM_NCLBUTTONDBLCLK:
350         case WM_LBUTTONDBLCLK: {
351             CLOCK_ToggleTitle();
352             break;
353         }
354
355         case WM_PAINT: {
356             CLOCK_Paint(hWnd);
357             break;
358
359         }
360
361         case WM_SIZE: {
362             Globals.MaxX = LOWORD(lParam);
363             Globals.MaxY = HIWORD(lParam);
364             CLOCK_ResetFont();
365             break;
366         }
367
368         case WM_COMMAND: {
369             CLOCK_MenuCommand(wParam);
370             break;
371         }
372             
373         case WM_TIMER: {
374             /* Could just invalidate what has changed,
375              * but it doesn't really seem worth the effort
376              */
377             InvalidateRect(Globals.hMainWnd, NULL, FALSE);
378             break;
379         }
380
381         case WM_DESTROY: {
382             PostQuitMessage (0);
383             break;
384         }
385
386         default:
387             return DefWindowProc (hWnd, msg, wParam, lParam);
388     }
389     return 0;
390 }
391
392
393 /***********************************************************************
394  *
395  *           WinMain
396  */
397
398 int PASCAL WinMain (HINSTANCE hInstance, HINSTANCE prev, LPSTR cmdline, int show)
399 {
400     MSG      msg;
401     WNDCLASS class;
402
403     static const char szClassName[] = "CLClass";  /* To make sure className >= 0x10000 */
404     static const char szWinName[]   = "Clock";
405
406     /* Setup Globals */
407     memset(&Globals.hFont, 0, sizeof (Globals.hFont));
408     Globals.bAnalog         = TRUE;
409     Globals.bSeconds        = TRUE;
410     
411     if (!prev){
412         class.style         = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
413         class.lpfnWndProc   = CLOCK_WndProc;
414         class.cbClsExtra    = 0;
415         class.cbWndExtra    = 0;
416         class.hInstance     = hInstance;
417         class.hIcon         = LoadIcon (0, IDI_APPLICATION);
418         class.hCursor       = LoadCursor (0, IDC_ARROW);
419         class.hbrBackground = 0;
420         class.lpszMenuName  = 0;
421         class.lpszClassName = szClassName;
422     }
423     
424     if (!RegisterClass (&class)) return FALSE;
425     
426     Globals.MaxX = Globals.MaxY = INITIAL_WINDOW_SIZE;
427     Globals.hMainWnd = CreateWindow (szClassName, szWinName, WS_OVERLAPPEDWINDOW,
428                                      CW_USEDEFAULT, CW_USEDEFAULT,
429                                      Globals.MaxX, Globals.MaxY, 0,
430                                      0, hInstance, 0);
431
432     if (!CLOCK_ResetTimer())
433         return FALSE;
434
435     Globals.hMainMenu = LoadMenu(0, MAKEINTRESOURCE(MAIN_MENU));
436     SetMenu(Globals.hMainWnd, Globals.hMainMenu);
437     CLOCK_UpdateMenuCheckmarks();
438     CLOCK_UpdateWindowCaption();
439     
440     ShowWindow (Globals.hMainWnd, show);
441     UpdateWindow (Globals.hMainWnd);
442     
443     while (GetMessage(&msg, 0, 0, 0)) {
444         TranslateMessage(&msg);
445         DispatchMessage(&msg);
446     }
447
448     KillTimer(Globals.hMainWnd, TIMER_ID);
449     DeleteObject(Globals.hFont);
450
451     return 0;
452 }