windows-nt/Source/XPSP1/NT/shell/osshell/accesory/clock/clock.c

2714 lines
84 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/****************************************************************************/
/* */
/* Touched by : Diane K. Oh */
/* On Date : June 11, 1992 */
/* Revision remarks by Diane K. Oh ext #15201 */
/* This file has been changed to comply with the Unicode standard */
/* Following is a quick overview of what I have done. */
/* */
/* Was Changed it into Remark */
/* === =============== ====== */
/* CHAR TCHAR if it refers to text */
/* LPCHAR & LPSTR LPTSTR if it refers to text */
/* PSTR & NPSTR LPTSTR if it refers to text */
/* "..." TEXT("...") compile time macro resolves it */
/* '...' TEXT('...') same */
/* */
/* strlen lstrlen compile time macro resolves it */
/* */
/* Notes: */
/* */
/* 1. Added LPTSTR typecast before MAKEINTRESOURCE to remove warning */
/* 2. Size was multiplied by sizeof(TCHAR) when allocating strings. */
/* 3. WinMain has not been Unicode enabled so lpCmdLine parameter */
/* remains as LPSTR type. */
/* 4. Changed typecast from FARPROC to LPCFHOOKPROC to remove warning */
/* */
/****************************************************************************/
#include <windows.h> /* required for all Windows applications */
#include <stdlib.h>
#include "commdlg.h"
#include "shellapi.h"
#include "clock.h" /* specific to this program */
#if defined(JAPAN)
#include <dlgs.h>
#endif
BOOL InitApplication (HANDLE);
HRGN CreateEllipticWndRgn(HWND,LPRECT);
LRESULT FAR PASCAL MainWndProc (HWND, UINT, WPARAM, LPARAM);
#if defined(JAPAN)
#define NATIVE_CHARSET SHIFTJIS_CHARSET
UINT APIENTRY ExceptVerticalFont(HWND, UINT, UINT, LONG);
#endif
HANDLE hInst; /* current instance */
void ParseSavedWindow(LPTSTR szBuf, PRECT pRect );
void NEAR PASCAL DeleteTools (void);
void NEAR PASCAL PrintShadowText (register HDC hDC, int nx, int ny, int sizex, int sizey, LPTSTR pszStr, int nStrLen, HDC hOffScrnDC);
void NEAR PASCAL ClockSize (register HWND hWnd,int newWidth,int newHeight,WORD SizeWord);
void NEAR PASCAL ClockTimerInterval (HWND hWnd);
void NEAR PASCAL CompClockDim (void);
void NEAR PASCAL ClockTimer (HWND hWnd);
void NEAR PASCAL FormatTimeStr (void);
void NEAR PASCAL ClockPaint (HWND hWnd, register HDC hDC, int hint);
void NEAR PASCAL DrawFace (HDC hDC);
void NEAR PASCAL DrawFatHand (register HDC hDC, int pos, HPEN hPen, BOOL hHand);
void NEAR PASCAL DrawHand (register HDC hDC, int pos, HPEN hPen, int scale, int patMode);
void NEAR PASCAL DrawIconBorder (HWND hWnd, register HDC hDC);
void NEAR PASCAL FormatDateStr (xDATE *pDate, BOOL bRealShort);
void NEAR PASCAL SizeFont (HWND hWnd, int newHeight, int newWidth);
void NEAR PASCAL SetMenuBar (HWND hWnd);
void NEAR PASCAL FormatInit (VOID);
VOID NEAR PASCAL SaveClockOptions (HWND hWnd);
void NEAR PASCAL ResetWinTitle (HWND hWnd);
void NEAR PASCAL CreateTools (void);
void NEAR PASCAL ClockCreate (HWND hWnd);
TIME oTime;
xDATE oDate;
CLOCKDISPSTRUCT ClockDisp;
LOGFONT FontStruct;
typedef struct
{
SHORT x;
SHORT y;
} TRIG;
#define FNOSHAD_MONOCHROME 1
#define FNOSHAD_USERSAYSNO 2
short fNoShadow = 0;
BOOL bUtc = FALSE;
BOOL bFirst = TRUE;
BOOL bColor = TRUE;
BOOL fShadedHands = FALSE;
BOOL bNewFont = TRUE;
BOOL bCantHide = FALSE;
BOOL bTmpHide = FALSE;
BOOL fDisplay = FALSE;
TCHAR szBuffer[BUFLEN]; /* buffer for stringtable stuff */
TCHAR szAppName[BUFLEN];
TCHAR szIniFile[20];
TCHAR szDfltFontFile[20];
TCHAR szSection[30];
TCHAR szFontFileKey[20];
HBRUSH hbrColorWindow;
HBRUSH hbrBtnHighlight;
HBRUSH hbrForeground;
HBRUSH hbrBlobColor;
HFONT hFont = NULL;
HFONT hFontDate = NULL;
#define bDisplayDate (!ClockDisp.bNoDate && !ClockDisp.bIconic)
RECT clockRect;
RECT rCoordRect;
TRIG clockCenter;
HPEN hpenForeground;
HPEN hpenShadow;
HPEN hpenBackground;
HPEN hpenBlobHlt;
HPEN hpenRed;
HCURSOR hCurWait;
/* win.ini strings... Don't internationalize */
TCHAR szMaximized[] = TEXT("Maximized");
TCHAR szOptions[] = TEXT("Options");
TCHAR szPosition[] = TEXT("Position");
TCHAR szNoShadow[] = TEXT("NoShadow");
#if defined(JAPAN)
TCHAR szCharSet[] = TEXT("Charset");
#endif
int TimerID = 1; /* number used for timer-id */
int clockRadius;
int HorzRes;
int VertRes;
int aspectD;
int aspectN;
extern TRIG CirTab[];
TRIG FAR *lpcirTab = CirTab;
#if defined(JAPAN) || defined(KOREA)
UINT FAR PASCAL ExceptVerticalFont(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
#endif
void MoveTo (HDC hdc, int x, int y)
{
MoveToEx (hdc, x, y, NULL);
}
INT MyAtoi (LPTSTR Str)
{
CHAR szAnsi [160];
BOOL fDefCharUsed;
#ifdef UNICODE
WideCharToMultiByte (CP_ACP, 0, Str, -1, szAnsi, 160, NULL, &fDefCharUsed);
return (atoi (szAnsi));
#else
return (atoi (Str));
#endif
}
/****************************************************************************
FUNCTION: WinMain(HANDLE, HANDLE, LPSTR, int)
PURPOSE: calls initialization function, processes message loop
COMMENTS:
Windows recognizes this function by name as the initial entry point
for the program. This function calls the application initialization
routine, if no other instance of the program is running, and always
calls the instance initialization routine. It then executes a message
retrieval and dispatch loop that is the top-level control structure
for the remainder of execution. The loop is terminated when a WM_QUIT
message is received, at which time this function exits the application
instance by returning the value passed by PostQuitMessage().
If this function must abort before entering the message loop, it
returns the conventional value NULL.
****************************************************************************/
int APIENTRY WinMain (HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
MSG msg; /* message */
HDC hDC;
HWND hWnd;
HMENU hMenu;
int iMaximized;
TCHAR szParseStr [80];
TIME nTime;
LPTSTR szTooMany;
TCHAR szTopmost [80];
#ifdef JAPAN
int i;
#endif
UNREFERENCED_PARAMETER( lpCmdLine );
hInst = hInstance;
FormatInit ();
if (!InitApplication (hInstance)) /* Initialize shared things */
return (FALSE); /* Exits if unable to initialize */
/* Perform initializations that apply to a specific instance */
LoadString (hInstance, IDS_APPNAME, szAppName, BUFLEN);
LoadString (hInstance, IDS_USNAME, szSection, 30);
LoadString (hInstance, IDS_INIFILE, szIniFile, 20);
ClockCreate ((HWND)NULL);
hDC = GetDC (NULL);
if ((UINT)GetDeviceCaps (hDC, NUMCOLORS) <= 2)
fNoShadow = FNOSHAD_MONOCHROME;
ReleaseDC (NULL, hDC);
if (GetPrivateProfileInt (szSection, szNoShadow, 0, szIniFile))
fNoShadow |= FNOSHAD_USERSAYSNO;
/* get window position and size from ini file */
GetPrivateProfileString (szSection, szPosition, TEXT(""), szParseStr,
80, szIniFile);
ParseSavedWindow (szParseStr, &rCoordRect);
hWnd = CreateWindow (szSection, /* The class name. */
szSection, /* The window instance name. */
WS_TILEDWINDOW,
rCoordRect.left, rCoordRect.top,
rCoordRect.right, rCoordRect.bottom,
NULL,
NULL,
hInstance,
NULL);
// Loop if control panel time being changed.
GetTime (&oTime);
do
{
GetTime (&nTime);
}
while (nTime.second == oTime.second && nTime.minute == oTime.minute &&
nTime.hour24 == oTime.hour24);
GetDate (&oDate);
if (!SetTimer (hWnd, TimerID, OPEN_TLEN, 0L))
{
/* Windows only supports 16 public timers */
szTooMany = (LPTSTR) LocalAlloc (LPTR, 160 * sizeof(TCHAR));
LoadString (hInstance, IDS_TOOMANY, szTooMany, 160);
MessageBox ((HWND)NULL, szTooMany, szBuffer, MB_OK | MB_ICONHAND | MB_SYSTEMMODAL);
DeleteTools ();
return (FALSE);
}
/* get font choice from ini file */
#ifdef JAPAN
lstrcpy (szDfltFontFile, TEXT("System"));
#else
LoadString (hInstance, IDS_FONTFILE, szDfltFontFile, 20);
#endif
LoadString (hInstance, IDS_FONTCHOICE, szFontFileKey, 20);
GetPrivateProfileString (szSection, szFontFileKey, szDfltFontFile,
FontStruct.lfFaceName, 20, szIniFile);
FontStruct.lfPitchAndFamily = FF_SWISS | VARIABLE_PITCH;
#ifdef JAPAN
if (!(i = GetPrivateProfileInt(szSection, szCharSet,
ANSI_CHARSET, szIniFile)))
FontStruct.lfCharSet = ANSI_CHARSET;
else
FontStruct.lfCharSet = i;
#else
FontStruct.lfCharSet = ANSI_CHARSET;
#endif
FontStruct.lfWeight = FW_NORMAL;
FontStruct.lfQuality = DEFAULT_QUALITY;
FontStruct.lfUnderline = FALSE;
FontStruct.lfStrikeOut = FALSE;
FontStruct.lfEscapement = 0;
FontStruct.lfOrientation = 0;
FontStruct.lfOutPrecision = OUT_DEFAULT_PRECIS;
FontStruct.lfClipPrecision = CLIP_DEFAULT_PRECIS;
#ifdef JAPAN
/* We can use only NATIVE_CHARSET */
if (FontStruct.lfCharSet != NATIVE_CHARSET)
{
FontStruct.lfCharSet = NATIVE_CHARSET;
lstrcpy (FontStruct.lfFaceName, TEXT("System"));
}
#endif
/* get clock options from ini file */
GetPrivateProfileString (szSection, szOptions, TEXT(""), szParseStr, 80, szIniFile);
ParseSavedFlags (szParseStr, &ClockDisp);
FormatTimeStr ();
FormatDateStr (&oDate, ClockDisp.bIconic);
hMenu = GetMenu (hWnd);
/* Check the default menu item either analog or digital */
CheckMenuItem (hMenu, ClockDisp.wFormat, MF_BYCOMMAND | MF_CHECKED);
if (ClockDisp.wFormat == IDM_ANALOG)
EnableMenuItem( hMenu, IDM_SETFONT, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
if (!ClockDisp.bNoSeconds)
{
CheckMenuItem (hMenu, IDM_SECONDS, MF_BYCOMMAND | MF_CHECKED);
ClockTimerInterval (hWnd);
}
if (!ClockDisp.bNoDate)
{
CheckMenuItem (hMenu, IDM_DATE, MF_BYCOMMAND | MF_CHECKED);
ResetWinTitle (hWnd);
}
hMenu = GetSystemMenu (hWnd, FALSE);
AppendMenu (hMenu, MF_SEPARATOR, 0, NULL);
LoadString (hInstance, IDS_TOPMOST, szTopmost, 79);
if (ClockDisp.bTopMost)
{
AppendMenu (hMenu, MF_ENABLED | MF_CHECKED | MF_STRING, IDM_TOPMOST,
szTopmost);
SetWindowPos (hWnd, (HWND)-1, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
}
else
AppendMenu (hMenu, MF_ENABLED | MF_UNCHECKED | MF_STRING, IDM_TOPMOST,
szTopmost);
if (ClockDisp.bNoTitle)
SetMenuBar (hWnd);
if (!ClockDisp.bIconic)
{
iMaximized = GetPrivateProfileInt (szSection, szMaximized, 0, szIniFile);
if (iMaximized)
ShowWindow (hWnd, SW_MAXIMIZE);
else
{
ShowWindow (hWnd, nCmdShow);
GetWindowRect (hWnd, &rCoordRect);
}
}
else
ShowWindow (hWnd, SW_MINIMIZE);
/* Acquire and dispatch messages until a WM_QUIT message is received. */
while (GetMessage (&msg, /* message structure */
NULL, /* handle of window receiving the message */
0, /* lowest message to examine */
0)) /* highest message to examine */
{
TranslateMessage (&msg); /* Translates virtual key codes */
DispatchMessage (&msg); /* Dispatches message to window */
}
return (int)(msg.wParam); /* Returns the value from PostQuitMessage */
hPrevInstance; //UNUSED
nCmdShow; //UNUSED
}
/****************************************************************************
FUNCTION: InitApplication(HANDLE)
PURPOSE: Initializes window data and registers window class
COMMENTS:
This function is called at initialization time only if no other
instances of the application are running. This function performs
initialization tasks that can be done once for any number of running
instances.
In this case, we initialize a window class by filling out a data
structure of type WNDCLASS and calling the Windows RegisterClass()
function. Since all instances of this application use the same window
class, we only need to do this when the first instance is initialized.
****************************************************************************/
BOOL InitApplication (HANDLE hInstance) /* current instance */
{
WNDCLASS ClockClass;
/* Fill in window class structure with parameters that describe the */
/* main window. */
ClockClass.cbClsExtra = ClockClass.cbWndExtra = 0;
ClockClass.lpszClassName = TEXT("Clock");
ClockClass.lpszMenuName = TEXT("Clock"); //szSection;
ClockClass.hbrBackground = GetStockObject(LTGRAY_BRUSH);
ClockClass.style = CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS;
ClockClass.hInstance = hInstance;
ClockClass.lpfnWndProc = MainWndProc;
ClockClass.hCursor = LoadCursor (NULL, IDC_ARROW);
ClockClass.hIcon = NULL;
/* Register the window class and return success/failure code. */
return (RegisterClass (&ClockClass));
}
/****************************************************************************
FUNCTION: MainWndProc(HWND, unsigned, WORD, LONG)
PURPOSE: Processes messages
MESSAGES:
WM_COMMAND - application menu (About dialog box)
WM_DESTROY - destroy window
****************************************************************************/
LRESULT APIENTRY MainWndProc (HWND hWnd, /* window handle */
UINT message, /* type of message */
WPARAM wParam, /* additional information */
LPARAM lParam) /* additional information */
{
HMENU hMenu;
PAINTSTRUCT ps;
switch (message)
{
case WM_COMMAND:
switch (LOWORD (wParam))
{
case IDM_ANALOG:
case IDM_DIGITAL:
if (LOWORD (wParam) != ClockDisp.wFormat)
{
/* Switch flag to other choice */
hMenu = GetMenu(hWnd);
CheckMenuItem(hMenu, ClockDisp.wFormat, MF_BYCOMMAND | MF_UNCHECKED);
CheckMenuItem(hMenu, ClockDisp.wFormat = (WORD)wParam, MF_BYCOMMAND | MF_CHECKED);
if (wParam == IDM_ANALOG)
EnableMenuItem (hMenu, IDM_SETFONT, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
else
EnableMenuItem (hMenu, IDM_SETFONT, MF_BYCOMMAND | MF_ENABLED);
ResetWinTitle (hWnd);
InvalidateRect (hWnd, (LPRECT)NULL, TRUE);
}
break;
case IDM_SETFONT:
{
CHOOSEFONT cf;
short cy, nOldFontHeight;
#ifdef JAPAN
/* Win 3.1 */
LOGFONT OldFontStruct;
#endif
/* calls the font chooser (in commdlg)
*/
cf.lStructSize = sizeof (CHOOSEFONT);
cf.hwndOwner = hWnd;
cf.hInstance = hInst;
cf.lpTemplateName = (LPTSTR) MAKEINTRESOURCE(IDD_FONT);
cf.hDC = NULL;
cf.lpLogFont = &FontStruct;
FontStruct.lfItalic = 0;
FontStruct.lfWeight = FW_NORMAL;
cf.Flags = CF_SCREENFONTS | CF_INITTOLOGFONTSTRUCT |
#ifdef JAPAN
CF_NOSIMULATIONS | CF_ENABLETEMPLATE |
#else
CF_NOSIMULATIONS | CF_ANSIONLY | CF_ENABLETEMPLATE |
#endif
CF_NOVECTORFONTS;
#if defined(JAPAN)
/* except vertical font from list */
cf.Flags |= CF_ENABLEHOOK;
cf.lpfnHook = (LPCFHOOKPROC)ExceptVerticalFont;
#else
cf.lpfnHook = (LPCFHOOKPROC) NULL;
#endif
#ifdef JAPAN
/* Win 3.1 */
OldFontStruct = FontStruct;
#endif
nOldFontHeight = (short) FontStruct.lfHeight;
cy = HIWORD (GetDialogBaseUnits ());
/* 36 is the (height - 1) of the stc5 static control in font.dlg template */
if (((36 * cy) / 8) < -FontStruct.lfHeight)
FontStruct.lfHeight = -((36 * cy) / 8);
if (ChooseFont (&cf))
{
#if defined(JAPAN) || defined(KOREA)
/* We can use only NATIVE_CHARSET */
if (FontStruct.lfCharSet != NATIVE_CHARSET)
{
FontStruct = OldFontStruct;
break;
}
#endif
/* write new font info to ini file */
WritePrivateProfileString (szSection, szFontFileKey,
FontStruct.lfFaceName, szIniFile);
bNewFont = TRUE;
InvalidateRect(hWnd, NULL, TRUE);
}
else /* restore old height */
FontStruct.lfHeight = nOldFontHeight;
break;
}
case IDM_UTC:
hMenu = GetMenu(hWnd);
if (!bUtc)
{
bUtc = TRUE;
CheckMenuItem(hMenu, IDM_UTC, MF_BYCOMMAND | MF_CHECKED);
}
else
{
bUtc = FALSE;
CheckMenuItem (hMenu, IDM_UTC, MF_BYCOMMAND | MF_UNCHECKED);
}
// call FormatInit to add or remove am/pm string (utc
// doesn't have it, non-utc does)
FormatInit();
ResetWinTitle (hWnd);
FormatTimeStr ();
bNewFont = TRUE;
InvalidateRect (hWnd, (LPRECT)NULL, TRUE);
ClockTimer (hWnd);
break;
case IDM_NOTITLE:
goto toggle_title;
case IDM_SECONDS:
/* toggle seconds option
*/
hMenu = GetMenu (hWnd);
if (ClockDisp.bNoSeconds)
{
CheckMenuItem (hMenu, IDM_SECONDS, MF_BYCOMMAND | MF_CHECKED);
ClockDisp.bNoSeconds = FALSE;
}
else
{
CheckMenuItem (hMenu, IDM_SECONDS, MF_BYCOMMAND | MF_UNCHECKED);
ClockDisp.bNoSeconds = TRUE;
}
ClockTimerInterval (hWnd);
FormatTimeStr ();
bNewFont = TRUE;
InvalidateRect (hWnd, (LPRECT)NULL, TRUE);
break;
case IDM_DATE:
/* toggles date option
*/
hMenu = GetMenu (hWnd);
if (ClockDisp.bNoDate)
{
CheckMenuItem (hMenu, IDM_DATE, MF_BYCOMMAND | MF_CHECKED);
ClockDisp.bNoDate = FALSE;
}
else
{
CheckMenuItem (hMenu, IDM_DATE, MF_BYCOMMAND | MF_UNCHECKED);
ClockDisp.bNoDate = TRUE;
}
bNewFont = TRUE;
if (ClockDisp.wFormat == IDM_DIGITAL)
InvalidateRect(hWnd, (LPRECT)NULL, TRUE);
else
ResetWinTitle (hWnd);
break;
case IDM_ABOUT:
ShellAbout(hWnd, szAppName, TEXT(""), LoadIcon(hInst, TEXT("cckk")));
break;
default:
goto defproc;
}
break;
case WM_MOUSEACTIVATE:
/* right button temporarily hides the window if topmost is
* enabled (window re-appears when right button is released).
* When this happens, we don't want to activate the clock window
* just before hiding it (it would look really bad), so we
* intercept the activate message.
*/
if (GetAsyncKeyState (VK_RBUTTON) & 0x8000)
return (MA_NOACTIVATE);
else
goto defproc;
break;
case WM_INITMENU:
bCantHide = TRUE;
goto defproc;
case WM_MENUSELECT:
if (LOWORD (lParam) == -1 && HIWORD(lParam) == 0)
bCantHide = FALSE;
goto defproc;
case WM_RBUTTONDOWN:
case WM_NCRBUTTONDOWN:
/* right button temporarily hides the window, if the window
* is topmost, and if no menu is currently "active"
*/
if (!bTmpHide && ClockDisp.bTopMost && !bCantHide)
{
ShowWindow (hWnd, SW_HIDE);
SetCapture (hWnd);
bTmpHide = TRUE;
}
break;
case WM_RBUTTONUP:
case WM_NCRBUTTONUP:
/* if window is currently hidden, right button up brings it
* back. Must make sure we show it in its previous state - ie:
* minimized, maximized or normal.
*/
if (bTmpHide)
{
ReleaseCapture ();
if (ClockDisp.bIconic)
ShowWindow (hWnd, SW_SHOWMINNOACTIVE);
else if (IsZoomed (hWnd))
ShowWindow (hWnd, SW_SHOWMAXIMIZED);
else
ShowWindow (hWnd, SW_SHOWNOACTIVATE);
bTmpHide = FALSE;
}
break;
case WM_KEYDOWN:
/* ESC key toggles the menu/title bar (just like a double click
* on the client area of the window.
*/
if ((wParam == VK_ESCAPE) && !(HIWORD (lParam) & 0x4000))
goto toggle_title;
break;
case WM_NCLBUTTONDBLCLK:
if (!ClockDisp.bNoTitle)
/* if we have title bars etc. let the normal sutff take place */
goto defproc;
/* else: no title bars, then this is actually a request to bring
* the title bars back...
*/
/* fall through */
case WM_LBUTTONDBLCLK:
toggle_title:
fDisplay = FALSE;
ClockDisp.bNoTitle = (ClockDisp.bNoTitle ? FALSE : TRUE);
SetMenuBar (hWnd);
break;
case WM_NCHITTEST:
/* if we have no title/menu bar, clicking and dragging the client
* area moves the window. To do this, return HTCAPTION.
* Note dragging not allowed if window maximized, or if caption
* bar is present.
*/
wParam = DefWindowProc(hWnd, message, wParam, lParam);
if (ClockDisp.bNoTitle && (wParam == HTCLIENT) && !IsZoomed(hWnd))
return HTCAPTION;
else
return wParam;
case WM_SIZE:
bNewFont = TRUE;
ClockSize(hWnd, (int) LOWORD (lParam), (int) HIWORD (lParam), (WORD) wParam);
UpdateWindow(hWnd);
break;
case WM_QUERYDRAGICON:
return (LRESULT) LoadIcon (hInst, TEXT("cckk"));
case WM_DESTROY:
{
HCURSOR hTempCursor;
KillTimer (hWnd, TimerID);
DeleteTools ();
if (hFont)
DeleteObject (hFont);
if (hFontDate)
DeleteObject (hFontDate);
if (ClockDisp.hBitmap)
DeleteObject (ClockDisp.hBitmap);
SetCapture (hWnd);
hTempCursor = SetCursor (LoadCursor (NULL, IDC_WAIT));
SaveClockOptions (hWnd);
PostQuitMessage (0);
break;
}
case WM_WININICHANGE:
FormatInit ();
FormatDateStr (&oDate, ClockDisp.bIconic);
ResetWinTitle (hWnd);
FormatTimeStr ();
bNewFont = TRUE;
InvalidateRect (hWnd, (LPRECT)NULL, TRUE);
break;
case WM_PAINT:
/* 25-Mar-1987. Added to force total repaint to solve
* problem of garbage under second hand when hidden
* by menu or popup.
*/
InvalidateRect (hWnd, (LPRECT)NULL, TRUE);
fDisplay = FALSE;
BeginPaint (hWnd, &ps);
ClockPaint (hWnd, ps.hdc, REPAINT);
EndPaint (hWnd, &ps);
break;
case WM_TIMECHANGE:
/* Redraw. */
oDate.day = 0;
InvalidateRect (hWnd, (LPRECT)NULL, TRUE);
/* fall through */
case WM_TIMER:
ClockTimer (hWnd);
break;
case WM_SYSCOMMAND:
switch (wParam)
{
case SC_MINIMIZE:
if (!IsZoomed (hWnd))
GetWindowRect (hWnd, &rCoordRect);
if (ClockDisp.bTopMost)
{
ClockDisp.bTopMost = FALSE;
PostMessage (hWnd, WM_SYSCOMMAND, IDM_TOPMOST, 0L);
}
break;
case SC_MAXIMIZE:
if (!IsIconic (hWnd))
GetWindowRect (hWnd, &rCoordRect);
break;
case IDM_TOPMOST:
{
/* toggles topmost option
*/
hMenu = GetSystemMenu (hWnd, FALSE);
if (ClockDisp.bTopMost)
{
CheckMenuItem (hMenu, IDM_TOPMOST, MF_BYCOMMAND | MF_UNCHECKED);
SetWindowPos (hWnd, HWND_NOTOPMOST, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
ClockDisp.bTopMost = FALSE;
}
else
{
CheckMenuItem (hMenu, IDM_TOPMOST, MF_BYCOMMAND | MF_CHECKED);
SetWindowPos (hWnd, HWND_TOPMOST, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
ClockDisp.bTopMost = TRUE;
}
break;
}
}
return (DefWindowProc(hWnd, message, wParam, lParam));
break;
case WM_SYSCOLORCHANGE:
DeleteTools ();
CreateTools ();
break;
case WM_ERASEBKGND:
{
RECT rect;
HBRUSH hBr;
DWORD rgbCol;
HDC hDC;
GetClientRect (hWnd, &rect);
hDC = GetDC (hWnd);
SetBkMode (hDC, OPAQUE);
/* Make a temp brush to color the background. This is to
* force use of a solid color so the hand motion is painted
* correctly.
*/
rgbCol = GetNearestColor(hDC, GetSysColor(COLOR_BTNFACE));
/* CHECK RETURN VALUE!! */
hBr = CreateSolidBrush(rgbCol);
FillRect((HDC)wParam, &rect, hBr);
ReleaseDC (hWnd, hDC);
DeleteObject(hBr);
break;
}
case WM_ENDSESSION:
if (wParam)
SaveClockOptions (hWnd);
break;
default: /* Passes it on if unproccessed */
defproc:
return (DefWindowProc (hWnd, message, wParam, lParam));
}
return (0);
}
/*
* ResetWinTitle() - Sets the window title with the date if appropriate
*
* The date is part of the window text when the clock is minimized, or when
* it is analog, and the date option is selected.
*/
void NEAR PASCAL ResetWinTitle (HWND hWnd)
{
TCHAR szNewTitle[BUFLEN+20];
if (((ClockDisp.wFormat == IDM_DIGITAL) && !ClockDisp.bIconic)
|| ClockDisp.bNoDate)
SetWindowText (hWnd, szAppName); /* no date in title if digital */
else
{
wsprintf (szNewTitle, TEXT("%s - %s"), szAppName, ClockDisp.szDate);
SetWindowText (hWnd, szNewTitle);
}
}
void NEAR PASCAL CreateTools (void)
{
#define BLOB_COLOR RGB (0,128,128)
hbrForeground = CreateSolidBrush (GetSysColor (COLOR_BTNSHADOW));
hbrColorWindow = CreateSolidBrush (GetSysColor (COLOR_BTNFACE));
hbrBtnHighlight= CreateSolidBrush (GetSysColor (COLOR_BTNHIGHLIGHT));
hbrBlobColor = CreateSolidBrush (BLOB_COLOR);
hpenForeground = CreatePen (0, 1, GetSysColor (COLOR_WINDOWTEXT));
hpenShadow = CreatePen (0, 1, GetSysColor (COLOR_BTNSHADOW));
hpenBackground = CreatePen (0, 1, GetSysColor (COLOR_BTNFACE));
hpenBlobHlt = CreatePen (0, 1, RGB (0,255,255));
hpenRed = CreatePen (0, 1, RGB (255, 0, 0));
hCurWait = LoadCursor (NULL, IDC_WAIT);
}
void NEAR PASCAL ClockCreate (HWND hWnd)
{
register HDC hDC;
int HorzSize;
int VertSize;
hDC = GetDC (hWnd);
VertRes = GetDeviceCaps (hDC, VERTRES);
HorzRes = GetDeviceCaps (hDC, HORZRES);
VertSize= GetDeviceCaps (hDC, VERTSIZE);
HorzSize= GetDeviceCaps (hDC, HORZSIZE);
ReleaseDC (hWnd, hDC);
aspectN = MulDiv (VertRes, 100, VertSize);
aspectD = MulDiv (HorzRes, 100, HorzSize);
CreateTools ();
}
/*
* 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.
* Gets the flags from ClockDisp structure.
*/
void PASCAL ClockTimerInterval (HWND hWnd)
{
if (ClockDisp.bIconic || ClockDisp.bNoSeconds)
{
/* Update once every 1/2 minute in the iconic state, or if
* seconds display suppressed
*/
KillTimer (hWnd, TimerID);
SetTimer (hWnd, TimerID, (WORD)ICON_TLEN, 0L);
}
else
{
/* Update every 1/2 second in the opened state. */
KillTimer (hWnd, TimerID);
SetTimer (hWnd, TimerID, OPEN_TLEN, 0L);
}
}
/*
* ClockTimer()
*
* msg - timer ID
*
* Called by windows to tell CLOCK there has been a time change.
*
*/
void NEAR PASCAL ClockTimer (HWND hWnd)
{
HDC hDC;
TIME nTime;
xDATE nDate;
GetTime (&nTime);
GetDate (&nDate);
if (fDisplay)
return;
if (ClockDisp.bNoSeconds || ClockDisp.bIconic)
{
KillTimer (hWnd, TimerID);
SetTimer (hWnd, TimerID, (WORD)(61 - nTime.second) * 100, 0L);
}
/* It's possible to change any part of the system at any time
* through the Control Panel. So we check everything.
*/
if (((nTime.second == oTime.second) || ClockDisp.bIconic || ClockDisp.bNoSeconds) &&
(nTime.minute == oTime.minute) &&
(nTime.hour24 == oTime.hour24) &&
((nDate.day == oDate.day &&
nDate.month == oDate.month &&
nDate.year == oDate.year ) || ClockDisp.bNoDate))
return;
hDC = GetDC (hWnd);
ClockPaint (hWnd, hDC, HANDPAINT);
ReleaseDC (hWnd, hDC);
}
VOID GetTime (TIME * pTime)
{
SYSTEMTIME st;
if (bUtc)
GetSystemTime (&st);
else
GetLocalTime (&st);
if (st.wHour > 11)
pTime->ampm = 1;
else
pTime->ampm = 0;
if (st.wHour == 0 && !ClockDisp.wTimeFormat)
st.wHour = 12;
pTime->hour = st.wHour;
pTime->hour24 = pTime->hour12 = st.wHour;
if (pTime->hour12 > 12)
pTime->hour12 -= 12;
pTime->minute = st.wMinute;
pTime->second = st.wSecond;
}
void NEAR GetDate (xDATE * pDate)
{
SYSTEMTIME st;
if (bUtc)
GetSystemTime(&st);
else
GetLocalTime(&st);
pDate->day= st.wDay;
pDate->month= st.wMonth;
pDate->year= st.wYear;
}
/*
* FormatTimeStr()
*
* Creates a template for the time, based on options in the ClockDisp
* structure. ie: seconds or no seconds, AM/PM string, etc.
* The result is used in ExtTextOut calls to determine the size of the
* time string.
* Note that this is based on the assumption that digits are fixed pitch
* (and so '8' is used as a dummy digit).
* Bad things will happen if the selected font has proportional digits!!!
*/
void NEAR PASCAL FormatTimeStr (void)
{
LPTSTR pstr;
int i;
TCHAR szAMPM[MAX_AMPM_LEN];
TCHAR szTime[MAX_TIME_SEP + MAX_AMPM_LEN + 8];
int iAMPMLen;
pstr = ClockDisp.szTimeFmt;
if (!ClockDisp.bIconic)
{
/*
* Build the widest possible AM/PM string.
* This assumes the widest proportional character is M.
*/
iAMPMLen = ClockDisp.nMaxAMPMLen;
for (i = 0; i < iAMPMLen; i++)
{
szAMPM[i] = TEXT('M');
}
if (ClockDisp.wAMPMPosition && iAMPMLen)
{
/*
* Length includes a space, so write the space to the
* buffer instead of the M.
*/
szAMPM[i - 1] = TEXT(' ');
}
szAMPM[i] = TEXT('\0');
/*
* Get the Time string.
*/
wsprintf (szTime, TEXT("88%c88"), ClockDisp.szTimeSep[0]);
if (!ClockDisp.bNoSeconds)
{
wsprintf (szTime + 5, TEXT("%c88"), ClockDisp.szTimeSep[0]);
}
lstrcat (szTime, TEXT(" "));
/*
* Build the actual format string in the proper order.
*/
if (ClockDisp.wAMPMPosition)
{
lstrcpy (pstr, szAMPM);
lstrcat (pstr, szTime);
}
else
{
lstrcpy (pstr, szTime);
lstrcat (pstr, szAMPM);
}
}
else
{
/*
* Icon - only show hours and minutes.
*/
wsprintf (pstr, TEXT("88%c88"), ClockDisp.szTimeSep[0] );
}
/*
* Remove trailing space, if necessary.
*/
ClockDisp.nTimeLen = lstrlen (pstr);
if (pstr[ClockDisp.nTimeLen - 1] == TEXT(' '))
{
ClockDisp.nTimeLen--;
pstr[ClockDisp.nTimeLen] = TEXT('\0');
}
}
/*
* ClockPaint()
*/
void NEAR PASCAL ClockPaint (HWND hWnd, register HDC hDC, int hint)
{
int hour;
RECT Rect;
TIME nTime;
xDATE nDate;
TCHAR szTime[6];
HDC hTmpDC = NULL;
HBITMAP hOldBM;
HFONT h;
GetTime (&nTime);
GetDate (&nDate);
if ((nDate.day != oDate.day) ||
(nDate.month != oDate.month) ||
(nDate.year != oDate.year))
{
/* new date - so reformat date string */
FormatDateStr (&nDate, ClockDisp.bIconic);
ResetWinTitle (hWnd);
bNewFont = TRUE;
}
if (ClockDisp.wFormat == IDM_DIGITAL)
{
/*******************/
/* Digital Display */
/*******************/
/* Is the font ready yet? */
if (!hFont || bNewFont)
{
GetClientRect (hWnd, &Rect);
/* Create a suitable font */
SizeFont (hWnd, Rect.bottom - Rect.top, Rect.right - Rect.left);
bNewFont = FALSE;
}
if (hFont)
h = SelectObject (hDC, hFont);
else
h = 0;
SetBkColor (hDC, GetSysColor (COLOR_BTNFACE));
SetTextAlign (hDC, TA_LEFT);
if (ClockDisp.wShdwOff && (hTmpDC = CreateCompatibleDC (hDC)))
{
/* we will want to print shadowed text. Attempt to allocate
* the offscreen DC and bitmap for this.
*/
if (!(hOldBM = SelectObject (hTmpDC, ClockDisp.hBitmap)))
{
/* select failed. Perhaps the bitmap was discarded.
* So attempt to reallocate a bitmap
*/
DeleteObject (ClockDisp.hBitmap);
ClockDisp.hBitmap = CreateDiscardableBitmap (hDC,
2*ClockDisp.nSizeChar + 2*ClockDisp.wShdwOff,
ClockDisp.nSizeY);
if (!ClockDisp.hBitmap ||
!(hOldBM = SelectObject (hTmpDC, ClockDisp.hBitmap)))
{
/* either we couldn't re-allocate the bitmap, or the
* select failed again (ie: not because the bitmap
* was discarded). Either way, we can't use an offscreen.
*/
DeleteDC (hTmpDC);
hTmpDC = NULL;
}
}
if (hTmpDC)
{
/* we can use our offscreen. Prepare it for use.
*/
if (hFont)
SelectObject (hTmpDC, hFont);
SetBkColor (hTmpDC, GetSysColor (COLOR_BTNFACE));
SetTextAlign (hTmpDC, TA_LEFT);
}
}
if (hint == REPAINT || ClockDisp.bIconic)
{
SelectObject (hDC, hbrColorWindow);
/* Set old values as undefined, so entire clock updated. */
oTime.hour24 = 25;
oTime.minute = 60;
oTime.ampm = 2;
oDate.day = 0;
/* paint the separators */
PrintShadowText (hDC,
ClockDisp.nPosSep1,
ClockDisp.nPosY,
ClockDisp.nSizeSep,
ClockDisp.nSizeY,
&ClockDisp.szTimeSep[0],
1,
hTmpDC);
if (!ClockDisp.bIconic && !ClockDisp.bNoSeconds)
PrintShadowText (hDC,
ClockDisp.nPosSep2,
ClockDisp.nPosY,
ClockDisp.nSizeSep,
ClockDisp.nSizeY,
&ClockDisp.szTimeSep[0],
1,
hTmpDC);
}
if (!ClockDisp.bIconic)
{
if (ClockDisp.wAMPMPosition)
{
/*
* AM/PM - Prefix.
*/
if ((oTime.ampm != nTime.ampm) || (oTime.hour24 != nTime.hour24))
{
Rect.left = ClockDisp.nPosAMPM;
Rect.top = ClockDisp.nPosY;
Rect.right = ClockDisp.nPosHr + ClockDisp.nSizeChar;
Rect.bottom = ClockDisp.nPosY + ClockDisp.nSizeY;
FillRect (hDC, &Rect, hbrColorWindow);
}
}
else
{
/*
* AM/PM - Suffix.
*/
if (oTime.ampm != nTime.ampm)
{
PrintShadowText (hDC,
ClockDisp.nPosAMPM,
ClockDisp.nPosY,
ClockDisp.nSizeAMPM,
ClockDisp.nSizeY,
ClockDisp.szAMPM[nTime.ampm],
lstrlen (ClockDisp.szAMPM[nTime.ampm]),
hTmpDC);
}
}
}
if (!ClockDisp.bIconic && !ClockDisp.bNoSeconds)
{
/* paint the seconds */
szTime[0] = TEXT('0') + nTime.second / 10;
szTime[1] = TEXT('0') + nTime.second % 10;
PrintShadowText (hDC,
ClockDisp.nPosSec,
ClockDisp.nPosY,
2 * ClockDisp.nSizeChar,
ClockDisp.nSizeY,
szTime,
2,
hTmpDC);
}
if (oTime.minute != nTime.minute)
{
/* paint the minutes */
szTime[0] = TEXT('0') + nTime.minute / 10;
szTime[1] = TEXT('0') + nTime.minute % 10;
PrintShadowText (hDC,
ClockDisp.nPosMin,
ClockDisp.nPosY,
2 * ClockDisp.nSizeChar,
ClockDisp.nSizeY,
szTime,
2,
hTmpDC);
}
if (oTime.hour24 != nTime.hour24)
{
/* paint the hours */
if (ClockDisp.wTimeFormat)
hour = nTime.hour24;
else
hour = nTime.hour12;
szTime[0] = TEXT('0') + hour / 10;
szTime[1] = TEXT('0') + hour % 10;
/* Kill Leading zero if needed. */
if (ClockDisp.wTimeLZero == 0 && szTime[0] == TEXT('0'))
{
PrintShadowText (hDC,
ClockDisp.nPosHr + ClockDisp.nSizeChar,
ClockDisp.nPosY,
ClockDisp.nSizeChar,
ClockDisp.nSizeY,
szTime + 1,
1,
hTmpDC);
if ((oTime.hour12 > 9) ||
(ClockDisp.wTimeFormat && (oTime.hour24 > 9)))
{
/* if we just switched from 12 to 1 (or 11 to 0),
* erase leading 1
*/
Rect.left = ClockDisp.nPosHr;
Rect.top = ClockDisp.nPosY;
Rect.right = ClockDisp.nPosHr + ClockDisp.nSizeChar;
Rect.bottom = ClockDisp.nPosY + ClockDisp.nSizeY;
FillRect (hDC, &Rect, hbrColorWindow);
}
}
else
{
PrintShadowText (hDC,
ClockDisp.nPosHr,
ClockDisp.nPosY,
2 * ClockDisp.nSizeChar,
ClockDisp.nSizeY,
szTime,
2,
hTmpDC);
}
}
if (ClockDisp.wAMPMPosition && !ClockDisp.bIconic)
{
if ((oTime.ampm != nTime.ampm) || (oTime.hour24 != nTime.hour24))
{
int hh;
if (ClockDisp.wTimeFormat)
hour = nTime.hour24;
else
hour = nTime.hour12;
szTime[0] = TEXT('0') + hour / 10;
/* Kill Leading zero if needed. */
if (ClockDisp.wTimeLZero == 0 && szTime[0] == TEXT('0'))
hh = ClockDisp.nSizeChar;
else
hh = 0;
PrintShadowText (hDC,
ClockDisp.nPosAMPM + hh,
ClockDisp.nPosY,
ClockDisp.nSizeAMPM,
ClockDisp.nSizeY,
ClockDisp.szAMPM[nTime.ampm],
lstrlen (ClockDisp.szAMPM[nTime.ampm]),
hTmpDC);
}
}
if (hTmpDC)
{
SelectObject (hDC, hOldBM);
DeleteDC (hTmpDC);
hTmpDC = NULL;
}
if ((oDate.day != nDate.day ||
oDate.month != nDate.month ||
oDate.year != nDate.year) && bDisplayDate)
{
/* paint the date - first erase old string, 'cause new string
* may be shorter. Problem: we no longer know the extent of
* the old string, so paint the whole x-extent of the clock
* window. Don't worry, this doesn't happen too often. */
GetClientRect (hWnd, &Rect);
Rect.top = ClockDisp.nPosDateY;
Rect.bottom = ClockDisp.nPosDateY + ClockDisp.nSizeDateY;
FillRect (hDC, &Rect, hbrColorWindow);
if (hFontDate)
h = SelectObject (hDC, hFontDate);
else
h = 0;
PrintShadowText (hDC,
ClockDisp.nPosDateX,
ClockDisp.nPosDateY,
ClockDisp.nSizeDateX,
ClockDisp.nSizeDateY,
ClockDisp.szDate,
ClockDisp.nDateLen,
hTmpDC);
}
if (h)
SelectObject(hDC, h);
}
else
{
/******************/
/* Analog display */
/******************/
if (hint == REPAINT)
{
SetBkMode (hDC, TRANSPARENT);
DrawFace (hDC);
if (!ClockDisp.bIconic)
{
DrawFatHand (hDC, oTime.hour * 5 + (oTime.minute / 12), hpenForeground, HHAND);
DrawFatHand (hDC, oTime.minute, hpenForeground, MHAND);
}
else
{
DrawHand (hDC, oTime.hour * 5 + (oTime.minute / 12), hpenForeground, HOURSCALE, R2_COPYPEN);
DrawHand (hDC, oTime.minute, hpenForeground, MINUTESCALE, R2_COPYPEN);
}
if (!ClockDisp.bIconic && !ClockDisp.bNoSeconds)
/* Draw the second hand. */
DrawHand (hDC, oTime.second, hpenBackground, SECONDSCALE, R2_NOT);
/* NOTE: Don't update oTime in this case! */
if (ClockDisp.bIconic)
DrawIconBorder (hWnd, hDC);
return;
}
else if (hint == HANDPAINT)
{
SetBkMode(hDC, TRANSPARENT);
if ((!ClockDisp.bIconic && !ClockDisp.bNoSeconds) && nTime.second != oTime.second)
/* Erase the old second hand. */
DrawHand (hDC, oTime.second, hpenBackground, SECONDSCALE, R2_NOT);
if (nTime.minute != oTime.minute || nTime.hour != oTime.hour)
{
if (ClockDisp.bIconic)
{
DrawHand (hDC, oTime.minute, hpenBackground, MINUTESCALE, R2_COPYPEN);
DrawHand (hDC, oTime.hour * 5 + (oTime.minute / 12), hpenBackground, HOURSCALE, R2_COPYPEN);
DrawHand (hDC, nTime.minute, hpenForeground, MINUTESCALE, R2_COPYPEN);
DrawHand (hDC, nTime.hour * 5 + (nTime.minute / 12), hpenForeground, HOURSCALE, R2_COPYPEN);
}
else
{
DrawFatHand (hDC, oTime.hour * 5 + (oTime.minute/12), hpenBackground, HHAND);
DrawFatHand (hDC, oTime.minute, hpenBackground, MHAND);
DrawFatHand (hDC, (nTime.hour) * 5 + (nTime.minute / 12), hpenForeground, HHAND);
DrawFatHand (hDC, nTime.minute, hpenForeground, MHAND);
}
}
if ((!ClockDisp.bIconic && !ClockDisp.bNoSeconds) && nTime.second != oTime.second)
/* Draw new second hand */
DrawHand (hDC, nTime.second, hpenBackground, SECONDSCALE, R2_NOT);
}
}
if (ClockDisp.bIconic)
DrawIconBorder (hWnd, hDC);
oTime = nTime;
oDate = nDate;
}
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 NEAR PASCAL DrawFatHand (register HDC hDC, int pos, HPEN hPen, BOOL hHand)
{
register int m;
int n;
int scale;
TRIG tip;
TRIG stip;
BOOL fErase;
SetROP2 (hDC, 13);
fErase = (hPen == hpenBackground);
SelectObject (hDC, hPen);
scale = hHand ? 7 : 5;
n = (pos + 15) % 60;
m = MulDiv (clockRadius, scale, 100);
stip.y = (SHORT) MulDiv (lpcirTab[n].y, m, 8000);
stip.x = (SHORT) MulDiv (lpcirTab[n].x, m, 8000);
scale = hHand ? 65 : 80;
tip.y = (SHORT) MulDiv (lpcirTab[pos % 60].y, MulDiv (clockRadius, scale, 100), 8000);
tip.x = (SHORT) MulDiv (lpcirTab[pos % 60].x, MulDiv (clockRadius, scale, 100), 8000);
{
POINT rgpt[4];
HBRUSH hbrInit;
rgpt[0].x = clockCenter.x + stip.x;
rgpt[0].y = clockCenter.y + stip.y;
rgpt[1].x = clockCenter.x + tip.x;
rgpt[1].y = clockCenter.y + tip.y;
rgpt[2].x = clockCenter.x - stip.x;
rgpt[2].y = clockCenter.y - stip.y;
scale = hHand ? 15 : 20;
n = (pos + 30) % 60;
m = MulDiv (clockRadius, scale, 100);
tip.y = (SHORT) MulDiv (lpcirTab[n].y, m, 8000);
tip.x = (SHORT) MulDiv (lpcirTab[n].x, m, 8000);
rgpt[3].x = clockCenter.x + tip.x;
rgpt[3].y = clockCenter.y + tip.y;
SelectObject (hDC, GetStockObject (NULL_PEN));
if (fErase)
{
hbrInit = SelectObject (hDC, hbrColorWindow);
}
if (fShadedHands)
{
Adjust (rgpt, 4, -2);
if (!fErase)
{
hbrInit = SelectObject (hDC, hbrBtnHighlight);
}
Polygon(hDC, rgpt, 4);
if (!fErase)
{
SelectObject (hDC, hbrForeground);
}
Adjust (rgpt, 4, 4);
Polygon (hDC, rgpt, 4);
Adjust (rgpt, 4, -2);
}
if (!fErase)
{
SelectObject (hDC, hbrBlobColor);
}
Polygon (hDC, rgpt, 4);
/*
* if we selected a brush in, reset it now.
*/
if (fErase || fShadedHands) {
SelectObject(hDC, hbrInit);
}
}
}
/*
* DrawFace()
*/
void NEAR PASCAL DrawFace(HDC hDC)
{
int i;
RECT tRect;
TRIG * ppt;
int blobHeight, blobWidth;
blobWidth = MulDiv (MAXBLOBWIDTH, (clockRect.right - clockRect.left), HorzRes);
blobHeight = MulDiv (blobWidth, aspectN, aspectD);
if (blobHeight < 2)
blobHeight = 1;
if (blobWidth < 2)
blobWidth = 2;
InflateRect (&clockRect, -(blobHeight >> 1), -(blobWidth >> 1));
clockRadius = (int) ((clockRect.right - clockRect.left-8) >> 1);
clockCenter.y = (SHORT) (clockRect.top + ((clockRect.bottom - clockRect.top) >> 1) - 1);
clockCenter.x = (SHORT) (clockRect.left + clockRadius+3);
for (i = 0; i < 60; i++)
{
ppt = lpcirTab + i;
tRect.top = MulDiv (ppt->y, clockRadius, 8000) + clockCenter.y;
tRect.left = MulDiv (ppt->x, clockRadius, 8000) + clockCenter.x;
fShadedHands = FALSE;
if (i % 5)
{
/* Draw a dot. */
if (blobWidth > 2 && blobHeight >= 2)
{
fShadedHands = TRUE;
tRect.right = tRect.left + 2;
tRect.bottom = tRect.top + 2;
FillRect (hDC, &tRect, GetStockObject (WHITE_BRUSH));
OffsetRect (&tRect, -1, -1);
FillRect (hDC, &tRect, hbrForeground);
tRect.left++;
tRect.top++;
FillRect (hDC, &tRect, hbrColorWindow);
}
}
else if (!ClockDisp.bIconic)
{
tRect.right = tRect.left + blobWidth;
tRect.bottom = tRect.top + blobHeight;
OffsetRect (&tRect, -(blobWidth >> 1) , -(blobHeight >> 1));
SelectObject (hDC, GetStockObject (BLACK_PEN));
SelectObject (hDC, hbrBlobColor);
Rectangle (hDC, tRect.left, tRect.top, tRect.right, tRect.bottom);
SelectObject (hDC, hpenBlobHlt);
MoveTo (hDC, tRect.left, tRect.bottom-1);
LineTo (hDC, tRect.left, tRect.top);
LineTo (hDC, tRect.right-1, tRect.top);
}
else
{
PatBlt (hDC, tRect.left, tRect.top, 2, 2, BLACKNESS);
PatBlt (hDC, tRect.left, tRect.top, 1, 1, WHITENESS);
}
}
InflateRect (&clockRect, (blobHeight >> 1), (blobWidth >> 1));
}
/*
* DrawIconBorder() - Draws a Border around either icon-clock.
*/
void NEAR PASCAL DrawIconBorder (HWND hWnd, register HDC hDC)
{
RECT Rect;
/* draws a "sunk-in" border, ie: double black outline top and left,
* single white outline bottom and right.
*/
GetClientRect (hWnd, &Rect);
SelectObject (hDC, GetStockObject (BLACK_PEN));
MoveTo (hDC, Rect.left, Rect.top + 1);
LineTo (hDC, Rect.left, Rect.bottom - 1);
MoveTo (hDC, Rect.left+1, Rect.bottom - 1);
LineTo (hDC, Rect.right - 1, Rect.bottom - 1);
MoveTo (hDC, Rect.right - 1, Rect.bottom - 2);
LineTo (hDC, Rect.right - 1, Rect.top);
MoveTo (hDC, Rect.right - 2, Rect.top);
LineTo (hDC, Rect.left, Rect.top);
MoveTo (hDC, Rect.left + 2, Rect.top + 2);
LineTo (hDC, Rect.left + 2, Rect.bottom - 3);
LineTo (hDC, Rect.right - 3, Rect.bottom - 3);
LineTo (hDC, Rect.right - 3, Rect.top + 2);
LineTo (hDC, Rect.left + 2, Rect.top + 2);
SelectObject (hDC, hpenRed);
MoveTo (hDC, Rect.left + 1, Rect.top + 1);
LineTo (hDC, Rect.left + 1, Rect.bottom - 2);
LineTo (hDC, Rect.right - 2, Rect.bottom - 2);
LineTo (hDC, Rect.right - 2, Rect.top + 1);
LineTo (hDC, Rect.left + 1, Rect.top + 1);
}
/*
* FormatDateStr() - prepare the formatted date string
*
* parameters:
* xDATE *pDate - structure contains current date
* BOOL bRealShort - if TRUE, the year is not added to the string (ie:
* only use the day and the month)
*
* This will format the given date according to the format specified
* by the current Locale, and place the result in ClockDisp.szDate.
*/
void NEAR PASCAL FormatDateStr (xDATE * pDate, BOOL bRealShort)
{
register int i = 0, j = 0;
BOOL bLead;
TCHAR cSep;
while (ClockDisp.szDateFmt[i] && (j < MAX_DATE_LEN - 1))
{
bLead = FALSE;
switch (cSep = ClockDisp.szDateFmt[i++])
{
case TEXT('d'):
while (ClockDisp.szDateFmt[i] == TEXT('d'))
{
bLead = TRUE;
i++;
}
if (bLead || (pDate->day / 10))
ClockDisp.szDate[j++] = TEXT('0') + pDate->day / 10;
ClockDisp.szDate[j++] = TEXT('0') + pDate->day % 10;
break;
case TEXT('M'):
while (ClockDisp.szDateFmt[i] == TEXT('M'))
{
bLead = TRUE;
i++;
}
if (bLead || (pDate->month / 10))
ClockDisp.szDate[j++] = TEXT('0') + pDate->month / 10;
ClockDisp.szDate[j++] = TEXT('0') + pDate->month % 10;
break;
case TEXT('y'):
i++;
if (ClockDisp.szDateFmt[i] == TEXT('y'))
{
bLead = TRUE;
i+=2;
}
if (bLead && !bRealShort)
{
ClockDisp.szDate[j++] = (pDate->year < 2000 ? TEXT('1') : TEXT('2'));
ClockDisp.szDate[j++] = (pDate->year < 2000 ? TEXT('9') : TEXT('0'));
}
if (!bRealShort)
{
ClockDisp.szDate[j++] = TEXT('0') + (pDate->year % 100) / 10;
ClockDisp.szDate[j++] = TEXT('0') + (pDate->year % 100) % 10;
}
break;
default:
/* copy the current character into the formatted string - it
* is a separator. BUT: don't copy a separator into the
* very first position (could happen if the year comes first,
* but we're not using the year)
*/
if (j)
ClockDisp.szDate[j++] = cSep;
break;
}
}
while ((ClockDisp.szDate[j-1] < TEXT('0')) || (ClockDisp.szDate[j-1] > TEXT('9')))
j--;
ClockDisp.szDate[j] = TEXT('\0');
ClockDisp.nDateLen = j;
}
/*
* PrintShadowText()
*
* Parameters:
* HDC hdc - the window DC
* int nx, ny, sizex, sizey - position and extent of bounding box
* PSTR pszStr - actual string to print
* int nStrLen - length of pszStr
* HDC hOffScrnDC - an offscreen DC used to paint the shadowed text.
* If this is NULL, the text is not shadowed.
* Assumes the bitmap associated with this DC can
* contain 2 digits. If the specified bounding box
* does not fit in the bitmap, the shadowed text is
* painted directly to the window DC (may cause flashing)
*
* Shadowed text is: a white highlight top and left, dark highlight bottom
* and right. The character itself is painted the same color as the background
* (so only noticeable if both highlights are noticeable).
*/
void NEAR PASCAL PrintShadowText (register HDC hDC, int nx, int ny,
int sizex, int sizey, LPTSTR pszStr,
int nStrLen, HDC hOffScrnDC)
{
RECT Rect;
register WORD Shadow;
if (!hOffScrnDC || fNoShadow)
{
/* If no valid offscreen DC is provided, then we can NOT print
* shadowed text. Simply print the normal font.
*/
SetTextColor (hDC, GetSysColor (COLOR_BTNTEXT));
Rect.top = ny;
Rect.bottom = ny + sizey;
Rect.left = nx;
Rect.right = nx + sizex;
ExtTextOut (hDC, nx, ny, ETO_OPAQUE | ETO_CLIPPED, &Rect,
pszStr, nStrLen, NULL);
}
else if (sizex > 2 * ClockDisp.nSizeChar)
{
/* print the shadowed text (but don't use offscreen bitmap because
* the string is too long to fit)
*/
Shadow = ClockDisp.wShdwOff;
SetTextColor (hDC, GetSysColor (COLOR_BTNHIGHLIGHT));
Rect.top = ny - Shadow;
Rect.bottom = ny + sizey + Shadow;
Rect.left = nx - Shadow;
Rect.right = nx + sizex + Shadow;
ExtTextOut (hDC, nx - Shadow, ny - Shadow,
ETO_OPAQUE | ETO_CLIPPED, &Rect, pszStr, nStrLen, NULL);
SetBkMode (hDC, TRANSPARENT);
SetTextColor (hDC, GetSysColor (COLOR_BTNSHADOW));
ExtTextOut (hDC, nx + Shadow, ny + Shadow,
ETO_CLIPPED, &Rect, pszStr, nStrLen, NULL);
SetTextColor (hDC, GetSysColor (COLOR_BTNFACE));
ExtTextOut (hDC, nx, ny, ETO_CLIPPED, &Rect, pszStr, nStrLen, NULL);
}
else
{
/* use the off-screen bitmap, and print shadowed text. */
Shadow = ClockDisp.wShdwOff;
Rect.left = 0;
Rect.right = sizex + 2* Shadow;
Rect.top = 0;
Rect.bottom = sizey + 2* Shadow;
SetTextColor (hOffScrnDC, GetSysColor (COLOR_BTNHIGHLIGHT));
ExtTextOut (hOffScrnDC, 0, 0, ETO_OPAQUE | ETO_CLIPPED, &Rect, pszStr,
nStrLen, NULL);
SetBkMode (hOffScrnDC, TRANSPARENT);
SetTextColor (hOffScrnDC, GetSysColor (COLOR_BTNSHADOW));
ExtTextOut (hOffScrnDC, 2 * Shadow, 2* Shadow,
ETO_CLIPPED, &Rect, pszStr, nStrLen, NULL);
SetTextColor (hOffScrnDC, GetSysColor (COLOR_BTNFACE));
ExtTextOut (hOffScrnDC, Shadow, Shadow,
ETO_CLIPPED, &Rect, pszStr, nStrLen, NULL);
BitBlt (hDC, nx-Shadow, ny-Shadow,
Rect.right, Rect.bottom, hOffScrnDC, 0, 0, SRCCOPY);
}
}
/*
* SizeFont() - size font according to window size
*
* Only useful in digital mode.
* Create a font that will fit the current time format (based on the
* time template). Center the string, set up the positions for the
* different components of the time string.
* Do the same for the date, unless that option is disabled.
* 2 results:
* fonts are created. The font handles are stored in global variables
* string is positioned. Results are stored in the ClockDisp structure
* (ie: starting positions and extents)
*/
void NEAR PASCAL SizeFont(HWND hWnd, int newHeight, int newWidth)
{
register HDC hDC;
SIZE sizeTimeExt;
SIZE sizeDateExt;
int nzExts[ 15 ];
#ifdef JAPAN // #1208:6/1/93:
int bFit;
LOGFONT wkFontStruct;
#endif
int nOldFit, nFit, i;
int nAMPMIndx = 0;
int iAMPMLen;
int DesiredWidth;
int DesiredHeight;
int InitialHeight;
HCURSOR hOldCur;
HFONT h;
if (ClockDisp.wFormat == IDM_DIGITAL)
{
hOldCur = SetCursor (hCurWait);
hDC = GetDC(hWnd);
if (hFont != NULL)
DeleteObject (hFont);
if (ClockDisp.bIconic) /* Adjust for fat border */
{
newWidth -= 4;
newHeight -= 4;
}
/* use up 7/8 of the x-extent of the window */
DesiredWidth = (newWidth * 7) / 8;
if (bDisplayDate)
/* display the date with the time - time only occupied a third
* of the screen height
*/
DesiredHeight = 3 * newHeight / 8;
else
/* no date - time occupies two-thirds the screen height */
DesiredHeight = 7 * newHeight / 8;
if (ClockDisp.bIconic) /* Readjust for fat border */
{
newWidth += 4;
newHeight += 4;
}
/* create initial font real big, for more precision in calculations.
* This will give us an extent for out string, we can then figure
* out the correct font height to fit the string (using a ratio).
*/
InitialHeight = 1000;
FontStruct.lfWidth = 0; /* don't stretch fonts */
FontStruct.lfHeight = -InitialHeight;
#ifdef JAPAN
/* In Win 3.0 if the following were not done some unexpected font
* maybe selected in special cases. Especially in Japan with
* vertical writing.
*/
FontStruct.lfEscapement = 0;
FontStruct.lfOrientation = 0;
#endif
#if defined(JAPAN) || defined(KOREA)
/* We can use only NATIVE_CHARSET */
if (FontStruct.lfCharSet != NATIVE_CHARSET)
{
FontStruct.lfCharSet = NATIVE_CHARSET;
lstrcpy (FontStruct.lfFaceName, TEXT("System"));
}
#endif
hFont = CreateFontIndirect(&FontStruct);
if (hFont)
h = SelectObject(hDC, hFont);
else
h = 0;
GetTextExtentPoint (hDC, ClockDisp.szTimeFmt, ClockDisp.nTimeLen, &sizeTimeExt);
if (bDisplayDate)
GetTextExtentPoint (hDC, ClockDisp.szDate, ClockDisp.nDateLen, &sizeDateExt);
if (h)
SelectObject(hDC, h);
/* compute appropriate font for time string:
* establish a ratio current size to required size.
* This may yield a font too tall to fit the window, so limit
* the font height.
*/
FontStruct.lfHeight = -MulDiv (InitialHeight, DesiredWidth,
sizeTimeExt.cx);
if (-FontStruct.lfHeight > DesiredHeight)
FontStruct.lfHeight = -DesiredHeight;
// pre-initialize nzExts in case this for loop doesn't execute
// (in really small cy cases).
for (i = 0; i < 15; nzExts[i] = 0, i++);
#ifdef JAPAN // #1208:6/1/93:
bFit = FALSE;
wkFontStruct = FontStruct;
#endif
for (nOldFit = 0, nFit = -1; FontStruct.lfHeight < 0;)
{
nOldFit = nFit;
/* little loop to take care of round-off errors, which may
* cause our initial "guess" to be slightly too large. So
* check that the string fits (in most cases it will),
* otherwise, re-create the font in a slightly smaller size.
*/
if (hFont)
DeleteObject (hFont);
hFont = CreateFontIndirect(&FontStruct);
if (hFont)
h = SelectObject(hDC, hFont);
else
h = 0;
GetTextExtentExPoint (hDC, ClockDisp.szTimeFmt, ClockDisp.nTimeLen,
DesiredWidth, &nFit, nzExts, &sizeTimeExt);
if (h)
SelectObject(hDC, h);
if (nFit == ClockDisp.nTimeLen) /* if string fits, exit loop */
#ifdef JAPAN // #1208:6/1/93:
{
bFit = TRUE;
break;
}
#else
break;
#endif
FontStruct.lfHeight += 2; /* remember height is negative! */
}
#ifdef JAPAN // #1208:6/1/93: Change display font fit iconic
if (!bFit && ClockDisp.bIconic)
{
wkFontStruct.lfPitchAndFamily = FF_SWISS | VARIABLE_PITCH;
lstrcpy (wkFontStruct.lfFaceName, TEXT(" "));
for (nOldFit = 0, nFit = -1; wkFontStruct.lfHeight < 0;)
{
nOldFit = nFit;
if (hFont)
DeleteObject (hFont);
hFont = CreateFontIndirect(&wkFontStruct);
if (hFont)
h = SelectObject(hDC, hFont);
else
h = 0;
GetTextExtentExPoint( hDC, ClockDisp.szTimeFmt,
ClockDisp.nTimeLen, DesiredWidth, &nFit, nzExts, &sizeTimeExt );
if (h)
SelectObject(hDC, h);
if (nFit == ClockDisp.nTimeLen)
break;
wkFontStruct.lfHeight += 2;
}
}
#endif
/* compute placement and extents */
iAMPMLen = ClockDisp.nMaxAMPMLen;
if (!ClockDisp.bIconic && ClockDisp.wAMPMPosition && iAMPMLen)
{
ClockDisp.nSizeChar = nzExts[iAMPMLen] - nzExts[iAMPMLen-1];
ClockDisp.nPosAMPM = (newWidth - sizeTimeExt.cx) / 2;
ClockDisp.nSizeAMPM = nzExts[iAMPMLen-2];
ClockDisp.nSizeSep = nzExts[iAMPMLen+2] - nzExts[iAMPMLen+1];
ClockDisp.nPosHr = ClockDisp.nPosAMPM + nzExts[iAMPMLen-1];
ClockDisp.nPosSep1 = ClockDisp.nPosAMPM + nzExts[iAMPMLen+1];
ClockDisp.nPosMin = ClockDisp.nPosAMPM + nzExts[iAMPMLen+2];
if (!ClockDisp.bNoSeconds)
{
ClockDisp.nPosSep2 = ClockDisp.nPosAMPM + nzExts[iAMPMLen+4];
ClockDisp.nPosSec = ClockDisp.nPosAMPM + nzExts[iAMPMLen+5];
}
}
else
{
ClockDisp.nSizeChar = nzExts[0];
ClockDisp.nSizeSep = nzExts[2] - nzExts[1];
ClockDisp.nPosHr = (newWidth - sizeTimeExt.cx) / 2;
ClockDisp.nPosSep1 = ClockDisp.nPosHr + nzExts[ 1 ];
ClockDisp.nPosMin = ClockDisp.nPosHr + nzExts[2];
if (!ClockDisp.bIconic)
{
if (!ClockDisp.bNoSeconds)
{
ClockDisp.nPosSep2 = ClockDisp.nPosHr + nzExts[4];
ClockDisp.nPosSec = ClockDisp.nPosHr + nzExts[5];
if (ClockDisp.nTimeLen > 8)
nAMPMIndx = 8;
}
else if (ClockDisp.nTimeLen > 5)
nAMPMIndx = 5;
if (nAMPMIndx)
{
ClockDisp.nPosAMPM = ClockDisp.nPosHr + nzExts[nAMPMIndx];
ClockDisp.nSizeAMPM = sizeTimeExt.cx - nzExts[nAMPMIndx];
}
else
{
ClockDisp.nPosAMPM = 0;
ClockDisp.nSizeAMPM = 0;
}
}
}
ClockDisp.nSizeY = sizeTimeExt.cy;
/* compute size of shadow offset - if the font is too small, no
* shadow (offset = 0)
*/
ClockDisp.wShdwOff = (WORD) (((ClockDisp.nSizeChar + ClockDisp.nSizeY) < 90) ?
0 : 2 * GetSystemMetrics (SM_CXBORDER));
/* allocate a bitmap for 2 digits */
if (ClockDisp.hBitmap != NULL)
DeleteObject (ClockDisp.hBitmap);
ClockDisp.hBitmap = CreateDiscardableBitmap (hDC, 2*ClockDisp.nSizeChar
+ 2*ClockDisp.wShdwOff, ClockDisp.nSizeY);
/* prepare a font for the date */
if (bDisplayDate)
{
DesiredHeight = -(FontStruct.lfHeight * 3) / 4;
/* compute appropriate font for date - same algorithm as time */
FontStruct.lfHeight = -MulDiv (InitialHeight, DesiredWidth,
sizeDateExt.cx);
if (-FontStruct.lfHeight > DesiredHeight)
FontStruct.lfHeight = -DesiredHeight;
for (; ;)
{
if (hFontDate != NULL)
DeleteObject (hFontDate);
hFontDate = CreateFontIndirect (&FontStruct);
if (hFontDate)
h = SelectObject (hDC, hFontDate);
else
h = 0;
GetTextExtentPoint (hDC, ClockDisp.szDate,
ClockDisp.nDateLen, &sizeDateExt);
if (h)
SelectObject (hDC, h);
if (sizeDateExt.cx < DesiredWidth)
break;
FontStruct.lfHeight += 2;
}
/* compute date placement and extents */
ClockDisp.nPosDateX = (newWidth - sizeDateExt.cx) / 2;
ClockDisp.nSizeDateX = sizeDateExt.cx;
ClockDisp.nSizeDateY = sizeDateExt.cy;
ClockDisp.nPosY = (newHeight - (ClockDisp.nSizeY +
ClockDisp.nSizeDateY)) / 2;
ClockDisp.nPosDateY = ClockDisp.nPosY + ClockDisp.nSizeY;
}
else
{
/* no date, so center the time */
ClockDisp.nPosY = (newHeight - ClockDisp.nSizeY) / 2;
}
ReleaseDC (hWnd, hDC);
SetCursor (hOldCur);
}
}
/*
* DrawHand() - Draw the second hand using XOR mode.
*/
void NEAR PASCAL DrawHand (register HDC hDC,
int pos,
HPEN hPen,
int scale,
int patMode)
{
TRIG * lppt;
int radius;
MoveTo (hDC, clockCenter.x, clockCenter.y);
radius = MulDiv (clockRadius, scale, 100);
lppt = lpcirTab + (pos % 60);
SetROP2 (hDC, patMode);
SelectObject (hDC, hPen);
LineTo (hDC, clockCenter.x + MulDiv (lppt->x, radius, 8000),
clockCenter.y + MulDiv (lppt->y, radius, 8000));
}
/*
* SetMenuBar() - places or removes the menu bar, etc.
*
* Based on the flags in ClockDisp structure (ie: do we want a menu/title
* bar or not?), adds or removes the window title and menu bar:
* Gets current style, toggles the bits, and re-sets the style.
* Must then resize the window frame and show it.
*/
void NEAR PASCAL SetMenuBar (HWND hWnd)
{
static DWORD wID;
DWORD dwStyle;
dwStyle = GetWindowLong (hWnd, GWL_STYLE);
if (ClockDisp.bNoTitle)
{
/* remove caption & menu bar, etc. */
dwStyle &= ~(WS_DLGFRAME | WS_SYSMENU |
WS_MINIMIZEBOX | WS_MAXIMIZEBOX);
wID = SetWindowLong (hWnd, GWL_ID, 0);
}
else
{
/* put menu bar & caption back in */
dwStyle = WS_TILEDWINDOW | dwStyle;
SetWindowLong (hWnd, GWL_ID, wID);
SetWindowRgn(hWnd, NULL, TRUE);
}
SetWindowLong (hWnd, GWL_STYLE, dwStyle);
SetWindowPos (hWnd, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE |
SWP_NOZORDER | SWP_FRAMECHANGED);
if (ClockDisp.wFormat == IDM_ANALOG && ClockDisp.bNoTitle)
{
HRGN hrgn;
RECT rc;
GetClientRect(hWnd,&rc);
if(hrgn = CreateEllipticWndRgn(hWnd,&rc))
SetWindowRgn(hWnd,hrgn,TRUE);
}
ShowWindow (hWnd, SW_SHOW);
}
/*
* FormatInit() - Retrieve current locale information.
*/
void NEAR PASCAL FormatInit (VOID)
{
LCID lcid;
TCHAR szBuf[3];
lcid = GetUserDefaultLCID ();
/*
* Get time format:
* 0 = 12 hour format
* 1 = 24 hour format
*/
if (!bUtc)
{
GetLocaleInfoW (lcid, LOCALE_ITIME, (LPWSTR) szBuf, 3);
ClockDisp.wTimeFormat = (WORD)MyAtoi (szBuf);
}
else
{
/*
* GMT - use 24 hour clock format.
*/
ClockDisp.wTimeFormat = 1;
}
/*
* Get time leading zero:
* 0 = no leading zero
* 1 = leading zero
*/
GetLocaleInfoW (lcid, LOCALE_ITLZERO, (LPWSTR) szBuf, 3);
ClockDisp.wTimeLZero = (WORD)MyAtoi (szBuf);
/*
* Get time marker position:
* 0 = suffix
* 1 = prefix
*/
GetLocaleInfoW (lcid, LOCALE_ITIMEMARKPOSN, (LPWSTR) szBuf, 3);
ClockDisp.wAMPMPosition = (WORD)MyAtoi (szBuf);
/*
* Get AM/PM designators.
*/
if (!bUtc)
{
GetLocaleInfoW (lcid, LOCALE_S1159, (LPWSTR) ClockDisp.szAMPM[0], MAX_AMPM_LEN);
GetLocaleInfoW (lcid, LOCALE_S2359, (LPWSTR) ClockDisp.szAMPM[1], MAX_AMPM_LEN);
ClockDisp.nMaxAMPMLen = max( lstrlen(ClockDisp.szAMPM[0]),
lstrlen(ClockDisp.szAMPM[1]) );
if ((ClockDisp.wAMPMPosition) && (ClockDisp.nMaxAMPMLen))
{
/*
* AM/PM is a Prefix, so need to add one to the length
* for the space between the time marker and the time string.
*/
(ClockDisp.nMaxAMPMLen)++;
}
}
else
{
*(ClockDisp.szAMPM[0]) = *(ClockDisp.szAMPM[1]) = TEXT('\0');
ClockDisp.nMaxAMPMLen = 0;
}
/*
* Get time separator string.
*/
GetLocaleInfoW (lcid, LOCALE_STIME, (LPWSTR) ClockDisp.szTimeSep, MAX_TIME_SEP);
/*
* Get short date format.
*/
GetLocaleInfoW (lcid, LOCALE_SSHORTDATE, (LPWSTR) ClockDisp.szDateFmt, MAX_DATE_LEN);
}
/*
* ClockSize()
*/
void NEAR PASCAL ClockSize (register HWND hWnd,
int newWidth,
int newHeight,
WORD SizeWord)
{
BOOL bChanged = FALSE;
SetRect (&clockRect, 0, 0, newWidth, newHeight);
CompClockDim ();
if (SizeWord == SIZEICONIC)
{
ClockDisp.bIconic = TRUE;
bChanged = TRUE;
}
else if (ClockDisp.bIconic)
{
ClockDisp.bIconic = FALSE;
bChanged = TRUE;
}
if (bChanged)
{
ClockTimerInterval (hWnd);
FormatTimeStr ();
if (!ClockDisp.bNoDate)
{
FormatDateStr (&oDate, ClockDisp.bIconic);
ResetWinTitle (hWnd); /* date has changed */
}
}
if (ClockDisp.wFormat == IDM_ANALOG && ClockDisp.bNoTitle)
{
HRGN hrgn;
RECT rc;
GetClientRect(hWnd,&rc);
if(hrgn = CreateEllipticWndRgn(hWnd,&rc))
SetWindowRgn(hWnd,hrgn,TRUE);
}
}
/*
* CompClockDim() - Recompute the clock's dimensions.
*/
void NEAR PASCAL CompClockDim (void)
{
int i;
register int tWidth;
register int tHeight;
tWidth = clockRect.right - clockRect.left;
tHeight = clockRect.bottom - clockRect.top;
if (tWidth > MulDiv (tHeight,aspectD,aspectN))
{
i = MulDiv (tHeight, aspectD, aspectN);
clockRect.left += (tWidth - i) >> 1;
clockRect.right = clockRect.left + i;
}
else
{
i = MulDiv (tWidth, aspectN, aspectD);
clockRect.top += (tHeight - i) >> 1;
clockRect.bottom = clockRect.top + i;
}
}
/*
* DeleteTools()
*/
void NEAR PASCAL DeleteTools (void)
{
DeleteObject (hbrForeground);
DeleteObject (hbrColorWindow);
DeleteObject (hbrBtnHighlight);
DeleteObject (hbrBlobColor);
DeleteObject (hpenForeground);
DeleteObject (hpenShadow);
DeleteObject (hpenBackground);
DeleteObject (hpenBlobHlt);
DeleteObject (hpenRed);
}
void ParseSavedWindow (LPTSTR szBuf, PRECT pRect)
{
PINT pint;
int count;
short cxFrame = (short) GetSystemMetrics (SM_CXFRAME);
short cxSize = (short) GetSystemMetrics (SM_CXSIZE);
short cyFrame = (short) GetSystemMetrics (SM_CYFRAME);
short cySize = (short) GetSystemMetrics (SM_CYSIZE);
count = 0;
pint = (PINT) pRect;
while (*szBuf && count < 4)
{
*pint = (int) MyAtoi (szBuf);
pint++; // advance to next field
while (*szBuf && *szBuf != TEXT(','))
szBuf++;
while (*szBuf && *szBuf == TEXT(','))
szBuf++;
count++;
}
if ((count < 4) ||
(pRect->left >= pRect->right) || (pRect->top >= pRect->bottom))
{
HDC hDC = GetDC(NULL);
int nPixMMX = GetDeviceCaps (hDC, HORZRES) / GetDeviceCaps (hDC, HORZSIZE);
int nPixMMY = GetDeviceCaps (hDC, VERTRES) / GetDeviceCaps (hDC, VERTSIZE);
ReleaseDC (NULL, hDC);
/* Bug #14014: These sizes chosen for showing date in title bar as well as
* instruction speed. 24 September 1991 Clark Cyr
*/
pRect->left = (LONG)CW_USEDEFAULT;
pRect->top = SW_SHOWNORMAL;
pRect->right = 64 * nPixMMX + 4 * cxFrame;
pRect->bottom = 64 * nPixMMY + 4 * cyFrame + cySize;
}
else
{
short cxScreen = (short) GetSystemMetrics (SM_CXSCREEN);
short cyScreen = (short) GetSystemMetrics (SM_CYSCREEN);
pRect->right -= pRect->left; /* right is now width */
pRect->bottom -= pRect->top; /* bottom is now height */
if (pRect->left > cxScreen - cxFrame - cxSize)
pRect->left = cxScreen - cxFrame - cxSize;
else if (pRect->left < cxFrame + cxSize - pRect->right)
pRect->left = cxFrame + cxSize - pRect->right;
if (pRect->top > cyScreen - cyFrame - cySize)
pRect->top = cyScreen - cyFrame - cySize;
else if (pRect->top < cxFrame + cxSize - pRect->bottom)
pRect->top = cxFrame + cxSize - pRect->bottom;
}
}
#define ADVANCE(sz) while (*sz && *sz != TEXT(',')) sz++; \
while (*sz && *sz == TEXT(',')) sz++; \
if (!*sz) return;
void NEAR PASCAL ParseSavedFlags (LPTSTR szBuf, PCLOCKDISPSTRUCT pClck)
{
pClck->wFormat = IDM_ANALOG;
pClck->bIconic = FALSE;
pClck->bNoSeconds = FALSE;
pClck->bNoTitle = FALSE;
pClck->bTopMost = FALSE;
pClck->bNoDate = FALSE;
if (!szBuf)
return;
pClck->wFormat = (WORD)(MyAtoi(szBuf) ? IDM_ANALOG : IDM_DIGITAL);
ADVANCE (szBuf);
pClck->bIconic = (MyAtoi(szBuf) ? TRUE : FALSE);
ADVANCE (szBuf);
pClck->bNoSeconds = (MyAtoi(szBuf) ? TRUE : FALSE);
ADVANCE (szBuf);
pClck->bNoTitle = (MyAtoi(szBuf) ? TRUE : FALSE);
ADVANCE (szBuf);
pClck->bTopMost = (MyAtoi(szBuf) ? TRUE : FALSE);
ADVANCE (szBuf);
pClck->bNoDate = (MyAtoi(szBuf) ? TRUE : FALSE);
}
VOID NEAR PASCAL PrepareSavedWindow (LPTSTR szBuf, PRECT pRect)
{
wsprintf (szBuf, TEXT("%i,%i,%i,%i"), pRect->left, pRect->top,
pRect->right, pRect->bottom);
}
TRIG CirTab[] = { // circle sin, cos, table
{ 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 }
};
VOID NEAR PASCAL PrepareSavedFlags (LPTSTR szBuf, PCLOCKDISPSTRUCT pClck)
{
wsprintf (szBuf, TEXT("%i,%i,%i,%i,%i,%i"),
(pClck->wFormat == IDM_ANALOG ? 1 : 0),
(pClck->bIconic ? 1 : 0), (pClck->bNoSeconds ? 1 : 0),
(pClck->bNoTitle ? 1 : 0), (pClck->bTopMost ? 1 : 0),
(pClck->bNoDate ? 1 : 0));
}
/*
* SaveClockOptions()
*/
VOID NEAR PASCAL SaveClockOptions (HWND hWnd)
{
TCHAR szInt[80];
INT i = (INT) IsZoomed (hWnd);
/* Bug 15058: Don't save the rectangle if we're maximized, assume it
* has already been saved for restoration when maximization took place.
* 18 October 1991 Clark Cyr
*/
if (!ClockDisp.bIconic && !i)
GetWindowRect (hWnd, &rCoordRect);
wsprintf (szInt, TEXT("%i"), i);
WritePrivateProfileString(szSection, szMaximized, szInt, szIniFile);
/* write current clock options */
PrepareSavedFlags (szInt, &ClockDisp);
WritePrivateProfileString (szSection, szOptions, szInt, szIniFile);
/* write window position and size */
PrepareSavedWindow (szInt, &rCoordRect);
WritePrivateProfileString (szSection, szPosition, szInt, szIniFile);
#ifdef JAPAN
WritePrivateProfileString(
szSection, szFontFileKey, FontStruct.lfFaceName, szIniFile );
wsprintf (szInt, TEXT("%i"), (INT)FontStruct.lfCharSet);
WritePrivateProfileString(szSection, szCharSet, szInt, szIniFile);
#endif
}
#if defined(JAPAN) || defined(KOREA)
/*
* ExceptVerticalFont()
*/
UINT FAR PASCAL ExceptVerticalFont(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
LOGFONT lf;
TCHAR szFaceName[LF_FACESIZE];
UINT nId, count;
switch (message)
{
case WM_INITDIALOG:
count = SendDlgItemMessage(hwnd, cmb1, CB_GETCOUNT, 0, 0L);
/* except vertical font */
for (nId = 0; nId < count; nId++)
{
SendDlgItemMessage(hwnd, cmb1, CB_GETLBTEXT,
nId, (LONG) szFaceName);
if (szFaceName[0] == TEXT('@'))
{
SendDlgItemMessage(hwnd, cmb1, CB_DELETESTRING, nId, 0L);
nId--;
count--;
}
}
/* set selection current selected facename */
SendMessage(hwnd, WM_CHOOSEFONT_GETLOGFONT, 0, (LONG) (LPTSTR) &lf);
nId = SendDlgItemMessage(hwnd, cmb1, CB_FINDSTRING,
0, (LONG) lf.lfFaceName);
SendDlgItemMessage(hwnd, cmb1, CB_SETCURSEL, nId, 0L);
// KKBUGFIX #1364: 12/10/92: Set focus on face name
return(TRUE);
default:
return(FALSE);
}
}
#endif
HRGN CreateEllipticWndRgn(HWND hWnd, LPRECT lprc)
{
int cSide;
int xOffset;
int yOffset;
HRGN hRgn = NULL;
if(lprc)
{
cSide = min(lprc->right, lprc->bottom);
xOffset = GetSystemMetrics(SM_CXFRAME) + ((lprc->right - cSide) >> 1);
yOffset = GetSystemMetrics(SM_CYFRAME) + ((lprc->bottom - cSide) >> 1);
hRgn = CreateEllipticRgn(xOffset, yOffset, xOffset+cSide, yOffset+cSide);
}
return(hRgn);
}