/* S C R O L L . C */
#include "word.h"
DEBUGASSERTSZ            /* WIN - bogus macro for assert string */
#include "heap.h"
#include "props.h"
#include "format.h"
#include "disp.h"
#include "doc.h"
#include "screen.h"
#include "scc.h"
#include "sel.h"
#include "layout.h"
#include "debug.h"
#include "keys.h"
#ifdef MAC
#include "mac.h"
#define CONTROLS
#define WINDOWS
#include "toolbox.h"
#endif


/* E X T E R N A L S */

extern int              vssc;
extern struct SCC       vsccAbove;
extern struct SCC       *vpsccBelow;
extern int              vcConseqScroll;
extern int              sbScroll;
extern int              sbScrollLast;
extern struct WCC       vwcc;
extern struct FLI       vfli;
extern struct CA        caPara;
extern struct CA        caTap;
extern struct DBS       vdbs;
extern struct MERR      vmerr;
extern CP               vcpFetch;
extern int              vccpFetch;
extern struct CHP       vchpFetch;
extern struct PAP       vpapFetch;
extern struct PREF      vpref;
extern struct SEL       selCur;
extern struct SEL       selDotted;
extern int              vfLastScrollDown;
extern int              vfLastScroll;
extern int              vfSeeSel;
extern int              wwMac;
extern struct WWD       **mpwwhwwd[];
extern int              vfCheckPlc;
extern int              wwCur;

extern struct DR *PdrWw();

#ifdef OPUS_X64
/* Remember a completed end-of-document pagination probe.  The old engine was
   written for page-at-a-time buttons and does not tolerate the same failed
   next-page repagination being requested for every wheel tick. */
static int vrgdocWin95NoNext[wwMax];
static int vrgipgdWin95NoNext[wwMax];
static CP vrgcpWin95NoNext[wwMax];
#endif

#ifdef WIN
extern int              vlm;
extern int              vflm;
extern struct SCI       vsci;
int cdlScroll;
extern BOOL fCdlScrollSet;
#endif /* WIN */

#ifdef MAC
extern int vwwScroll;
extern int wwHelp;
#endif



#ifdef NOTUSED
/* S C R O L L  U P  E N D */
/* scroll up, but check for endmark not moving higher than midpoint */
/* %%Function:ScrollUpEnd %%Owner:NOTUSED */
ScrollUpEnd(ww, dyp)
int dyp;
{
	int dl;
	struct WWD *pwwd = PwwdWw(ww);
	struct EDL edl;

	Assert(!pwwd->fPageView);
	if ((dl = DlFromCpCheck(HwwdWw(ww), 0, CpMacDoc(PdrGalley(pwwd)->doc), fFalse)) > 0)
		{
		GetPlc(PdrGalley(pwwd)->hplcedl, dl - 1, &edl);
		dyp = min(dyp, max(0, edl.ypTop + edl.dyp -
				PdrGalley(pwwd)->dyl / 2));
		}
	ScrollUp(ww, dyp, dyp + WinMac(dysMinAveLineSci, 24));
}


#endif /* NOTUSED */


/* S C R O L L  U P */
/* scrolls current window contents up (down in the document) by
at least dypLow but at most dypHigh. Within these constraints procedure
will find the least amount to scroll to a even line boundary if
possible.

Plan:
	1. find dl with top yp pos at least dypLow below the start of the
	window. Choose dypAboveNew depending on dypHigh. Do not scroll
	below fEnd dl.
	2. let dlFirstFull be first full height line. Copy lines
	[dlFirstFull, dl] to sccAbove.
		To do this, we have to determine number of dl's to be
		deleted from the front of the scc, taking into account
		the dl and yp limits.
	3. scroll window contents so that dl will be first line and
	its top position will be dypAbove above ypMin.
	cpFirst of window to get cpMin of dl0.
	Window is marked dirty so that UpdateWindow can do the rest.
*/
/* %%Function:ScrollUp %%Owner:bryanl */
ScrollUp(ww, dypLow, dypHigh)
int dypLow, dypHigh;
{
	int dlMacOld;
	struct WWD *pwwd, **hwwd;
	int ypLow;
	int dypAbove, dypVisi;
	int dl, cdl, cdlAboveNew;
	int dlMac, cdlT;
	struct PLCEDL **hplcedl;
	struct EDL edl;
	Debug(int fCheckPlcSave = vfCheckPlc);

	vfLastScroll = fTrue;
	vfLastScrollDown = fFalse;
	vfSeeSel = fFalse;
	UpdateWw(ww, fFalse);


	pwwd = *(hwwd = HwwdWw(ww));
	if (pwwd->fPageView)
		{
		ScrollPageDye(ww, dypLow);
		return;
		}

#ifdef WIN
	cdlScroll=0;
	fCdlScrollSet=fTrue;
#endif

	Debug(vfCheckPlc = fFalse);
	hplcedl = PdrGalley(pwwd)->hplcedl;
	if (hplcedl == hNil)
		return; /* can happen coming out of pageview from low in memory */
	FreezeHp();

	ClearInsertLine(&selCur);
	ClearInsertLine(&selDotted);
	ypLow = dypLow;
	cdlT = IMacPlc(hplcedl);
	if (cdlT == 0)
		{
		MeltHp();
		Beep();
		return;
		}
		{{ /* NATIVE - ScrollUp */
		for (dl = 0; cdlT-- > 0; dl++)
			{
			GetPlc(hplcedl, dl, &edl);
			if (edl.fEnd || edl.ypTop + edl.dyp >= ypLow)
				break;
			}
		}}
	if (edl.fEnd)
		{
/* scroll end dl to the top */
		dypAbove = 0;
/* but don't be scrollin' past CpMacDoc */
		if (CpPlc(hplcedl, dl) > CpMacDoc(PdrGalley(pwwd)->doc))
			--dl;
		Assert(CpPlc(hplcedl, dl) <= CpMacDoc(PdrGalley(pwwd)->doc));
		}
	else  if ((dypVisi = edl.ypTop + edl.dyp - dypHigh) > 0)
/* scroll to line dl, with dypAbove to limit scrolling */
		{
		dypAbove = edl.dyp - dypVisi;
		Assert(dypAbove > 0 && dypAbove < edl.dyp);
		}
	else
		{
/* scroll to line dl+1, with dypAbove=0 */
		dl++;
		dypAbove = 0;
		}
/* scroll line dl to the top with dypAbove hanging out */
#ifdef MAC
	if (ww == wwHelp && FScrollHelp(dl, dypAbove))
		return;	/* FScrollHelp melted heap */
#endif

/* save full height lines in sccAbove. NOTE: dlFirstFull is 0/1 depending
on pwwd->dypAbove != 0. cdlAboveNew means that the line dl itself
will have to be saved in the cache above if the new dypAbove is not 0 */
	cdlAboveNew = dypAbove != 0;
	if ((cdl = dl - (PdrGalley(pwwd)->dypAbove != 0) + cdlAboveNew) > 0) /* # of dl's to be copied */
		{
#ifdef MAC
		if (vpsccBelow)
/* take care of bitmaps */
			MoveToSccAbove(ww, dl - cdl + cdlAboveNew, dl + cdlAboveNew);
		else
#endif
				{
				if (PdrGalley(pwwd)->cpFirst != vsccAbove.rgcp[vsccAbove.plcedl.dlMac])
					PutIMacPlc(vsccAbove.hplcedl, 0);
				if (vsccAbove.plcedl.dlMac + cdl > vsccAbove.plcedl.dlMax - 1)
					{
					cdl = min(cdl, vsccAbove.plcedl.dlMax - 1);
				/* make room in dndl by shoving dl's toward 0 */
					dlMacOld = vsccAbove.plcedl.dlMac;
					PutIMacPlc(vsccAbove.hplcedl, vsccAbove.plcedl.dlMax - 1 - cdl);
					Assert(vsccAbove.plcedl.dlMac >= 0);
					MoveEdls(&vsccAbove, &vsccAbove, dlMacOld - vsccAbove.plcedl.dlMac, 0,
							vsccAbove.plcedl.dlMac);
					}
/* now there is dl room above dlMac for cd dl's. Copy edl's from window
to cache starting at dl - cdl + cdlAboveNew. Note: cdl might have been clipped by
dlMax */

				MoveEdls(pwwd, &vsccAbove, dl - cdl + cdlAboveNew,
						vsccAbove.plcedl.dlMac, cdl);
				PutIMacPlc(vsccAbove.hplcedl, vsccAbove.plcedl.dlMac + cdl);
				Assert(vsccAbove.plcedl.dlMac >= 0);
				}
		}
/* now scroll window contents */
	PdrGalley(pwwd)->cpFirst = CpPlc(hplcedl, dl);
	if (dl != 0)
		{
		GetPlc(hplcedl, dl - 1, &edl);
		PdrGalley(pwwd)->dcpDepend = edl.dcpDepend;
		}

	MeltHp();
	dlMac=IMacPlc(hplcedl);
	if (dl < dlMac && FScrollOK(ww))
		{
		GetPlc(hplcedl, dl, &edl);

		ScrollDrUp(ww, hwwd, 0, PdrGalley(*hwwd)->hplcedl, dl, 0 /*dlTo*/,
				edl.ypTop, -dypAbove /*ypTo*/,
				YpFromYw(hwwd, 0, (*hwwd)->ywMin) /* ypFirstShow */,
				YpFromYw(hwwd, 0, (*hwwd)->ywMac) /*ypLimShow*/);
		}
#ifdef WIN
	if (dl <= dlMac)	cdlScroll=dl;
#endif
	pwwd = *hwwd;
	pwwd->fDirty = fTrue;
	PdrGalley(pwwd)->dypAbove = dypAbove;
	pwwd->fSetElev = fTrue;
	Debug(vfCheckPlc = fCheckPlcSave);
#ifdef MAC
	if (vpsccBelow && FScrollOK(ww))
		ScrollUpLarge(ww);
#endif	/* MAC */
	SynchCheck(ww, fFalse /*fCkVisibility*/);
}


