WindowsXP-SP1/windows/richedit/lssrc/dispmisc.c
2020-09-30 16:53:49 +02:00

544 lines
15 KiB
C

#include "dispmisc.h"
#include "lsdnode.h"
#include "lssubl.h"
static long AddSublineAdvanceWidth(PLSSUBL plssubl);
static PLSDNODE AdvanceToNextVisualDnodeCore(PLSDNODE, LSTFLOW, POINTUV*);
static PLSDNODE NextVisualDnodeOnTheLevel(PLSDNODE pdn, LSTFLOW lstflowMain);
#define fUVerticalPlusVDirection (fUVertical|fVDirection) // see comments in lstfset.c
// Has this dnode submitted subline(s) for display?
#define FIsSubmittingDnode(pdn) (FIsDnodeReal(pdn) && (pdn)->u.real.pinfosubl != NULL && \
(pdn)->u.real.pinfosubl->fUseForDisplay)
// Has this dnode accepted subline(s) for display?
#define FIsAcceptingDnode(pdn) (FIsDnodeReal(pdn) && (pdn)->u.real.pinfosubl != NULL && \
((pdn)->u.real.pinfosubl->rgpsubl)[0]->fAcceptedForDisplay)
// %%Function: CreateDisplayTree
// %%Contact: victork
//
/* CreateDisplayTree sets plsdnUpTemp in sublines to be displayed with given subline,
* rejects wrong sublines, submitted for display, sets fAcceptedForDisplay in good ones
*/
void CreateDisplayTree(PLSSUBL plssubl)
{
LSTFLOW lstflowMain = plssubl->lstflow;
PLSDNODE pdn = plssubl->plsdnFirst;
long dupSum;
BOOL fAccept;
DWORD i;
LSTFLOW lstflowSubline;
while (pdn != NULL) /* don't care about break */
{
if (FIsSubmittingDnode(pdn))
{
Assert(pdn->u.real.pinfosubl->cSubline > 0);
fAccept = fTrue;
lstflowSubline = ((pdn->u.real.pinfosubl->rgpsubl)[0])->lstflow;
// reject if one tflow is vertical, another is horizontal or v-directions are not the same
// (see explanation of bits meaning in lstfset.c)
if ((lstflowSubline ^ lstflowMain) & fUVerticalPlusVDirection)
{
fAccept = fFalse;
}
dupSum = 0;
for (i = 0; i < pdn->u.real.pinfosubl->cSubline; i++)
{
dupSum += AddSublineAdvanceWidth((pdn->u.real.pinfosubl->rgpsubl)[i]);
// all tflows should be the same
if (((pdn->u.real.pinfosubl->rgpsubl)[i])->lstflow != lstflowSubline)
{
fAccept = fFalse;
}
// Submitting empty sublines is prohibited
if (((pdn->u.real.pinfosubl->rgpsubl)[i])->plsdnFirst == NULL)
{
fAccept = fFalse;
}
}
// reject if sublines don't sum up to the dnode width
if (dupSum != pdn->u.real.dup)
{
fAccept = fFalse;
}
if (fAccept)
{
for (i = 0; i < pdn->u.real.pinfosubl->cSubline; i++)
{
((pdn->u.real.pinfosubl->rgpsubl)[i])->plsdnUpTemp = pdn;
((pdn->u.real.pinfosubl->rgpsubl)[i])->fAcceptedForDisplay = fTrue;
CreateDisplayTree((pdn->u.real.pinfosubl->rgpsubl)[i]);
}
}
}
pdn = pdn->plsdnNext;
}
}
// %%Function: DestroyDisplayTree
// %%Contact: victork
//
/*
* DestroyDisplayTree nulls plsdnUpTemp in sublines displayed with given subline.
*/
void DestroyDisplayTree(PLSSUBL plssubl)
{
PLSDNODE pdn = plssubl->plsdnFirst;
DWORD i;
while (pdn != NULL) /* don't care about break */
{
if (FIsAcceptingDnode(pdn))
{
for (i = 0; i < pdn->u.real.pinfosubl->cSubline; i++)
{
((pdn->u.real.pinfosubl->rgpsubl)[i])->plsdnUpTemp = NULL;
((pdn->u.real.pinfosubl->rgpsubl)[i])->fAcceptedForDisplay = fFalse;
DestroyDisplayTree((pdn->u.real.pinfosubl->rgpsubl)[i]);
}
}
pdn = pdn->plsdnNext;
}
}
// %%Function: AdvanceToNextDnode
// %%Contact: victork
//
/*
* Advance to the next (visual) node and update pen position, skipping submitting dnodes.
*/
PLSDNODE AdvanceToNextDnode(PLSDNODE pdn, LSTFLOW lstflowMain, POINTUV* pptpen)
{
// move to the next
pdn = AdvanceToNextVisualDnodeCore(pdn, lstflowMain, pptpen);
// skip submitting dnodes
while (pdn != NULL && FIsAcceptingDnode(pdn))
{
pdn = AdvanceToNextVisualDnodeCore(pdn, lstflowMain, pptpen);
}
return pdn;
}
// %%Function: AdvanceToFirstDnode
// %%Contact: victork
//
PLSDNODE AdvanceToFirstDnode(PLSSUBL plssubl, LSTFLOW lstflowMain, POINTUV* pptpen)
{
PLSDNODE pdn = plssubl->plsdnFirst;
if (pdn != NULL && FIsAcceptingDnode(pdn))
{
pdn = AdvanceToNextDnode(pdn, lstflowMain, pptpen);
}
return pdn;
}
// %%Function: AdvanceToNextSubmittingDnode
// %%Contact: victork
//
/*
* Advance to the next (visual) node and update pen position, stopping only at submitting dnodes.
*/
PLSDNODE AdvanceToNextSubmittingDnode(PLSDNODE pdn, LSTFLOW lstflowMain, POINTUV* pptpen)
{
// move to the next
pdn = AdvanceToNextVisualDnodeCore(pdn, lstflowMain, pptpen);
// skip non-submitting dnodes
while (pdn != NULL && !FIsAcceptingDnode(pdn))
{
pdn = AdvanceToNextVisualDnodeCore(pdn, lstflowMain, pptpen);
}
return pdn;
}
// %%Function: AdvanceToFirstSubmittingDnode
// %%Contact: victork
//
PLSDNODE AdvanceToFirstSubmittingDnode(PLSSUBL plssubl, LSTFLOW lstflowMain, POINTUV* pptpen)
{
PLSDNODE pdn = plssubl->plsdnFirst;
if (pdn != NULL && !FIsAcceptingDnode(pdn))
{
pdn = AdvanceToNextSubmittingDnode(pdn, lstflowMain, pptpen);
}
return pdn;
}
// %%Function: AdvanceToNextVisualDnodeCore
// %%Contact: victork
//
/*
* Advance to the next node and update pen position
* Goes into sublines, submitted for display, traversing the whole display tree.
* Stops at dnodes that submitted subline on the way down, skips them going up, so that
* every dnode is visited once with pen position at the start of it in visual order.
*/
static PLSDNODE AdvanceToNextVisualDnodeCore(PLSDNODE pdn, LSTFLOW lstflowMain, POINTUV* pptpen)
{
PLSDNODE pdnNextVisual, pdnTop;
PLSSUBL plssublCurrent;
long cSublines, i;
PLSSUBL* rgpsubl;
if (FIsAcceptingDnode(pdn))
{
// Last time we stopped at submitting dnode -
// now don't move pen point, go down to the VisualStart of the VisualFirst subline.
rgpsubl = pdn->u.real.pinfosubl->rgpsubl;
cSublines = pdn->u.real.pinfosubl->cSubline;
if (rgpsubl[0]->lstflow == lstflowMain)
{
pdnNextVisual = rgpsubl[0]->plsdnFirst;
}
else
{
pdnNextVisual = rgpsubl[cSublines - 1]->plsdnLastDisplay;
}
}
else
{
// update pen position - we always move to the (visual) right, all vs are the same tflow
if (pdn->klsdn == klsdnReal)
{
pptpen->u += pdn->u.real.dup;
}
else
{
pptpen->u += pdn->u.pen.dup;
pptpen->v += pdn->u.pen.dvp;
}
plssublCurrent = pdn->plssubl;
// go to the next dnode of the current subline in visual order
pdnNextVisual = NextVisualDnodeOnTheLevel(pdn, lstflowMain);
// If current subline is ended, (try) change subline.
if (pdnNextVisual == NULL)
{
// Change subline
//
// In the loop: pdnNextVisual != NULL signals that next dnode is successfully found.
// If pdnNextVisual == NULL, plssublCurrent is the subline just exhausted.
// One run of the loop replaces current subline with another subline on the same level
// (such change always ends the loop) or with parent subline.
while (pdnNextVisual == NULL && plssublCurrent->plsdnUpTemp != NULL)
{
// find (the index of) the current subline in the list of submitted sublines
pdnTop = plssublCurrent->plsdnUpTemp;
rgpsubl = pdnTop->u.real.pinfosubl->rgpsubl;
cSublines = pdnTop->u.real.pinfosubl->cSubline;
for (i=0; i < cSublines && plssublCurrent != rgpsubl[i]; i++);
Assert(i < cSublines);
// do we have "next" subline? If we do, pdnNextVisual we seek "starts" it.
if (pdnTop->plssubl->lstflow == lstflowMain)
{
i++;
if (i < cSublines)
{
plssublCurrent = rgpsubl[i];
pdnNextVisual = plssublCurrent->plsdnFirst;
}
}
else
{
i--;
if (i >= 0)
{
plssublCurrent = rgpsubl[i];
pdnNextVisual = plssublCurrent->plsdnLastDisplay;
}
}
// We don't, let's try next dnode on the upper level.
if (pdnNextVisual == NULL)
{
plssublCurrent = pdnTop->plssubl;
pdnNextVisual = NextVisualDnodeOnTheLevel(pdnTop, lstflowMain);
}
}
}
}
return pdnNextVisual;
}
// %%Function: NextVisualDnodeOnTheLevel
// %%Contact: victork
//
// find next dnode on the level moving right or left, signalling end with a NULL
static PLSDNODE NextVisualDnodeOnTheLevel(PLSDNODE pdn, LSTFLOW lstflowMain)
{
if (pdn->plssubl->lstflow == lstflowMain)
{
if (pdn == pdn->plssubl->plsdnLastDisplay)
{
return NULL;
}
else
{
return pdn->plsdnNext;
}
}
return pdn->plsdnPrev;
}
// %%Function: AddSublineAdvanceWidth
// %%Contact: victork
//
// Note: It is not subline width as calculated in GetObjDimSubline
static long AddSublineAdvanceWidth(PLSSUBL plssubl)
{
long dupSum;
PLSDNODE pdn;
pdn = plssubl->plsdnFirst;
dupSum = 0;
while (pdn != NULL)
{
if (pdn->klsdn == klsdnReal)
{
dupSum += pdn->u.real.dup;
}
else /* pen, border */
{
dupSum += pdn->u.pen.dup;
}
if (pdn == plssubl->plsdnLastDisplay)
{
pdn = NULL;
}
else
{
pdn = pdn->plsdnNext;
Assert(pdn != NULL); // plsdnLastDisplay should prevent this
}
}
return dupSum;
}
// NB Victork - following functions were used only for upClipLeft, upClipRight optimization.
// If we'll decide that we do need that optimization after Word integration - I'll uncomment.
#ifdef NEVER
// %%Function: RectUVFromRectXY
// %%Contact: victork
//
// There is an assymetry in the definition of the rectangle.
// (Left, Top) belongs to rectangle and (Right, Bottom) doesn't,
// It makes following procedures hard to understand and write.
// So I first cut off the points that don't belong, then turn the rectangle, then add extra
// points again and hope compiler will make it fast.
// RectUVFromRectXY calculates (clip) rectangle in local (u,v) coordinates given
// (clip) rectangle in (x,y) and point of origin
void RectUVFromRectXY(const POINT* pptXY, /* IN: point of origin for local coordinates (x,y) */
const RECT* prectXY, /* IN: input rectangle (x,y) */
LSTFLOW lstflow, /* IN: local text flow */
RECTUV* prectUV) /* OUT: output rectangle (u,v) */
{
switch (lstflow)
{
case lstflowES: /* latin */
prectUV->upLeft = (prectXY->left - pptXY->x);
prectUV->upRight = (prectXY->right - 1 - pptXY->x) + 1;
prectUV->vpTop = -(prectXY->top - pptXY->y);
prectUV->vpBottom = -(prectXY->bottom - 1 - pptXY->y) - 1;
return;
case lstflowSW: /* vertical FE */
prectUV->upLeft = (prectXY->top - pptXY->y);
prectUV->upRight = (prectXY->bottom - 1 - pptXY->y) + 1;
prectUV->vpTop = (prectXY->right - 1 - pptXY->x);
prectUV->vpBottom = (prectXY->left - pptXY->x) - 1;
return;
case lstflowWS: /* BiDi */
prectUV->upLeft = -(prectXY->right - 1 - pptXY->x);
prectUV->upRight = -(prectXY->left - pptXY->x) + 1;
prectUV->vpTop = -(prectXY->top - pptXY->y);
prectUV->vpBottom = -(prectXY->bottom - 1 - pptXY->y) - 1;
return;
case lstflowEN:
prectUV->upLeft = (prectXY->left - pptXY->x);
prectUV->upRight = (prectXY->right - 1 - pptXY->x) + 1;
prectUV->vpTop = (prectXY->bottom - 1 - pptXY->y);
prectUV->vpBottom = (prectXY->top - pptXY->y) - 1;
return;
case lstflowSE:
prectUV->upLeft = (prectXY->top - pptXY->y);
prectUV->upRight = (prectXY->bottom - 1 - pptXY->y) + 1;
prectUV->vpTop = -(prectXY->left - pptXY->x);
prectUV->vpBottom = -(prectXY->right - 1 - pptXY->x) - 1;
return;
case lstflowWN:
prectUV->upLeft = -(prectXY->right - 1 - pptXY->x);
prectUV->upRight = -(prectXY->left - pptXY->x) + 1;
prectUV->vpTop = (prectXY->bottom - 1 - pptXY->y);
prectUV->vpBottom = (prectXY->top - pptXY->y) - 1;
return;
case lstflowNE:
prectUV->upLeft = -(prectXY->bottom - 1 - pptXY->y);
prectUV->upRight = -(prectXY->top - pptXY->y) + 1;
prectUV->vpTop = -(prectXY->left - pptXY->x);
prectUV->vpBottom = -(prectXY->right - 1 - pptXY->x) - 1;
return;
case lstflowNW:
prectUV->upLeft = -(prectXY->bottom - 1 - pptXY->y);
prectUV->upRight = -(prectXY->top - pptXY->y) + 1;
prectUV->vpTop = (prectXY->right - 1 - pptXY->x);
prectUV->vpBottom = (prectXY->left - pptXY->x) - 1;
return;
default:
NotReached();
}
}
// %%Function: RectXYFromRectUV
// %%Contact: victork
//
// RectXYFromRectUV calculates rectangle in (x,y) coordinates given rectangle in local (u,v)
// and point of origin (x,y) for local coordinate system
void RectXYFromRectUV(const POINT* pptXY, /* IN: point of origin for local coordinates (x,y) */
PCRECTUV prectUV, /* IN: input rectangle (u,v) */
LSTFLOW lstflow, /* IN: local text flow */
RECT* prectXY) /* OUT: output rectangle (x,y) */
{
switch (lstflow)
{
case lstflowES: /* latin */
prectXY->left = pptXY->x + prectUV->upLeft;
prectXY->right = pptXY->x + (prectUV->upRight - 1) + 1;
prectXY->top = pptXY->y - (prectUV->vpTop);
prectXY->bottom = pptXY->y - (prectUV->vpBottom + 1) + 1;
return;
case lstflowSW: /* vertical FE */
prectXY->left = pptXY->x + (prectUV->vpBottom + 1);
prectXY->right = pptXY->x + (prectUV->vpTop) + 1;
prectXY->top = pptXY->y + prectUV->upLeft;
prectXY->bottom = pptXY->y + (prectUV->upRight - 1) + 1;
return;
case lstflowWS: /* BiDi */
prectXY->left = pptXY->x - (prectUV->upRight - 1);
prectXY->right = pptXY->x - prectUV->upLeft + 1;
prectXY->top = pptXY->y - (prectUV->vpTop);
prectXY->bottom = pptXY->y - (prectUV->vpBottom + 1) + 1;
return;
case lstflowEN:
prectXY->left = pptXY->x + prectUV->upLeft;
prectXY->right = pptXY->x + (prectUV->upRight - 1) + 1;
prectXY->top = pptXY->y + (prectUV->vpBottom + 1);
prectXY->bottom = pptXY->y + (prectUV->vpTop) + 1;
return;
case lstflowSE:
prectXY->left = pptXY->x - (prectUV->vpTop);
prectXY->right = pptXY->x - (prectUV->vpBottom + 1) + 1;
prectXY->top = pptXY->y + prectUV->upLeft;
prectXY->bottom = pptXY->y + (prectUV->upRight - 1) + 1;
return;
case lstflowWN:
prectXY->left = pptXY->x - (prectUV->upRight - 1);
prectXY->right = pptXY->x - prectUV->upLeft + 1;
prectXY->top = pptXY->y + (prectUV->vpBottom + 1);
prectXY->bottom = pptXY->y + (prectUV->vpTop) + 1;
return;
case lstflowNE:
prectXY->left = pptXY->x - (prectUV->vpTop);
prectXY->right = pptXY->x - (prectUV->vpBottom + 1) + 1;
prectXY->top = pptXY->y - (prectUV->upRight - 1);
prectXY->bottom = pptXY->y - prectUV->upLeft + 1;
return;
case lstflowNW:
prectXY->left = pptXY->x + (prectUV->vpBottom + 1);
prectXY->right = pptXY->x + (prectUV->vpTop) + 1;
prectXY->top = pptXY->y - (prectUV->upRight - 1);
prectXY->bottom = pptXY->y - prectUV->upLeft + 1;
return;
default:
NotReached();
}
}
#endif /* NEVER */