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