/* S C R O L L  D O W N */

/* Scrolls current window contents down (up in the document) by at least
dypLow but at most dypHigh. Within these constraints procedure
will find the least amount to scroll to a even line boundary if
possible.

Plan:
	1. if first line in ww is partially visible, consider next line
	as dlFirst and increment parameters by the dyp that is visible.
	so that params are always relative to a base dl called dlFirst.
	2. determine the actual cdlTotal and total height dypTotal
	taken up by the lines we have to scroll over. This is done by
	looping within the cache sccAbove, and if there is not sufficient
	data in there, reload cache, decrement parameters by amount
	already seen, and loop.
	3. once we have the precise height we need to back up, the
	window can be scrolled by that much. UpdateWindow will take care
	of the rest. The empty space will be invalidated and in large
	systems refilled from sccAbove.

*/
/* %%Function:ScrollDown %%Owner:bryanl */
ScrollDown(ww, dypLow, dypHigh)
int ww, dypLow, dypHigh;
{
	struct WWD *pwwd = PwwdWw(ww);

	vfLastScroll = fTrue;
	vfLastScrollDown = fTrue;
	vfSeeSel = fFalse;
	if (pwwd->fPageView)
		{
		ScrollPageDye(ww, -dypHigh);
		return;
		}

	ScrollDown2(ww, 0, dypLow, dypHigh, WinMac( fFalse, vpsccBelow && !pwwd->fOutline));
}


/* %%Function:ScrollDown2 %%Owner:bryanl */
ScrollDown2(ww, idr, dypLow, dypHigh, fUseScc)
int ww, idr, dypLow, dypHigh, fUseScc;
{
	int dl, dlFirst;
	int ypFirst;
	int dypVisi, dypTotal, dypAbove, ypLimWw;
	int cdlTotal;
	struct PLCEDL **hplcedl;
	struct WWD *pwwd = PwwdWw(ww);
	BOOL fDirty;
	struct EDL *dndl;
	int ypTo;
	int dlMac;
	struct DR *pdr;
	struct EDL edl;
	Debug(int fCheckPlcSave = vfCheckPlc;)

			Win( Assert( fUseScc == fFalse ) );
	if (idr >= pwwd->idrMac)
		return;	/* failsafe for page view problems; 0 may not exist */
	Debug(vfCheckPlc = fFalse);
	fDirty = pwwd->fDirty;
	hplcedl = PdrWw(ww, idr)->hplcedl;
	if (hplcedl == hNil)
		return;  /* can happen coming out of pageview from low in memory */

	dlFirst = 0;
	dypVisi = 0;
	ypFirst = 0;

#ifdef WIN
	cdlScroll = 0;
	fCdlScrollSet = fTrue;
#endif

	ClearInsertLine(&selCur);
	ClearInsertLine(&selDotted);
	if (PdrWw(ww, idr)->dypAbove)
		{
		GetPlc(hplcedl, 0, &edl);
		ypFirst = edl.ypTop + edl.dyp;
		dypVisi = ypFirst;
		dlFirst = 1;
		dypLow += dypVisi;
		dypHigh += dypVisi;
		}
	dypTotal = DypScanAbove(ww, dypLow, &cdlTotal);
	if (dypLow == 0)
		dypTotal = cdlTotal = 0;
	dypAbove = 0;
	if (dypTotal > dypHigh)
		{
		/* High limitation exceeded, show only part of last line */
		dypAbove = dypTotal - dypHigh; 
		dypTotal = dypHigh;
		}
	if (dlFirst != 0)
		{
		cdlTotal--;
		Break2();
		dypTotal -= dypVisi;
		Assert(dypTotal >= 0);
		}
/* now we have: dypTotal, cdlTotal describing precise amount to scroll down
from dl0 and ypMin, the desired dypAbove, and the cp of the proposed top line
in pwwd->cpFirst. */
/* ensure there are at least cdlTotal + dlFirst dl's in plcedl */
	dlMac = IMacPlc(hplcedl);
	if (!FRareF(300, FStretchPlc(hplcedl,
			cdlTotal + dlFirst - dlMac)))
		goto LEnd;

	if (dypTotal < PdrWw(ww, idr)->dyl && FScrollOK(ww))
		{
/* prepare UpdateWindow by scrolling down window contents and invalidating
the first cdlTotal lines. The above "if" means no sense in scrolling if the
whole window is invalid anyway */
		ypTo = ypFirst + dypTotal;
#ifdef MAC
		if (fUseScc && !fDirty)
			MoveToSccBelow(ww, dlFirst, ypTo);
#endif
		if (dypTotal != 0 && dlFirst < dlMac)
	/* check is here to prevent displaying for "cpFirst blessing" scrolls by 0,
eg. after window splits. */
			{
			pwwd = PwwdWw(ww);
			GetPlc(hplcedl, dlFirst, &edl);
			ypLimWw = YpFromYw(HwwdWw(ww), idr, pwwd->ywMac);


			ScrollDrDown(ww, HwwdWw(ww), idr, hplcedl, dlFirst, dlFirst + cdlTotal /*dlTo*/,
					edl.ypTop, ypTo,
					YpFromYw(HwwdWw(ww), idr, pwwd->ywMin) /*ypFirstShow*/,
					ypLimWw, ypLimWw);
			}
/* line 0 is always invalid, dlFirst may be 0 or 1 */
		for (dl = 0; dl < cdlTotal + dlFirst; dl++)
			{
			GetPlc(hplcedl, dl, &edl);
			edl.fDirty = fTrue;
			PutPlcLast(hplcedl, dl, &edl);
			}
		}
#ifdef WIN
	if (cdlTotal <= dlMac)
		cdlScroll = cdlTotal;
#endif
LEnd:
	PwwdWw(ww)->fDirty = fTrue; /* for the benefit of the cpFirst == 0 case */
	pdr = PdrWw(ww, idr);
	pdr->fCpBad = fFalse;
	pdr->dypAbove = dypAbove;
	Debug(vfCheckPlc = fCheckPlcSave);
#ifdef MAC
	if (fUseScc)
		ScrollDownLarge(ww, cdlTotal + dlFirst);
#endif
	if (dypLow)
		{
		SynchCheck(ww, fFalse/*fCkVisibility*/);
		PwwdWw(ww)->fSetElev = fTrue;
		}
}


