oleaut32: Fix CLSIDToString behaviour when passed an invalid CLSID-string.
[wine.git] / programs / clock / main.c
blobbbbb69121b5f36e5778a727e2eace1511bdd005a
1 /*
2 * Clock
4 * Copyright 1998 Marcel Baur <mbaur@g26.ethz.ch>
6 * Clock is partially based on
7 * - Program Manager by Ulrich Schmied
8 * - rolex.c by Jim Peterson
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.
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.
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
26 #include <stdio.h>
28 #include "windows.h"
29 #include "commdlg.h"
31 #include "main.h"
32 #include "winclock.h"
34 #define INITIAL_WINDOW_SIZE 200
35 #define TIMER_ID 1
37 CLOCK_GLOBALS Globals;
39 static VOID CLOCK_UpdateMenuCheckmarks(VOID)
41 HMENU hPropertiesMenu;
42 hPropertiesMenu = GetSubMenu(Globals.hMainMenu, 0);
43 if (!hPropertiesMenu)
44 return;
46 if(Globals.bAnalog) {
48 /* analog clock */
49 CheckMenuRadioItem(hPropertiesMenu, IDM_ANALOG, IDM_DIGITAL, IDM_ANALOG, MF_CHECKED);
50 EnableMenuItem(hPropertiesMenu, IDM_FONT, MF_GRAYED);
52 else
54 /* digital clock */
55 CheckMenuRadioItem(hPropertiesMenu, IDM_ANALOG, IDM_DIGITAL, IDM_DIGITAL, MF_CHECKED);
56 EnableMenuItem(hPropertiesMenu, IDM_FONT, 0);
59 CheckMenuItem(hPropertiesMenu, IDM_NOTITLE, (Globals.bWithoutTitle ? MF_CHECKED : MF_UNCHECKED));
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));
66 static VOID CLOCK_UpdateWindowCaption(VOID)
68 WCHAR szCaption[MAX_STRING_LEN];
69 int chars = 0;
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';
83 LoadStringW(0, IDS_CLOCK, szCaption + chars, MAX_STRING_LEN - chars);
84 SetWindowTextW(Globals.hMainWnd, szCaption);
87 /***********************************************************************
89 * CLOCK_ResetTimer
91 static BOOL CLOCK_ResetTimer(void)
93 UINT period; /* milliseconds */
95 KillTimer(Globals.hMainWnd, TIMER_ID);
97 if (Globals.bSeconds)
98 if (Globals.bAnalog)
99 period = 50;
100 else
101 period = 500;
102 else
103 period = 1000;
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;
112 return TRUE;
115 /***********************************************************************
117 * CLOCK_ResetFont
119 static VOID CLOCK_ResetFont(VOID)
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;
129 ReleaseDC(Globals.hMainWnd, dc);
133 /***********************************************************************
135 * CLOCK_ChooseFont
137 static VOID CLOCK_ChooseFont(VOID)
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();
153 /***********************************************************************
155 * CLOCK_ToggleTitle
157 static VOID CLOCK_ToggleTitle(VOID)
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);
165 else {
166 style = (style & ~(WS_POPUP|WS_THICKFRAME)) | WS_OVERLAPPEDWINDOW;
167 SetMenu(Globals.hMainWnd, Globals.hMainMenu);
168 SetWindowRgn(Globals.hMainWnd, 0, TRUE);
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);
174 CLOCK_UpdateMenuCheckmarks();
175 CLOCK_UpdateWindowCaption();
178 /***********************************************************************
180 * CLOCK_ToggleOnTop
182 static VOID CLOCK_ToggleOnTop(VOID)
184 if ((Globals.bAlwaysOnTop = !Globals.bAlwaysOnTop)) {
185 SetWindowPos(Globals.hMainWnd, HWND_TOPMOST, 0,0,0,0,
186 SWP_NOMOVE|SWP_NOSIZE);
188 else {
189 SetWindowPos(Globals.hMainWnd, HWND_NOTOPMOST, 0,0,0,0,
190 SWP_NOMOVE|SWP_NOSIZE);
192 CLOCK_UpdateMenuCheckmarks();
194 /***********************************************************************
196 * CLOCK_MenuCommand
198 * All handling of main menu events
201 static int CLOCK_MenuCommand (WPARAM wParam)
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;
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;
223 /* change font */
224 case IDM_FONT: {
225 CLOCK_ChooseFont();
226 break;
228 /* hide title bar */
229 case IDM_NOTITLE: {
230 CLOCK_ToggleTitle();
231 break;
233 /* always on top */
234 case IDM_ONTOP: {
235 CLOCK_ToggleOnTop();
236 break;
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;
248 /* show or hide date */
249 case IDM_DATE: {
250 Globals.bDate = !Globals.bDate;
251 CLOCK_UpdateMenuCheckmarks();
252 CLOCK_UpdateWindowCaption();
253 break;
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;
263 return 0;
266 /***********************************************************************
268 * CLOCK_Paint
270 static VOID CLOCK_Paint(HWND hWnd)
272 PAINTSTRUCT ps;
273 HDC dcMem, dc;
274 HBITMAP bmMem, bmOld;
276 dc = BeginPaint(hWnd, &ps);
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);
283 bmOld = SelectObject(dcMem, bmMem);
285 SetViewportOrgEx(dcMem, -ps.rcPaint.left, -ps.rcPaint.top, NULL);
286 /* Erase the background */
287 FillRect(dcMem, &ps.rcPaint, GetSysColorBrush(COLOR_3DFACE));
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);
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);
302 SelectObject(dcMem, bmOld);
303 DeleteObject(bmMem);
304 DeleteDC(dcMem);
306 EndPaint(hWnd, &ps);
309 /***********************************************************************
311 * CLOCK_WndProc
314 static LRESULT WINAPI CLOCK_WndProc (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
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;
325 case WM_NCLBUTTONDBLCLK:
326 case WM_LBUTTONDBLCLK: {
327 CLOCK_ToggleTitle();
328 break;
331 case WM_PAINT: {
332 CLOCK_Paint(hWnd);
333 break;
337 case WM_SIZE: {
338 Globals.MaxX = LOWORD(lParam);
339 Globals.MaxY = HIWORD(lParam);
340 if (Globals.bAnalog && Globals.bWithoutTitle)
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 );
353 CLOCK_ResetFont();
354 break;
357 case WM_COMMAND: {
358 CLOCK_MenuCommand(wParam);
359 break;
362 case WM_TIMER: {
363 /* Could just invalidate what has changed,
364 * but it doesn't really seem worth the effort
366 InvalidateRect(Globals.hMainWnd, NULL, FALSE);
367 break;
370 case WM_DESTROY: {
371 PostQuitMessage (0);
372 break;
375 default:
376 return DefWindowProcW(hWnd, msg, wParam, lParam);
378 return 0;
382 /***********************************************************************
384 * WinMain
387 int PASCAL WinMain (HINSTANCE hInstance, HINSTANCE prev, LPSTR cmdline, int show)
389 MSG msg;
390 WNDCLASSW class;
392 static const WCHAR szClassName[] = {'C','L','C','l','a','s','s',0};
393 static const WCHAR szWinName[] = {'C','l','o','c','k',0};
395 /* Setup Globals */
396 memset(&Globals.hFont, 0, sizeof (Globals.hFont));
397 Globals.bAnalog = TRUE;
398 Globals.bSeconds = TRUE;
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;
413 if (!RegisterClassW(&class)) return FALSE;
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);
421 if (!CLOCK_ResetTimer())
422 return FALSE;
424 Globals.hMainMenu = LoadMenuW(0, MAKEINTRESOURCEW(MAIN_MENU));
425 SetMenu(Globals.hMainWnd, Globals.hMainMenu);
426 CLOCK_UpdateMenuCheckmarks();
427 CLOCK_UpdateWindowCaption();
429 ShowWindow (Globals.hMainWnd, show);
430 UpdateWindow (Globals.hMainWnd);
432 while (GetMessageW(&msg, 0, 0, 0)) {
433 TranslateMessage(&msg);
434 DispatchMessageW(&msg);
437 KillTimer(Globals.hMainWnd, TIMER_ID);
438 DeleteObject(Globals.hFont);
440 return 0;