/*
 *   Windows Calendar
 *   Copyright (c) 1985 by Microsoft Corporation, all rights reserved.
 *   Written by Mark L. Chamberlin, consultant to Microsoft.
 *
*/

/*
 *****
 ***** calday2.c
 *****
*/

#include "cal.h"


/**** SetDayScrollRange */

VOID APIENTRY SetDayScrollRange ()

     {

     /* Set the range so that the minimum thumb position corresponds to
        having the first TM of the day at the top of the window and
        the maximum thumb position corresponds to having the last TM
        of the day at the bottom of the window.  We want to always be
        able to work from the first TM in the window, so we observe
        that the last TM of the day is at the bottom of the window
        when the last windowful (or page) of TMs is up, and this
        is when the (ctmDay - vcln)th TM is at the top of the window.
        For example, if there are 100 TMs (0 - 99), and we can display
        10 TMs in the window (vcln == 10), we are maxed out when
        90 through 99 are in the window.
        Note that calling ItmFromTm with TMNILHIGH returns the count
        of TMs in the day.
     */
     SetScrollRange (vhwnd2B, SB_VERT, 0, ItmFromTm (TMNILHIGH) - vcln, FALSE);

     }


/**** AdjustDayScrollRange - adjust the scroll bar range by the specified
      number of TMs.
*/

VOID APIENTRY AdjustDayScrollRange (INT ctm)
     {

     INT  itmMin;
     INT  itmMax;

     GetScrollRange (vhwnd2B, SB_VERT, (LPINT)&itmMin, (LPINT)&itmMax);
     SetScrollRange (vhwnd2B, SB_VERT, 0, itmMax + ctm, FALSE);

     }


/**** SetDayScrollPos - position the thumb. */

VOID APIENTRY SetDayScrollPos (register INT itm)
     {

     /* If called with itm == -1, position the thumb based on the TM
        at the top of the window.
     */
     if (itm == -1)
          itm = ItmFromTm (vtld [0].tm);

     SetScrollPos (vhwnd2B, SB_VERT, itm, TRUE);

     }


/**** AdjustDayScrollPos - adjust the position of the thumb relative
      to its old position.
*/

VOID APIENTRY AdjustDayScrollPos (INT ctm)
			/* Positive to increase thumb position (towards
                            bottom of window), negative to decrease thumb
                            (towards top of window).
                         */

     {

     SetDayScrollPos (GetScrollPos (vhwnd2B, SB_VERT) + ctm);

     }



/**** ItmFromTm - map a TM to an index within the range of TMs for the day.
      If tmToMap does not exist within the day (not a regular time and
      not in the tqr), we return the index of the next
      highest TM.  Therefore, to find out how many TMs there are in
      the day, call ItmFromTm with tmToMap == TMNILHIGH.  SetDayScrollRange
      depends on this.

      Note - tmToMap must be in the closed interval 0 through TMNILHIGH.
*/

INT  APIENTRY ItmFromTm (TM tmToMap)
     {

     INT  itm;

     itm = -1;

     MapTmAndItm (&tmToMap, &itm);
     return (itm);

     }


/**** TmFromItm - map TM to an index within the range of TMs for this date. */

TM   APIENTRY TmFromItm (INT  itm)
     {

     TM   tm;

     MapTmAndItm (&tm, &itm);
     return (tm);

     }


/**** MapTmAndItm - map TM to itm and vice versa.
      To map a TM to an itm:  *ptmMap == TM to map,  *pitmMap == -1.
      To map an itm to a TM:: *ptmMap == don't care, *pitmMap == itm to map.

*/

VOID APIENTRY MapTmAndItm (
    TM   *ptmMap,
    INT  *pitmMap)

     {

     register TM tmCur;
     register TM tmFromQr;
     TM   tmMap;
     DR   *pdr;
     PQR	pqrCur;
     PQR	pqrMax;
     INT  itm;
     BOOL fMapTmToItm;

     tmMap = ((fMapTmToItm = *pitmMap) == -1) ? *ptmMap : TMNILHIGH;

     /* Lock the DR, and get First and Max pointers for the tqr. */
     pdr = PdrLockCur ();

     pqrMax = (PQR)((BYTE*)(pqrCur = (PQR)PbTqrFromPdr(pdr)) + pdr->cbTqr);

     /* Find the first QR time. */
     tmFromQr = TmFromQr (&pqrCur, pqrMax);

     /* Starting at the first possible TM of the day (0), keep going
        until we find a TM greater than or equal to the one we're mapping.
     */
     for (itm = (INT)(tmCur = 0); tmCur < tmMap && itm != *pitmMap; itm++)
          {
          if ((tmCur = TmNextRegular (tmCur)) >= tmFromQr)
               {
               /* The QR TM is less than or equal to the next
                  regular TM, so use the one from the QR in order to
                  skip over it.

                  Note - At this point, both tmCur and tmFromQr could
                  be TMNILHIGH (because there aren't anymore QRs
                  and there are no more regular times.  This works OK
                  since we end up using TMNILHIGH which will terminate
                  terminate the loop since TMNILHIGH is the highest value
                  the caller is permitted to pass.  Calling TmFromQr
                  with pqrCur == pqrMax works OK too, so no problem there.
               */
               if ((tmFromQr = TmFromQr (&pqrCur, pqrMax)) < tmCur)
                    tmCur = tmFromQr;
               }
          }

     DrUnlockCur ();

     /* Pass back the mapped value to the caller. */
     if (fMapTmToItm)
          *pitmMap = itm;
     else
          *ptmMap = tmCur;

     }


/**** TmFromQr - return TM of the current QR or TMNILHIGH if there
      isn't one.  Also update pqrCur to point past the last QR we inspect.

      Note - Guaranteed to do the right thing if called with
      pqrCur == pqrMax.  In this case we return TMNILHIGH and pqrCur
      is unchanged.
*/

TM   APIENTRY TmFromQr (
    PQR	*ppqrCur,          /* Input - pqrCur points to the current QR.
				Output - pqrCur points to the next QR or
				is equal to pqrMax if no next QR.
			     */
    PQR	pqrMax)

     {

     register PQR pqr;
     register TM tm;

     /* Assume there are no more QRs. */
     tm = TMNILHIGH;

     if ((pqr = *ppqrCur) < pqrMax)
          {
          tm = pqr -> tm;
          *ppqrCur = (PQR)((BYTE*)pqr + pqr->cb);
          }

     return (tm);

     }


/**** TmNextRegular - return the next regular TM or TMNILHIGH if there
      is no next regular TM.
*/

TM   APIENTRY TmNextRegular (register TM tm)
     {

     register TM tmNext;

     /* Calculate the next regular appointment time. */
     tmNext = tm + vcMinInterval - (tm % vcMinInterval);

     /* Return TMNILHIGH if beyond the end of the day. */
     if (tmNext > TMLAST)
          tmNext = TMNILHIGH;

     return (tmNext);

     }
