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