cmd: Update Dutch translation.
[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 CLOCK_UpdateMenuCheckmarks(VOID)
40 {
41     HMENU hPropertiesMenu;
42     hPropertiesMenu = GetSubMenu(Globals.hMainMenu, 0);
43     if (!hPropertiesMenu)
44         return;
45
46     if(Globals.bAnalog) {
47
48         /* analog clock */
49         CheckMenuRadioItem(hPropertiesMenu, IDM_ANALOG, IDM_DIGITAL, IDM_ANALOG, MF_CHECKED);
50         EnableMenuItem(hPropertiesMenu, IDM_FONT, MF_GRAYED);
51     }
52     else
53     {
54         /* digital clock */
55         CheckMenuRadioItem(hPropertiesMenu, IDM_ANALOG, IDM_DIGITAL, IDM_DIGITAL, MF_CHECKED);
56         EnableMenuItem(hPropertiesMenu, IDM_FONT, 0);
57     }
58
59     CheckMenuItem(hPropertiesMenu, IDM_NOTITLE, (Globals.bWithoutTitle ? MF_CHECKED : MF_UNCHECKED));
60
61     CheckMenuItem(hPropertiesMenu, IDM_ONTOP, (Globals.bAlwaysOnTop ? MF_CHECKED : MF_UNCHECKED));
62     CheckMenuItem(hPropertiesMenu, IDM_SECONDS, (Globals.bSeconds ? MF_CHECKED : MF_UNCHECKED));
63     CheckMenuItem(hPropertiesMenu, IDM_DATE, (Globals.bDate ? MF_CHECKED : MF_UNCHECKED));
64 }
65
66 static VOID CLOCK_UpdateWindowCaption(VOID)
67 {
68     WCHAR szCaption[MAX_STRING_LEN];
69     int chars = 0;
70
71     /* Set frame caption */
72     if (Globals.bDate) {
73         chars = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_LONGDATE, NULL, NULL,
74                                szCaption, sizeof(szCaption)/sizeof(WCHAR));
75         if (chars) {
76             --chars;
77             szCaption[chars++] = ' ';
78             szCaption[chars++] = '-';
79             szCaption[chars++] = ' ';
80             szCaption[chars] = '\0';
81         }
82     }
83     LoadStringW(0, IDS_CLOCK, szCaption + chars, MAX_STRING_LEN - chars);
84     SetWindowTextW(Globals.hMainWnd, szCaption);
85 }
86
87 /***********************************************************************
88  *
89  *           CLOCK_ResetTimer
90  */
91 static BOOL CLOCK_ResetTimer(void)
92 {
93     UINT period; /* milliseconds */
94
95     KillTimer(Globals.hMainWnd, TIMER_ID);
96
97     if (Globals.bSeconds)
98         if (Globals.bAnalog)
99             period = 50;
100         else
101             period = 500;
102     else
103         period = 1000;
104
105     if (!SetTimer (Globals.hMainWnd, TIMER_ID, period, NULL)) {
106         static const WCHAR notimersW[] = {'N','o',' ','a','v','a','i','l','a','b','l','e',' ','t','i','m','e','r','s',0};
107         WCHAR szApp[MAX_STRING_LEN];
108         LoadStringW(Globals.hInstance, IDS_CLOCK, szApp, MAX_STRING_LEN);
109         MessageBoxW(0, notimersW, 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     LOGFONTW lf;
140     CHOOSEFONTW 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 (ChooseFontW(&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 = GetWindowLongW(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         SetWindowRgn(Globals.hMainWnd, 0, TRUE);
169     }
170     SetWindowLongW(Globals.hMainWnd, GWL_STYLE, style);
171     SetWindowPos(Globals.hMainWnd, 0,0,0,0,0, 
172                  SWP_DRAWFRAME|SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER);
173     
174     CLOCK_UpdateMenuCheckmarks();
175     CLOCK_UpdateWindowCaption();
176 }
177
178 /***********************************************************************
179  *
180  *           CLOCK_ToggleOnTop
181  */
182 static VOID CLOCK_ToggleOnTop(VOID)
183 {
184     if ((Globals.bAlwaysOnTop = !Globals.bAlwaysOnTop)) {
185         SetWindowPos(Globals.hMainWnd, HWND_TOPMOST, 0,0,0,0,
186                      SWP_NOMOVE|SWP_NOSIZE);
187     }
188     else {
189         SetWindowPos(Globals.hMainWnd, HWND_NOTOPMOST, 0,0,0,0,
190                      SWP_NOMOVE|SWP_NOSIZE);
191     }
192     CLOCK_UpdateMenuCheckmarks();
193 }
194 /***********************************************************************
195  *
196  *           CLOCK_MenuCommand
197  *
198  *  All handling of main menu events
199  */
200
201 static int CLOCK_MenuCommand (WPARAM wParam)
202 {
203     WCHAR szApp[MAX_STRING_LEN];
204     WCHAR szAppRelease[MAX_STRING_LEN];
205     switch (wParam) {
206         /* switch to analog */
207         case IDM_ANALOG: {
208             Globals.bAnalog = TRUE;
209             CLOCK_UpdateMenuCheckmarks();
210             CLOCK_ResetTimer();
211             InvalidateRect(Globals.hMainWnd, NULL, FALSE);
212             break;
213         }
214             /* switch to digital */
215         case IDM_DIGITAL: {
216             Globals.bAnalog = FALSE;
217             CLOCK_UpdateMenuCheckmarks();
218             CLOCK_ResetTimer();
219             CLOCK_ResetFont();
220             InvalidateRect(Globals.hMainWnd, NULL, FALSE);
221             break;
222         }
223             /* change font */
224         case IDM_FONT: {
225             CLOCK_ChooseFont();
226             break;
227         }
228             /* hide title bar */
229         case IDM_NOTITLE: {
230             CLOCK_ToggleTitle();
231             break;
232         }
233             /* always on top */
234         case IDM_ONTOP: {
235             CLOCK_ToggleOnTop();
236             break;
237         }
238             /* show or hide seconds */
239         case IDM_SECONDS: {
240             Globals.bSeconds = !Globals.bSeconds;
241             CLOCK_UpdateMenuCheckmarks();
242             CLOCK_ResetTimer();
243             if (!Globals.bAnalog)
244                 CLOCK_ResetFont();
245             InvalidateRect(Globals.hMainWnd, NULL, FALSE);
246             break;
247         }
248             /* show or hide date */
249         case IDM_DATE: {
250             Globals.bDate = !Globals.bDate;
251             CLOCK_UpdateMenuCheckmarks();
252             CLOCK_UpdateWindowCaption();
253             break;
254         }
255             /* show "about" box */
256         case IDM_ABOUT: {
257             LoadStringW(Globals.hInstance, IDS_CLOCK, szApp, sizeof(szApp)/sizeof(WCHAR));
258             lstrcpyW(szAppRelease,szApp);
259             ShellAboutW(Globals.hMainWnd, szApp, szAppRelease, 0);
260             break;
261         }
262     }
263     return 0;
264 }
265
266 /***********************************************************************
267  *
268  *           CLOCK_Paint
269  */
270 static VOID CLOCK_Paint(HWND hWnd)
271 {
272     PAINTSTRUCT ps;
273     HDC dcMem, dc;
274     HBITMAP bmMem, bmOld;
275
276     dc = BeginPaint(hWnd, &ps);
277
278     /* Use an offscreen dc to avoid flicker */
279     dcMem = CreateCompatibleDC(dc);
280     bmMem = CreateCompatibleBitmap(dc, ps.rcPaint.right - ps.rcPaint.left,
281                                     ps.rcPaint.bottom - ps.rcPaint.top);
282
283     bmOld = SelectObject(dcMem, bmMem);
284
285     SetViewportOrgEx(dcMem, -ps.rcPaint.left, -ps.rcPaint.top, NULL);
286     /* Erase the background */
287     FillRect(dcMem, &ps.rcPaint, GetSysColorBrush(COLOR_3DFACE));
288
289     if(Globals.bAnalog)
290         AnalogClock(dcMem, Globals.MaxX, Globals.MaxY, Globals.bSeconds, Globals.bWithoutTitle);
291     else
292         DigitalClock(dcMem, Globals.MaxX, Globals.MaxY, Globals.bSeconds, Globals.hFont);
293
294     /* Blit the changes to the screen */
295     BitBlt(dc, 
296            ps.rcPaint.left, ps.rcPaint.top,
297            ps.rcPaint.right - ps.rcPaint.left, ps.rcPaint.bottom - ps.rcPaint.top,
298            dcMem,
299            ps.rcPaint.left, ps.rcPaint.top,
300            SRCCOPY);
301
302     SelectObject(dcMem, bmOld);
303     DeleteObject(bmMem);
304     DeleteDC(dcMem);
305     
306     EndPaint(hWnd, &ps);
307 }
308
309 /***********************************************************************
310  *
311  *           CLOCK_WndProc
312  */
313
314 static LRESULT WINAPI CLOCK_WndProc (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
315 {
316     switch (msg) {
317         /* L button drag moves the window */
318         case WM_NCHITTEST: {
319             LRESULT ret = DefWindowProcW(hWnd, msg, wParam, lParam);
320             if (ret == HTCLIENT)
321                 ret = HTCAPTION;
322             return ret;
323         }
324
325         case WM_NCLBUTTONDBLCLK:
326         case WM_LBUTTONDBLCLK: {
327             CLOCK_ToggleTitle();
328             break;
329         }
330
331         case WM_PAINT: {
332             CLOCK_Paint(hWnd);
333             break;
334
335         }
336
337         case WM_SIZE: {
338             Globals.MaxX = LOWORD(lParam);
339             Globals.MaxY = HIWORD(lParam);
340             if (Globals.bAnalog && Globals.bWithoutTitle)
341             {
342                 RECT rect;
343                 INT diameter = min( Globals.MaxX, Globals.MaxY );
344                 HRGN hrgn = CreateEllipticRgn( (Globals.MaxX - diameter) / 2,
345                                                (Globals.MaxY - diameter) / 2,
346                                                (Globals.MaxX + diameter) / 2,
347                                                (Globals.MaxY + diameter) / 2 );
348                 GetWindowRect( hWnd, &rect );
349                 MapWindowPoints( 0, hWnd, (LPPOINT)&rect, 2 );
350                 OffsetRgn( hrgn, -rect.left, -rect.top );
351                 SetWindowRgn( Globals.hMainWnd, hrgn, TRUE );
352             }
353             CLOCK_ResetFont();
354             break;
355         }
356
357         case WM_COMMAND: {
358             CLOCK_MenuCommand(wParam);
359             break;
360         }
361             
362         case WM_TIMER: {
363             /* Could just invalidate what has changed,
364              * but it doesn't really seem worth the effort
365              */
366             InvalidateRect(Globals.hMainWnd, NULL, FALSE);
367             break;
368         }
369
370         case WM_DESTROY: {
371             PostQuitMessage (0);
372             break;
373         }
374
375         default:
376             return DefWindowProcW(hWnd, msg, wParam, lParam);
377     }
378     return 0;
379 }
380
381
382 /***********************************************************************
383  *
384  *           WinMain
385  */
386
387 int PASCAL WinMain (HINSTANCE hInstance, HINSTANCE prev, LPSTR cmdline, int show)
388 {
389     MSG      msg;
390     WNDCLASSW class;
391
392     static const WCHAR szClassName[] = {'C','L','C','l','a','s','s',0};
393     static const WCHAR szWinName[]   = {'C','l','o','c','k',0};
394
395     /* Setup Globals */
396     memset(&Globals.hFont, 0, sizeof (Globals.hFont));
397     Globals.bAnalog         = TRUE;
398     Globals.bSeconds        = TRUE;
399     
400     if (!prev){
401         class.style         = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
402         class.lpfnWndProc   = CLOCK_WndProc;
403         class.cbClsExtra    = 0;
404         class.cbWndExtra    = 0;
405         class.hInstance     = hInstance;
406         class.hIcon         = LoadIconW(0, (LPCWSTR)IDI_APPLICATION);
407         class.hCursor       = LoadCursorW(0, (LPCWSTR)IDC_ARROW);
408         class.hbrBackground = 0;
409         class.lpszMenuName  = 0;
410         class.lpszClassName = szClassName;
411     }
412     
413     if (!RegisterClassW(&class)) return FALSE;
414     
415     Globals.MaxX = Globals.MaxY = INITIAL_WINDOW_SIZE;
416     Globals.hMainWnd = CreateWindowW(szClassName, szWinName, WS_OVERLAPPEDWINDOW,
417                                      CW_USEDEFAULT, CW_USEDEFAULT,
418                                      Globals.MaxX, Globals.MaxY, 0,
419                                      0, hInstance, 0);
420
421     if (!CLOCK_ResetTimer())
422         return FALSE;
423
424     Globals.hMainMenu = LoadMenuW(0, MAKEINTRESOURCEW(MAIN_MENU));
425     SetMenu(Globals.hMainWnd, Globals.hMainMenu);
426     CLOCK_UpdateMenuCheckmarks();
427     CLOCK_UpdateWindowCaption();
428     
429     ShowWindow (Globals.hMainWnd, show);
430     UpdateWindow (Globals.hMainWnd);
431     
432     while (GetMessageW(&msg, 0, 0, 0)) {
433         TranslateMessage(&msg);
434         DispatchMessageW(&msg);
435     }
436
437     KillTimer(Globals.hMainWnd, TIMER_ID);
438     DeleteObject(Globals.hFont);
439
440     return 0;
441 }