|
|
/*++
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); }
|