windows-nt/Source/XPSP1/NT/shell/osshell/accesory/calendar/calendar.c
2020-09-26 16:20:57 +08:00

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);
}