[FORMATTING] Remove trailing whitespace. Addendum to 34593d93.
[reactos.git] / dll / cpl / timedate / clock.c
1 /*
2 * PROJECT: ReactOS Timedate Control Panel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: dll/cpl/timedate/clock.c
5 * PURPOSE: Draws the analog clock
6 * COPYRIGHT: Copyright 2006 Ged Murphy <gedmurphy@gmail.com>
7 * Copyright 2007 Eric Kohl
8 */
9
10 /* Code based on clock.c from Programming Windows, Charles Petzold */
11
12 #include "timedate.h"
13
14 #include <math.h>
15
16 typedef struct _CLOCKDATA
17 {
18 HBRUSH hGreyBrush;
19 HPEN hGreyPen;
20 INT cxClient;
21 INT cyClient;
22 SYSTEMTIME stCurrent;
23 SYSTEMTIME stPrevious;
24 BOOL bTimer;
25 } CLOCKDATA, *PCLOCKDATA;
26
27
28 #define TWOPI (2 * 3.14159)
29
30 static const WCHAR szClockWndClass[] = L"ClockWndClass";
31
32 static VOID
33 RotatePoint(POINT pt[], INT iNum, INT iAngle)
34 {
35 INT i;
36 POINT ptTemp;
37
38 for (i = 0 ; i < iNum ; i++)
39 {
40 ptTemp.x = (INT) (pt[i].x * cos (TWOPI * iAngle / 360) +
41 pt[i].y * sin (TWOPI * iAngle / 360));
42
43 ptTemp.y = (INT) (pt[i].y * cos (TWOPI * iAngle / 360) -
44 pt[i].x * sin (TWOPI * iAngle / 360));
45
46 pt[i] = ptTemp;
47 }
48 }
49
50
51 static INT
52 DrawClock(HDC hdc, PCLOCKDATA pClockData)
53 {
54 INT iAngle,Radius;
55 POINT pt[3];
56 HBRUSH hBrushOld;
57 HPEN hPenOld = NULL;
58
59 /* Grey brush to fill the dots */
60 hBrushOld = SelectObject(hdc, pClockData->hGreyBrush);
61
62 hPenOld = GetCurrentObject(hdc, OBJ_PEN);
63
64 // TODO: Check if this conversion is correct resp. usable
65 Radius = min(pClockData->cxClient,pClockData->cyClient) * 2;
66
67 for (iAngle = 0; iAngle < 360; iAngle += 6)
68 {
69 /* Starting coords */
70 pt[0].x = 0;
71 pt[0].y = Radius;
72
73 /* Rotate start coords */
74 RotatePoint(pt, 1, iAngle);
75
76 /* Determine whether it's a big dot or a little dot
77 * i.e. 1-4 or 5, 6-9 or 10, 11-14 or 15 */
78 if (iAngle % 5)
79 {
80 pt[2].x = pt[2].y = 7;
81 SelectObject(hdc, pClockData->hGreyPen);
82 }
83 else
84 {
85 pt[2].x = pt[2].y = 16;
86 SelectObject(hdc, GetStockObject(BLACK_PEN));
87 }
88
89 pt[0].x -= pt[2].x / 2;
90 pt[0].y -= pt[2].y / 2;
91
92 pt[1].x = pt[0].x + pt[2].x;
93 pt[1].y = pt[0].y + pt[2].y;
94
95 Ellipse(hdc, pt[0].x, pt[0].y, pt[1].x, pt[1].y);
96 }
97
98 SelectObject(hdc, hBrushOld);
99 SelectObject(hdc, hPenOld);
100 return Radius;
101 }
102
103
104 static VOID
105 DrawHands(HDC hdc, SYSTEMTIME * pst, BOOL fChange, INT Radius)
106 {
107 POINT pt[3][5] = { {{0, (INT)-Radius/6}, {(INT)Radius/9, 0},
108 {0, (INT)Radius/1.8}, {(INT)-Radius/9, 0}, {0, (INT)-Radius/6}},
109 {{0, (INT)-Radius/4.5}, {(INT)Radius/18, 0}, {0, (INT) Radius*0.89},
110 {(INT)-Radius/18, 0}, {0, (INT)-Radius/4.5}},
111 {{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, (INT) Radius*0.89}} };
112 INT i, iAngle[3];
113 POINT ptTemp[3][5];
114
115 /* Black pen for outline, white brush for fill */
116 SelectObject(hdc, GetStockObject(BLACK_PEN));
117 SelectObject(hdc, GetStockObject(WHITE_BRUSH));
118
119 iAngle[0] = (pst->wHour * 30) % 360 + pst->wMinute / 2;
120 iAngle[1] = pst->wMinute * 6;
121 iAngle[2] = pst->wSecond * 6;
122
123 CopyMemory(ptTemp, pt, sizeof(pt));
124
125 for (i = fChange ? 0 : 2; i < 3; i++)
126 {
127 RotatePoint(ptTemp[i], 5, iAngle[i]);
128
129 Polygon(hdc, ptTemp[i], 5);
130 }
131 }
132
133
134 static LRESULT CALLBACK
135 ClockWndProc(HWND hwnd,
136 UINT uMsg,
137 WPARAM wParam,
138 LPARAM lParam)
139 {
140 PCLOCKDATA pClockData;
141 HDC hdc, hdcMem;
142 PAINTSTRUCT ps;
143
144 pClockData = (PCLOCKDATA)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
145
146 switch (uMsg)
147 {
148 case WM_CREATE:
149 pClockData = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CLOCKDATA));
150 SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LONG_PTR)pClockData);
151
152 pClockData->hGreyPen = CreatePen(PS_SOLID, 1, RGB(128, 128, 128));
153 pClockData->hGreyBrush = CreateSolidBrush(RGB(128, 128, 128));
154
155 GetLocalTime(&pClockData->stCurrent);
156 pClockData->stPrevious = pClockData->stCurrent;
157
158 pClockData->bTimer = (SetTimer(hwnd, ID_TIMER, 1000 - pClockData->stCurrent.wMilliseconds, NULL) != 0);
159 break;
160
161 case WM_SIZE:
162 pClockData->cxClient = LOWORD(lParam);
163 pClockData->cyClient = HIWORD(lParam);
164 break;
165
166 case WM_TIMECHANGE:
167 case WM_TIMER:
168 GetLocalTime(&pClockData->stCurrent);
169 InvalidateRect(hwnd, NULL, FALSE);
170 pClockData->stPrevious = pClockData->stCurrent;
171
172 // Reset timeout.
173 if (pClockData->bTimer)
174 {
175 SetTimer(hwnd, ID_TIMER, 1000 - pClockData->stCurrent.wMilliseconds, NULL);
176 }
177 break;
178
179 case WM_PAINT:
180 hdc = BeginPaint(hwnd, &ps);
181
182 hdcMem = CreateCompatibleDC(hdc);
183 if (hdcMem)
184 {
185 HBITMAP hBmp, hBmpOld;
186
187 hBmp = CreateCompatibleBitmap(hdc,
188 pClockData->cxClient,
189 pClockData->cyClient);
190 if (hBmp)
191 {
192 RECT rcParent;
193 HWND hParentWnd = GetParent(hwnd);
194 INT oldMap, Radius;
195 POINT oldOrg;
196
197 hBmpOld = SelectObject(hdcMem, hBmp);
198
199 SetRect(&rcParent, 0, 0, pClockData->cxClient, pClockData->cyClient);
200 MapWindowPoints(hwnd, hParentWnd, (POINT*)&rcParent, 2);
201 OffsetViewportOrgEx(hdcMem, -rcParent.left, -rcParent.top, &oldOrg);
202 SendMessage(hParentWnd, WM_PRINT, (WPARAM)hdcMem, PRF_ERASEBKGND | PRF_CLIENT);
203 SetViewportOrgEx(hdcMem, oldOrg.x, oldOrg.y, NULL);
204
205 oldMap = SetMapMode(hdcMem, MM_ISOTROPIC);
206 SetWindowExtEx(hdcMem, 3600, 2700, NULL);
207 SetViewportExtEx(hdcMem, 800, -600, NULL);
208 SetViewportOrgEx(hdcMem,
209 pClockData->cxClient / 2,
210 pClockData->cyClient / 2,
211 &oldOrg);
212
213 Radius = DrawClock(hdcMem, pClockData);
214 DrawHands(hdcMem, &pClockData->stPrevious, TRUE, Radius);
215
216 SetMapMode(hdcMem, oldMap);
217 SetViewportOrgEx(hdcMem, oldOrg.x, oldOrg.y, NULL);
218
219 BitBlt(hdc,
220 0,
221 0,
222 pClockData->cxClient,
223 pClockData->cyClient,
224 hdcMem,
225 0,
226 0,
227 SRCCOPY);
228
229 SelectObject(hdcMem, hBmpOld);
230 DeleteObject(hBmp);
231 }
232
233 DeleteDC(hdcMem);
234 }
235
236 EndPaint(hwnd, &ps);
237 break;
238
239 /* No need to erase background, handled during paint */
240 case WM_ERASEBKGND:
241 return 1;
242
243 case WM_DESTROY:
244 DeleteObject(pClockData->hGreyPen);
245 DeleteObject(pClockData->hGreyBrush);
246
247 if (pClockData->bTimer)
248 KillTimer(hwnd, ID_TIMER);
249
250 HeapFree(GetProcessHeap(), 0, pClockData);
251 break;
252
253 case CLM_STOPCLOCK:
254 if (pClockData->bTimer)
255 {
256 KillTimer(hwnd, ID_TIMER);
257 pClockData->bTimer = FALSE;
258 }
259 break;
260
261 case CLM_STARTCLOCK:
262 if (!pClockData->bTimer)
263 {
264 SYSTEMTIME LocalTime;
265
266 GetLocalTime(&LocalTime);
267 pClockData->bTimer = (SetTimer(hwnd, ID_TIMER, 1000 - LocalTime.wMilliseconds, NULL) != 0);
268 }
269 break;
270
271 default:
272 DefWindowProcW(hwnd,
273 uMsg,
274 wParam,
275 lParam);
276 }
277
278 return TRUE;
279 }
280
281
282 BOOL
283 RegisterClockControl(VOID)
284 {
285 WNDCLASSEXW wc = {0};
286
287 wc.cbSize = sizeof(WNDCLASSEXW);
288 wc.lpfnWndProc = ClockWndProc;
289 wc.hInstance = hApplet;
290 wc.hCursor = LoadCursorW(NULL, IDC_ARROW);
291 wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
292 wc.lpszClassName = szClockWndClass;
293
294 return RegisterClassExW(&wc) != (ATOM)0;
295 }
296
297
298 VOID
299 UnregisterClockControl(VOID)
300 {
301 UnregisterClassW(szClockWndClass,
302 hApplet);
303 }