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 } 304