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
10 /* Code based on clock.c from Programming Windows, Charles Petzold */
16 typedef struct _CLOCKDATA
23 SYSTEMTIME stPrevious
;
25 } CLOCKDATA
, *PCLOCKDATA
;
28 #define TWOPI (2 * 3.14159)
30 static const WCHAR szClockWndClass
[] = L
"ClockWndClass";
33 RotatePoint(POINT pt
[], INT iNum
, INT iAngle
)
38 for (i
= 0 ; i
< iNum
; i
++)
40 ptTemp
.x
= (INT
) (pt
[i
].x
* cos (TWOPI
* iAngle
/ 360) +
41 pt
[i
].y
* sin (TWOPI
* iAngle
/ 360));
43 ptTemp
.y
= (INT
) (pt
[i
].y
* cos (TWOPI
* iAngle
/ 360) -
44 pt
[i
].x
* sin (TWOPI
* iAngle
/ 360));
52 DrawClock(HDC hdc
, PCLOCKDATA pClockData
)
59 /* Grey brush to fill the dots */
60 hBrushOld
= SelectObject(hdc
, pClockData
->hGreyBrush
);
62 hPenOld
= GetCurrentObject(hdc
, OBJ_PEN
);
64 // TODO: Check if this conversion is correct resp. usable
65 Radius
= min(pClockData
->cxClient
,pClockData
->cyClient
) * 2;
67 for (iAngle
= 0; iAngle
< 360; iAngle
+= 6)
73 /* Rotate start coords */
74 RotatePoint(pt
, 1, iAngle
);
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 */
80 pt
[2].x
= pt
[2].y
= 7;
81 SelectObject(hdc
, pClockData
->hGreyPen
);
85 pt
[2].x
= pt
[2].y
= 16;
86 SelectObject(hdc
, GetStockObject(BLACK_PEN
));
89 pt
[0].x
-= pt
[2].x
/ 2;
90 pt
[0].y
-= pt
[2].y
/ 2;
92 pt
[1].x
= pt
[0].x
+ pt
[2].x
;
93 pt
[1].y
= pt
[0].y
+ pt
[2].y
;
95 Ellipse(hdc
, pt
[0].x
, pt
[0].y
, pt
[1].x
, pt
[1].y
);
98 SelectObject(hdc
, hBrushOld
);
99 SelectObject(hdc
, hPenOld
);
105 DrawHands(HDC hdc
, SYSTEMTIME
* pst
, BOOL fChange
, INT Radius
)
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}} };
115 /* Black pen for outline, white brush for fill */
116 SelectObject(hdc
, GetStockObject(BLACK_PEN
));
117 SelectObject(hdc
, GetStockObject(WHITE_BRUSH
));
119 iAngle
[0] = (pst
->wHour
* 30) % 360 + pst
->wMinute
/ 2;
120 iAngle
[1] = pst
->wMinute
* 6;
121 iAngle
[2] = pst
->wSecond
* 6;
123 CopyMemory(ptTemp
, pt
, sizeof(pt
));
125 for (i
= fChange
? 0 : 2; i
< 3; i
++)
127 RotatePoint(ptTemp
[i
], 5, iAngle
[i
]);
129 Polygon(hdc
, ptTemp
[i
], 5);
134 static LRESULT CALLBACK
135 ClockWndProc(HWND hwnd
,
140 PCLOCKDATA pClockData
;
144 pClockData
= (PCLOCKDATA
)GetWindowLongPtrW(hwnd
, GWLP_USERDATA
);
149 pClockData
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(CLOCKDATA
));
150 SetWindowLongPtrW(hwnd
, GWLP_USERDATA
, (LONG_PTR
)pClockData
);
152 pClockData
->hGreyPen
= CreatePen(PS_SOLID
, 1, RGB(128, 128, 128));
153 pClockData
->hGreyBrush
= CreateSolidBrush(RGB(128, 128, 128));
155 GetLocalTime(&pClockData
->stCurrent
);
156 pClockData
->stPrevious
= pClockData
->stCurrent
;
158 pClockData
->bTimer
= (SetTimer(hwnd
, ID_TIMER
, 1000 - pClockData
->stCurrent
.wMilliseconds
, NULL
) != 0);
162 pClockData
->cxClient
= LOWORD(lParam
);
163 pClockData
->cyClient
= HIWORD(lParam
);
168 GetLocalTime(&pClockData
->stCurrent
);
169 InvalidateRect(hwnd
, NULL
, FALSE
);
170 pClockData
->stPrevious
= pClockData
->stCurrent
;
173 if (pClockData
->bTimer
)
175 SetTimer(hwnd
, ID_TIMER
, 1000 - pClockData
->stCurrent
.wMilliseconds
, NULL
);
180 hdc
= BeginPaint(hwnd
, &ps
);
182 hdcMem
= CreateCompatibleDC(hdc
);
185 HBITMAP hBmp
, hBmpOld
;
187 hBmp
= CreateCompatibleBitmap(hdc
,
188 pClockData
->cxClient
,
189 pClockData
->cyClient
);
193 HWND hParentWnd
= GetParent(hwnd
);
197 hBmpOld
= SelectObject(hdcMem
, hBmp
);
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
);
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,
213 Radius
= DrawClock(hdcMem
, pClockData
);
214 DrawHands(hdcMem
, &pClockData
->stPrevious
, TRUE
, Radius
);
216 SetMapMode(hdcMem
, oldMap
);
217 SetViewportOrgEx(hdcMem
, oldOrg
.x
, oldOrg
.y
, NULL
);
222 pClockData
->cxClient
,
223 pClockData
->cyClient
,
229 SelectObject(hdcMem
, hBmpOld
);
239 /* No need to erase background, handled during paint */
244 DeleteObject(pClockData
->hGreyPen
);
245 DeleteObject(pClockData
->hGreyBrush
);
247 if (pClockData
->bTimer
)
248 KillTimer(hwnd
, ID_TIMER
);
250 HeapFree(GetProcessHeap(), 0, pClockData
);
254 if (pClockData
->bTimer
)
256 KillTimer(hwnd
, ID_TIMER
);
257 pClockData
->bTimer
= FALSE
;
262 if (!pClockData
->bTimer
)
264 SYSTEMTIME LocalTime
;
266 GetLocalTime(&LocalTime
);
267 pClockData
->bTimer
= (SetTimer(hwnd
, ID_TIMER
, 1000 - LocalTime
.wMilliseconds
, NULL
) != 0);
283 RegisterClockControl(VOID
)
285 WNDCLASSEXW wc
= {0};
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
;
294 return RegisterClassExW(&wc
) != (ATOM
)0;
299 UnregisterClockControl(VOID
)
301 UnregisterClassW(szClockWndClass
,