/************************************************************/
/* Windows Write, Copyright 1985-1992 Microsoft Corporation */
/************************************************************/
/* disp.c -- MW display routines */
#define NOKEYSTATE
#define NOSYSCOMMANDS
#define NOSHOWWINDOW
//#define NOATOM
#define NOCLIPBOARD
#define NOGDICAPMASKS
#define NOCTLMGR
#define NOWINSTYLES
//#define NOVIRTUALKEYCODES
#define NOSYSMETRICS
#define NOMENUS
#define NOSOUND
#define NOCOMM
#define NOOPENFILE
#define NOWH
#define NOWINOFFSETS
#define NOMETAFILE
#define NOMB
#define NODRAWTEXT
#include <windows.h>
#define NOUAC
#include "mw.h"
#include "debug.h"
#include "cmddefs.h"
#include "dispdefs.h"
#include "wwdefs.h"
#define NOKCCODES /* Removes all kc code defines */
#include "ch.h"
#include "docdefs.h"
#include "fmtdefs.h"
#include "propdefs.h"
#include "macro.h"
#include "printdef.h"
#include "fontdefs.h"
#if defined(OLE)
#include "obj.h"
#endif
#ifdef DBCS
#include "dbcs.h"
#endif
#ifdef CASHMERE /* No VisiMode in WinMemo */
extern int vfVisiMode;
#endif /* CASHMERE */
extern int vcchBlted;
extern int vidxpInsertCache;
extern int vdlIns;
extern int vfInsLast;
extern struct PAP vpapAbs;
extern struct SEP vsepAbs;
extern int rgval[];
extern struct DOD (**hpdocdod)[];
extern typeCP cpMacCur;
extern int vfSelHidden;
extern struct WWD rgwwd[];
extern int wwCur, wwMac;
extern struct FLI vfli;
extern struct SEL selCur;
extern struct WWD *pwwdCur;
extern int docCur;
extern struct CHP (**vhgchpFormat)[];
extern int vichpFormat;
extern typeCP cpMinCur;
extern typeCP cpMinDocument;
extern int vfInsertOn;
extern int vfTextBltValid;
extern typeCP vcpFirstParaCache;
extern typeCP vcpLimParaCache;
extern unsigned vpgn;
extern struct SEP vsepAbs;
extern CHAR stBuf[];
extern typeCP CpEdge();
extern typeCP CpMacText();
extern int vdocPageCache;
extern int vfPictSel;
extern int vfAwfulNoise;
extern int vfSkipNextBlink;
extern int dypMax;
extern HDC vhMDC;
extern HWND vhWndPageInfo;
extern struct FMI vfmiScreen;
extern int docScrap;
extern long rgbBkgrnd;
extern long ropErase;
extern BOOL vfMonochrome;
extern int dxpbmMDC;
extern int dypbmMDC;
extern HBITMAP hbmNull;
extern int vfOutOfMemory;
extern int vfSeeSel;
extern int vfInsEnd; /* Is insert point at end-of-line? */
extern int vipgd;
extern typeCP vcpMinPageCache;
extern typeCP vcpMacPageCache;
/* actual position of the cursor line */
extern int vxpCursLine;
extern int vypCursLine;
extern int vdypCursLine;
extern int vfScrollInval; /* means scroll did not take and UpdateWw must be repeated */
extern BOOL vfDead;
extern HRGN vhrgnClip;
/* G L O B A L S
int dlsMac = 0;*/
#ifdef DBCS
int donteat = 0; /* propagate not to eat message */
#endif
/* D I S P L A Y F L I */
/* Display formatted line in window ww at line dl */
DisplayFli(ww, dl, fDontDisplay)
int ww;
int dl;
int fDontDisplay; /* True if we set up dl info but don't display */
{
typeCP dcp;
typeCP dcpMac;
struct WWD *pwwd = &rgwwd[ww];
HDC hDC = pwwd->hDC;
int xp; /* Current xp to write text */
int yp; /* Current yp to write text */
int xpMin = pwwd->xpMin; /* Minimum xp in window */
int xpMac = pwwd->xpMac; /* Maximum xp in window */
int ypLine; /* Screen yp for current line */
int dxp; /* Width of current run */
int dyp; /* Line height */
int dxpExtra; /* Width of pad for each space */
typeCP cpMin;
typeCP cpMac;
int xpSel; /* xp of the start of the selection */
int dxpSel = 0; /* Width of the selection. */
CHAR chMark = '\0'; /* style character */
struct CHP *pchp;
BOOL fTabsKludge = (vfli.ichLastTab >= 0);
BOOL fInsertOn = FALSE;
int cBreakRun; /* break characters in run (no relation to Dick or Jane) */
#ifdef SMFONT
RECT rcOpaque;
#endif /* SMFONT */
#ifdef DDISP
CommSzNumNum(" DisplayFli: dl/fDontDisplay ", dl, fDontDisplay);
#endif
Assert(ww >= 0 && ww < wwMax);
#ifdef SMFONT
Assert(!fDontDisplay || vfli.fGraphics)
#endif /* SMFONT */
Scribble(5,'D');
/* Fill up EDL and set some useful locals */
{
register struct EDL *pedl = &(**pwwd->hdndl)[dl];
if (dl == vdlIns)
{
/* Overwriting chars blted during fast insert; reset blt count */
vcchBlted = 0;
vidxpInsertCache = -1;
}
pedl->xpLeft = vfli.xpLeft;
pedl->xpMac = vfli.xpReal;
cpMin = pedl->cpMin = vfli.cpMin;
pedl->dcpMac = (cpMac = vfli.cpMac) - cpMin;
dyp = pedl->dyp = vfli.dypLine;
pedl->ichCpMin = vfli.ichCpMin;
pedl->dcpDepend = (cpMin == cpMac) ? 0xff : vfli.dcpDepend;
pedl->fValid = TRUE;
pedl->fGraphics = vfli.fGraphics;
pedl->fSplat = vfli.fSplat;
/* The position of current line equals the position of the previous line
+ height of this line. */
#ifdef SMFONT
pedl->yp = rcOpaque.bottom = dyp + (ypLine = rcOpaque.top = (dl == 0 ?
pwwd->ypMin : (pedl - 1)->yp));
#else /* not SMFONT */
pedl->yp = dyp + (ypLine = (dl == 0 ? pwwd->ypMin :
(pedl - 1)->yp));
#endif /* SMFONT */
if (pedl->fIchCpIncr = (vfli.ichCpMac != 0))
{
/* Look at final text column */
++cpMac;
/* Since this is true, we can compress pedl->ichCpMac to 1 bit. */
Assert(vfli.ichCpMac == pedl->ichCpMin + 1);
}
}
if (vfli.doc == docNil)
{
/* This is the space beyond the end mark. */
PatBlt(hDC, 0, ypLine, xpMac, dyp, ropErase);
goto Finished;
}
/* Is there a character in the "style bar"? */
if (cpMin != cpMac)
{
#ifdef CASHMERE
/* This line is not completely empty (not after the end mark); check for
painting marks on the style bar. */
if (cpMin == vcpFirstParaCache && vpapAbs.rhc != 0)
{
/* This is a running-head. */
chMark = chStatRH;
}
else if ((**hpdocdod)[vfli.doc].hpgtb != 0)
#else /* not CASHMERE */
if (vpapAbs.rhc == 0 && (**hpdocdod)[vfli.doc].hpgtb != 0)
#endif /* CASHMERE */
{
if (vdocPageCache != vfli.doc || cpMac > vcpMacPageCache || cpMac <=
vcpMinPageCache)
{
CachePage(vfli.doc, cpMac - 1);
}
/* We are now guaranteed that cpMac is within the cached page. */
if (cpMin <= vcpMinPageCache && (!vfli.fGraphics || vfli.ichCpMin ==
0))
{
/* This is the first line of new page; show page mark. */
chMark = chStatPage;
}
}
}
#ifdef SMFONT
#ifdef DDISP
/* black out this line to test how efficiently/correctly we
overwrite pixels from previously-resident lines of text */
PatBlt(hDC, 0, ypLine, xpMac, dyp, BLACKNESS);
{ long int i; for (i=0; i < 500000; i++) ; }
#endif
/* Calculate dcpMac now, so we might be able to know how much to erase. */
dcpMac = vfli.fSplat ? vfli.ichMac : vfli.ichReal;
/* Erase any character that might be in the style bar. */
dxp = xpSelBar + 1;
if (!vfli.fGraphics)
{
dxp = xpMac; // clear the whole line
}
PatBlt(hDC, 0, ypLine, dxp, dyp, ropErase);
/* If this is graphics then go draw any characters in the style bar. */
if (vfli.fGraphics)
{
goto DrawMark;
}
/* If there are no "real" characters on this line then we can skip alot of
this. */
if (dcpMac == 0)
{
goto EndLine2;
}
#else /* not SMFONT */
if (vfli.fGraphics || fDontDisplay)
{
/* Erase any character that might be in the style bar. */
PatBlt(hDC, 0, ypLine, xpSelBar, dyp, ropErase);
goto DrawMark;
}
#endif /* SMFONT */
ValidateMemoryDC();
if (vhMDC == NULL)
{
Error:
/* Notify the user that an error has occured and simply erase this line.
*/
WinFailure();
PatBlt(hDC, xpSelBar, ypLine, xpMac - xpSelBar, dyp, ropErase);
goto Finished;
}
#ifndef SMFONT
/* Create a new bitmap for the memory DC if the current bitmap is not big
enough. */
if (xpMac > dxpbmMDC || dyp > dypbmMDC)
{
HBITMAP hbm;
/* If there is an old bitmap, then delete it. */
if (dxpbmMDC != 0 || dypbmMDC != 0)
{
DeleteObject(SelectObject(vhMDC, hbmNull));
}
/* Create the new bitmap and select it in. */
if ((hbm = CreateBitmap(dxpbmMDC = xpMac, dypbmMDC = dyp, 1, 1,
(LPSTR)NULL)) == NULL)
{
/* There should be a graceful way to recover if the bitmap is ever
NULL (e.g we don't have enough memory for it). */
dxpbmMDC = dypbmMDC = 0;
goto Error;
}
SelectObject(vhMDC, hbm);
}
/* Erase the are of the bitmap we are going to use. */
PatBlt(vhMDC, xpSelBar, 0, xpMac, dyp, vfMonochrome ? ropErase : WHITENESS);
#endif /* not SMFONT */
/* Initialize some of the variables we'll need. */
pchp = &(**vhgchpFormat)[0];
#ifdef SMFONT
xp = rcOpaque.left = rcOpaque.right = vfli.xpLeft + xpSelBar - xpMin + 1;
#else /* not SMFONT */
dcpMac = vfli.fSplat ? vfli.ichMac : vfli.ichReal;
xp = vfli.xpLeft + xpSelBar - xpMin + 1;
#endif /* SMFONT */
dxpExtra = fTabsKludge ? 0 : vfli.dxpExtra;
#ifdef SMFONT
/* If we are horizontally scrolled, then set the clip area to the area
outside of the selection bar. */
if (xpMin != 0)
{
IntersectClipRect(hDC, xpSelBar, rcOpaque.top, xpMac, rcOpaque.bottom);
}
#endif /* SMFONT */
for (dcp = 0; dcp < dcpMac; pchp++)
{
/* For all runs do: */
int ichFirst; /* First character in the current run */
int cchRun; /* Number of characters in the current run */
dcp = ichFirst = pchp->ichRun;
dcp += pchp->cchRun;
if (dcp > dcpMac)
{
dcp = dcpMac;
}
cchRun = dcp - ichFirst;
/* Compute dxp = sum of width of characters in current run (formerly
DxaFromIcpDcp). */
{
register int *pdxp;
register int cchT = cchRun;
PCH pch = vfli.rgch + ichFirst;
dxp = cBreakRun = 0;
pdxp = &vfli.rgdxp[ichFirst];
while (cchT-- > 0)
{
dxp += *pdxp++;
if (*pch++ == chSpace)
++cBreakRun;
}
#ifdef DDISP
CommSzNum(" dxp=",dxp);
#endif
}
if (dxp > 0)
{
int cchDone;
PCH pch = &vfli.rgch[ichFirst];
LoadFont(vfli.doc, pchp, mdFontScreen);
yp = (dyp - (vfli.dypBase + (pchp->hpsPos != 0 ? (pchp->hpsPos <
hpsNegMin ? ypSubSuper : -ypSubSuper) : 0))) -
vfmiScreen.dypBaseline;
/* Note: tabs and other special characters are guaranteed to come at
the start of a run. */
SetTextJustification(vhMDC, dxpExtra * cBreakRun, cBreakRun);
#ifdef SMFONT
SetTextJustification(hDC, dxpExtra * cBreakRun, cBreakRun);
#endif /* SMFONT */
cchDone = 0;
while (cchDone < cchRun)
{
int cch;
/* Does the wide-space zone begin in this run? */
if (vfli.fAdjSpace && (vfli.ichFirstWide < ichFirst + cchRun) &&
(ichFirst + cchDone <= vfli.ichFirstWide))
{
int cchDoneT = cchDone;
/* Is this the beginning of the wide-space zone? */
if (ichFirst + cchDone == vfli.ichFirstWide)
{
/* Reset the width of the spaces. */
SetTextJustification(vhMDC, ++dxpExtra * cBreakRun, cBreakRun);
#ifdef SMFONT
SetTextJustification(hDC, dxpExtra * cBreakRun, cBreakRun);
#endif /* SMFONT */
cch = cchRun - cchDone;
cchDone = cchRun;
}
else
{
cchDone = cch = vfli.ichFirstWide - ichFirst;
}
/* This run is cut short because of a wide space, so we need
to calculate a new width. */
{
register int *pdxp;
register int cchT = cch;
PCH pch = &vfli.rgch[ichFirst + cchDoneT];
dxp = 0;
pdxp = &vfli.rgdxp[ichFirst + cchDoneT];
while (cchT-- > 0)
{
dxp += *pdxp++;
if (*pch++ == chSpace)
++cBreakRun;
}
}
}
else
{
cchDone = cch = cchRun;
}
while (cch > 0)
{
switch (*pch)
{
CHAR ch;
int dxpT;
case chTab:
#ifdef CASHMERE
/* chLeader contains tab leader character (see
FormatLine) */
if ((ch = pchp->chLeader) != chSpace)
{
int cxpTab;
CHAR rgch[32];
int dxpLeader = CharWidth(ch);
int xpT = xp;
int iLevelT = SaveDC(vhMDC);
SetBytes(&rgch[0], ch, 32);
dxpT = vfli.rgdxp[ichFirst];
cxpTab = ((dxpT + dxpLeader - 1) / dxpLeader + 31)
>> 5;
xp += dxpT;
while (cxpTab-- > 0)
{
TextOut(vhMDC, xpT, yp, (LPSTR)rgch, 32);
xpT += dxpLeader << 5;
}
RestoreDC(vhMDC, iLevelT);
}
else
#endif /* CASHMERE */
{
#ifdef SMFONT
/* Expand the opaque rectangle to include the tab.
*/
rcOpaque.right += vfli.rgdxp[ichFirst];
#endif /* SMFONT */
xp += vfli.rgdxp[ichFirst];
}
if (fTabsKludge && ichFirst >= vfli.ichLastTab)
{
SetTextJustification(vhMDC, (dxpExtra =
vfli.dxpExtra) * cBreakRun, cBreakRun);
#ifdef SMFONT
SetTextJustification(hDC, dxpExtra * cBreakRun, cBreakRun);
#endif /* SMFONT */
fTabsKludge = FALSE;
}
dxp -= vfli.rgdxp[ichFirst];
pch++;
cch--;
goto EndLoop;
#ifdef CASHMERE
case schPage:
if (!pchp->fSpecial)
{
goto EndLoop;
}
stBuf[0] = CchExpPgn(&stBuf[1], vpgn, vsepAbs.nfcPgn,
flmSandMode, ichMaxLine);
goto DrawSpecial;
case schFootnote:
if (!pchp->fSpecial)
{
goto EndLoop;
}
stBuf[0] = CchExpFtn(&stBuf[1], cpMin + ichFirst,
flmSandMode, ichMaxLine);
DrawSpecial:
#else /* not CASHMERE */
case schPage:
case schFootnote:
if (!pchp->fSpecial)
{
goto EndLoop;
}
stBuf[0] = *pch == schPage && (wwdCurrentDoc.fEditHeader
|| wwdCurrentDoc.fEditFooter) ? CchExpPgn(&stBuf[1],
vpgn, 0, flmSandMode, ichMaxLine) :
CchExpUnknown(&stBuf[1], flmSandMode, ichMaxLine);
#endif /* not CASHMERE */
#ifdef SMFONT
/* Calculate the opaque rectangle. */
rcOpaque.right += vfli.rgdxp[ichFirst] +
vfmiScreen.dxpOverhang;
TextOut(hDC, xp, ypLine+yp, &stBuf[1], stBuf[0]);
#else /* not SMFONT */
TextOut(vhMDC, xp, yp, (LPSTR)&stBuf[1], stBuf[0]);
#endif /* SMFONT */
break;
default:
goto EndLoop;
}
dxp -= vfli.rgdxp[ichFirst];
#ifdef SMFONT
/* End the line if no more will fit into the window. */
if ((xp += vfli.rgdxp[ichFirst++]) >= xpMac) {
goto EndLine;
}
rcOpaque.left = (rcOpaque.right = xp) +
vfmiScreen.dxpOverhang;
#else /* not SMFONT */
xp += vfli.rgdxp[ichFirst++];
#endif /* SMFONT */
pch++;
cch--;
}
EndLoop:
#ifdef SMFONT
if (cch == 0)
{
Assert(dxp == 0);
}
else
{
/* Calculate the opaque rectangle. */
rcOpaque.right += dxp + vfmiScreen.dxpOverhang;
#if 0
{
char msg[180];
wsprintf(msg,"putting out %d characters\n\r",cch);
OutputDebugString(msg);
}
#endif
/* Output cch characters starting at pch */
TextOut(hDC, xp, ypLine+yp, pch, cch);
/* End the line if no more will fit into the window. */
if ((xp += dxp) >= xpMac)
{
goto EndLine;
}
rcOpaque.left = (rcOpaque.right = xp) +
vfmiScreen.dxpOverhang;
pch += cch;
}
#else /* not SMFONT */
/* Output cch characters starting at pch */
TextOut(vhMDC, xp, yp, (LPSTR)pch, cch);
xp += dxp;
pch += cch;
#endif /* SMFONT */
} /* end while (cchDone<cchRun) */
} /* end if (dxp>0) */
} /* end for dcp=0..dcpMac */
#ifdef SMFONT
EndLine:
/* Restore the clip region if need be. */
if (xpMin != 0)
{
SelectClipRgn(hDC, NULL);
}
EndLine2:
#endif /* SMFONT */
#ifdef CASHMERE
if (vfVisiMode)
{
AddVisiSpaces(ww, &(**pwwd->hdndl)[dl], vfli.dypBase, vfli.dypAfter +
vfli.dypFont);
}
#endif /* CASHMERE */
vfTextBltValid = FALSE;
if ((ww == wwCur) && (pwwd->doc != docScrap) && !vfSelHidden &&
(selCur.cpLim >= cpMin))
{
if (selCur.cpFirst <= cpMac)
{
/* Show selection */
int xpFirst;
int xpLim;
#ifdef ENABLE
if (vfli.fSplatNext && selCur.cpFirst == selCur.cpLim &&
selCur.cpFirst == cpMac)
{
vfInsEnd = TRUE;
ClearInsertLine();
}
vfInsertOn = FALSE;
#endif /* ENABLE */
if (selCur.cpFirst <= cpMin && selCur.cpLim >= cpMac)
{
xpFirst = vfli.xpLeft;
xpLim = vfli.xpReal;
}
else if (selCur.cpFirst < cpMac || (selCur.cpLim == cpMac &&
vfInsEnd))
{
typeCP cpBegin = CpMax(cpMin, selCur.cpFirst);
typeCP cpEnd = CpMin(cpMac, selCur.cpLim);
dxp = DxpDiff((int)(cpBegin - cpMin), (int)(cpEnd - cpBegin),
&xpFirst);
xpLim = min(xpMin + vfli.xpReal, xpFirst + dxp);
}
else
{
goto DidntHighlight;
}
xpSel = xpSelBar + max(xpFirst - xpMin, 0);
if (xpLim > xpFirst)
{
/* Set highlighting at desired screen position. */
dxpSel = max(xpLim - max(xpFirst, xpMin), 0);
}
else if (selCur.cpFirst == selCur.cpLim && ((selCur.cpLim != cpMac)
^ vfInsEnd))
{
vfInsertOn = FALSE; /* Because we redisplayed insert pt line */
#ifdef CASHMERE
vdypCursLine = min(vfli.dypFont, vfli.dypLine - vfli.dypAfter);
vypCursLine = ypLine + dyp - vfli.dypAfter;
#else /* not CASHMERE */
vdypCursLine = vfli.dypFont;
vypCursLine = ypLine + dyp;
#endif /* not CASHMERE */
vxpCursLine = xpSel;
/* Start blinking in a while */
vfSkipNextBlink = TRUE;
fInsertOn = xpFirst >= xpMin;
}
DidntHighlight:;
}
}
#ifdef SMFONT
/* Invert the selection */
if (dxpSel != 0) {
PatBlt(hDC, xpSel, ypLine, dxpSel, dyp, DSTINVERT);
}
#else /* not SMFONT */
/* Blt the line of text onto the screen. */
PatBlt(vhMDC, 0, 0, xpSelBar, dyp, vfMonochrome ? ropErase : WHITENESS);
if (dxpSel == 0)
{
BitBlt(hDC, 0, ypLine, xpMac, dyp, vhMDC, 0, 0, SRCCOPY);
}
else
{
BitBlt(hDC, 0, ypLine, xpSel, dyp, vhMDC, 0, 0, SRCCOPY);
BitBlt(hDC, xpSel, ypLine, dxpSel, dyp, vhMDC, xpSel, 0, NOTSRCCOPY);
xpSel += dxpSel;
BitBlt(hDC, xpSel, ypLine, xpMac - xpSel, dyp, vhMDC, xpSel, 0,
SRCCOPY);
}
#endif /* SMFONT */
/* Draw the insertion bar if necessary. */
if (fInsertOn)
{
DrawInsertLine();
}
DrawMark:
/* Draw the character in the style bar if necessary. */
if (chMark != '\0')
{
#ifdef SYSENDMARK
struct CHP chpT;
extern struct CHP vchpNormal;
blt(&vchpNormal, &chpT, cwCHP);
chpT.ftc = ftcSystem;
chpT.ftcXtra = 0;
chpT.hps = hpsDefault;
/* Draw the style character in the standard font. */
LoadFont(vfli.doc, &chpT, mdFontScreen);
TextOut(hDC, 0, ypLine + dyp - vfli.dypBase - vfmiScreen.dypBaseline,
(LPSTR)&chMark, 1);
#else /* ifdef SYSENDMARK */
/* Draw the style character in the standard font. */
LoadFont(vfli.doc, NULL, mdFontScreen);
TextOut(hDC, 0, ypLine + dyp - vfli.dypBase - vfmiScreen.dypBaseline,
(LPSTR)&chMark, 1);
#endif /* if-else-def SYSENDMARK */
}
if (vfli.fGraphics)
{
DisplayGraphics(ww, dl, fDontDisplay);
}
Finished:
Scribble(5,' ');
}
/* D X P D I F F */
DxpDiff(dcpFirst, dcp, pdxpFirst)
int dcpFirst;
int dcp;
int *pdxpFirst;
{
#if 1
register int *pdxp = &vfli.rgdxp[0];
register int cch;
int dxp = vfli.xpLeft;
#ifdef ENABLE /* Not used */
int ichLim = dcpFirst + dcp;
#endif
if (dcp > vfli.ichMac - dcpFirst)
{ /* This should not be, but is when we have a CR */
//Assert( dcpFirst < vfli.ichMac );
dcp = vfli.ichMac - dcpFirst;
}
for (cch = 0; cch < dcpFirst; ++cch)
{
dxp += *pdxp++;
}
*pdxpFirst = dxp;
dxp = 0;
for (cch = 0; cch < dcp; ++cch)
{
dxp += *pdxp++;
}
return dxp;
#else
int dxp;
if (dcp > vfli.ichMac - dcpFirst)
{ /* This should not be, but is when we have a CR */
Assert( dcpFirst < vfli.ichMac );
dcp = vfli.ichMac - dcpFirst;
}
/* first get space up to first character */
*pdxpFirst = LOWORD(GetTextExtent(hDC,vfli.rgch,dcpFirst)) + vfli.xpLeft;
/* now get space between first and first+dcp */
dxp = LOWORD(GetTextExtent(hDC,vfli.rgch+dcpFirst,dcp));
return dxp;
#endif
}
UpdateDisplay(fAbortOK)
int fAbortOK;
{
int ww;
if (wwMac <= 0)
{
return;
}
#ifdef CASHMERE
for (ww = 0; ww < wwMac; ww++)
if ( rgwwd[ww].doc != docScrap )
{
UpdateWw(ww, fAbortOK);
if (rgwwd[ww].fDirty || vfOutOfMemory)
{
return; /* update has been interrupted */
}
}
#else /* not CASHMERE */
UpdateWw(wwDocument, fAbortOK);
if (wwdCurrentDoc.fDirty || vfOutOfMemory)
{
/* Update has been interrupted */
return;
}
#endif /* not CASHMERE */
if (wwdCurrentDoc.fRuler)
{
UpdateRuler();
}
}
/* U P D A T E W W */
UpdateWw(ww, fAbortOK)
int ww, fAbortOK;
{ /* Redisplay ww as necessary */
extern int vfWholePictInvalid;
register struct WWD *pwwd = &rgwwd[ww];
int dlMac;
int dlOld, dlNew;
int doc;
int ichCp;
struct EDL *pedlNew;
register struct EDL *pedl;
struct EDL (**hdndl)[]=pwwd->hdndl;
int dypDiff;
int ypTop;
int ypFirstInval;
int dr;
int fLastNotShown;
typeCP cp, cpMacWw;
if (!pwwd->fDirty)
{
return;
}
if (!((**hpdocdod)[pwwd->doc].fDisplayable))
return;
if (fAbortOK && FImportantMsgPresent())
return;
#if 0 // how to get first and last cp's in invalid rect?
#if defined(OLE)
/*
Load visible objects. Do it now rather than in DisplayGraphics()
because here it has less chance of disrupting the state variables
upon which UpdateWw depends.
*/
ObjEnumInRange(docCur,cpMinCur,cpMacCur,ObjLoadObjectInDoc);
#endif
#endif
dlMac = pwwd->dlMac;
ypTop = pwwd->ypMin;
Assert( ww >= 0 && ww < wwMax );
vfli.doc = docNil; /* An aid to Fast Insert */
UpdateInvalid(); /* InvalBand for what Windows considers to be invalid */
ypFirstInval = pwwd->ypFirstInval;
#ifndef CASHMERE
Assert( ww == wwCur ); /* A MEMO-only assumption */
#endif /* CASHMERE */
Scribble(5, 'U');
ValidateMemoryDC(); /* to do any update, we need a good memory DC */
if (vhMDC == NULL)
{
WinFailure();
return;
}
doc = pwwd->doc;
vfli.doc = docNil;
if (pwwd->fCpBad)
{
/* cp first displayed has not been blessed */
#ifdef CASHMERE /* Must do this if ww != wwCur assertion is FALSE */
int wwT = wwCur;
if (ww != wwCur && wwCur >= 0)
/* CtrBackTrs cache is only good for wwCur. Treat != case */
{
if (pwwdCur->fDirty) /* Do wwCur first, saving cache */
UpdateWw(wwCur, fAbortOK);
if (fAbortOK && FImportantMsgPresent())
return;
ChangeWw(ww, false);
CtrBackDypCtr( 0, 0 ); /* Validate pwwdCur->cpFirst */
ChangeWw(wwT, false);
}
else
#endif /* CASHMERE */
{
if (fAbortOK && FImportantMsgPresent())
return;
CtrBackDypCtr( 0, 0 ); /* Validate pwwdCur->cpFirst */
}
}
/* check for cpMin accessible in this ww */
RestartUpdate:
vfWholePictInvalid = fTrue; /* Tells DisplayGraphics to
abandon accumulated partial pict rect */
fLastNotShown = fFalse;
cp = CpMax(pwwd->cpMin, pwwd->cpFirst);
cpMacWw = pwwd->cpMac;
ichCp = pwwd->ichCpFirst;
/* Note test for dlNew==0 that guarantees that there will be at least
one dl -- this was added for WRITE because we do not have
the ability to enforce a minimum window size */
for (dlNew = dlOld = 0; ypTop < pwwd->ypMac || (dlNew == 0) ; dlNew++)
/* we have: cp, ichCP: pints to text desired on the coming line dlNew
ypTop: desired position for top of dlNew -1
dlOld: next line to be considered for re-use
*/
/* check for having to extend dndl array */
{
if (dlNew >= (int)pwwd->dlMax)
{
/* extend the array with uninitialized dl's, increment max, break if no space.
We assume that dlMac(Old) was <= dlMax, so the dl's will not be looked at
but used only to store new lines */
#define ddlIncr 5
if (!FChngSizeH(hdndl, (pwwd->dlMax + ddlIncr) * cwEDL, fFalse))
break;
pwwd->dlMax += ddlIncr;
}
/* discard unusable dl's */
for (; dlOld < dlMac; dlOld++)
{ /* Set dlOld and pedl to the next good dl */
int ypTopOld, ypOld;
/* Re-entrant Heap Movement */
if (fAbortOK && !fLastNotShown && FImportantMsgPresent())
goto RetInval;
pedl = &(**hdndl)[dlOld];
ypOld = pedl->yp;
/* loop if: invalid, passed over in cp space, passed over in dl space,
passed over in yp space,
in invalid band, passed over in ich space */
if (!pedl->fValid || dlOld < dlNew || pedl->cpMin < cp
|| (ypTopOld = (ypOld - pedl->dyp)) < ypTop
|| (ypOld >= ypFirstInval && ypTopOld <= pwwd->ypLastInval)
|| (pedl->cpMin == cp && pedl->ichCpMin < ichCp))
continue;
/* now we have dlOld, an acceptable if not necessarily useful dl.
now compute dlNew either from scratch or by re-using dlOld. To be
re-useable, dlOld must have right cp/ichCp pair, plus be totally on screen
or, if it is a partial line, it must stay still or move down - not up */
if (pedl->cpMin == cp && pedl->ichCpMin == ichCp &&
(ypOld <= pwwd->ypMac || ypTopOld <= ypTop))
{
/* Re-use this dl */
int yp = ypTop;
if (fLastNotShown)
{
/* HEAP MOVEMENT */
DisplayFli(ww, dlNew - 1, fLastNotShown = fFalse);
pedl = &(**hdndl)[dlOld];
}
cp = pedl->cpMin + pedl->dcpMac;
ichCp = pedl->fIchCpIncr ? pedl->ichCpMin + 1 : 0;
ypTop += pedl->dyp;
if (dlOld != dlNew || ypTopOld != yp)
{
DypScroll(ww, dlOld, dlNew - dlOld, yp);
if (vfScrollInval)
{
/* There was a popup; invalid region might have changed */
/* fLastNotShown test is for interrupting picture display */
/* before we've really displayed it */
(**hdndl) [dlOld].fValid = fFalse;
goto Restart1;
}
dlMac += dlNew - dlOld;
}
dlOld = dlNew + 1;
goto NextDlNew;
}
break;
}
/* cpMin > cp, the line is not anywhere so it will have to be formatted
from scratch */
if (fAbortOK && !fLastNotShown && FImportantMsgPresent())
goto RetInval;
FormatLine(doc, cp, ichCp, cpMacWw, flmSandMode); /* Creates vfli */
if (vfOutOfMemory)
goto RetInval;
ichCp = vfli.ichCpMac;
cp = vfli.cpMac;
/* advance invalid band so that update can resume after an interruption */
pwwd->ypFirstInval = (ypTop += vfli.dypLine);
pedl = &(**hdndl)[dlOld];
if (dlOld < dlMac && pedl->cpMin == cp && pedl->ichCpMin == ichCp)
{
int dlT = dlOld;
/* line at dlOld is a valid, existing line that will abutt the line just about
to be displayed. */
if (dlOld == dlNew && pedl->yp - pedl->dyp <= ypTop)
/* the line about to be overwritten will be re-used in the next loop.
Hence, it is worthwhile to save this line and its dl */
DypScroll(ww, dlOld++, 1, ypTop);
else
/* Move the next line to its abutting position. We know that it has not yet been
overwritten (yp, dlOld all > than ypTop, dlNew) */
DypScroll(ww, dlOld, 0, ypTop);
if (vfScrollInval)
{
/* There was a popup; invalid region might have changed */
/* fLastNotShown test is for interrupting picture display */
/* before we've really displayed it */
(**hdndl) [dlT].fValid = fFalse;
Restart1:
if (fLastNotShown)
{
pwwd->ypFirstInval = pwwd->ypMin;
}
ypFirstInval = pwwd->ypFirstInval;
ypTop = pwwd->ypMin;
goto RestartUpdate;
}
}
/* true in 3rd param means put off picture redisplay till later */
/* condition: graphics & not last in picture & not last in y space and
not in front of a invalid or valid transition in the picture */
DisplayFli(ww, dlNew, fLastNotShown =
(vfli.fGraphics && vfli.ichCpMac!=0 && ypTop < pwwd->ypMac));
NextDlNew:;
}
Break1:
pwwd->dlMac = dlNew;
#ifdef CASHMERE
/* condition is here to avoid swapping */
if (pwwd->fSplit && rgwwd[pwwd->ww].fFtn)
CalcFtnLimits(pwwd);
#endif /* CASHMERE */
SetCurWwVScrollPos(); /* Set Scroll bar position */
vfTextBltValid = false;
/* reset invalid indications */
pwwd->fDirty = false;
pwwd->ypFirstInval = ypMaxAll;
pwwd->ypLastInval = 0; /* so that max in InvalBand will work */
Scribble(5, ' ');
goto Validate;
/* Before returning from an interrupt, invalidate lines that were overwritten
within the present update. */
RetInval:
Scribble(5, ' ');
for (; dlOld < dlMac; dlOld++)
{
pedl = &(**hdndl)[dlOld];
if ((pedl->yp - pedl->dyp) < ypTop)
pedl->fValid = fFalse;
else
break;
}
Validate: ;
#ifdef ENABLE /* We will let UpdateInvalid handle this in case
further invalidation occurred during the update */
{ /* Tell Windows that the part we updated is valid */
RECT rc;
rc.left = 0;
rc.top = pwwd->ypMin;
rc.right = pwwd->xpMac;
rc.bottom = imin( pwwd->ypMac, ypTop );
ValidateRect( pwwd->wwptr, (LPRECT)&rc );
}
#endif
}
/* D Y P S C R O L L */
DypScroll(ww, dlFirst, ddl, ypTo)
int ww, dlFirst, ddl, ypTo;
{
/* Scroll dl's in a window, from dlFirst to end, down ddl lines (or up -ddl).
Bitmap is moved from top of dlFirst to ypTo. The yp's of the dl's are updated.
Returns the amount scrolled. (positive means down). */
register struct WWD *pwwd = &rgwwd[ww];
int dlMac;
int dlT;
int ypFrom;
int dypChange;
int cdlBelow;
struct EDL *pedl;
struct EDL *pedlT;
/* Do not call procedures while dndl is loaded up to avoid heap movement */
struct EDL *dndl = &(**(pwwd->hdndl))[0];
Assert( ww >= 0 && ww < wwMax );
vfScrollInval = fFalse;
/* Number of dl's below (and including) the first one to be scrolled */
cdlBelow = pwwd->dlMac - dlFirst;
pwwd->dlMac = min(pwwd->dlMac + ddl, pwwd->dlMax);
cdlBelow = max(0, min(cdlBelow, pwwd->dlMac - ddl - dlFirst));
pedlT = &dndl[dlFirst];
ypFrom = pedlT->yp - pedlT->dyp;
/* Length of area to be moved */
dypChange = ypTo - ypFrom;
if (cdlBelow > 0)
{
int dlTo = dlFirst + ddl;
int ypMac = pwwd->ypMac;
pedlT = &dndl[dlTo];
if (ddl != 0)
{
blt(&dndl[dlFirst], pedlT, cwEDL * cdlBelow);
}
for (dlT = dlTo; dlT < pwwd->dlMac; ++dlT, ++pedlT)
{
if (dypChange < 0 && pedlT->yp > ypMac)
{
/* Invalidate dl's that are pulled in from the ozone below ypMac
*/
pedlT->fValid = fFalse;
}
else
{
pedlT->yp += dypChange;
}
}
}
if (dypChange != 0)
{
RECT rc;
SetRect( (LPRECT)&rc, 0, min(ypFrom, ypTo),
pwwd->xpMac, pwwd->ypMac );
Assert( ww == wwCur ); /* A MEMO-only assumption */
ScrollCurWw( &rc, 0, dypChange );
}
return dypChange;
}
FImportantMsgPresent()
{
/* If the next message is important enough to interrupt a screen update, we
return TRUE; if it can wait, we return FALSE */
BOOL fToggledKey;
extern MSG vmsgLast;
#ifdef DEBUG
unsigned wHeapVal = *(pLocalHeap + 1);
Assert( wHeapVal == 0 ); /* Heap should not be frozen */
#endif
#ifdef DBCS
if( donteat )
return TRUE;
#endif
while (PeekMessage((LPMSG) &vmsgLast, NULL, NULL, NULL, PM_NOREMOVE))
{
if (((vmsgLast.wParam == VK_MENU) || (vmsgLast.wParam == VK_CONTROL)))
{
if (vmsgLast.wParam == VK_CONTROL)
{
GetMessage((LPMSG) &vmsgLast, NULL, NULL, NULL);
SetShiftFlags();
}
return TRUE;
}
/* Filter uninteresting or easily handled events */
else if (fToggledKey = FCheckToggleKeyMessage(&vmsgLast) ||
(vmsgLast.message == WM_KEYUP && vmsgLast.hwnd == wwdCurrentDoc.wwptr))
{
/* This is so the Windows keyboard interface mechanism will see toggle
key and key-up transitions */
GetMessage((LPMSG) &vmsgLast, NULL, NULL, NULL);
#ifdef WIN30
/* PeekMessage has been changed in Win 3.0 so that GetKeyState()
called from FCheckToggleKeyMessage() is really only valid if
you've done a PeekMessage(...,PM_REMOVE) or GetMessage() first.
That is, while the FCheckToggleKeyMessage() call might succeed
above, it will NOT have set the vfShiftKey/vfCommandKey flags
correctly -- so we do it here ..pault */
if (fToggledKey)
FCheckToggleKeyMessage(&vmsgLast);
#endif
if (vmsgLast.hwnd != wwdCurrentDoc.wwptr)
{
/* Just in case a modeless dialog's window proc cares */
TranslateMessage((LPMSG)&vmsgLast);
DispatchMessage((LPMSG)&vmsgLast);
}
#ifdef DBCS
if (vmsgLast.message == WM_CHAR || vmsgLast.message == WM_KEYDOWN ) {
donteat = TRUE;
return( TRUE );
} /* else Ok, you are KEYUP message. do normal */
#endif
}
else
{
switch (vmsgLast.message)
{
case WM_MOUSEMOVE:
/* Process mouse move messages immediately; they are not really
important. NOTE: This assumes that we have not captured all mouse
events; in which case, they are important. */
DispatchMessage((LPMSG)&vmsgLast);
case WM_TIMER:
case WM_SYSTIMER:
/* Remove timer and mouse move messages from the queue. */
GetMessage((LPMSG) &vmsgLast, NULL, NULL, NULL);
break;
default:
Assert( *(pLocalHeap+1) == 0 ); /* Heap should still not be frozen */
return (TRUE);
}
}
}
Assert( *(pLocalHeap + 1) == 0 ); /* Heap should still not be frozen */
return (FALSE);
}
/* C P B E G I N L I N E */
typeCP CpBeginLine(pdl, cp)
int *pdl;
typeCP cp;
{ /* return the cp and dl containing cp */
int dlMin, dlLim;
typeCP cpGuess;
struct EDL *dndl;
do
{
UpdateWw(wwCur, false);
PutCpInWwVert(cp); /* Ensure cp on screen */
} while (pwwdCur->fDirty && !vfOutOfMemory);
dndl = &(**(pwwdCur->hdndl))[0];
dlMin = 0;
dlLim = pwwdCur->dlMac;
while (dlMin + 1 < dlLim)
{ /* Binary search the ww */
int dlGuess = (dlMin + dlLim) >> 1;
struct EDL *pedl = &dndl[dlGuess];
if ((cpGuess = pedl->cpMin) <= cp && (cpGuess != cp || pedl->ichCpMin == 0))
{ /* guess is low or right */
dlMin = dlGuess;
if (cp == cpGuess && pedl->cpMin + pedl->dcpMac != cp)
break; /* Got it right */
}
else /* Guess is high */
dlLim = dlGuess;
}
*pdl = dlMin;
return dndl[dlMin].cpMin;
}
/* T O G G L E S E L */
ToggleSel(cpFirst, cpLim, fOn)
typeCP cpFirst, cpLim; /* selection bounds */
int fOn;
{ /* Flip selection highlighting on and off */
extern int vfPMS;
struct EDL *pedl;
int dlT;
int xpMin;
int dxpRoom;
int xpFirst;
int xpLim;
int fInsertPoint = (cpFirst == cpLim);
if (vfSelHidden || cpFirst > cpLim || cpLim < /*cp0*/ cpMinCur || vfDead)
return;
if ( vfPictSel && vfPMS &&
(CachePara( docCur, cpFirst ), vpapAbs.fGraphics) &&
(vcpLimParaCache == cpLim) )
{ /* Don't show inversion if we're moving or sizing a picture */
return;
}
dxpRoom = pwwdCur->xpMac - xpSelBar;
xpMin = pwwdCur->xpMin;
for (dlT = 0; dlT < pwwdCur->dlMac; dlT++)
{
typeCP cpMin, cpMac; /* line bounds */
pedl = &(**(pwwdCur->hdndl))[dlT];
if (!pedl->fValid)
continue;
cpMin = pedl->cpMin;
if (cpMin > cpLim || cpMin > cpMacCur || (cpMin == cpLim && cpLim != cpFirst))
break;
cpMac = cpMin + pedl->dcpMac;
if (cpFirst <= cpMin && cpLim >= cpMac)
{
/* entire line is highlighted */
xpFirst = pedl->xpLeft;
if (pedl->fGraphics && cpLim == cpMac && cpMin == cpMac)
/* Special kludge for graphics paras */
xpLim = xpFirst;
else
xpLim = pedl->xpMac;
}
else if (fInsertPoint && cpFirst == cpMac && vfInsEnd)
{ /* Special kludge for an insert point at the end of a line */
xpLim = xpFirst = pedl->xpMac;
}
else if (cpFirst < cpMac)
{
/* Bite the bullet */
int dxp;
typeCP cpBegin = CpMax(cpMin, cpFirst);
typeCP cpEnd = CpMin(cpMac, cpLim);
FormatLine(docCur, cpMin, pedl->ichCpMin, cpMacCur, flmSandMode);
dxp = DxpDiff((int) (cpBegin - cpMin),
(int) (cpEnd - cpBegin), &xpFirst);
xpLim = xpFirst + dxp;
/* reload pedl because procedures were called */
pedl = &(**(pwwdCur->hdndl))[dlT];
}
else
continue;
/* now we have: pedl valid, xpFirst, xpLast describe highlight */
/* xpFirst = max(xpFirst, xpMin); */
xpLim = min(xpLim, xpMin + pedl->xpMac);
if (xpLim > xpFirst)
{
if (xpLim > xpMin)
{
RECT rc;
rc.top = pedl->yp - pedl->dyp;
rc.left = xpSelBar + max(xpFirst - xpMin, 0);
rc.bottom = pedl->yp;
rc.right = xpSelBar + xpLim - xpMin;
InvertRect( wwdCurrentDoc.hDC, (LPRECT)&rc);
}
}
/* ToggleSel modified 7/28/85 -- added explicit check for fInsertPoint, since
the xpLim == xpFirst test sometimes succeeded bogusly when a selection
was extended backwards. BL */
else if (fInsertPoint && (xpLim == xpFirst)) /* Insertion point */
{
/* vfli should usually be cached already, so will be fast. */
int yp = pedl->yp;
FormatLine(docCur, cpMin, pedl->ichCpMin, cpMacCur, flmSandMode);
if (fOn ^ vfInsertOn)
{
if (!vfInsertOn)
{
vxpCursLine = xpSelBar + xpFirst - xpMin;
vypCursLine = yp - vfli.dypAfter;
vdypCursLine = min(vfli.dypFont, vfli.dypLine - vfli.dypAfter);
/* Start blinking in a while */
vfSkipNextBlink = TRUE;
}
DrawInsertLine();
}
return;
}
}
}
/* T R A S H W W */
TrashWw(ww)
{ /* Invalidate all dl's in ww */
Assert( ww >= 0 && ww < wwMax );
InvalBand(&rgwwd[ww], 0, ypMaxAll);
}
/* I N V A L B A N D */
/* invalidate the band ypFirst, ypLast inclusive */
InvalBand(pwwd, ypFirst, ypLast)
struct WWD *pwwd; int ypFirst, ypLast;
{
/* this covers some peculiar rects received from update event after a
window resize by 1 pixel. CS */
if (ypLast < 0 || ypFirst == ypLast) return;
pwwd->fDirty = true;
pwwd->ypFirstInval = min(pwwd->ypFirstInval, ypFirst);
pwwd->ypLastInval = max(ypLast, pwwd->ypLastInval);
}
/* T R A S H A L L W W S */
TrashAllWws()
{ /* trash them all */
int ww;
#ifdef CASHMERE
for (ww = 0; ww < wwMac; ++ww)
TrashWw(ww);
#else
TrashWw( wwDocument );
#endif
vfli.doc = docNil; /* Mark vfli invalid */
}
/* T U R N O F F S E L */
TurnOffSel()
{ /* Remove sel highlighting from screen */
/* HideSel has no effect */
if (!vfSelHidden)
{
ToggleSel(selCur.cpFirst, selCur.cpLim, false);
vfSelHidden = true;
}
}
/* D R A W I N S E R T L I N E */
DrawInsertLine()
{ /* Draw (in Xor mode) a vertical bar at screen position v*CursLine */
/* Toggles both the display and the vfInsertOn flag */
/* Adjustments in cursor draw must be reflected in DisplayFli, above */
/* Last-minute correction for a bug: assure that the insert line
does not extend above ypMin */
if (!vfInsertOn && vdypCursLine > vypCursLine - wwdCurrentDoc.ypMin)
vdypCursLine = vypCursLine - wwdCurrentDoc.ypMin;
/* Tell GDI to invert the caret line */
PatBlt( wwdCurrentDoc.hDC, vxpCursLine, vypCursLine - vdypCursLine,
2, vdypCursLine , DSTINVERT );
vfInsertOn = 1 - vfInsertOn;
}
/* C L E A R I N S E R T L I N E */
ClearInsertLine()
{
if ( vfInsertOn) DrawInsertLine();
}