A clock is the most obvious application for the timer. Although digital clocks were once in fashion, the pendulum has swung back (so to speak) to analog clocks. But you already have an analog clock with Windows. Although the CLOCK program has a digital-clock setting, I'm going to write a good old digital-clock program—because it provides an interesting example of the use of the timer. The DIGCLOCK program, shown in Figure 5-8, creates a popup window that positions itself in the lower right corner of the display in the icon area. The program displays the day of the week, the time, and the date. (See Figure 5-9 on page 202.)
DIGCLOCK.MAK
#------------------------
# DIGCLOCK.MAK make file
#------------------------
digclock.exe : digclock.obj digclock.def
link digclock, /align:16, NUL, /nod slibcew libw, digclock
rc digclock.exe
digclock.obj : digclock.c
cl -c -Gsw -Ow -W2 -Zp digclock.c
DIGCLOCK.C
/*-----------------------------------------
DIGCLOCK.C -- Digital Clock Program
(c) Charles Petzold, 1990
-----------------------------------------*/
#include <windows.h>
#include <time.h>
#define ID_TIMER 1
#define YEAR (datetime->tm_year % 100)
#define MONTH (datetime->tm_mon + 1)
#define MDAY (datetime->tm_mday)
#define WDAY (datetime->tm_wday)
#define HOUR (datetime->tm_hour)
#define MIN (datetime->tm_min)
#define SEC (datetime->tm_sec)
long FAR PASCAL WndProc (HWND, WORD, WORD, LONG);
void SizeTheWindow (short *, short *, short *, short *) ;
char sDate [2], sTime [2], sAMPM [2][5] ;
int iDate, iTime ;
int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
LPSTR lpszCmdLine, int nCmdShow)
{
static char szAppName[] = "DigClock" ;
HWND hwnd;
MSG msg;
short xStart, yStart, xClient, yClient ;
WNDCLASS wndclass ;
if (!hPrevInstance)
{
wndclass.style = CS_HREDRAW | CS_VREDRAW ;
wndclass.lpfnWndProc = WndProc ;
wndclass.cbClsExtra = 0 ;
wndclass.cbWndExtra = 0 ;
wndclass.hInstance = hInstance ;
wndclass.hIcon = NULL ;
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
wndclass.hbrBackground = GetStockObject (WHITE_BRUSH) ;
wndclass.lpszMenuName = NULL ;
wndclass.lpszClassName = szAppName ;
RegisterClass (&wndclass) ;
}
SizeTheWindow (&xStart, &yStart, &xClient, &yClient) ;
hwnd = CreateWindow (szAppName, szAppName,
WS_POPUP | WS_DLGFRAME | WS_SYSMENU,
xStart, yStart,
xClient, yClient,
NULL, NULL, hInstance, NULL) ;
if (!SetTimer (hwnd, ID_TIMER, 1000, NULL))
{
MessageBox (hwnd, "Too many clocks or timers!", szAppName,
MB_ICONEXCLAMATION | MB_OK) ;
return FALSE ;
}
ShowWindow (hwnd, SW_SHOWNOACTIVATE) ;
UpdateWindow (hwnd) ;
while (GetMessage (&msg, NULL, 0, 0))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
return msg.wParam ;
}
void SizeTheWindow (short *pxStart, short *pyStart,
short *pxClient, short *pyClient)
{
HDC hdc ;
TEXTMETRIC tm ;
hdc = CreateIC ("DISPLAY", NULL, NULL, NULL) ;
GetTextMetrics (hdc, &tm) ;
DeleteDC (hdc) ;
*pxClient = 2 * GetSystemMetrics (SM_CXDLGFRAME) + 16*tm.tmAveCharWidth ;
*pxStart = GetSystemMetrics (SM_CXSCREEN) - *pxClient ;
*pyClient = 2 * GetSystemMetrics (SM_CYDLGFRAME) + 2*tm.tmHeight ;
*pyStart = GetSystemMetrics (SM_CYSCREEN) - *pyClient ;
}
void SetInternational (void)
{
static char cName [] = "intl" ;
iDate = GetProfileInt (cName, "iDate", 0) ;
iTime = GetProfileInt (cName, "iTime", 0) ;
GetProfileString (cName, "sDate", "/", sDate, 2) ;
GetProfileString (cName, "sTime", ":", sTime, 2) ;
GetProfileString (cName, "s1159", "AM", sAMPM [0], 5) ;
GetProfileString (cName, "s2359", "PM", sAMPM [1], 5) ;
}
void WndPaint (HWND hwnd, HDC hdc)
{
static char szWday[] = "Sun\0Mon\0Tue\0Wed\0Thu\0Fri\0Sat" ;
char cBuffer[40] ;
long lTime ;
RECT rect ;
short nLength ;
struct tm *datetime ;
time (&lTime) ;
datetime = localtime (&lTime) ;
nLength = wsprintf (cBuffer, " %s %d%s%02d%s%02d \r\n",
(LPSTR) szWday + 4 * WDAY,
iDate == 1 ? MDAY : iDate == 2 ? YEAR : MONTH, (LPSTR) sDate,
iDate == 1 ? MONTH : iDate == 2 ? MONTH : MDAY, (LPSTR) sDate,
iDate == 1 ? YEAR : iDate == 2 ? MDAY : YEAR) ;
if (iTime == 1)
nLength += wsprintf (cBuffer + nLength, " %02d%s%02d%s%02d ",
HOUR, (LPSTR) sTime, MIN, (LPSTR) sTime, SEC) ;
else
nLength += wsprintf (cBuffer + nLength, " %d%s%02d%s%02d %s ",
(HOUR % 12) ? (HOUR % 12) : 12,
(LPSTR) sTime, MIN, (LPSTR) sTime, SEC,
(LPSTR) sAMPM [HOUR / 12]) ;
GetClientRect (hwnd, &rect) ;
DrawText (hdc, cBuffer, -1, &rect, DT_CENTER | DT_NOCLIP) ;
}
long FAR PASCAL WndProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)
{
HDC hdc ;
PAINTSTRUCT ps ;
switch (message)
{
case WM_CREATE :
SetInternational () ;
return 0 ;
case WM_TIMER :
InvalidateRect (hwnd, NULL, FALSE) ;
return 0 ;
case WM_PAINT :
hdc = BeginPaint (hwnd, &ps) ;
WndPaint (hwnd, hdc) ;
EndPaint (hwnd, &ps) ;
return 0 ;
case WM_WININICHANGE :
SetInternational () ;
InvalidateRect (hwnd, NULL, TRUE) ;
return 0 ;
case WM_DESTROY :
KillTimer (hwnd, ID_TIMER) ;
PostQuitMessage (0) ;
return 0 ;
}
return DefWindowProc (hwnd, message, wParam, lParam) ;
}
DIGCLOCK.DEF
;-------------------------------------
; DIGCLOCK.DEF module definition file
;-------------------------------------
NAME DIGCLOCK
DESCRIPTION 'Digital Clock (c) Charles Petzold, 1990'
EXETYPE WINDOWS
STUB 'WINSTUB.EXE'
CODE PRELOAD MOVEABLE DISCARDABLE
DATA PRELOAD MOVEABLE MULTIPLE
HEAPSIZE 1024
STACKSIZE 8192
EXPORTS WndProc
All the programs shown so far have used the window style WS_OVERLAPPEDWINDOW as the third parameter to the CreateWindow function. DIGCLOCK uses the window style:
WS_POPUP | WS_DLGFRAME | WS_SYSMENU
This creates a style of window known as ”popup,“ with a dialog box frame and a system menu. The popup style is most commonly used for dialog boxes and message boxes, and only rarely for applications. DIGCLOCK also uses yet another variation of the ShowWindow call:
ShowWindow (hwnd, SW_SHOWNOACTIVATE) ;
Normally, a program becomes the active window when you run it. SW_SHOWNOACTIVATE tells Windows that the program that loaded DIGCLOCK should remain the active window. You can make DIGCLOCK active, however, by clicking on its window with the mouse or by pressing Alt-Tab or Alt-Esc. Although DIGCLOCK has no system menu box, you can still access the system menu when DIGCLOCK is active by pressing Alt-Spacebar. If you select Move, you can move the window with the keyboard.