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