/* D Y P  S C A N  A B O V E */
/* retreats over lines > dyp in height, # of lines and actual height
skipped over are returned. wwdCur.cpFirst, fCpDirty are set to start
of "last" line skipped over.
Skipping starts just above the first full line in the window.
*/
/* %%Function:DypScanAbove %%Owner:bryanl */
int DypScanAbove(ww, dyp, pcdl)
int ww;
int dyp, *pcdl;
{
	CP CpFirstFull();
	int dypTotal = 0;
	int cdlTotal = 0;
	int cdl, dl;
	int wwT;
	struct EDL *pedl;
	struct WWD *pwwd = PwwdWw(ww);
	CP cpFirst = CpFirstFull(pwwd);
	int fSynchScc = fFalse;
	struct DRF drfFetch;
	struct DR *pdr;

	if (cpFirst == 0 || pwwd->idrMac <= 0)
		{
		*pcdl = 0;
		return 0;
		}

	if (ww != vsccAbove.ww)
		{
		wwT = vsccAbove.ww;
		SynchSccWw(&vsccAbove, ww);
		fSynchScc = fTrue;
		}
/* first we ensure that the contents of sccAbove has a seamless join
with the first full line in the current window */
	Assert (PdrFetchAndFree(HwwdWw(ww), 0 /* idr */, &drfFetch)->doc == vsccAbove.doc);
	if (vsccAbove.plcedl.dlMac > 0)
		{
		if (vsccAbove.rgcp[vsccAbove.plcedl.dlMac] != cpFirst)
			{
/* if desired point is in the cache but not at its end.. */
			PutIMacPlc(vsccAbove.hplcedl, max(0,
					IInPlcRef(vsccAbove.hplcedl, cpFirst)));
			Assert(vsccAbove.plcedl.dlMac >= 0);
			if (vsccAbove.fBreak && vsccAbove.dlBreak >= vsccAbove.plcedl.dlMac)
				{
				vsccAbove.fBreak = fFalse;
				vsccAbove.dlBreak = 0;
				}
			}
#ifdef MAC
		if (vpsccBelow)
/* eliminate fBreak in the cache */
			if (vsccAbove.fBreak && vsccAbove.plcedl.dlMac != 0)
				{
				Assert(!vsccAbove.fBreak || vsccAbove.dlBreak <= vsccAbove.plcedl.dlMac);
				ScrollScc(&vsccAbove, vsccAbove.dlBreak,
						-vsccAbove.dlBreak, 0);
				vsccAbove.fBreak = fFalse;
				}
#endif
		}

	vsccAbove.fFull = fFalse;
	cdl = vsccAbove.plcedl.dlMac;
	for (;;)
		{
		/* for each cache load [0, cdl) */
		pdr = PdrFetch(HwwdWw(ww), 0 /* idr */, &drfFetch);
		for (dl = cdl - 1; dl >= 0; dl--)
			{
			if (dyp == 0 && cpFirst == vsccAbove.rgcp[cdl])
				goto LRet2;
			cdlTotal++;
			pedl = &(vsccAbove.dndl[dl]);
			if (((dypTotal += pedl->dyp) >= dyp)
					|| (vsccAbove.rgcp[dl] == cp0))
				/* line found in cache */
				{
LRet1:

				pdr->cpFirst = vsccAbove.rgcp[dl];
/* 0 should really be vsccAbove.dcpDepend, and the latter should be maintained */
				pdr->dcpDepend = dl == 0 ? 0 :
						vsccAbove.dndl[dl -1].dcpDepend;
				PwwdWw(ww)->fDirty = fTrue;
LRet2:
				pdr->fCpBad = fFalse;
				*pcdl = cdlTotal;
				FreePdrf(&drfFetch);

				if (fSynchScc)
					SynchSccWw(&vsccAbove, wwT);
				return (dypTotal);
				}
			}
		FreePdrf(&drfFetch);
		/* not in cache, must extend further backward */
/* cache is loaded backwards "starting" from cpFirst of the current window */
		cdl = CdlEnrichSccAbove(ww, cpFirst);
		if (cdl == 0)
			{
			dl = 0;
			pdr = PdrFetch(HwwdWw(ww), 0 /* idr */, &drfFetch);
			goto LRet1;
			}
		}

	if (fSynchScc)
		SynchSccWw(&vsccAbove, wwT);

}


/* C D L  E N R I C H  S C C  A B O V E */
/*
Extends the sccAbove cache backwards by inserting some more dl's in front.
Returns the number of new edls. First new edl will be at dl = 0;
As dl's accumulate, excess will be pushed off the high end, then when
the point of insertion reaches the high end, excess is pushed off the low end.

Follows EnrichSccAbove plan loosely.
Uses wwCur. Assumes sccAbove matches wwCur.
Strats from cpFirst if cache is empty.
*/
/* %%Function:CdlEnrichSccAbove %%Owner:bryanl */
CdlEnrichSccAbove(ww, cpFirst)
int ww;
CP cpFirst;
{
	CP CpFirstFull();
	int dlMax = vsccAbove.dlMax;
	int dlMac = vsccAbove.plcedl.dlMac; /* will be kept up to date */
	int cdl = 0; /* total number of lines inserted */
	int dl;
	int ipad;
	struct EDL *pedl;
	BOOL fOutline;
	int dyp;
	int dcpDepend;
	CP cpT, cpLim, cpMin;
	int dypT1, dypT2;
#ifdef MAC
	struct WDS wds;
#endif

	fOutline = PwwdWw(ww)->fOutline;

	cpLim = dlMac == 0 ? cpFirst : vsccAbove.rgcp[0];
/* skip over invisible stuff directly above cpFirst */
	if (fOutline && cpLim != cp0)
		{
		struct PLCPAD **hplcpad = (PdodDoc(PdrGalley(PwwdWw(ww))->doc)->hplcpad);
		int ipad;
		struct PAD pad;
		if (hplcpad != hNil)
			{
			ipad = IInPlc(hplcpad, cpLim - 1);

			while (ipad >= 0)
				{
				GetPlc(hplcpad, ipad, &pad);
				if (pad.fShow)
					break;
				cpLim = CpPlc(hplcpad, ipad--);
				}
			}
		}
	if (cpLim == 0)
		return 0;

#ifdef MAC
	SaveWds(&wds);
	PwwdSetWw(ww, cptDoc);
	cpLim = CpMin(cpLim, CpMacDoc(vsccAbove.doc));
	BackOneNotFlatPara(vsccAbove.doc, cpLim);
	cpT = caPara.cpFirst;
#else
	cpLim = CpMin(cpLim, CpMacDoc(vsccAbove.doc));
	cpT = CpFormatFrom( ww, vsccAbove.doc, cpLim-1);
#endif
	dl = 0;
	if (fOutline)
		ClearCap();
	for (;;)
		{
		int dlT = dl;
		if (fOutline)
			cpT = CpOutlineAdvance(vsccAbove.doc, cpT);
		if (cpT >= cpLim) break;
/* pointer to the EDL which is to be set up from formatting results */
		pedl = &(vsccAbove.dndl[dlT]);
		if (dl < dlMax)
			{
			int cdlT = (dlMac - dl);
			cdl++;
			if (dlMac < dlMax)
/* move all EDL's up one, increment dlMac */
				{
				dlMac++;
				PutIMacPlc(vsccAbove.hplcedl, dlMac);
				}
			else  /* dlMac = dlMax */
/* move all EDL's up, overwriting last line */
				cdlT--;
			MoveEdls(&vsccAbove, &vsccAbove, dl, dl + 1, cdlT);
			dl++;
			}
		else  /* dl == dlMax */			
			{
/* move all EDL's down, losing the first one */
			pedl--; 
			dlT--;
			MoveEdls(&vsccAbove, &vsccAbove, 1, 0, dlMax - 1);
			}
		CachePara(vsccAbove.doc, cpT);
		if (!FInTableVPapFetch(vsccAbove.doc, cpT))
			{
/* format line starting at cpT, to be stored at dl in sccAbove.dndl */
			FormatLine(ww, vsccAbove.doc, cpT);
			cpMin = vfli.cpMin;
			cpT = vfli.cpMac;
			dcpDepend = vfli.dcpDepend;
			dyp = vfli.dypLine;
			}
		else
			{
/* format table row to determine same quantities */
			CpFirstTap(vsccAbove.doc, cpT);
			cpMin = caTap.cpFirst;
			cpT = caTap.cpLim;
			dcpDepend = 0;
			dyp = DysHeightTable(ww, vsccAbove.doc, cpT - 1,
					fFalse, fFalse, &dypT1, &dypT2);
			dyp += dypT1 + dypT2;
			}
/* set up EDL */
		vsccAbove.rgcp[dlT] = cpMin;
		vsccAbove.rgcp[dlT + 1] = cpT;
		pedl->dcp = (uns) (cpT - cpMin);

		pedl->ypTop = 0;
		pedl->dyp = dyp;
		pedl->fHasBitmap = fFalse;
		pedl->fDirty = fFalse;
		pedl->dcpDepend = dcpDepend;
		}
	SetMacsScc(&vsccAbove);
#ifdef MAC
	RestoreWds(&wds);
#endif
	return (min(cdl, dlMax));
}


