/*++ Copyright (c) 1994-1998, Microsoft Corporation All rights reserved. Module Name: clock.c Abstract: This module implements the clock control for the Date/Time applet. Revision History: --*/ // // Include Files. // #include "timedate.h" #include "rc.h" #include "clock.h" // // Constant Declarations. // #define TIMER_ID 1 #define SECONDSCALE 80 #define HHAND TRUE #define MHAND FALSE #define MAXBLOBWIDTH 25 #define REPAINT 0 #define HANDPAINT 1 #define OPEN_TLEN 450 /* < half second */ #define MINDESIREDHEIGHT 3 // // Macro Definitions. // #ifdef WIN32 #define MoveTo(hdc, x, y) MoveToEx(hdc, x, y, NULL) #define GetWindowPtr(w, o) GetWindowLongPtr(w, o) #define SetWindowPtr(w, o, p) SetWindowLongPtr(w, o, (LPARAM)(p)) #else #define GetWindowPtr(w, o) GetWindowWord(w, o) #define SetWindowPtr(w, o, p) SetWindowWord(w, o, p) #endif // // Typedef Declarations. // typedef struct { int hour; // 0 - 11 hours for analog clock int minute; int second; } TIME; typedef struct { HWND hWnd; // Us. HWND hwndGetTime; // window to provide get/set time // Brushes HBRUSH hbrColorWindow; HBRUSH hbrBtnHighlight; HBRUSH hbrForeground; HBRUSH hbrBlobColor; // Pens HPEN hpenForeground; HPEN hpenBackground; HPEN hpenBlobHlt; // Dimensions of clock RECT clockRect; int clockRadius; int HorzRes; int VertRes; int aspectD; int aspectN; // Position of clock POINT clockCenter; TIME oTime; TIME nTime; } CLOCKSTR, *PCLOCKSTR; typedef struct { SHORT x; SHORT y; } TRIG; // // Array containing the sine and cosine values for hand positions. // POINT rCircleTable[] = { { 0, -7999}, { 836, -7956}, { 1663, -7825}, { 2472, -7608}, { 3253, -7308}, { 3999, -6928}, { 4702, -6472}, { 5353, -5945}, { 5945, -5353}, { 6472, -4702}, { 6928, -4000}, { 7308, -3253}, { 7608, -2472}, { 7825, -1663}, { 7956, -836 }, { 8000, 0 }, { 7956, 836 }, { 7825, 1663 }, { 7608, 2472 }, { 7308, 3253 }, { 6928, 4000 }, { 6472, 4702 }, { 5945, 5353 }, { 5353, 5945 }, { 4702, 6472 }, { 3999, 6928 }, { 3253, 7308 }, { 2472, 7608 }, { 1663, 7825 }, { 836, 7956 }, { 0, 7999 }, { -836, 7956 }, { -1663, 7825 }, { -2472, 7608 }, { -3253, 7308 }, { -4000, 6928 }, { -4702, 6472 }, { -5353, 5945 }, { -5945, 5353 }, { -6472, 4702 }, { -6928, 3999 }, { -7308, 3253 }, { -7608, 2472 }, { -7825, 1663 }, { -7956, 836 }, { -7999, -0 }, { -7956, -836 }, { -7825, -1663}, { -7608, -2472}, { -7308, -3253}, { -6928, -4000}, { -6472, -4702}, { -5945, -5353}, { -5353, -5945}, { -4702, -6472}, { -3999, -6928}, { -3253, -7308}, { -2472, -7608}, { -1663, -7825}, { -836 , -7956}, }; // // Function prototypes. // LRESULT CALLBACK ClockWndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); void ClockCreate(HWND hWnd, PCLOCKSTR np); void ClockTimer(HWND hWnd, WPARAM idTimer, PCLOCKSTR np); void ClockPaint(PCLOCKSTR np, HDC hDC, int hint); void ClockTimerInterval( HWND hWnd, PCLOCKSTR np ); void CompClockDim(HWND hWnd, PCLOCKSTR np); void CreateTools(PCLOCKSTR np); void DeleteTools(PCLOCKSTR np); void DrawFace(HDC hDC, PCLOCKSTR np); void DrawFatHand( HDC hDC, int pos, HPEN hPen, BOOL hHand, PCLOCKSTR np); void DrawHand( HDC hDC, int pos, HPEN hPen, int scale, int patMode, PCLOCKSTR np); //////////////////////////////////////////////////////////////////////////// // // ClockInit // // Registers the clock class. // //////////////////////////////////////////////////////////////////////////// TCHAR const c_szClockClass[] = CLOCK_CLASS; BOOL ClockInit( HINSTANCE hInstance) { WNDCLASS wc; if (!GetClassInfo(hInstance, c_szClockClass, &wc)) { wc.cbClsExtra = 0; wc.cbWndExtra = sizeof(PCLOCKSTR); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = NULL; wc.hIcon = NULL; wc.lpszMenuName = NULL; wc.lpszClassName = c_szClockClass; wc.hInstance = hInstance; wc.style = CS_VREDRAW | CS_HREDRAW ; wc.lpfnWndProc = ClockWndProc; return (RegisterClass(&wc)); } return (TRUE); } //////////////////////////////////////////////////////////////////////////// // // GetTimeClock // // Gets the time that we should display on the clock. // The client could have specified a function to call to get this // or an HWND to pass a message to to get it from. // //////////////////////////////////////////////////////////////////////////// void GetTimeClock( TIME *pt, PCLOCKSTR np) { SYSTEMTIME st; // // Call our time provider or default to GetTime. // if (np->hwndGetTime) { SendMessage( np->hwndGetTime, CLM_UPDATETIME, CLF_GETTIME, (LPARAM)(LPSYSTEMTIME)&st ); pt->hour = st.wHour % 12; pt->minute = st.wMinute; pt->second = st.wSecond; } else { #ifdef WIN32 GetLocalTime(&st); pt->hour = st.wHour; pt->minute = st.wMinute; pt->second = st.wSecond; #else // // No function call back and no HWND callback. // GetTime(); pt->hour = wDateTime[HOUR] % 12; pt->minute = wDateTime[MINUTE]; pt->second = wDateTime[SECOND]; #endif } } //////////////////////////////////////////////////////////////////////////// // // CreateTools // //////////////////////////////////////////////////////////////////////////// void CreateTools( PCLOCKSTR np) { #define BLOB_COLOR RGB(0, 128, 128) np->hbrForeground = GetSysColorBrush(COLOR_BTNSHADOW); np->hbrColorWindow = GetSysColorBrush(COLOR_BTNFACE); np->hbrBlobColor = CreateSolidBrush( BLOB_COLOR ); np->hbrBtnHighlight = GetSysColorBrush(COLOR_BTNHIGHLIGHT); np->hpenForeground = CreatePen(0, 1, GetSysColor(COLOR_WINDOWTEXT)); np->hpenBackground = CreatePen(0, 1, GetSysColor(COLOR_BTNFACE)); np->hpenBlobHlt = CreatePen(0, 1, RGB(0, 255, 255)); } //////////////////////////////////////////////////////////////////////////// // // DeleteTools // //////////////////////////////////////////////////////////////////////////// void DeleteTools( PCLOCKSTR np) { // DeleteObject(np->hbrForeground); // DeleteObject(np->hbrColorWindow); DeleteObject(np->hbrBlobColor); // DeleteObject(np->hbrBtnHighlight); DeleteObject(np->hpenForeground); DeleteObject(np->hpenBackground); DeleteObject(np->hpenBlobHlt); } //////////////////////////////////////////////////////////////////////////// // // CompClockDim // // Calculates the clock dimensions. // //////////////////////////////////////////////////////////////////////////// void CompClockDim( HWND hWnd, PCLOCKSTR np) { int i; int tWidth; int tHeight; tWidth = np->clockRect.right - np->clockRect.left; tHeight = np->clockRect.bottom - np->clockRect.top; if (tWidth > MulDiv(tHeight,np->aspectD,np->aspectN)) { i = MulDiv(tHeight, np->aspectD, np->aspectN); np->clockRect.left += (tWidth - i) / 2; np->clockRect.right = np->clockRect.left + i; } else { i = MulDiv(tWidth, np->aspectN, np->aspectD); np->clockRect.top += (tHeight - i) / 2; np->clockRect.bottom = np->clockRect.top + i; } } //////////////////////////////////////////////////////////////////////////// // // ClockTimerInterval // // Sets the timer interval. Two things affect this interval: // 1) if the window is iconic, or // 2) if seconds option has been disabled // In both cases, timer ticks occur every half-minute. Otherwise, timer // every half-second. // //////////////////////////////////////////////////////////////////////////// void ClockTimerInterval( HWND hWnd, PCLOCKSTR np) { // // Update every 1/2 second in the opened state. // KillTimer(hWnd, TIMER_ID); SetTimer(hWnd, TIMER_ID, OPEN_TLEN, 0L); } //////////////////////////////////////////////////////////////////////////// // // ClockSize // // Sizes the clock to the specified size. // //////////////////////////////////////////////////////////////////////////// void ClockSize( PCLOCKSTR np, int newWidth, int newHeight) { SetRect(&np->clockRect, 0, 0, newWidth, newHeight); CompClockDim(np->hWnd, np); ClockTimerInterval(np->hWnd, np); } //////////////////////////////////////////////////////////////////////////// // // DrawFace // // Draws the clock face. // //////////////////////////////////////////////////////////////////////////// void DrawFace( HDC hDC, PCLOCKSTR np) { int i; RECT tRect; LPPOINT ppt; int blobHeight, blobWidth; blobWidth = MulDiv( MAXBLOBWIDTH, (np->clockRect.right - np->clockRect.left), np->HorzRes ); blobHeight = MulDiv(blobWidth, np->aspectN, np->aspectD); if (blobHeight < 2) { blobHeight = 1; } if (blobWidth < 2) { blobWidth = 2; } InflateRect(&np->clockRect, -(blobHeight / 2), -(blobWidth / 2)); np->clockRadius = (np->clockRect.right - np->clockRect.left - 8) / 2; np->clockCenter.y = np->clockRect.top + ((np->clockRect.bottom - np->clockRect.top) / 2) - 1; np->clockCenter.x = np->clockRect.left + np->clockRadius + 3; for (i = 0; i < 60; i++) { ppt = rCircleTable + i; tRect.top = MulDiv(ppt->y, np->clockRadius, 8000) + np->clockCenter.y; tRect.left = MulDiv(ppt->x, np->clockRadius, 8000) + np->clockCenter.x; if (i % 5) { // // Draw a dot. // if (blobWidth > 2 && blobHeight >= 2) { tRect.right = tRect.left + 2; tRect.bottom = tRect.top + 2; FillRect(hDC, &tRect, GetStockObject(WHITE_BRUSH)); OffsetRect(&tRect, -1, -1); FillRect(hDC, &tRect, np->hbrForeground); tRect.left++; tRect.top++; FillRect(hDC, &tRect, np->hbrColorWindow); } } else { tRect.right = tRect.left + blobWidth; tRect.bottom = tRect.top + blobHeight; OffsetRect(&tRect, -(blobWidth / 2) , -(blobHeight / 2)); SelectObject(hDC, GetStockObject(BLACK_PEN)); SelectObject(hDC, np->hbrBlobColor); Rectangle(hDC, tRect.left, tRect.top, tRect.right, tRect.bottom); SelectObject(hDC, np->hpenBlobHlt); MoveTo(hDC, tRect.left, tRect.bottom - 1); LineTo(hDC, tRect.left, tRect.top); LineTo(hDC, tRect.right - 1, tRect.top); } } InflateRect(&np->clockRect, blobHeight / 2, blobWidth / 2); } //////////////////////////////////////////////////////////////////////////// // // DrawHand // // Draws the hands of the clock. // //////////////////////////////////////////////////////////////////////////// void DrawHand( HDC hDC, int pos, HPEN hPen, int scale, int patMode, PCLOCKSTR np) { LPPOINT lppt; int radius; MoveTo(hDC, np->clockCenter.x, np->clockCenter.y); radius = MulDiv(np->clockRadius, scale, 100); lppt = rCircleTable + pos; SetROP2(hDC, patMode); SelectObject(hDC, hPen); LineTo( hDC, np->clockCenter.x + MulDiv(lppt->x, radius, 8000), np->clockCenter.y + MulDiv(lppt->y, radius, 8000) ); } //////////////////////////////////////////////////////////////////////////// // // Adjust // //////////////////////////////////////////////////////////////////////////// void Adjust( POINT *rgpt, int cPoint, int iDelta) { int i; for (i = 0; i < cPoint; i++) { rgpt[i].x += iDelta; rgpt[i].y += iDelta; } } //////////////////////////////////////////////////////////////////////////// // // DrawFatHand // // Draws either hour or minute hand. // //////////////////////////////////////////////////////////////////////////// void DrawFatHand( HDC hDC, int pos, HPEN hPen, BOOL hHand, PCLOCKSTR np) { int m; int n; int scale; TRIG tip; TRIG stip; BOOL fErase; POINT rgpt[4]; HBRUSH hbrInit, hbrControl = NULL; SetROP2(hDC, 13); fErase = (hPen == np->hpenBackground); SelectObject(hDC, hPen); scale = hHand ? 7 : 5; n = (pos + 15) % 60; m = MulDiv(np->clockRadius, scale, 100); stip.y = (SHORT)MulDiv(rCircleTable[n].y, m, 8000); stip.x = (SHORT)MulDiv(rCircleTable[n].x, m, 8000); scale = hHand ? 65 : 80; tip.y = (SHORT)MulDiv(rCircleTable[pos % 60].y, MulDiv(np->clockRadius, scale, 100), 8000); tip.x = (SHORT)MulDiv(rCircleTable[pos % 60].x, MulDiv(np->clockRadius, scale, 100), 8000); rgpt[0].x = np->clockCenter.x + stip.x; rgpt[0].y = np->clockCenter.y + stip.y; rgpt[1].x = np->clockCenter.x + tip.x; rgpt[1].y = np->clockCenter.y + tip.y; rgpt[2].x = np->clockCenter.x - stip.x; rgpt[2].y = np->clockCenter.y - stip.y; scale = hHand ? 15 : 20; n = (pos + 30) % 60; m = MulDiv(np->clockRadius, scale, 100); tip.y = (SHORT)MulDiv(rCircleTable[n].y, m, 8000); tip.x = (SHORT)MulDiv(rCircleTable[n].x, m, 8000); rgpt[3].x = np->clockCenter.x + tip.x; rgpt[3].y = np->clockCenter.y + tip.y; SelectObject(hDC, GetStockObject(NULL_PEN)); if (fErase) { hbrControl = (HBRUSH)SendMessage(GetParent(np->hWnd), WM_CTLCOLORSTATIC, (WPARAM)hDC, (LPARAM)np->hWnd); hbrInit = SelectObject(hDC, hbrControl ? hbrControl : np->hbrColorWindow); } else { hbrInit = SelectObject(hDC, np->hbrBtnHighlight); } Adjust(rgpt, 4, -2); Polygon(hDC, rgpt, 4); if (!fErase) { SelectObject(hDC, np->hbrForeground); } Adjust(rgpt, 4, 4); Polygon(hDC, rgpt, 4); if (!fErase) { SelectObject(hDC, np->hbrBlobColor); } Adjust(rgpt, 4, -2); Polygon(hDC, rgpt, 4); // // If we selected a brush in, reset it now. // if (fErase) { SelectObject(hDC, hbrInit); } } //////////////////////////////////////////////////////////////////////////// // // ClockPaint // // Only paints the clock. // // It assumes you have set nTime already. This allows it to be called by // the timer or by the client. // //////////////////////////////////////////////////////////////////////////// void ClockPaint( PCLOCKSTR np, HDC hDC, int hint) { SetBkMode(hDC, TRANSPARENT); if (hint == REPAINT) { // // If doing a full repaint, we do not advance the time. // Otherwise we will create artifacts when there is a clipping // region. // DrawFace(hDC, np); DrawFatHand(hDC, np->oTime.hour * 5 + (np->oTime.minute / 12), np->hpenForeground, HHAND,np); DrawFatHand(hDC, np->oTime.minute, np->hpenForeground, MHAND,np); // // Draw the second hand. // DrawHand(hDC, np->oTime.second, np->hpenBackground, SECONDSCALE, R2_NOT,np); } else if (hint == HANDPAINT) { DrawHand(hDC, np->oTime.second, np->hpenBackground, SECONDSCALE, R2_NOT, np); if (np->nTime.minute != np->oTime.minute || np->nTime.hour != np->oTime.hour) { DrawFatHand(hDC, np->oTime.minute, np->hpenBackground, MHAND, np); DrawFatHand(hDC, np->oTime.hour * 5 + (np->oTime.minute / 12), np->hpenBackground, HHAND,np); DrawFatHand(hDC, np->nTime.minute, np->hpenForeground, MHAND, np); DrawFatHand(hDC, (np->nTime.hour) * 5 + (np->nTime.minute / 12), np->hpenForeground, HHAND, np); } DrawHand(hDC, np->nTime.second, np->hpenBackground, SECONDSCALE, R2_NOT, np); np->oTime = np->nTime; } } //////////////////////////////////////////////////////////////////////////// // // ClockTimer // // Update the clock. Called on a timer tick. // //////////////////////////////////////////////////////////////////////////// void ClockTimer( HWND hWnd, UINT_PTR idTimer, PCLOCKSTR np) { HDC hDC; GetTimeClock(&np->nTime, np); if ((np->nTime.second == np->oTime.second) && (np->nTime.minute == np->oTime.minute) && (np->nTime.hour == np->oTime.hour)) { return; } hDC = GetDC(hWnd); ClockPaint(np, hDC, HANDPAINT); ReleaseDC(hWnd, hDC); } //////////////////////////////////////////////////////////////////////////// // // ClockCreate // //////////////////////////////////////////////////////////////////////////// void ClockCreate( HWND hWnd, PCLOCKSTR np) { int i; HDC hDC; int HorzSize; int VertSize; LPPOINT lppt; hDC = GetDC(hWnd); np->VertRes = GetDeviceCaps(hDC, VERTRES); np->HorzRes = GetDeviceCaps(hDC, HORZRES); VertSize= GetDeviceCaps(hDC, VERTSIZE); HorzSize= GetDeviceCaps(hDC, HORZSIZE); ReleaseDC(hWnd, hDC); np->aspectN = MulDiv(np->VertRes, 100, VertSize); np->aspectD = MulDiv(np->HorzRes, 100, HorzSize); // // Instance stuff. // np->hWnd = hWnd; CreateTools(np); // // Scale cosines for aspect ratio if this is the first instance. // for (i = 0; i < 60; i++) { lppt = rCircleTable + i; lppt->y = MulDiv(lppt->y, np->aspectN, np->aspectD); } } //////////////////////////////////////////////////////////////////////////// // // ClockWndProc // // Deals with the clock messages. // //////////////////////////////////////////////////////////////////////////// LRESULT CALLBACK ClockWndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { PCLOCKSTR np = (PCLOCKSTR)GetWindowPtr(hWnd, 0); switch (message) { case ( WM_DESTROY ) : { if (np) { KillTimer(hWnd, TIMER_ID); DeleteTools(np); LocalFree((HLOCAL)np); SetWindowPtr(hWnd, 0, 0); } break; } case ( WM_CREATE ) : { // // Allocate the instance data space. // np = (PCLOCKSTR)LocalAlloc(LPTR, sizeof(CLOCKSTR)); if (!np) { return (-1); } SetWindowPtr(hWnd, 0, np); ClockCreate(hWnd, np); SetLayout(GetDC(hWnd), LAYOUT_BITMAPORIENTATIONPRESERVED); // // Loop if control panel time being changed. // GetTimeClock(&(np->nTime), np); do { GetTimeClock(&(np->oTime), np); } while (np->nTime.second == np->oTime.second && np->nTime.minute == np->oTime.minute && np->nTime.hour == np->oTime.hour); SetTimer(hWnd, TIMER_ID, OPEN_TLEN, 0L); ClockSize( np, ((LPCREATESTRUCT)lParam)->cx, ((LPCREATESTRUCT)lParam)->cy ); break; } case ( WM_SIZE ) : { if (np) { ClockSize(np, LOWORD(lParam), HIWORD(lParam)); } break; } case ( WM_PAINT ) : { PAINTSTRUCT ps; BeginPaint(hWnd, &ps); GetTimeClock(&(np->nTime), np); ClockPaint(np, ps.hdc, REPAINT); EndPaint(hWnd, &ps); break; } case ( WM_TIMECHANGE ) : { // // I'm not top level - so I wont get this message. // InvalidateRect(hWnd, NULL, TRUE); if (np->hwndGetTime) { SYSTEMTIME System; GetTime(); System.wHour = wDateTime[HOUR]; System.wMinute = wDateTime[MINUTE]; System.wSecond = wDateTime[SECOND]; SendMessage( np->hwndGetTime, CLM_UPDATETIME, CLF_SETTIME, (LPARAM)(LPSYSTEMTIME)&System ); } // fall thru... } case ( WM_TIMER ) : { ClockTimer(hWnd, wParam, np); break; } case ( WM_SYSCOLORCHANGE ) : { DeleteTools(np); CreateTools(np); break; } case ( CLM_UPDATETIME ) : { // // Force the clock to repaint. lParam will point to a // SYSTEMTIME struct. // switch (wParam) { case ( CLF_SETTIME ) : { // // Caller wants us to reflect a new time. // HDC hDC; LPSYSTEMTIME lpSysTime = (LPSYSTEMTIME)lParam; np->nTime.hour = lpSysTime->wHour; np->nTime.minute = lpSysTime->wMinute; np->nTime.second = lpSysTime->wSecond; hDC = GetDC(hWnd); ClockPaint(np, hDC, HANDPAINT); ReleaseDC(hWnd, hDC); break; } case ( CLF_GETTIME ) : { // // Caller wants to know what we think the time is. // LPSYSTEMTIME lpSysTime = (LPSYSTEMTIME)lParam; lpSysTime->wHour = (WORD)np->nTime.hour; lpSysTime->wMinute = (WORD)np->nTime.minute; lpSysTime->wSecond = (WORD)np->nTime.second; break; } } break; } case ( CLM_TIMEHWND ) : { // // Get/Set the HWND that we ask to provide the time. // switch (wParam) { case ( CLF_SETHWND ) : { // // Caller wants us to reflect a new time. // np->hwndGetTime = (HWND)lParam; break; } case ( CLF_GETTIME ) : { // // Caller wants to know what we think the time is. // *((HWND *)lParam) = np->hwndGetTime; break; } } break; } default : { return ( DefWindowProc(hWnd, message, wParam, lParam) ); } } return (0); }