407 lines
14 KiB
C
407 lines
14 KiB
C
|
/*
|
||
|
* Windows Calendar
|
||
|
* Copyright (c) 1985 by Microsoft Corporation, all rights reserved.
|
||
|
* Written by Mark L. Chamberlin, consultant to Microsoft.
|
||
|
*
|
||
|
***** calmain.c - small segment containing main loop and caltimer stuff
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
#include "cal.h"
|
||
|
|
||
|
|
||
|
/**** WinMain ****/
|
||
|
|
||
|
MMain(hInstance, hPrevInstance, lpszCmdLine, cmdShow)
|
||
|
/* { */
|
||
|
MSG msg;
|
||
|
|
||
|
|
||
|
if (!CalInit (hInstance, hPrevInstance, lpszCmdLine, cmdShow))
|
||
|
return (FALSE);
|
||
|
|
||
|
// OK to process WM_ACTIVATE from now onwards
|
||
|
fInitComplete = TRUE;
|
||
|
|
||
|
while (GetMessage (&msg, NULL, 0, 0))
|
||
|
{
|
||
|
/* Filter the special keys BEFORE calling translate message.
|
||
|
* This way, the WM_KEYDOWN messages will get trapped before
|
||
|
* the WM_CHAR messages are created so we need not worry about
|
||
|
* trapping the latter messages.
|
||
|
*/
|
||
|
if (!FKeyFiltered (&msg))
|
||
|
{
|
||
|
if (TranslateAccelerator (vhwnd0, vhAccel, &msg) == 0)
|
||
|
{
|
||
|
TranslateMessage (&msg);
|
||
|
DispatchMessage (&msg);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return (int)msg.wParam;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/**** FKeyFiltered - return TRUE if the key has been filtered. ****/
|
||
|
|
||
|
BOOL APIENTRY FKeyFiltered (MSG *pmsg)
|
||
|
{
|
||
|
register WPARAM wParam;
|
||
|
|
||
|
wParam = pmsg -> wParam;
|
||
|
|
||
|
/* Handle TIMER message here so we don't pull in another segment. */
|
||
|
if (pmsg -> message == WM_TIMER)
|
||
|
{
|
||
|
CalTimer(FALSE);
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
/* Look for key down messages going to the edit controls.
|
||
|
Karl Stock says there is no need to filter out the
|
||
|
key up messages, and we will not call TranslateMessage
|
||
|
for the filtered keys so there will be no WM_CHAR messages
|
||
|
to filter.
|
||
|
*/
|
||
|
if (pmsg -> message == WM_KEYDOWN)
|
||
|
{
|
||
|
if (pmsg -> hwnd == vhwnd2C)
|
||
|
{
|
||
|
/* In the notes area. Tab means leave the notes area. */
|
||
|
if (wParam == VK_TAB)
|
||
|
{
|
||
|
/* Leave the notes area. */
|
||
|
if (!vfDayMode)
|
||
|
{
|
||
|
/* Give the focus to the monthly calendar. */
|
||
|
CalSetFocus (vhwnd2B);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/* In day mode - give focus to the appointment
|
||
|
description edit control.
|
||
|
*/
|
||
|
CalSetFocus (vhwnd3);
|
||
|
}
|
||
|
return (TRUE);
|
||
|
}
|
||
|
return (FALSE);
|
||
|
}
|
||
|
|
||
|
else if (vfDayMode)
|
||
|
{
|
||
|
switch (wParam)
|
||
|
{
|
||
|
case VK_RETURN:
|
||
|
case VK_DOWN:
|
||
|
/* If on last appointment, scroll up one appoinment.
|
||
|
* If not on last appointment in window, change
|
||
|
* focus to next appointment in window.
|
||
|
*/
|
||
|
if (vlnCur == vlnLast)
|
||
|
ScrollUpDay (1, FALSE);
|
||
|
else
|
||
|
SetQdEc (vlnCur + 1);
|
||
|
|
||
|
break;
|
||
|
|
||
|
case VK_UP:
|
||
|
/* If on first appointment in window, scroll down
|
||
|
* one appointment.
|
||
|
* If not on first appointment in window, change
|
||
|
* focus to previous appointment in window.
|
||
|
*/
|
||
|
if (vlnCur == 0)
|
||
|
ScrollDownDay (1, FALSE, FALSE);
|
||
|
else
|
||
|
SetQdEc (vlnCur-1);
|
||
|
|
||
|
break;
|
||
|
|
||
|
case VK_NEXT:
|
||
|
case VK_PRIOR:
|
||
|
if (GetKeyState (VK_CONTROL) < 0)
|
||
|
{
|
||
|
/* Control Pg Up and Control Pg Dn are
|
||
|
* the accelerators for Show Previous and
|
||
|
* Show Next. We want TranslateAccelerator
|
||
|
* to see them, so return FALSE.
|
||
|
*/
|
||
|
return (FALSE);
|
||
|
}
|
||
|
|
||
|
/* Translate into a scroll command (as if area
|
||
|
* below or above thumb had been clicked).
|
||
|
*/
|
||
|
SendMessage(vhwnd2B, WM_VSCROLL,
|
||
|
wParam==VK_NEXT ? SB_PAGEDOWN : SB_PAGEUP, 0L);
|
||
|
break;
|
||
|
|
||
|
case VK_TAB:
|
||
|
/* Switch to the notes area. */
|
||
|
CalSetFocus (vhwnd2C);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
return (FALSE);
|
||
|
}
|
||
|
return (TRUE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return (FALSE);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**** CalTimer ****/
|
||
|
|
||
|
VOID APIENTRY CalTimer (BOOL fAdjust)
|
||
|
{
|
||
|
HDC hDC;
|
||
|
D3 d3New;
|
||
|
DT dtNew;
|
||
|
TM tmNew;
|
||
|
FT ftPrev;
|
||
|
|
||
|
if (vfFlashing)
|
||
|
FlashWindow (vhwnd0, TRUE);
|
||
|
|
||
|
if (vcAlarmBeeps != 0)
|
||
|
{
|
||
|
MessageBeep (ALARMBEEP);
|
||
|
vcAlarmBeeps--;
|
||
|
}
|
||
|
|
||
|
/* Fetch the date and time. */
|
||
|
ReadClock (&d3New, &tmNew);
|
||
|
|
||
|
/* See if the time or date has changed. Note that it's necessary
|
||
|
* to check all parts in order to immediartely detect all changes.
|
||
|
* (I used to just check the time, but that meant a date change was
|
||
|
* not detected until the minute changed.)
|
||
|
*/
|
||
|
|
||
|
if (tmNew != vftCur.tm || d3New.wMonth != vd3Cur.wMonth
|
||
|
|| d3New.wDay != vd3Cur.wDay || d3New.wYear != vd3Cur.wYear)
|
||
|
{
|
||
|
/* Remember the old date and time */
|
||
|
ftPrev = vftCur;
|
||
|
vftCur.tm = tmNew;
|
||
|
|
||
|
/* Show new date/time only if not iconic */
|
||
|
if (!IsIconic(vhwnd0))
|
||
|
{
|
||
|
hDC = CalGetDC (vhwnd2A);
|
||
|
DispTime (hDC);
|
||
|
|
||
|
if ((dtNew = DtFromPd3 (&d3New)) != vftCur.dt)
|
||
|
{
|
||
|
vftCur.dt = dtNew;
|
||
|
vd3Cur = d3New;
|
||
|
if (!vfDayMode)
|
||
|
{
|
||
|
/* Display the new date. */
|
||
|
DispDate (hDC, &vd3Cur);
|
||
|
|
||
|
/* If the old or new date is in the
|
||
|
month currently being displayed, redisplay to get rid of
|
||
|
the >< on the old date.
|
||
|
Also, if the new date is in the month being displayed,
|
||
|
it will get marked with the >< as a result.
|
||
|
*/
|
||
|
if ((vd3Cur.wMonth == vd3Sel.wMonth && vd3Cur.wYear ==
|
||
|
vd3Sel.wYear) || (d3New.wMonth == vd3Sel.wMonth
|
||
|
&& d3New.wYear == vd3Sel.wYear))
|
||
|
{
|
||
|
/* Note - neither vcDaysMonth nor vwDaySticky
|
||
|
has changed, so UpdateMonth will end up selecting
|
||
|
the same day that's currently selected (which
|
||
|
is what we want).
|
||
|
*/
|
||
|
vd3To = vd3Sel;
|
||
|
UpdateMonth ();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ReleaseDC (vhwnd2A, hDC);
|
||
|
}
|
||
|
|
||
|
/* If the new date/time is less than the previous one, or the
|
||
|
new one is a day (1440 minutes) or more greater than the
|
||
|
previous one, we want to resynchronize the next alarm.
|
||
|
Obviously, if the date/time is less than the previouse one,
|
||
|
the system clock has been adjusted (except in the case
|
||
|
where it wraps on December 31, 2099, which I am not worried
|
||
|
about). However, it is not obvious when the clock has been
|
||
|
set forward. For example, if the user is running Calendar
|
||
|
and then switches to an old application that grabs the
|
||
|
whole machine (.g., Lotus 123), Calendar will not get
|
||
|
timer messages while the olf app is running. It is completely
|
||
|
reasonable to expect that the user may not return to Windows
|
||
|
for a long time (on the order of hours), so we only assume
|
||
|
the clock has been set forward if it changes by a day or
|
||
|
more (1440 minutes). We don't want to make this period
|
||
|
too great either since if we don't think the clock has been
|
||
|
set ahead, we will put all the alarms that have passed into
|
||
|
the alarm acknowledgement listbox. In fact, avoiding this
|
||
|
was the main reason for detecting clock adjustments. Without
|
||
|
setting the date/time on a machine without a hardware clock,
|
||
|
the date/time would start out back in January, 1980. If he
|
||
|
then noticed the date was wrong and set it, all the alarms
|
||
|
since January 1980 would be put into the listbox, which is
|
||
|
not only rediculous, but could take a long time to read
|
||
|
the disk for a bunch of old dates. With the one day adjustment
|
||
|
period, this is no longer a problem, because we ignore alarms
|
||
|
that go off due to a forward clock adjustment.
|
||
|
Note - do not set vfMustSyncAlarm FALSE in any case since
|
||
|
it may already be TRUE and hasn't been serviced yet
|
||
|
(because uProcessAlarms is locked).
|
||
|
*/
|
||
|
|
||
|
/* If there is no NextAlarm present, then we dont have to resync
|
||
|
* any alaram at all;
|
||
|
* Fix for Bug #6196 --SANKAR-- 11-14-89
|
||
|
*/
|
||
|
if (fAdjust && CompareFt (&vftCur, &ftPrev) != 0
|
||
|
&& vftAlarmNext.dt != DTNIL)
|
||
|
{
|
||
|
/* The clock has been adjusted - set flag to tell
|
||
|
uProcessAlarms we want to resync, and force the
|
||
|
call to AlarmCheck (below) to trigger an alarm
|
||
|
immediately by setting the alarm time to the current
|
||
|
time.
|
||
|
*/
|
||
|
|
||
|
vfMustSyncAlarm=TRUE;
|
||
|
vftAlarmNext=vftCur;
|
||
|
}
|
||
|
|
||
|
/* See if it's time to trigger the alarm (also handle
|
||
|
resynchronization).
|
||
|
*/
|
||
|
AlarmCheck ();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/**** AlarmCheck ****/
|
||
|
|
||
|
VOID APIENTRY AlarmCheck ()
|
||
|
{
|
||
|
|
||
|
FT ftTemp;
|
||
|
|
||
|
/* If the current time plus the early ring period is greater than or
|
||
|
equal to the next alarm time, trigger the alarm.
|
||
|
*/
|
||
|
ftTemp = vftCur;
|
||
|
AddMinsToFt (&ftTemp, vcMinEarlyRing);
|
||
|
if (CompareFt (&ftTemp, &vftAlarmNext) > -1)
|
||
|
{
|
||
|
/* Sound the alarm if sound is enabled. Give the first beep
|
||
|
right now and the rest at one second intervals in the timer
|
||
|
message routine.
|
||
|
*/
|
||
|
if (vfSound)
|
||
|
{
|
||
|
MessageBeep (ALARMBEEP);
|
||
|
vcAlarmBeeps = CALARMBEEPS - 1;
|
||
|
}
|
||
|
|
||
|
if (vftAlarmFirst.dt == DTNIL)
|
||
|
{
|
||
|
/* This is the first unacknowledged alarm - remember it. */
|
||
|
vftAlarmFirst = vftAlarmNext;
|
||
|
|
||
|
if (GetActiveWindow () == vhwnd0)
|
||
|
{
|
||
|
/* We are the active window, so process the alarm now. */
|
||
|
uProcessAlarms ();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* Not the active window, so fall through. */
|
||
|
}
|
||
|
|
||
|
/* Let the user know there are unacknowledged alarms. */
|
||
|
StartStopFlash (TRUE);
|
||
|
|
||
|
/* The next alarm is the first one > the one that just went off.
|
||
|
GetNextAlarm looks for >=, so add one minute.
|
||
|
Do not go to the disk - only arm the next alarm if it's in
|
||
|
memory. Note that this is absolutely necessary in the case
|
||
|
where we don't have the focus (the user is doing something
|
||
|
else, so it would be rude to start spinning the the disk and
|
||
|
possibly asking for the correct floppy to be inserted). In
|
||
|
the case where we are active but the alarm acknowledgement
|
||
|
dialog is already up, it would actually be OK to go to the disk,
|
||
|
but I have decided it would be too confusing for the user if
|
||
|
a disk I/O error were to occur at this point.
|
||
|
*/
|
||
|
ftTemp = vftAlarmNext;
|
||
|
AddMinsToFt (&ftTemp, 1);
|
||
|
GetNextAlarm (&vftAlarmNext, &ftTemp, FALSE, (HWND)NULL);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/**** AddMinsToFt ****/
|
||
|
|
||
|
VOID APIENTRY AddMinsToFt (
|
||
|
FT *pft,
|
||
|
UINT cMin) /* Not to exceed the minutes in one day (1440). */
|
||
|
{
|
||
|
/* Add cMin to the time. Note that the highest legitimate
|
||
|
TM and the largest cMin cannot overflow a WORD, which
|
||
|
is what a TM is, so we needn't worry about overflow here.
|
||
|
*/
|
||
|
if ((pft -> tm += (TM)cMin) > TMLAST)
|
||
|
{
|
||
|
/* The time wrapped into the next day. Adjust down the time,
|
||
|
and increment the day. If the date goes beyond DTLAST (that
|
||
|
of December 31, 2099, the value should still be OK for the
|
||
|
caller since it will only be used for comparison purposes.
|
||
|
Anyway, I will be dead when that case comes up, so if it doesn't
|
||
|
work correctly, it won't be my problem.
|
||
|
*/
|
||
|
pft -> tm -= TMLAST + 1;
|
||
|
pft -> dt++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/**** CompareFt - compare the two FTs returning:
|
||
|
-1 iff ft1 < ft2
|
||
|
0 iff ft1 = ft2
|
||
|
+1 iff ft1 > ft2
|
||
|
****/
|
||
|
|
||
|
INT APIENTRY CompareFt (
|
||
|
FT *pft1,
|
||
|
FT *pft2)
|
||
|
{
|
||
|
register FT *pft1Temp;
|
||
|
register FT *pft2Temp;
|
||
|
|
||
|
if ((pft1Temp = pft1) -> dt < (pft2Temp = pft2) -> dt)
|
||
|
return (-1);
|
||
|
|
||
|
if (pft1Temp -> dt > pft2Temp -> dt)
|
||
|
return (1);
|
||
|
|
||
|
/* DTs are equal, compare the TMs. */
|
||
|
if (pft1Temp -> tm < pft2Temp -> tm)
|
||
|
return (-1);
|
||
|
|
||
|
if (pft1Temp -> tm > pft2Temp -> tm)
|
||
|
return (1);
|
||
|
|
||
|
return (0);
|
||
|
}
|