/* C P  F I R S T  F U L L */
/* returns the cpMin of the first full line. Note: does not assume that
dlMac > 1! */
/* %%Function:yCpFirstFull %%Owner:bryanl */
CP CpFirstFull(pwwd)
struct WWD *pwwd;
{
	return (PdrGalley(pwwd)->dypAbove == 0 ?
			PdrGalley(pwwd)->cpFirst :
			CpPlc(PdrGalley(pwwd)->hplcedl, 1));
}


/* T H U M B  T O  C P */
/* Scrolls to a given cp. Searches all caches for the cp to make the
operation more efficient.
Uses wwCur.
Plan:
	1. Is cp in the window? if so, we have new cpFirst, set fDirty, return
	2. Is it in sccAbove? if so, ScrollDown the appropriate amount.
	(3. Large sys only. Is it in sccBelow? if so, ScrollUp.)
	4. Otherwise set fCpBad and let UpdateWw handle it.

fNoOpt is used in help to avoid scrolling the endmark to the upper part of the
screen.
ncp is either 0 or ncpForceYPos. the latter releases the restriction at the 
end of document.
*/

/* %%Function:ThumbToCp %%Owner:bryanl */
ThumbToCp(ww, cp, fEnd, fNoOpt, ncp)
CP cp; 
int fEnd;
{
	struct EDL *dndl;
	int dyp, dypBound;
	int dl;
	struct WWD *pwwd;
	struct PLCEDL **hplcedl;

	UpdateWw(ww, fFalse);
	vfSeeSel = fFalse;
	pwwd = PwwdWw(ww);
	if (pwwd->idrMac <= 0)
		return;

	Assert(!pwwd->fPageView);
	hplcedl = PdrGalley(pwwd)->hplcedl;
	if (hplcedl == hNil)
		return; /* can happen coming out of pageview from low in memory */

	dypBound = ncp & ncpForceYPos ? 0 : PdrGalley(pwwd)->dyl / 2;
	if (!fNoOpt && (dl = DlFromCpCheck(HwwdWw(ww), 0, cp, fEnd)) >= 0)
		{
		struct EDL edl;
		int dyp = -PdrGalley(pwwd)->dypAbove, dypEnd;
		int dlT;
		for (dlT = 0; dlT < dl; dlT++)
			{
			GetPlc(hplcedl, dlT, &edl);
			dyp += edl.dyp;
			}
/* search for end mark. do not scroll it above midpoint. */
		for (dypEnd = dyp; dlT < IMacPlc(hplcedl); dlT++)
			{
			GetPlc(hplcedl, dlT, &edl);
			if (edl.fEnd)
				{
				dyp = min(dyp, max(0, dypEnd - dypBound));
				break;
				}
			dypEnd += edl.dyp;
			}
		GetPlc(hplcedl, 0, &edl);
		if (dyp >= edl.dyp /* will scroll over at least first line */)
			ScrollUp(ww, dyp, dyp + WinMac(dysMinAveLineSci, 18));
		goto LSynch;
		}
	else
		{
		CP cpMac = CpMacDoc(PdrGalley(pwwd)->doc);
/* limitation on scrolling endpoint into upper half of screen. Do this only
when near the end of the document because it is relatively expensive */
		if (cpMac - 400 < cp)
			{
			int cdlT;
			PdrGalley(pwwd)->cpFirst = cpMac;
			pwwd->fDirty = fTrue;
			PdrGalley(pwwd)->dypAbove = 0;
			DypScanAbove(ww, dypBound, &cdlT);
			if (cp >= PdrGalley(PwwdWw(ww))->cpFirst)
				goto LSynch;
			}
		else
			{
			struct SCC *psccAbove = &vsccAbove;
			if (vsccAbove.ww == ww &&
					(dl = DlFromCpCheck(&psccAbove, 0, cp, fEnd)) >= 0)
				{
/* check if above cache matches ww seamlessly */
				if (vsccAbove.rgcp[vsccAbove.plcedl.dlMac] ==
						PdrGalley(pwwd)->cpFirst)
					{
					for (dyp = 0; dl < vsccAbove.plcedl.dlMac; dl++)
						dyp += vsccAbove.dndl[dl].dyp;

					ScrollDown(ww, dyp, dyp + WinMac(dysMinAveLineSci, 24));
					goto LSynch;
					}
				}
#ifdef MAC
			if (vpsccBelow && !PwwdWw(ww)->fOutline)
				{
				ThumbToCpLarge(ww, 0,  cp, fEnd);
				goto LSynch;
				}
#endif	/* MAC */
			}
		ClearSccs();
		pwwd = PwwdWw(ww);
		PdrGalley(pwwd)->cpFirst = cp;
		PdrGalley(pwwd)->fCpBad = fTrue;
		}
	pwwd->fDirty = fTrue;
LSynch:
	pwwd = PwwdWw(ww);
	if (PdrGalley(pwwd)->dypAbove)
		PdrGalley(pwwd)->dypAbove = 0, pwwd->fDirty = fTrue;
	Mac( PwwdWw(ww)->fSetElev = fFalse );
	Win( PwwdWw(ww)->fSetElev = fTrue );
	SynchCheck(ww, fFalse/*fCkVisibility*/);
}


/* S E T  M A C S  S C C */
/* sets ypMac and rgcp[dlMac] from dl's; copy dlMac to plcedl. */
/* %%Function:SetMacsScc %%Owner:bryanl */
SetMacsScc(pscc)
struct SCC *pscc;
{
	int dlMac = pscc->plcedl.dlMac;
	int ywMac = 0;
	CP cpMac = 0;
	int dl;
	if (dlMac == 0) pscc->fBreak = fFalse;
	if (dlMac && pscc == &vsccAbove)
		{{ /* NATIVE - SetMacsScc */
		struct EDL *pedl;

		pedl = &(pscc->dndl[0]);
		for (dl = 0 ; dl < dlMac ; dl++, pedl++)
			{
			if (pedl->fHasBitmap)
				ywMac += pedl->dyp;
			}
		pedl = &(pscc->dndl[dlMac - 1]);
		cpMac = pscc->rgcp[dlMac - 1] + pedl->dcp;
		}}
	else  if (dlMac)
		{
		struct EDL *pedl = &(pscc->dndl[dlMac - 1]);
		if (pedl->fHasBitmap)
			ywMac = pedl->ypTop + pedl->dyp;
		cpMac = pscc->rgcp[dlMac - 1] + pedl->dcp;
		}

	Assert(ywMac >= 0 && dlMac >= 0);
	pscc->rgcp[dlMac] = cpMac;
	pscc->ywMac = ywMac;
	PutIMacPlc(pscc->hplcedl, dlMac);
}


/* M O V E  E D L S */
/* blt's edl's and cp's in plcedl. Move is assumed to end at dlMac, hence
the number of cp's moved is one greater than cdl */
/* %%Function:MoveEdls %%Owner:bryanl */
MoveEdls(pwwdFrom, pwwdTo, dlFrom, dlTo, cdl)
struct WWD *pwwdFrom, *pwwdTo;
int dlFrom, dlTo, cdl;
{
	struct EDL *pedl, *pedlLim;
	struct PLCEDL *pplcedl;
#ifdef DEBUG
	int *pdlMacT = &((*(PdrGalley(pwwdFrom)->hplcedl))->dlMac);
	int dlMacT = *pdlMacT;
	*pdlMacT = *(pdlMacT + 1);
#endif

	/* DavidMck says this needs to be rethought for Mac.
	/* Remember that edl.dyl for a table row can be artificially
	/* small if the row was clipped to the parent DR
	/**/
	Mac(Assert(fFalse));
	CopyMultPlc(cdl, PdrGalley(pwwdFrom)->hplcedl, dlFrom,
			PdrGalley(pwwdTo)->hplcedl, dlTo, cp0, 0, 1);

	Assert(pwwdTo == &vsccAbove Mac(|| pwwdTo == vpsccBelow));
	pplcedl = *PdrGalley(pwwdTo)->hplcedl;
	Assert(!pplcedl->fExternal);
	pedl = (struct EDL *)&pplcedl->rgcp[pplcedl->dlMax];
	pedlLim = pedl + cdl;
	for ( ;pedl < pedlLim; ++pedl)
		pedl->hpldr = hNil;
	Debug(*pdlMacT = dlMacT);
}


