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

765 lines
20 KiB
C
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/*
* Windows Calendar
* Copyright (c) 1985 by Microsoft Corporation, all rights reserved.
* Written by Mark L. Chamberlin, consultant to Microsoft.
*
* cal.c
*
*/
#include "cal.h"
BOOL FAR APIENTRY IsDefaultPrinterStillValid(LPSTR);
/**** FCalSize ****/
BOOL APIENTRY FCalSize (
HWND hwnd,
INT x,
INT y,
INT code)
{
INT cy, ytop, cx, xleft,
cyUseable, vclnOld, dln = 0,
tmp, dx, dy;
if (hwnd==vhwnd0)
{
/* Store appointment currently being edited so it gets repainted
* by DayPaint.
*/
if (GetFocus () == vhwnd3)
{
StoreQd ();
}
switch (code)
{
case SIZEFULLSCREEN:
case SIZENORMAL:
MoveWindow(vhwnd1, xleft=XcoWnd1(),
ytop=YcoWnd1(), vcxWnd1,
vcyWnd1, TRUE);
cy = vcyWnd2B;
if (cy > y-ytop-vcyWnd2A)
cy=y-ytop-vcyWnd2A;
cx = vcxWnd1 - vcxBorder;
if (cx > x-xleft)
cx=x-xleft;
MoveWindow(vhwnd2B, 0, vcyWnd2A, cx, cy, FALSE);
/* Reset global variables according to new window size. */
cyUseable = cy - 2 * vcyBorder - vcyExtLead ;
vclnOld = vcln;
vcln = cyUseable/vcyLineToLine;
/* Always display at least one line. In addition to
avoiding div by 0 errors, this shows user that there
is something that is "trying" to be displayed even if
there is not enough space. */
//- FCalSize: Fixed to handle vcln < 0.
if (vcln <= 0)
vcln = 1;
vlnLast = vcln-1;
/* If we're in day mode, reset the scroll range so user
can scroll 11:00 pm to bottom of window. (If in month
mode, smallest unit of scrolling is one month.) */
/* foll. lines set up vertical scroll globals for month view
Set vmScrollMax so that if less than a certain fraction of
the bottom line is visible, scrolling should be possible.
This has been determined by trial and error
*/
dy = (vcyWnd2BBot - vcyBorder)/ vcWeeksMonth;
tmp = y/dy;
if ((y%dy) < (5*dy/6))
tmp--;
vmScrollMax =max ( 0, vcWeeksMonth + 1 - tmp);
vmScrollPos = 0;
/* foll lines set up horizontal scroll globals for month view
Set hmScrollMax so that if less than a certain fraction of
the rightmost column is visible, scrolling should be possible.
This has been determined by trial and error */
dx = (vcxWnd2B + vcxBorder)/7;
tmp = x/dx;
if ((x%dx) < (5*dx/6))
tmp-- ;
hmScrollMax = max (0, 6 - tmp);
hmScrollPos = 0;
if (vfDayMode)
{
SetDayScrollRange();
/* If our resizing made window bigger, pick up extra
ld records that need to be put in tld. */
if ((dln = vcln - vclnOld) > 0)
{
while (dln && FGetNextLd(vtld[vlnLast-dln].tm, &vtld[vlnLast-dln+1]))
dln--;
/* If there is going to be extra space at bottom
of window, scroll to fill that space. */
ScrollDownDay(dln, TRUE, TRUE);
}
}
else
{
/* set up horiz. and vertical scroll bars in monthmode */
SetScrollPos (vhwnd2B, SB_VERT, vmScrollPos, TRUE);
SetScrollRange (vhwnd2B, SB_VERT, 0, vmScrollMax,TRUE);
SetScrollPos (vhwnd2B, SB_HORZ, hmScrollPos, TRUE);
SetScrollRange (vhwnd2B, SB_HORZ, 0, hmScrollMax, TRUE);
}
return (TRUE);
}
}
return (FALSE);
}
/**** CalWndProc ****/
LRESULT APIENTRY CalWndProc (
HWND hwnd,
UINT message,
WPARAM wParam,
LPARAM lParam)
{
PAINTSTRUCT ps;
register BOOL fActed; /* TRUE if we acted on the message. */
HCURSOR hcsr;
register WORD wlParamHi;
INT lnT;
fActed=TRUE;
wlParamHi=HIWORD(lParam);
switch(message)
{
case WM_CLOSE:
if (FCheckSave (FALSE))
DestroyWindow (vhwnd0);
break;
case WM_QUERYENDSESSION:
return (FCheckSave (TRUE));
case WM_DESTROY:
if (hwnd == vhwnd0)
{
/* Time to say goodbye - only field this message
for our main window.
*/
/* Get rid of the change file - ignore errors since
there is nothing to be done about it now and the
the user has either said to discard the changes
or they have already been saved.
*/
DeleteChangeFile ();
WinHelp(hwnd, vszHelpFile, HELP_QUIT, 0L);
/* Free all global objects */
CalTerminate(2);
/* Terminate with exit code 0, meaning no errors. */
PostQuitMessage (0);
}
else
fActed = FALSE;
break;
case WM_ENDSESSION:
/* If wParam is TRUE, we are never coming back again. */
if (wParam)
DeleteChangeFile ();
/* Get rid of the change file - ignore errors since
there is nothing to be done about it now and the
the user has either said to discard the changes
or they have already been saved.
*/
break;
case WM_SIZE:
fActed=FCalSize(hwnd, (SHORT)LOWORD(lParam),
(SHORT)HIWORD(lParam), (int)wParam);
break;
case WM_PAINT:
/* Hiding the caret of the appointment description edit
control before painting wnd2B in day mode is necessary
to prevent leaving cursor droppings around. So hide
the caret here, and show it after painting. Note that
this is OK even if we're not painting wnd2B, so no
extra code is used here to only do it for the wnd2B
case.
*/
HideCaret (vhwnd3);
BeginPaint (hwnd, &ps);
SetDefaultColors (ps.hdc);
CalPaint (hwnd, ps.hdc);
EndPaint (hwnd, &ps);
ShowCaret (vhwnd3);
break;
case WM_COMMAND:
/* HIWORD (lParam) == 0 means a command has been selected
via the menu. 1 means a command has been selected
via an accelerator. Something other than 0 or 1 is
the window handle of a control sending a notification.
*/
if (GET_WM_COMMAND_CMD (wParam, lParam) <= 1)
{
/* A menu item has been selected. */
CalCommand (hwnd, GET_WM_MENUSELECT_CMD (wParam, lParam));
}
else
{
/* Handle notifications from edit controls. */
//- EcNotification (wParam, GET_WM_COMMAND_ID(wParam, lParam));
EcNotification ((WORD)wParam, (WORD)HIWORD (wParam));
/* Even if we handled the message, say we didn't
in case something else needs to be done with it.
*/
fActed = FALSE;
}
break;
case WM_SYSCOMMAND:
fActed = FALSE;
break;
case WM_TIMER:
CalTimer (FALSE);
break;
case WM_TIMECHANGE:
CalTimer(TRUE);
break;
case WM_VSCROLL:
if (IsWindowEnabled(vhwnd0))
{
if (fActed == vfDayMode)
{
FScrollDay (GET_WM_VSCROLL_CODE(wParam, lParam),
GET_WM_VSCROLL_POS(wParam, lParam));
}
else
{
FScrollMonth (GET_WM_VSCROLL_CODE(wParam, lParam),
GET_WM_VSCROLL_POS(wParam, lParam));
}
}
break;
case WM_HSCROLL: /* added 11/3/88 for horiz. scroll in month view */
/* We have replaced the bitmap arrows with a scrollbar control; So, the
* following code handles the scroll messages from it
* Fix for Bug #8560 -- SANKAR -- 01-28-90
*/
#ifndef BUG_8560
/* Check if this is the Horizontal Scroll bar control */
if(GET_WM_HSCROLL_HWND(wParam, lParam) == vhScrollWnd)
{
switch(GET_WM_HSCROLL_CODE(wParam, lParam))
{
case SB_LINEUP:
CalCommand (vhwnd0, IDCM_PREVIOUS);
break;
case SB_LINEDOWN:
CalCommand (vhwnd0, IDCM_NEXT);
break;
}
break;
}
#endif
if (IsWindowEnabled(vhwnd0))
{
if (fActed != vfDayMode)
FHorizScrollMonth (GET_WM_HSCROLL_CODE(wParam, lParam),
GET_WM_HSCROLL_POS(wParam, lParam));
}
case WM_MOUSEMOVE:
/* The mouse cursor is an arrow everywhere except when
in an appointment description or in the notes area, when
it must be the Ibeam. There are two reasons we can't just
let the edit controls take care of this:
1) The appointment description edit control only covers
one appointment description, and we want the Ibeam
to appear on all the descriptions.
2) The notes edit control does not use up the entire
bottom box of the calendar, and we want the Ibeam in the
entire box. Note that we make the Ibeam start if the
cursor is below the line we drew at vycoNotesBox.
*/
hcsr = vhcsrArrow;
if (hwnd == vhwnd2B && vfDayMode
&& (INT)LOWORD (lParam) >= vxcoQdFirst
|| hwnd == vhwnd1 && (INT)HIWORD (lParam) > vycoNotesBox)
hcsr = vhcsrIbeam;
SetCursor (hcsr);
break;
case WM_LBUTTONDBLCLK:
case WM_LBUTTONDOWN:
if (hwnd == vhwnd1 && (INT)HIWORD (lParam) > vycoNotesBox)
{
/* Click in the bottom box (below the line at vycoNotesBox)
but not in the notes edit control. Pass the click
to the notes edit control.
The mouse coordinates we were passed are
relative to the origin of wnd1. Make
them relative to the origin of the notes
edit control.
*/
((POINTS*)&lParam)->x -= (short)vxcoWnd2C;
((POINTS*)&lParam)->y -= (short)vycoWnd2C;
PostMessage (vhwnd2C, message, wParam, lParam);
break;
}
if (hwnd == vhwnd2A )
{
/* Double clicking in the date field is the same as
using the View Month command (i.e., switch to
month mode).
*/
if (message == WM_LBUTTONDBLCLK &&
(INT)LOWORD (lParam) >= vxcoDate)
{
if (vfDayMode)
CalCommand (vhwnd0, IDCM_MONTH);
else
/* Switch back to today */
DayMode (&vd3Sel);
}
break;
}
if (hwnd == vhwnd2B)
{
if (vfDayMode)
{
/* If we just clicked on a new line, "move" edit ctl
window to new line. Otherwise, just pass mouse
message to edit ctl. */
if ((lnT = LnFromYco (wlParamHi)) != vlnCur) {
/* Suppose that the appointment window is not clean.
In particular, we are concerned about the
rectangle that we are about to put the appointment
description edit control on top of. SetQdEc
validates the edit control after moving it to
prevent it from repainting, and the ValidateRect
call in turn validates that portion of the parent.
This means that if it was dirty before calling
SetQdEc, it won't get repainted by DayPaint, which
it should. In order to get around this problem,
make sure everything is clean before calling
SetQdEc. An example of where this was a problem:
Zoom and immediately click on a new appointment.
The click was seen before all painting has been
done, so a hole was left where the edit control
was moved.
Force everthing to be clean by calling
UpdateWindow for our main window (using
wnd2B caused out-of-sequence painting and
really left a mess on the screen).
*/
UpdateWindow (vhwnd0);
SetQdEc (lnT);
}
/* Let the edit control see the click too.
The mouse coordinates we were passed are
relative to the origin of wnd2B. Make
them relative to the origin of the QD
edit control.
*/
((POINTS*)&lParam)->x -= (short)vxcoQdFirst;
((POINTS*)&lParam)->y -= (short)YcoFromLn(vlnCur);
PostMessage (vhwnd3, message, wParam, lParam);
}
else
{
/* Note - if the mouse position is not on a box for a
valid day of the month, the click is ignored.
However, we still leave fActed == TRUE since the
mouse click has been acted on by us in the sense
that we do not expect Windows to do anything
further with it.
*/
MouseSelectDay(MAKEMPOINT(lParam),
message == WM_LBUTTONDBLCLK);
}
}
else
{
fActed = FALSE;
}
break;
case WM_KEYDOWN:
fActed = FCalKey (hwnd, wParam);
break;
case WM_ACTIVATE:
if (!fInitComplete)
{
fActed = FALSE;
break;
}
if (GET_WM_ACTIVATE_STATE(wParam, lParam))
{
/* Becoming active. */
/* If not iconic, give the focus to the last one who
had it.
*/
if (GET_WM_ACTIVATE_FMINIMIZED(wParam, lParam))
CalSetFocus (vhwndFocus);
/* Tell the user about any alarms that went off while
we were inactive.
*/
PostMessage(hwnd, CM_PROCALARMS, 0, 0L);
}
else
{
/* Becoming inactive - pass this off to DefWindowProc. */
fActed = FALSE;
}
break;
case CM_PROCALARMS:
uProcessAlarms ();
break;
case WM_SETFOCUS:
/* If the monthly calendar is getting the focus, create,
position, and show its caret. Otherwise, do not process
this message.
*/
if (hwnd == vhwnd2B && !vfDayMode)
{
/* Create a caret for month mode. Specifying NULL for the
second parameter gives a black caret. The third
parameter is the width, and by making the fourth
parameter 0, we get a height of a horizontal border
(same as vcyBorder).
*/
CreateCaret (vhwnd2B, (HBITMAP)NULL, 2 * vcxFont, 0);
/* Position the caret to the selected day. */
PositionCaret ();
/* Make the caret visible. */
ShowCaret (vhwnd2B);
/* Remember we last had the focus so we get it
back when re-activated.
*/
vhwndFocus = vhwnd2B;
}
else if (hwnd == vhwnd0)
/* 12-Mar-1987. to make sure focus set somewhere when
* parent gets focus.
*/
CalSetFocus (vhwndFocus);
else
fActed = FALSE;
break;
case WM_KILLFOCUS:
/* If the monthly calendar is losing the focus,
destroy its caret. Otherwise, do not process this message.
*/
if (hwnd == vhwnd2B && !vfDayMode)
DestroyCaret ();
else
fActed = FALSE;
break;
case WM_SYSCOLORCHANGE:
/* The system colors have changed. Destroy and recreate
the brushes.
*/
DestroyBrushes ();
CreateBrushes ();
/* Repaint since AppWorkspace color may have changed */
InvalidateRect(hwnd, NULL, TRUE);
break;
case WM_ERASEBKGND:
PaintBack (hwnd, (HDC)wParam);
break;
case WM_INITMENU:
/* Menu is being pulled down. Enable/disable, check/uncheck
menu items.
*/
InitMenuItems ();
break;
case WM_WININICHANGE:
CalWinIniChange();
break;
default:
fActed = FALSE;
break;
}
return (fActed ? 0L : DefWindowProc (hwnd, message, wParam, lParam));
}
/**** XcoWnd1 - return the xco of where to put Wnd1 */
INT APIENTRY XcoWnd1 ()
{
RECT rect;
int cxDesired, cxAvailable, xcoLeft;
GetClientRect (vhwnd0, &rect);
cxDesired = vcxWnd1;
cxAvailable = rect.right - rect.left;
xcoLeft = 0;
if (cxAvailable > cxDesired)
xcoLeft = (cxAvailable - cxDesired) / 2;
#ifdef DISABLE
return (max (xcoLeft, vcxFont));
#endif
return (xcoLeft);
}
/**** YcoWnd1 - return the yco of where to put Wnd1 */
INT APIENTRY YcoWnd1 ()
{
RECT rect;
INT cyAvailable;
INT ycoTop;
GetClientRect (vhwnd0, &rect);
cyAvailable = rect.bottom - rect.top;
ycoTop = 0;
if (cyAvailable > vcyWnd1)
ycoTop = (cyAvailable - vcyWnd1) / 2;
#ifdef DISABLE
return (max (ycoTop, vcyBorder));
#endif
return (ycoTop);
}
/**** CalSetFocus - Set the focus unless vfNoGrabFocus is set. This
is used to prevent Calendar from grabbing the focus if brought
up iconic.
*/
VOID APIENTRY FAR CalSetFocus (HWND hwnd)
{
if (!vfNoGrabFocus)
SetFocus (hwnd);
}
/**** InitMenuItems */
VOID APIENTRY InitMenuItems ()
{
register WORD mf1;
register WORD mf2;
HMENU hMenu;
WORD2DWORD iSelFirst;
WORD2DWORD iSelLast;
UINT wFmt;
/* Get a handle to the menu. */
hMenu = GetMenu (vhwnd0);
/* Cut and Copy - enable iff edit control has focus and
some text is selected.
Paste - enable iff edit control has focus and the clipboard
is not empty.
*/
mf1 = mf2 = MF_GRAYED;
if (vhwndFocus != vhwnd2B)
{
/* Focus is not on monthly calendar so it must be on either
the appointment edit control or the notes edit control.
*/
/* Enable Cut and Copy if the selection isn't null. */
MSendMsgEM_GETSEL(vhwndFocus, &iSelFirst, &iSelLast);
if (iSelFirst != iSelLast)
mf1 = MF_ENABLED;
/* Enable Paste if the clipboard isn't empty. */
if (OpenClipboard (vhwnd0))
{
wFmt = 0;
/* If clipboard has any text data, enable paste item. otherwise
leave it grayed. */
while ((wFmt = EnumClipboardFormats(wFmt)) != 0)
{
if (wFmt == CF_TEXT)
{
mf2 = MF_ENABLED;
break;
}
}
CloseClipboard ();
}
}
EnableMenuItem (hMenu, IDCM_CUT, mf1);
EnableMenuItem (hMenu, IDCM_COPY, mf1);
EnableMenuItem (hMenu, IDCM_PASTE, mf2);
/* Check day if in day mode, check month if in month mode.
Uncheck the other.
*/
mf1 = MF_CHECKED;
mf2 = MF_UNCHECKED;
if (!vfDayMode)
{
mf1 = MF_UNCHECKED;
mf2 = MF_CHECKED;
}
CheckMenuItem (hMenu, IDCM_DAY, mf1);
CheckMenuItem (hMenu, IDCM_MONTH, mf2);
/* Alarm Set - enable iff focus is on an appointment.
If enabled, check iff appointment has alarm set.
*/
mf1 = MF_GRAYED;
mf2 = MF_UNCHECKED;
if (vhwndFocus == vhwnd3)
{
mf1 = MF_ENABLED;
if (FAlarm (vlnCur))
mf2 = MF_CHECKED;
}
EnableMenuItem (hMenu, IDCM_SET, mf1);
CheckMenuItem (hMenu, IDCM_SET, mf2);
/* Options special time - enable if in day mode. */
mf1 = MF_GRAYED;
if (vfDayMode)
mf1 = MF_ENABLED;
EnableMenuItem (hMenu, IDCM_SPECIALTIME, mf1);
}
VOID APIENTRY CalWinIniChange()
{
HMENU hMenu;
SHORT id, nx;
CHAR ch;
static bszDecRead=FALSE;
/* Set decimal to scan for */
if (bszDecRead)
ch=szDec[0]; /* If we already changed it. */
else
ch='.'; /* First time. */
bszDecRead=TRUE;
/* Get the intl decimal character for use in Page Setup Box. */
GetProfileString("intl", "sDecimal", ".", szDec, 4);
/* Scan for . and replace with intl decimal */
for (id=2; id<6; id++)
{
for (nx=0; nx < lstrlen((LPSTR)chPageText[id]); nx++)
if (chPageText[id][nx]==ch)
chPageText[id][nx]=szDec[0];
}
hMenu = GetMenu(vhwnd0);
/* Check if a default printer exists */
/* Fix for Bug #5607 --SANKAR-- 10-30-89 */
if(bPrinterSetupDone)
{
if(!IsDefaultPrinterStillValid((LPSTR)szPrinter))
bPrinterSetupDone = FALSE; /* No longer valid */
}
#ifdef SLOWTHINGSDOWN
if (!(hdc = GetPrinterDC()))
{
EnableMenuItem(hMenu, IDCM_PRINT, MF_GRAYED);
}
else
{
DeleteDC(hdc);
EnableMenuItem(hMenu, IDCM_PRINT, MF_ENABLED);
}
#endif
InitTimeDate (vhInstance, vfHour24 ? GTS_24HOUR : GTS_12HOUR);
/* Force repainting of windows that contain date time strings. */
InvalidateRect(vhwnd0, NULL, TRUE);
}