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