/* S Y N C H  C H E C K */
/* check if SynchRef or SynchOutline should be called
after scrolling ww.
*/
/* %%Function:SynchCheck %%Owner:bryanl */
SynchCheck(ww, fCkVisibility)
int ww;
BOOL fCkVisibility;
{
	struct WWD *pwwd = PwwdWw(ww);
	struct WWD *pwwdOther;

	if (PmwdWw(ww)->fSplit && !pwwd->fPageView && !(pwwdOther =  PwwdOther(ww))->fPageView)
		{
		if (pwwd->wk == wkFtn || pwwdOther->wk == wkFtn)
			SynchRef(ww, fCkVisibility);
#ifdef WIN
		else  if (pwwd->wk == wkAtn || pwwdOther->wk == wkAtn)
			SynchRef(ww, fCkVisibility);
		else  if (pwwd->wk == wkHdr || pwwdOther->wk == wkHdr)
			return;
#endif
		else  if (pwwd->fOutline != pwwdOther->fOutline)
			SynchOutline(ww);
		}
}


#ifdef MAC
/* B A C K  O N E  N O T  F L A T  P A R A */
/* A flat paragraph is one consisting of all vanished characters.
Prec returns greatest caPara.cpFirst < cpLim not preceded by flat para
and followed by exactly one non-flat para.
Used to back over text which may contain fully vanished paragraphs. */
/* %%Function:BackOneNotFlatPara %%Owner:NOTUSED */
BackOneNotFlatPara(doc, cpLim)
int doc; 
CP cpLim;
{
	BOOL fOneSeen = fFalse;
	Assert (cpLim > cp0);
	for (;;)
		{
		CachePara(doc, cpLim - 1);
		if (vpref.fSeeHidden) return;
		FetchCp(doc, caPara.cpFirst, fcmProps);
		for (;;)
			{
			if (!vchpFetch.fVanish)
				{
				if (fOneSeen)
					{
					CachePara(doc, caPara.cpLim);
					return;
					}
				else
					{
					fOneSeen = fTrue;
					goto LLoop;
					}
				}
			if (vcpFetch + vccpFetch >= caPara.cpLim) break;
			FetchCp(docNil, cpNil, fcmProps);
			}
/* flat paragraph or first non-flat finished. Try previous. */
LLoop:          
		if ((cpLim = caPara.cpFirst) == cp0)
			return;
		}
}


#endif /* MAC */

/* S E T  W W  C P  F I R S T */
/* class store routine for wwd.cpFirst */
/* %%Function:SetWwCpFirst %%Owner:bryanl */
SetWwCpFirst(ww, cpFirst)
int ww; 
CP cpFirst;
{
	struct WWD *pwwd = PwwdWw(ww);

	PdrGalley(pwwd)->cpFirst = cpFirst;
	PdrGalley(pwwd)->dypAbove = 0;
	PdrGalley(pwwd)->fCpBad = fTrue;
	pwwd->fDirty = fTrue;
	pwwd->fSetElev = fTrue;
}


/* S Y N C H  R E F */
/* synch other pane with wwMaster. Know: pwwd->fFtn/pwwd->fAtn.
if master is main text:
	find first ref r with cp>=first cp displayed.
	find corresponding footnote text cpFirst.
	if cpFirst not visible: scroll to cpFirst
else master is footnote text
	find cpFirst of reference to first displayed footnote text.
	return if none displayed.
	if cpFirst not visible: scroll to cpFirst
*/
/* %%Function:SynchRef %%Owner:bryanl */
SynchRef(ww, fCkVisibility)
int ww;
BOOL fCkVisibility;
{
	struct WWD *pwwd = PwwdWw(ww);
	CP cpFirst;
	int docSync;
	CP cpRefMom;
	struct SEL *psel = PselActive();

	Assert(!pwwd->fPageView);
/* first set cpFirst to the master ww */
	if (fCkVisibility && FCpVisible(ww, psel->doc, psel->cpFirst, fFalse, fFalse, fFalse))
		{
		cpFirst = CpActiveCur();
		pwwd = PwwdWw(ww);
		}
	else
		{
		pwwd = PwwdWw(ww);
		cpFirst = PdrGalley(pwwd)->cpFirst;
		}

	if (cpFirst >= CpMacDoc(PdrGalley(pwwd)->doc)) return;

	if ((docSync = DocCpRefFromWw(ww, &cpFirst, &cpRefMom)) == docNil)
		return;

	if (PmwdWw(ww)->wwUpper == ww && fCkVisibility &&
			!FCpVisible(ww, psel->doc, cpRefMom, fFalse, fFalse, fFalse))
/* cpRef in mother doc is not yet in window, don't have to scroll ref text in yet */
		return;

	if (PwwdOther(ww)->fDirty || !FCpVisible(WwOther(ww), docSync, cpFirst, fFalse, fFalse, fFalse))
		SetWwCpFirst(WwOther(ww), cpFirst);
}




/* F  S C R O L L  P A G E  D Y E  H O M E */
/* scrolls vertically, with stops at the "home" positions, and with
automatic paging forward and backward. Returns true iff paging is called.
(dye<0 means text is moved down in the window)
if fHome is false, stop at home point is suppressed.
if ncp has ncpForceYPos set, limitation on gray area is suppressed in up.
*/
/* %%Function:FScrollPageDyeHome %%Owner:bryanl */
FScrollPageDyeHome(ww, dye, fHome, ncp, fNoNewPage)
{
	struct WWD *pwwd = PwwdWw(ww);
	int dyeHome = pwwd->yeScroll - YeTopPage(ww);
#ifndef OPUS_X64
	int w;
	BOOL fSmallPage = FSmallPage(ww, &w, &w);
#else
	int dypPage = DyOfRc(&pwwd->rcePage);
	int dypPageGap = max(8, dypGrayOutsideSci / 3);
	int yeHome = YeTopPage(ww);
	int yeLastPage = min(yeHome,
			pwwd->rcwDisp.ywBottom - dypPage - dypGrayOutsideSci);
	int yeLimit;
	int dyeRequested;
	int dyeRemainder;
	int docScroll;
	int ipgdMacScroll;
	CP cpMacScroll;
#endif

#ifdef WIN
	int ipgdNew;
#endif
#ifdef OPUS_X64
	/* Word 1.x normally swaps its single Page View display as soon as the
	   current sheet reaches the viewport edge.  Let the sheet instead travel
	   one full page plus a gray inter-page gap.  The Win95 chrome paints the
	   adjacent sheet in that space; when it reaches the home position we hand
	   the live layout to that page. */
	if (dye >= 0)
		{
		dyeRequested = dye;
		ipgdNew = IpgdNextWw(ww, fFalse);
		/* Repaginate only when the following sheet is about to become visible.
		   This avoids doing page layout on every ordinary line-scroll. */
		if (ipgdNew == ipgdNil &&
				pwwd->yeScroll - dye <= yeLastPage)
			{
			docScroll = PmwdWw(ww)->doc;
			cpMacScroll = CpMacDocEdit(docScroll);
			ipgdMacScroll = IMacPlc(PdodDoc(docScroll)->hplcpgd);
			if (vrgdocWin95NoNext[ww] != docScroll ||
					vrgipgdWin95NoNext[ww] != pwwd->ipgd + 1 ||
					vrgcpWin95NoNext[ww] != cpMacScroll)
				{
				/* Once the page table already contains multiple pages, its
				   active last page is authoritative.  Typing/background layout
				   extends that table when content grows; a wheel tick must not
				   re-enter pagination on the live last-page descriptors. */
				if (ipgdMacScroll > 1 &&
						pwwd->ipgd + 1 >= ipgdMacScroll)
					ipgdNew = ipgdNil;
				else
					ipgdNew = IpgdNextWw(ww, fTrue);
				if (ipgdNew == ipgdNil)
					{
					vrgdocWin95NoNext[ww] = docScroll;
					vrgipgdWin95NoNext[ww] = pwwd->ipgd + 1;
					vrgcpWin95NoNext[ww] = cpMacScroll;
					}
				}
			}
		yeLimit = (!fNoNewPage && ipgdNew != ipgdNil) ?
				yeHome - dypPage - dypPageGap : yeLastPage;
		if ((ncp & ncpForceYPos) == 0)
			dye = min(dye, max(0, pwwd->yeScroll - yeLimit));
		if (dyeHome > 0 && fHome)
			dye = min(dye, dyeHome);
		dyeRemainder = max(0, dyeRequested - dye);
		if (dye <= 0)
			{
			if (fNoNewPage)
				return fTrue;
			if (ipgdNew != ipgdNil)
				SetPageDisp(ww, ipgdNew, yeHome, fFalse, fFalse);
			else
				Beep();
			return fTrue;
			}
		ScrollPageDye(ww, dye);
		if (!fNoNewPage && ipgdNew != ipgdNil &&
				PwwdWw(ww)->yeScroll <= yeLimit)
			{
			SetPageDisp(ww, ipgdNew, yeHome, fFalse, fFalse);
			if (dyeRemainder > 0)
				FScrollPageDyeHome(ww, dyeRemainder, fFalse, ncp,
						fFalse);
			return fTrue;
			}
		}
	else
		{
		dyeRequested = -dye;
		ipgdNew = IpgdPrevWw(ww);
		yeLimit = (!fNoNewPage && ipgdNew != ipgdNil) ?
				yeHome + dypPage + dypPageGap : yeHome;
		dye = min(-dye, max(0, yeLimit - pwwd->yeScroll));
		if (dyeHome < 0 && fHome)
			dye = min(dye, -dyeHome);
		dyeRemainder = max(0, dyeRequested - dye);
		if (dye <= 0)
			{
			if (fNoNewPage)
				return fTrue;
			if (ipgdNew != ipgdNil)
				SetPageDisp(ww, ipgdNew, yeHome, fFalse, fFalse);
			else
				Beep();
			return fTrue;
			}
		ScrollPageDye(ww, -dye);
		if (!fNoNewPage && ipgdNew != ipgdNil &&
				PwwdWw(ww)->yeScroll >= yeLimit)
			{
			SetPageDisp(ww, ipgdNew, yeHome, fFalse, fFalse);
			if (dyeRemainder > 0)
				FScrollPageDyeHome(ww, -dyeRemainder, fFalse, ncp,
						fFalse);
			return fTrue;
			}
		}
	return fFalse;
#else /* !OPUS_X64 */
	if (dye >= 0)
		{
		if ((ncp & ncpForceYPos) == 0)
			dye = min(dye, DyOfRc(&pwwd->rcePage) + dypGrayOutsideSci
					- DyOfRc(&pwwd->rcwDisp) + pwwd->yeScroll);
		if (dye	<= 0 || fSmallPage)
			{
			if (fNoNewPage) return fTrue;
#ifdef MAC
			vwwScroll = ww;
			ScrollSpc(spcPgvPageDown);
#else /* WIN */
			if ((ipgdNew = IpgdNextWw(ww, fTrue)) != ipgdNil)
				SetPageDisp(ww, ipgdNew, YeTopPage(ww), fFalse, fFalse);
			else
				Beep(); /* Have to stop at the last page */
#endif /* ~MAC */
			return fTrue;
			}
		else
			{
/* how far are we from home point ? */
			if (dyeHome > 0 && fHome)
				dye = min(dye, dyeHome); /* if cross => go to home */
			ScrollPageDye(ww, dye);
			}
		}
	else
		{
		if ((dye = min(-dye, dypGrayOutsideSci - pwwd->yeScroll)) <= 0
				|| fSmallPage)
			{
#ifdef MAC
			vwwScroll = ww;
			ScrollSpc(spcPgvPageUp);
#else /* WIN */
			if ((ipgdNew = IpgdPrevWw(ww)) != ipgdNil)
				SetPageDisp(ww, ipgdNew, YeBottomPage(ww), fFalse, fFalse);
			else
				Beep(); /* Have to stop at the first page */
#endif /* ~MAC */
			return fTrue;
			}
		else
			{
/* how far are we from home point ? */
			if (dyeHome < 0 && fHome)
				dye = min(dye, -dyeHome); /* if cross => go to home */
			ScrollPageDye(ww, -dye);
			}
		}
	return fFalse;
#endif /* OPUS_X64 */
}



