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

607 lines
17 KiB
C

/*
* Windows Calendar
* Copyright (c) 1985 by Microsoft Corporation, all rights reserved.
* Written by Mark L. Chamberlin, consultant to Microsoft.
*
****** calmonth.c
*
*/
#include "cal.h"
/**** GetWeekday - calculate day of week from D3. */
WORD APIENTRY GetWeekday (D3*pd3)
{
/* Add 2 since January 1, 1980 was a Tuesday. */
return ((DtFromPd3 (pd3) + 2) % 7);
}
/**** CDaysMonth - return the number of days in the month specified
by the month and year of the D3 argument.
****/
INT APIENTRY CDaysMonth (D3*pd3)
{
register INT cDays;
/* Calculate the number of days in the current month (adding in
one if this is a leap year and the month is past February.
*/
cDays = vrgcDaysMonth [pd3 -> wMonth];
if (pd3 -> wYear % 4 == 0 && pd3 -> wMonth == MONTHFEB)
cDays++;
return (cDays);
}
/**** SetUpMonth - Based on vd3Sel, set up the following:
- vcDaysMonth - number of days in the month being displayed.
- vcWeeksMonth - the number of weeks needed to display the month
(4, 5, or 6).
- vrgbMonth - the array of days. 0's indicate unused entries.
****/
VOID APIENTRY SetUpMonth ()
{
WORD wDay;
INT *pb; /* changed from BYTE to int */
D3 d3Temp;
INT i;
for (i=0; i< CBMONTHARRAY;i++)
vrgbMonth[i]=0;
/* FillBuf (vrgbMonth, CBMONTHARRAY*sizeof(int), 0); */
/* Set up the count of days in the month. */
vcDaysMonth = CDaysMonth (&vd3Sel);
/* Get the weekday of the the first day of the month. */
d3Temp = vd3Sel;
d3Temp.wDay = 0;
vwWeekdayFirst = GetWeekday (&d3Temp);
/* Calculate the number of weeks we will need to display. */
vcWeeksMonth = (vwWeekdayFirst + 6 + vcDaysMonth) / 7;
/* Fill in the days. */
pb = vrgbMonth + vwWeekdayFirst;
for (wDay = 1; (WORD)wDay <= (WORD)vcDaysMonth; wDay++)
/* *pb++ = (BYTE)wDay; */
*pb++ = wDay;
/* Set the TODAY bit of the appropriate day if today
is in this month.
*/
if (vd3Cur.wMonth == vd3Sel.wMonth && vd3Cur.wYear == vd3Sel.wYear)
vrgbMonth [vwWeekdayFirst + vd3Cur.wDay] |= TODAY;
/* Set the marked bits for the marked days in this month. */
GetMarkedDays ();
}
/**** BuildMonthGrid */
VOID APIENTRY BuildMonthGrid ()
{
INT dx;
INT dy;
INT xco;
INT yco;
INT cLines;
/* Calculate the x coordinates if vertical scrollbar is absent */
if (vmScrollMax == 0)
{
dx = (vcxWnd2B + vcxBorder + vcxVScrollBar)/ 7;
vrgxcoGrid [7] = vcxWnd2B + vcxHScrollBar;
}
else
{
/* Calculate the x coordinates if vertical scrollbar is present */
dx = (vcxWnd2B + vcxBorder) / 7;
vrgxcoGrid [7] = vcxWnd2B ;
}
xco = - vcxBorder;
for (cLines = 0; cLines < 7; cLines++)
{
vrgxcoGrid [cLines] = xco;
xco += dx;
}
/* Calculate the y coordinates if horiz. scrollbar is absent. */
if (hmScrollMax == 0)
{
dy = (vcyWnd2BBot - vcyBorder + vcyHScrollBar)/vcWeeksMonth;
vrgycoGrid [vcWeeksMonth] = vcyWnd2B;
}
/* Calculate the y coordinates if horiz. scrollbar is present. */
else
{
dy = (vcyWnd2BBot - vcyBorder) / vcWeeksMonth;
vrgycoGrid [vcWeeksMonth] = vcyWnd2B - vcyHScrollBar;
}
yco = vcyWnd2BTop;
for (cLines = 0; cLines < vcWeeksMonth; cLines++)
{
vrgycoGrid [cLines] = yco;
yco += dy;
}
}
/**** PaintMonthGrid - Paint the grid for the monthly calendar display. */
VOID APIENTRY PaintMonthGrid (HDC hDC)
{
INT *pcoCur;
INT *pcoMax;
BuildMonthGrid ();
/* Draw the horizontal lines. */
pcoCur = vrgycoGrid;
for (pcoMax = pcoCur+vcWeeksMonth; pcoCur < pcoMax; pcoCur++)
PatBlt (hDC, 0, *pcoCur, vcxWnd2B + vcxVScrollBar, vcyBorder, PATCOPY);
/* Draw the vertical lines. */
pcoCur = vrgxcoGrid + 1;
for (pcoMax = pcoCur+6; pcoCur < pcoMax; pcoCur++)
PatBlt(hDC, *pcoCur, vcyWnd2BTop, vcxBorder, vcyWnd2BBot + vcyHScrollBar, PATCOPY);
}
/**** PaintMonth */
VOID APIENTRY PaintMonth (HDC hDC)
{
INT xcoBox;
INT ycoBox;
INT dx;
INT dy;
INT xcoText;
INT ycoText;
INT cDay;
INT cWeek;
INT cch;
CHAR *pch;
INT *pb; /* changed from BYTE to int */
INT *pxcoCur;
INT *pycoCur;
CHAR rgchDayAbbrevs[4];
CHAR rgch[CCHDATEDISP];
DOSDATE dd;
extern HANDLE hinstTimeDate;
INT iWeekStart; /* week no. of month which will appear on top row
in monthview */
INT iWeekEnd; /* week no. which will appear on last row of
month view */
INT iDayStart; /* weekday which will appear on leftmost column */
INT iDayEnd; /* weekday which will appear on rightmost column */
INT MarkedBits; /* the marked bits extracted from a day */
dd.year = vd3Sel.wYear + 1980;
dd.month = vd3Sel.wMonth + 1;
cch = GetLongDateString(&dd, rgch, GDS_LONG | GDS_NODAY);
xcoText = (vcxWnd2B - cch * vcxFont) / 2;
ycoText = 2;
TextOut(hDC, xcoText, ycoText, (LPSTR)rgch, cch );
iDayEnd = 7;
iWeekEnd = vcWeeksMonth + 2;
/* for an unscrolled window */
if ((vmScrollPos == 0) && (hmScrollPos == 0))
{
iWeekStart = 0;
iDayStart = 0;
pb = vrgbMonth;
pycoCur = vrgycoGrid;
}
else /* scrolled window */
{
iWeekStart = vmScrollPos +1;
iDayStart = hmScrollPos;
pycoCur = vrgycoGrid + iWeekStart -(vmScrollPos + 1);
pb = vrgbMonth + (iWeekStart -1)*7 + iDayStart;
}
ycoBox = *pycoCur;
dy = *pycoCur -ycoBox;
pxcoCur = vrgxcoGrid;
pb -= iDayStart - hmScrollPos ;
/* display "S M T W ..." above month grid */
for (cDay = iDayStart; cDay < iDayEnd; cDay++ )
{
INT cchT;
xcoBox = *pxcoCur++;
dx = *pxcoCur -xcoBox;
cchT = LoadString(hinstTimeDate, IDS_DAYABBREVS+cDay,
(LPSTR)rgchDayAbbrevs, 4);
xcoText = xcoBox + (dx - cchT*vcxFont)/2;
ycoText = vcyWnd2BTop -vcyLineToLine;
TextOut (hDC, xcoText, ycoText, (LPSTR)rgchDayAbbrevs, cchT);
}
/* draw the month grid and fill in the dates */
for (cWeek = iWeekStart ; cWeek < iWeekEnd ; cWeek++,pb+=iDayStart)
{
ycoBox = *pycoCur++;
dy = *pycoCur - ycoBox;
pxcoCur = vrgxcoGrid;
for (cDay = iDayStart; cDay < iDayEnd ; cDay++, pb++)
{
xcoBox = *pxcoCur++;
dx = *pxcoCur - xcoBox;
if (*pb !=0)
{
cch = 2;
ByteTo2Digs (*pb & ~(MARK_BOX| TODAY), pch = rgch);
if (*pch == '0')
{
cch--;
pch++;
}
xcoText = xcoBox + (dx - cch * vcxFont) / 2;
ycoText = ycoBox + vcyBorder + (dy - vcyFont) / 2;
/* draw mark symbols, if any */
if ((MarkedBits = ((~CLEARMARKEDBITS) & (*pb))) != 0)
DrawMark (hDC, xcoBox, ycoText, dx, MarkedBits);
if (*pb & TODAY)
ShowToday (hDC, xcoBox, ycoText, dx);
TextOut (hDC, xcoText, ycoText, (LPSTR)pch, cch);
}
}
}
InvertDay (hDC, vd3Sel.wDay);
/* Don't mess with the caret position if the notes have the focus. */
if (vhwndFocus == vhwnd2B)
PositionCaret ();
}
/**** DrawMark ***/
/***********************************************************************
*
* VOID PASCAL DrawMark (hDC, xcoBox, ycoText, dx, MarkedBits)
*
* purpose : To extract the marked bits one by one and draw the corresp.
* mark symbols next to the selected day in month mode.
*
* paramteters : hDC - the display context
* xcoBox - the x-coordinate of the left vertical edge
* of box containing selected day
* ycoText - y-coordinate of text to be written in box
* dx - width of box
* MarkedBits - variable with bits set corresp. to marks on
* selected day
*
* returns : none
*
* called by : PaintMonth
*
***********************************************************************/
VOID APIENTRY DrawMark (
HDC hDC,
INT xcoBox,
INT ycoText,
INT dx,
INT MarkedBits)
{
INT xcoLeft; /* left x coordinate of box mark */
INT xcoRight; /* right x coordinate of box mark */
INT ycoTop; /* top y coordinate of box mark */
INT ycoBottom; /* bottom y coordinate of box mark */
/* Note - the assumption is that numeric digits will not have
descenders. Therefore, we subtract out the descent when
calculating the space below the date digits in the monthly
calendar display.
*/
xcoLeft = xcoBox + (dx - 2 * vcxFont) / 2 - 2 * vcxBorder;
xcoRight = xcoLeft + 2 * vcxFont + 6 * vcxBorder;
ycoTop = ycoText - 2 * vcyBorder;
ycoBottom = ycoText + vcyFont + max (vcyBorder - vcyDescent, 0);
if (MarkedBits & MARK_BOX) /* a box-type mark */
{
PatBlt (hDC, xcoLeft, ycoTop, xcoRight - xcoLeft + 1, vcyBorder, PATCOPY);
PatBlt (hDC, xcoLeft, ycoBottom, xcoRight - xcoLeft + 1, vcyBorder, PATCOPY);
PatBlt (hDC, xcoLeft, ycoTop, vcxBorder, ycoBottom - ycoTop + 1, PATCOPY);
PatBlt (hDC, xcoRight, ycoTop, vcxBorder, ycoBottom - ycoTop + 1, PATCOPY);
}
if (MarkedBits & MARK_CIRCLE) /* a "o" -type mark */
Ellipse (hDC, xcoLeft - 7*vcxBorder, ycoBottom +2*vcyBorder,
xcoLeft - 3*vcxBorder, ycoBottom + 6*vcyBorder);
if (MarkedBits & MARK_PARENTHESES) /* a parentheses-type mark */
{
TextOut ( hDC, xcoLeft - 4*vcxFont/3,ycoText, vszMarkLeftParen, 1);
TextOut ( hDC, xcoRight + 2*vcxBorder,ycoText, vszMarkRightParen,1);
}
if (MarkedBits & MARK_CROSS) /* an "x" mark */
{
(void)MMoveTo (hDC, xcoLeft - 2*vcxBorder, ycoTop - 2*vcxBorder);
LineTo (hDC, xcoLeft - 7*vcxBorder, ycoTop -6*vcxBorder);
(void)MMoveTo (hDC, xcoLeft - 6*vcxBorder, ycoTop - 2*vcxBorder);
LineTo (hDC, xcoLeft - vcxBorder, ycoTop -6*vcxBorder);
}
if (MarkedBits & MARK_UNDERSCORE) /* a "_" mark */
{
/*
ycoBottom = ycoText + 14 * vcyBorder;
*/
PatBlt (hDC, xcoLeft, ycoBottom, xcoRight - xcoLeft + 1, vcyBorder, PATCOPY);
}
}
/**** ShowToday */
VOID APIENTRY ShowToday (
HDC hDC,
INT xcoBox,
INT ycoText,
INT dx)
{
TextOut (hDC, xcoBox + 1, ycoText, (LPSTR)">", 1);
TextOut (hDC, xcoBox + dx - vcxFont - 2, ycoText, (LPSTR)"<", 1);
}
/**** InvertDay - Invert the specified day. */
VOID APIENTRY InvertDay (
HDC hDC,
WORD wDay)
{
RECT rect;
MapDayToRect (wDay, &rect);
InvertRect (hDC, (LPRECT)&rect);
}
/**** PositionCaret */
VOID APIENTRY PositionCaret ()
{
RECT rect;
INT xcoCaret;
INT ycoCaret;
MapDayToRect (vd3Sel.wDay, &rect);
/* Center the caret horizontally (it is 2 * vcxFont wide),
and put it just above the bottom of the date box.
*/
xcoCaret = (rect.left + rect.right) / 2 - vcxFont;
ycoCaret = rect.bottom - vcyBorder;
SetCaretPos (xcoCaret, ycoCaret);
}
/**** MapDayToRect */
VOID APIENTRY MapDayToRect (
WORD wDay,
RECT *prect)
{
INT ixco;
INT iyco;
INT irgb;
ixco = (irgb = vwWeekdayFirst + wDay - vmScrollPos*7 - hmScrollPos)%7 ;
iyco = irgb / 7;
if ((ixco < 0)||(ixco > 6)||(iyco < 0) ||(iyco > vcWeeksMonth))
{
prect->left = prect->right = 0;
prect->top = prect->bottom = 0;
}
else
{
prect -> left = vrgxcoGrid [ixco] + vcxBorder;
prect -> right = vrgxcoGrid [ixco + 1 ];
prect -> top = vrgycoGrid [iyco] + vcyBorder;
prect -> bottom = vrgycoGrid [iyco + 1];
}
}
/**** FMapCoToIGrid */
BOOL APIENTRY FMapCoToIGrid (
INT co, /* INPUT - the coordinate to map. */
INT *pco, /* INPUT - Coordinates of the grid. */
INT cco, /* INPUT - Count of coordinates in the grid. */
INT *pico) /* OUTPUT - the index of the grid coordinate. */
{
/* Note - by using co >= *pco in the loop control, we map the
the leftmost pixel of the calendar to the first column and
the topmost pixel to the first row. Then, because inside
the loop we use co <= *++pco, the rightmost and bottommost
pixels get mapped to the last column and bottom row.
A click on the border between two adjacent date boxes is
mapped to the one to the right (for vertical borders),
or bottom (for horizontal borders).
*/
for (*pico = 0; *pico < cco && co >= *pco; (*pico)++)
{
if (co <= *++pco)
return (TRUE);
}
return (FALSE);
}
/**** DtFromPd3 - convert D3 to DT. */
DT APIENTRY FAR DtFromPd3 (D3 *pd3)
{
static INT cDaysAccum [12] =
{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
DT dt;
dt = (pd3 -> wYear) * 365;
/* Add in the days for the preceding leap years. */
if (pd3 -> wYear != 0)
dt += 1 + (pd3 -> wYear - 1) / 4;
/* Add in the days for the full months before the current one. */
dt += (DT)(cDaysAccum [pd3 -> wMonth]);
/* If this is a leap year and the current month is beyond February,
add in an extra day.
*/
if (pd3 -> wYear % 4 == 0 && pd3 -> wMonth > MONTHFEB)
dt++;
/* Add in the days of the current month (prior to the current one). */
dt += pd3 -> wDay;
return (dt);
}
/**** GetMarkedDays - set the marked bits in vrgbMonth for the days
of the current month that are marked.
****/
VOID APIENTRY GetMarkedDays ()
{
D3 d3Temp;
DT dtFirst; /* First day of month. */
DT dtMax; /* Last day of month + 1. */
DT dtCur; /* Day of month of current dd. */
INT itdd; /* Index into the tdd. */
DD *pdd; /* Pointer into the tdd. */
/* Get the DTs of the first and (last + 1) days of the month. */
d3Temp = vd3Sel;
d3Temp.wDay = 0;
dtMax = (dtFirst = DtFromPd3 (&d3Temp)) + vcDaysMonth;
/* Look for the first day of the month. If it's found, itdd will
be its index. If it's not found, itdd will be the index of the
first entry in the tdd that is beyond the first day of the month.
In either case, this is the place where we start looking for marked
days within the current month.
*/
FSearchTdd (dtFirst, &itdd);
/* Lock the tdd and looking at all dates in the current month,
set their marked bits in vrgbMonth if they are marked in the
tdd.
*/
for (pdd = TddLock () + itdd;
itdd < vcddUsed && (dtCur = pdd -> dt) < dtMax; pdd++, itdd++)
/* set marked bits on each day */
if (pdd -> fMarked)
vrgbMonth [vwWeekdayFirst + (dtCur - dtFirst)] |= pdd -> fMarked;
TddUnlock ();
}
/**** MonthMode - Switch to month mode. */
VOID APIENTRY MonthMode ()
{
if (vfDayMode)
{
/* Record edits and disable focus for now. (When UpdateMonth
calls FFetchNewDate the focus should be NULL so it doesn't
get set to the wrong thing (like Wnd3)).
*/
CalSetFocus ((HWND)NULL);
/* Say we are in Month mode. */
vfDayMode = FALSE;
SetScrollRange (vhwnd2B, SB_VERT, 0, SCROLLMONTHLAST, FALSE);
SetScrollPos (vhwnd2B, SB_VERT, vd3Sel.wYear * 12 + vd3Sel.wMonth,
TRUE);
/* Hide the appointment description edit control. */
ShowWindow (vhwnd3, HIDE_WINDOW);
/* Repaint Wnd2A to display "Today is: ..." message. */
InvalidateRect (vhwnd2A, (LPRECT)NULL, TRUE);
/* Note - we are coming from View Month in CalCommand, so
vd3To == vd3Sel. This, along with setting vwDaySticky
to the current selected day will insure that the selected
date does not change when we call UpDateMonth.
*/
vwDaySticky = vd3Sel.wDay;
SetScrollPos (vhwnd2B, SB_HORZ, hmScrollPos, TRUE);
SetScrollPos (vhwnd2B, SB_VERT, vmScrollPos, TRUE);
SetScrollRange (vhwnd2B, SB_HORZ, 0, hmScrollMax, TRUE);
SetScrollRange (vhwnd2B, SB_VERT, 0, vmScrollMax, TRUE);
UpdateMonth ();
/* If the focus was in the notes area in day mode, leave it in
the notes area in month mode. Otherwise put the focus on
the calendar.
*/
CalSetFocus (vhwndFocus == vhwnd2C ? vhwnd2C : vhwnd2B);
}
}