/* S C R O L L  P A G E  D Y E  */
/* Scrolls page vertically in PageView by dye.
(dye<0 means text is moved down in the window)
*/
/* %%Function:ScrollPageDye %%Owner:bryanl */
ScrollPageDye(ww, dye)
{
#ifndef OPUS_X64
	int w;
#endif

	Assert(cHpFreeze == 0);
	vfLastScroll = fTrue;
	Assert(PwwdWw(ww)->fPageView);
#ifndef OPUS_X64
	if (FSmallPage(ww, &w, &w))
		{ 
		Beep(); 
		return; 
		}
#endif

	ClearInsertLine(&selCur);
	ClearInsertLine(&selDotted);

	ScrollWwDyw(ww, dye, PwwdWw(ww)->rcwDisp.ywTop);
	OffsetRect(&PwwdWw(ww)->rcePage, 0, -dye);
	/* redraw uncovered area */
	UpdateWindow(WinMac(PwwdWw(ww)->hwnd, PwwdWw(ww)->wwptr));
	SetElevWw(ww);
#ifdef MAC
	DrawControls(PwwdSetWw(ww, cptAll)->wwptr);
#endif
}



/* R E F R E S H  P A G E  - erase, trash, and redraw window */
/* (typically used after positioning to a new page) */
/* %%Function:RefreshPage %%Owner:chic %%reviewed: 7/10/89 */
RefreshPage(ww)
{
	struct WWD *pwwd;
	struct RC rcw;
	Mac( struct WDS wds; )

			Mac( SaveWds(&wds); )
			pwwd = WinMac(PwwdWw(ww), PwwdSetWw(ww, cptAll));
	rcw = pwwd->rcwDisp;
	rcw.xwLeft = 0;
	if (vssc != sscNil && ww == selDotted.ww)
		ClearInsertLine(&selDotted);
	if (ww == selCur.ww)
		ClearInsertLine(&selCur);

	/* set scrollbar values */
	SetElevWw(ww);

	/* update llc if this is wwCur */
	if (ww == wwCur)
		CacheLlc(cpNil);  /* WIN: does nothing */

	TrashWw(ww);
#ifdef MAC
	if (FRulerUp(ww))
		UpdateRuler();
#endif
	UpdateWindow(WinMac(PwwdWw(ww)->hwnd, PwwdWw(ww)->wwptr));
	Mac( RestoreWds(&wds); )
}



/* S E T  S C R O L L  H O R I Z */
/* Changes the horizontal scroll of ww by dxp */
/* %%Function:SetScrollHoriz %%Owner:bryanl */
SetScrollHoriz(ww, dxpScroll)
int ww, dxpScroll;
{
	struct WWD *pwwd = PwwdWw(ww);
	int dxp = dxpScroll;

	if (pwwd->fPageView)
		{
#ifdef MAC		
		OffsetRect(&pwwd->rcePage, -dxpScroll, 0);
#else
		OffsetRect((LPRECT) &pwwd->rcePage, -dxpScroll, 0);
#endif
		pwwd->xhScroll = pwwd->xeScroll;
		}
	else
		{
		/* Change rcwDisp depending on Scrolling */
		if (pwwd->xhScroll < 0 && pwwd->xhScroll - dxpScroll >= 0)
			{
			pwwd->rcwDisp.xwLeft -= dxwSelBarSci;
			dxp -= dxwSelBarSci;
			}
		else  if (pwwd->xhScroll >= 0 && pwwd->xhScroll - dxpScroll < 0)
			{
			pwwd->rcwDisp.xwLeft += dxwSelBarSci;
			dxp += dxwSelBarSci;
			}
#ifdef MAC		
		OffsetRect(&pwwd->rcePage, -dxp, 0);
#else
		OffsetRect((LPRECT) &pwwd->rcePage, -dxp, 0);
#endif
		pwwd->xhScroll -= dxpScroll;
		}


}


/* T H U M B  P A G E */
/* Handles vertical thumbing in PageView mode */
/* %%Function:ThumbPage %%Owner:bryanl */
ThumbPage(ww, dqThumb, dqThumbOld)
int ww, dqThumb, dqThumbOld;
{
	int dyeScrollMac, dyeScrollOld, dyeScrollNew, dyeT, fSetElev = fFalse;
	struct WWD *pwwd;
	int doc, dye, ipgd;
	CP cpNew, cpCurPage, cpNextPage, cpMac;
	extern CP CpFromIpgd();

	Assert(cHpFreeze == 0);
	if (dqThumb == dqThumbOld)
		goto LSetReturn;        /* Didn't move */

	/* And now it's time to Guess That Cp! */
	cpMac = CpMacDoc((doc = DocBaseWw(ww)));
	cpNew = CpScaleThumb(ww, dqThumb);
	if (cpNew < cp0)
		cpNew = cp0;
	if ((ipgd = IpgdNextWw(ww, fFalse)) == ipgdNil)
		cpNextPage = cpMac;
	else
		cpNextPage = CpFromIpgd(doc, ww, ipgd); /* HM */
	cpCurPage = CpFromIpgd(doc, ww, max(0, PwwdWw(ww)->ipgd)); /* HM */

	if ((cpNew > cpNextPage)
			|| cpNew < cpCurPage)
		{       /* Go to another page */
		BOOL fNormHoriz = FNormHoriz(ww);
		struct LBS lbsText, lbsFtn;

		SetPageDisp(ww,
				IpgdRepagToCpIpgd(ww, doc, cpNew, ipgdNil, &lbsText, &lbsFtn),
				YeTopPage(ww), fTrue, fNormHoriz);
		/* Clear up these structures */
		EndLayout(&lbsText, &lbsFtn);
		fSetElev = fTrue;
		}
	else
		{
		int dqCurPage, dqNextPage;
		/* We stay on the same page but scroll up or down
			This is tricky.  We assume that the cps are continuous
			over the page and interpolate to find where we
			"want" to be.  That is compared with the current
			scroll and, presto, we scroll */
		pwwd = PwwdWw(ww);
#ifdef OPUS_X64
		if (ipgd == ipgdNil)
			{
			int yeHome = YeTopPage(ww);
			int yeLastPage = min(yeHome, pwwd->rcwDisp.ywBottom -
					DyOfRc(&pwwd->rcePage) - dypGrayOutsideSci);
			dyeScrollMac = max(0, yeHome - yeLastPage);
			}
		else
			dyeScrollMac = DyOfRc(&pwwd->rcePage) +
					max(8, dypGrayOutsideSci / 3);
		dyeScrollOld = min(dyeScrollMac,
				max(0, YeTopPage(ww) - pwwd->rcePage.yeTop));
#else
		dyeScrollMac = DyOfRc(&pwwd->rcePage) - DyOfRc(&pwwd->rcwDisp);
		dyeScrollOld = max(0, -pwwd->rcePage.yeTop);
#endif
		dqCurPage = DqRatio(cpCurPage, dqMax - dqPgvwElevSpace, cpMac);
		dqNextPage = DqRatio(cpNextPage, dqMax - dqPgvwElevSpace, cpMac);
		dyeScrollNew = NMultDiv(min(dqThumb - dqCurPage, dqNextPage - dqCurPage),
				dyeScrollMac, max(1, dqNextPage - dqCurPage));
#ifdef OPUS_X64
		dyeScrollNew = min(dyeScrollMac, max(0, dyeScrollNew));
#else
		dyeScrollNew = max(-YeTopPage(ww), dyeScrollNew);
#endif
		if (dqThumb > dqThumbOld)
			{
#ifdef OPUS_X64
			dye = dyeScrollNew - dyeScrollOld;
#else
			dyeT = pwwd->rcePage.yeBottom + pwwd->rcwDisp.ywTop
					- pwwd->rcwDisp.ywBottom;
			dye = min(dyeT, dyeScrollNew - dyeScrollOld);
#endif
			if (dye > 0)
				ScrollPageDye(ww, dye);
			}
		else
			{
/* move up on the page (move text down, looking at higher portions of page) */
#ifdef OPUS_X64
			dye = dyeScrollOld - dyeScrollNew;
#else
			if (cpCurPage == cp0 && pwwd->rcePage.yeTop
					>= YeTopPage(ww))
				dye = -pwwd->rcePage.yeTop + dypGrayOutsideSci;
			else
				{
				dyeT = pwwd->ywMin - pwwd->rcePage.yeTop;
				dye = min(dyeT, dyeScrollOld - dyeScrollNew);
				}
#endif
			if (dye > 0)
				ScrollPageDye(ww, -dye);
			}
		}

LSetReturn:
	/* Set the elevator if needed */
	if (dqThumb > dqMax - dqPgvwElevSpace || fSetElev)
		{
		PwwdWw(ww)->dqElevator = dqThumb;
		SetElevWw(ww);
		}
}




/* D O C  C P R E F  F R O M  W W */
/* Find the doc and cp in the wwOther to be sync with ww wrt footnote/annotation */
/* If there is no need to sync, return docNil. */
/* return cpRef in the mother doc also - only if there is a doc to sync to */
/* %%Function:DocCpRefFromWw %%Owner:bryanl */
DocCpRefFromWw(ww, pcp, pcpMom)
int ww;
CP *pcp;
CP *pcpMom;
{
	int wwOther = WwOther(ww);
	struct WWD *pwwd = PwwdWw(ww);
	struct DOD *pdod = PdodDoc(DocBaseWw(ww));
	CP cpFirst = *pcp;
	struct PLC **hplcDn, **hplcRg;
	int iFooFirst;
	struct DRP *pdrp;
	int docSync;
	int edc;
	int wk;

	if (wwOther == wwNil)
		return docNil;

	FreezeHp();
	if ((wk = pwwd->wk) == wkFtn Win(|| wk == wkAtn))
		{
/* subdoc text to reference mapping */
/* pdod points to sub dod with the fnd. */
		edc = wk == wkFtn ? edcDrpFtn : edcDrpAtn;
		Mac(Assert(edc == edcDrpFtn));
		hplcDn = pdod->hplcfnd;
		iFooFirst = IInPlc(hplcDn, cpFirst);
		if (iFooFirst >= IMacPlc(hplcDn) - 1 ||
				iFooFirst < 0 /* bullet proof */)
			goto LRetDocNil;
		pdrp =  ((int *)PdodDoc(docSync = pdod->doc)) + edc;
		hplcRg = pdrp->hplcRef;
		*pcpMom = CpPlc(hplcRg, iFooFirst);
		}
	else  if ((wk = PwwdWw(wwOther)->wk) == wkFtn Win(|| wk == wkAtn))
		{
/* references to footnote/annotation text mapping */
/* pdod points to main dod with the frd. */
		edc = wk == wkFtn ? edcDrpFtn : edcDrpAtn;
		Mac(Assert(edc == edcDrpFtn));
		pdrp = ((int *)pdod) + edc;
		hplcDn = pdrp->hplcRef;
		docSync = pdrp->doc;
		iFooFirst = IInPlcRef(hplcDn, cpFirst);
		if (iFooFirst == IMacPlc(hplcDn) ||
				iFooFirst < 0 /* bullet proof */)
			goto LRetDocNil;
		hplcRg = PdodDoc(docSync)->hplcfnd;
		*pcpMom = CpPlc(hplcDn, iFooFirst);
		}
	else
		{
LRetDocNil:
		MeltHp();
		return docNil;
		}

	*pcp = CpPlc(hplcRg, iFooFirst);
	MeltHp();
	return docSync;
}


/* C P  S C A L E  T H U M B */
/* %%Function:CpScaleThumb %%Owner:bryanl */
CP CpScaleThumb(ww, dq)
{
	CP cpMac = CpMacDoc(DocBaseWw(ww));
	int dqScale = cpMac > 0xffff ? 128 : 1;
	int dqLim = PwwdWw(ww)->fPageView ? dqMax - dqPgvwElevSpace : 
	dqMax - 1;
	return CpMin((cpMac / dqScale
			* dq + (dqLim >> 1))  / dqLim * dqScale, cpMac - 1);
}


/* S E T  P A G E  D I S P */
/* procedure can scroll in a ww to a given ipgd, scroll within the ipgd to a
given ye (and normalize xe or leave it unchanged).
Page display is refreshed.
*/
/* %%Function:SetPageDisp %%Owner:chic %%reviewed: 7/10/89 */
SetPageDisp(ww, ipgd, ye, fSetNorm, fNorm)
int ww; 
int ipgd; 
int ye; 
BOOL fSetNorm, fNorm;
{
	struct RC *prce;
	if (!fSetNorm) fNorm = FNormHoriz(ww);

	if (ipgd != ipgdNil)
		{
		if (ipgd != IpgdCurrentWw(ww))
			IpgdPldrFromIpgd(ww, ipgd);
		else if (ye == PwwdWw(ww)->yeScroll)
			{
			/* we are in the requested page and ye position, just set
			the dirty bit to let UpdateWw later have a chance to examine
			and update if any bad cps in the DR */
			PwwdWw(ww)->fDirty = fTrue;
			return;
			}
		}

	if (PwwdWw(ww)->ipgd == ipgdNil)
		{
		/* Not enough memory to create drs */
		return;
		}

	/* scroll page rect vertically as requested. */
	if (fNorm)
		TrashRuler();

	prce = &(PwwdWw(ww))->rcePage;
	MoveRc(prce, fNorm ? XeLeftPage(ww) : prce->xeLeft, ye);
	if (fNorm)
		PwwdWw(ww)->xhScroll = PwwdWw(ww)->xeScroll;

	/* refresh display */
	RefreshPage(ww);

	if (fNorm)
		{
		PwwdSetWw(ww, cptAll);
		SyncSbHor(ww);
		}
}


#ifdef WIN

/* %%Function:MwdVertScroll %%Owner:bryanl */
void MwdVertScroll(ww, code, dqThumb)
int ww;
WORD code;
int dqThumb; /* new position */
{
	int	(*pfScroll)();
	int dypPage, dysMinScroll, dysMaxScroll;
	struct PLCEDL **hplcedl;
	CP cpMac;
	int dye;
	extern int ScrollUp(), ScrollDown();
	extern int vkcPrev;

	struct WWD *pwwd = PwwdWw(ww);

	Assert( mpwwhwwd[ww] != hNil );

	pfScroll = (code == SB_LINEDOWN) ? ScrollUp : ScrollDown;
	sbScrollLast = sbScroll;
	sbScroll = code;
	fCdlScrollSet=fFalse;

	if (sbScrollLast != sbScroll)
		{
		vcConseqScroll = 0;
		vkcPrev = kcNil;
		}

	switch (code) 
		{
	default:
		/* case SB_THUMBTRACK:*/
		return;
	case SB_LINEUP:
	case SB_LINEDOWN:
		Profile(vpfi == pfiScroll ? StartProf(30) : 0);
		if (vcConseqScroll < 0x7FFF)
			{
			vcConseqScroll++;
			}

		if (pwwd->fPageView)
			{
			/* pick random dlMax because pageview do not have that information */
			ScrollDelta(1, vcConseqScroll, 30 /* dlMax */,
					pwwd->ywMac - pwwd->ywMin,
					&dysMinScroll, &dysMaxScroll);
			dye = dysMinScroll;
			FScrollPageDyeHome(ww, code == SB_LINEUP ? -dye : dye, fTrue, 0, fFalse);
			}
		else
			{
			hplcedl = PdrGalley(pwwd)->hplcedl;
			if (hplcedl == hNil)
				break; /* can happen coming out of pageview from low in memory */

			ScrollDelta(1, vcConseqScroll, (*hplcedl)->dlMax,
					pwwd->ywMac - pwwd->ywMin,
					&dysMinScroll, &dysMaxScroll);
			(*pfScroll)( ww, dysMinScroll, dysMaxScroll);
			}
		break;
	case SB_PAGEUP:
	case SB_PAGEDOWN:
		Profile(vpfi == pfiScroll ? StartProf(30) : 0);
		if (pwwd->fPageView)
			{
			dye = DyOfRc(&pwwd->rcwDisp) - vsci.dysMinAveLine;
			FScrollPageDyeHome(ww, code == SB_PAGEUP ? -dye : dye, fTrue, 0, fFalse);
			}
		else
			{
			dypPage = pwwd->ywMac - pwwd->ywMin;
			if (dypPage > vsci.dysMacAveLine)
				dypPage -= vsci.dysMacAveLine;
			(*(code == SB_PAGEUP ? ScrollDown : ScrollUp))
					( ww, dypPage, dypPage + vsci.dysMacAveLine - vsci.dysMinAveLine );

			}
		break;
	case SB_THUMBPOSITION:
		Profile(vpfi == pfiScroll ? StartProf(30) : 0);
		if (pwwd->fPageView)
			{
			int dqThumbOld = SpsFromHwndRSB(pwwd->hwndVScroll);
			ThumbPage(ww, dqThumb, dqThumbOld);
			}
		else
			{
			ThumbToCp( ww, CpScaleThumb(ww, dqThumb), fFalse, fFalse, 0 );
			}
		break;
		}

	UpdateWindowWw( ww );
	Profile(vpfi == pfiScroll ? StopProf() : 0);
}



/* %%Function:MwdHorzScroll %%Owner:bryanl */
void MwdHorzScroll(ww, code, dqThumb)
int ww;
WORD code;
int dqThumb; /*New position */
{
/* the mod is used so that small changes in rcwDisp caused by the scroll
itself do not change the amount of scroll, but larger changes in window
size do. Otherwise Left scroll would not be the inverse of Right scroll.
Note: dxpMinScrollVsci must be divisible by 16! (to avoid truncation by
scroll) */
	struct WWD *pwwd = PwwdWw(ww);
	int dxpScroll = DxOfRc(&pwwd->rcwDisp) /
	(dxpMinScrollSci / 2) * (dxpMinScrollSci / 2);
#ifdef CHIC
	int dxpWwScroll = NMultDiv(DxOfRc(&pwwd->rcwDisp), 2, 3);
	int dxpScroll = vsci.dxpMinScroll;
#endif
	BOOL fLeft = fFalse;
	int xpThumb;

	Assert( mpwwhwwd[ww] != hNil );
	switch (code) 
		{
	default:
		/* case SB_THUMBTRACK: */
		return;
	case SB_LINEUP:
		SetOurKeyState();
		dxpScroll = vsci.dxpMinScroll;
		break;
	case SB_LINEDOWN:
		fLeft = fTrue;
		dxpScroll = vsci.dxpMinScroll;
		break;
	case SB_PAGEUP:
		break;
	case SB_PAGEDOWN:
		fLeft = fTrue;
		break;
	case SB_THUMBPOSITION:
		if (pwwd->fPageView)
			{
			xpThumb = NMultDiv(dqThumb,
					max(XeLeftPage(ww), 
					DxOfRc(&pwwd->rcePage) - DxOfRc(&pwwd->rcwDisp)
					+ dxpGrayOutsideSci)
					+ dxpGrayOutsideSci,
					vsci.xpRightMax)
					- dxpGrayOutsideSci;
			}
		else
			{
			xpThumb = NMultDiv(dqThumb,
					vsci.xpRightMax - DxOfRc(&pwwd->rcwDisp),
					vsci.xpRightMax);
			}
		dxpScroll = xpThumb + pwwd->xhScroll;

		if (dxpScroll > 0)
			fLeft = fTrue;
		else
			dxpScroll = -dxpScroll;
		break;
		}

	if (dxpScroll == 0)
		{
		Beep();
		return;
		}

	if (fLeft)
		ScrollLeft(ww, dxpScroll);
	else
		{
/* if not pageview, at or left of home pos, no dl's hanging to the left,
and no shift key depressed => not allowed. */
		if (!pwwd->fPageView && DxpScrollHorizWw(ww) >= 0 &&
				!FAnyDlNotInXw(ww, fTrue) && 
				!(vgrpfKeyBoardState & wKbsShiftMask))
			Beep();
		else
			ScrollRight(ww, dxpScroll);
		}


/* Only force update if we have at least 1/2 inch wide invalid rect */
/* or if we have scrolled to dxpScroll == 0; prevents weird failure
	to update until user lets up on the mouse */

	pwwd = PwwdWw(ww);
	UpdateWindowWw(ww);

#ifdef CHIC
	UpdateWw( ww, (DxpScrollHorizWw(ww) != 0) &&
			(pwwd->rcwInval.xpRight - pwwd->rcwInval.xpLeft
			< (vfli.dxsInch >> 1)) );
#endif
}



/* S C R O L L  W W  D Y W
*  Scrolls window vertically below ywTop by dyw pixels.
*  Invalidates uncovered area.  Window is NOT updated here.
*/
/* %%Function:ScrollWwDyw %%Owner:bryanl */
ScrollWwDyw(ww, dyw, ywTop)
{
	struct RC rcw, rcwUpdate;
	struct WWD *pwwd = PwwdWw(ww);

	GetClientRect(pwwd->hwnd, (LPRECT)&rcw);
	rcw.ywTop = ywTop;
	rcw.ywBottom = pwwd->ywMac;

	ScrollDC( pwwd->hdc, 0, -dyw, (LPSTR) &rcw,(LPRECT)&rcw,
			(HRGN) NULL, (LPRECT)&rcwUpdate );

/* InvalWwRc will not work because rcePage has not been offset yet, 
it is done in caller */
#ifdef OPUS_X64
	/* DrawBlankPage and the Win95 neighbor overlay repaint the uncovered strip;
	   suppress a separate background erase that otherwise flashes gray first. */
	InvalidateRect(PwwdWw(ww)->hwnd, (LPRECT)&rcwUpdate, fFalse);
#else
	InvalidateRect(PwwdWw(ww)->hwnd, (LPRECT)&rcwUpdate, fTrue);
#endif
}


#endif /* WIN */
