windows-nt/Source/XPSP1/NT/windows/richedit/lssrc/tabutils.c

727 lines
20 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
#include "lsidefs.h"
#include "dninfo.h"
#include "tabutils.h"
#include "lstbcon.h"
#include "plstbcon.h"
#include "lstabs.h"
#include "lsktab.h"
#include "lscaltbd.h"
#include "lstext.h"
#include "zqfromza.h"
#include "iobj.h"
#include "chnutils.h"
#include "lscbk.h"
#include "limqmem.h"
#include "posichnk.h"
#include <limits.h>
static LSERR FillTabsContext(PLSTABSCONTEXT, LSTFLOW);
static LSERR ItbdMergeTabs(PLSTABSCONTEXT plstabscontext, LSTFLOW lstflow, const LSTABS* plstabs,
BOOL fHangingTab,
long duaHangingTab,
WCHAR wchHangingTabLeader, DWORD* picaltbdMac);
static LSERR FindTab (PLSTABSCONTEXT plstabscontext, long urPen, BOOL fUseHangingTabAsDefault,
BOOL fZeroWidthUserTab,
DWORD* picaltbd, BOOL* pfBreakThroughTab);
static LSERR IncreaseTabsArray(PLSTABSCONTEXT plstabscontext, DWORD ccaltbdMaxNew);
/* G E T C U R T A B I N F O C O R E*/
/*----------------------------------------------------------------------------
%%Function: GetCurTabInfoCore
%%Contact: igorzv
Parameters:
plstabscontext - (IN) pointer to tabs context
plsdnTab - (IN) dnode with tab
urTab - (IN) position before this tab
fResolveAllTabsAsLeft(IN) switch all other tab to left
plsktab - (OUT) type of current tab
pfBreakThroughTab- (OUT)is break through tab occurs
Provides information about nearest tab stop
----------------------------------------------------------------------------*/
LSERR GetCurTabInfoCore(PLSTABSCONTEXT plstabscontext, PLSDNODE plsdnTab,
long urTab, BOOL fResolveAllTabsAsLeft,
LSKTAB* plsktab, BOOL* pfBreakThroughTab)
{
LSERR lserr;
LSCALTBD* plscaltbd;
DWORD icaltbd;
long durTab;
BOOL fUseHangingTabAsDefault = fFalse;
BOOL fZeroWidthUserTab = fFalse;
long urToFindTabStop;
Assert(plsktab != NULL);
Assert(FIsLSDNODE(plsdnTab));
Assert(plsdnTab->fTab);
Assert(FIsDnodeReal(plsdnTab));
// Assert(plstabscontext->plsdnPendingTab == NULL);
/* first tab on line */
if (!plstabscontext->fTabsInitialized)
{
lserr = FillTabsContext(plstabscontext, LstflowFromDnode(plsdnTab));
if (lserr != lserrNone)
return lserr;
}
urToFindTabStop = urTab;
if (plstabscontext->fResolveTabsAsWord97 ) /* such strange behaviour was in Word97 */
{
if (plsdnTab->fTabForAutonumber)
{
fZeroWidthUserTab = fTrue;
fUseHangingTabAsDefault = fTrue;
}
else
{
urToFindTabStop++;
}
}
/* find tab in tabs table */
lserr = FindTab(plstabscontext, urToFindTabStop, fUseHangingTabAsDefault,
fZeroWidthUserTab, &icaltbd, pfBreakThroughTab);
if (lserr != lserrNone)
return lserr;
plsdnTab->icaltbd = icaltbd;
plscaltbd = &(plstabscontext->pcaltbd[icaltbd]);
/* ask text to set tab leader in his structures */
if (plscaltbd->wchTabLeader != 0)
{
lserr = SetTabLeader(plsdnTab->u.real.pdobj, plscaltbd->wchTabLeader);
if (lserr != lserrNone)
return lserr;
}
*plsktab = plscaltbd->lskt;
if (fResolveAllTabsAsLeft)
*plsktab = lsktLeft;
/* offset calculation for left tab , register pending tab for all others */
switch (*plsktab)
{
default:
NotReached();
break;
case lsktLeft:
durTab = plscaltbd->ur - urTab;
Assert(durTab >= 0);
SetDnodeDurFmt(plsdnTab, durTab);
break;
case lsktRight:
case lsktCenter:
case lsktDecimal:
case lsktChar:
plstabscontext->plsdnPendingTab = plsdnTab;
plstabscontext->urBeforePendingTab = urTab;
break;
}
return lserrNone;
}
/* R E S O L V E P R E V T A B C O R E*/
/*----------------------------------------------------------------------------
%%Function: ResolvePrevTabCore
%%Contact: igorzv
Parameters:
plstabscontext - (IN) pointer to tabs context
plsdnCurrent - (IN) current dnode
urCurrentPen - (IN) current pen position
pdurPendingTab - (OUT)offset because of pending tab
Resolves if exists previous pending tab (right, decimal or center)
----------------------------------------------------------------------------*/
LSERR ResolvePrevTabCore(PLSTABSCONTEXT plstabscontext, PLSDNODE plsdnCurrent,
long urCurrentPen, long* pdurPendingTab)
{
PLSDNODE plsdnPendingTab;
LSCALTBD* plscaltbd;
long urTabStop, durTab, durSeg;
PLSDNODE plsdn;
long durTrail;
GRCHUNKEXT grchnkext;
DWORD index;
long durToDecimalPoint;
LSERR lserr;
PLSCHUNKCONTEXT plschunkcontext;
DWORD cchTrail;
PLSDNODE plsdnStartTrail;
LSDCP dcpStartTrailingText;
PLSDNODE plsdnTrailingObject;
LSDCP dcpTrailingObject;
int cDnodesTrailing;
BOOL fClosingBorderStartsTrailing;
PLSDNODE plsdnDecimalPoint;
*pdurPendingTab = 0;
plsdnPendingTab = plstabscontext->plsdnPendingTab;
if (plsdnPendingTab == NULL ||
plsdnPendingTab->cpFirst >= plsdnCurrent->cpFirst)
/* this second condition can happen after break when because of increased margin
we fetch pending tab but later break was found before */
{
/* no business for us */
return lserrNone;
}
/* pending in an other subline */
if (SublineFromDnode(plsdnCurrent) != SublineFromDnode(plsdnPendingTab))
{
/* cancell pending tab */
CancelPendingTab(plstabscontext);
return lserrNone;
}
Assert(FIsLSDNODE(plsdnCurrent));
plschunkcontext = PlschunkcontextFromSubline(SublineFromDnode(plsdnCurrent));
Assert(plstabscontext->fTabsInitialized);
Assert(FIsLSDNODE(plsdnPendingTab));
Assert(plsdnPendingTab->fTab);
Assert(FIsDnodeReal(plsdnPendingTab));
plscaltbd = &(plstabscontext->pcaltbd[plsdnPendingTab->icaltbd]);
urTabStop = plscaltbd->ur;
durSeg = urCurrentPen - plstabscontext->urBeforePendingTab;
/* find durTrail */
/* collect last chunk */
plsdn = plsdnCurrent;
/* If we resolve pending tab because of other tab we should
use previous dnode to calculate correct group chunk . We also must
be careful keeping in mind that line can be stopped right after pending tab */
if ((plsdn->fTab && plsdn != plsdnPendingTab))
plsdn = plsdn->plsdnPrev;
Assert(FIsLSDNODE(plsdn));
Assert(!FIsNotInContent(plsdn));
lserr = GetTrailingInfoForTextGroupChunk(plsdn,
plsdn->dcp, IdObjFromDnode(plsdnPendingTab),
&durTrail, &cchTrail, &plsdnStartTrail,
&dcpStartTrailingText, &cDnodesTrailing,
&plsdnTrailingObject, &dcpTrailingObject, &fClosingBorderStartsTrailing);
if (lserr != lserrNone)
return lserr;
switch (plscaltbd->lskt)
{
default:
case lsktLeft:
NotReached();
break;
case lsktRight:
case lsktCenter:
durSeg -= durTrail;
if (plscaltbd->lskt == lsktCenter)
durSeg /= 2;
break;
case lsktDecimal:
case lsktChar:
InitGroupChunkExt(plschunkcontext, IdObjFromDnode(plsdnPendingTab), &grchnkext);
plsdn = plsdnPendingTab->plsdnNext;
Assert(FIsLSDNODE(plsdn));
lserr = CollectTextGroupChunk(plsdn, plsdnCurrent->cpLimOriginal,
CollectSublinesForDecimalTab, &grchnkext);
if (lserr != lserrNone)
return lserr;
if (grchnkext.plsdnLastUsed == NULL)
{
/* there are now dnodes between tabs */
durSeg = 0;
}
else
{
if (grchnkext.lsgrchnk.clsgrchnk > 0)
{
if (plscaltbd->lskt == lsktDecimal)
{
lserr = LsGetDecimalPoint(&(grchnkext.lsgrchnk), lsdevReference,
&index, &durToDecimalPoint);
if (lserr != lserrNone)
return lserr;
}
else
{
Assert(plscaltbd->lskt == lsktChar);
lserr = LsGetCharTab(&(grchnkext.lsgrchnk), plscaltbd->wchCharTab, lsdevReference,
&index, &durToDecimalPoint);
if (lserr != lserrNone)
return lserr;
}
}
else
{
index = idobjOutside;
durToDecimalPoint = 0;
}
if (index != idobjOutside) /* decimal point has been found */
{
plsdnDecimalPoint = grchnkext.plschunkcontext->pplsdnChunk[index];
}
else
{
/* we allign end of the last logical cp to the tab stop */
plsdnDecimalPoint = grchnkext.plsdnLastUsed;
durToDecimalPoint = DurFromDnode(plsdnDecimalPoint);
}
FindPointOffset(plsdn, lsdevReference, LstflowFromDnode(plsdn),
CollectSublinesForDecimalTab,
plsdnDecimalPoint,
durToDecimalPoint, &durSeg);
}
break;
}
durTab = urTabStop - plstabscontext->urBeforePendingTab - durSeg;
if (urTabStop < plstabscontext->urColumnMax &&
(durTab + urCurrentPen - durTrail > plstabscontext->urColumnMax))
{
/* this code is for compatibility with Word: when we are not in a situation
of break through tab we dont allow line to leap right margin after we resolve
pending tab */
durTab = plstabscontext->urColumnMax - urCurrentPen + durTrail;
}
if (durTab > 0)
{
SetDnodeDurFmt(plsdnPendingTab, durTab);
*pdurPendingTab = durTab;
}
plstabscontext->plsdnPendingTab = NULL;
return lserrNone;
}
/* F I L L T A B S C O N T E X T*/
/*----------------------------------------------------------------------------
%%Function: FillTabsContext
%%Contact: igorzv
Parameters:
plstabscontext - (IN) pointer to tabs context
lstflow - (IN) text flow of the line
Initializes tabs context using clients callback FetchTabs
----------------------------------------------------------------------------*/
LSERR FillTabsContext(PLSTABSCONTEXT plstabscontext, LSTFLOW lstflow)
{
LSTABS lstabs;
BOOL fHangingTab;
long uaHangingTab;
WCHAR wchHangingTabLeader;
LSERR lserr;
lserr = plstabscontext->plscbk->pfnFetchTabs(plstabscontext->pols, plstabscontext->cpInPara,
&lstabs, &fHangingTab, &uaHangingTab, &wchHangingTabLeader);
if (lserr != lserrNone)
return lserr;
plstabscontext->durIncrementalTab = UrFromUa(lstflow, &(plstabscontext->plsdocinf->lsdevres), lstabs.duaIncrementalTab);
/* Copy tabs from LSTABS to rgcaltbd[], inserting a hanging tab if
* required.
*/
if (fHangingTab || lstabs.iTabUserDefMac > 0)
{
lserr = ItbdMergeTabs(plstabscontext, lstflow,
&lstabs, fHangingTab,
uaHangingTab, wchHangingTabLeader, &plstabscontext->icaltbdMac);
if (lserr != lserrNone)
return lserr;
}
else
{
plstabscontext->icaltbdMac = 0;
}
plstabscontext->fTabsInitialized = fTrue;
return lserrNone;
}
/* I N I T T A B S C O N T E X T F O R A U T O D E C I M A L T A B*/
/*----------------------------------------------------------------------------
%%Function: InitTabsContextForAutoDecimalTab
%%Contact: igorzv
Parameters:
plstabscontext - (IN) pointer to tabs context
durAutoDecimalTab- (IN) tab stop for autodecimal tab
Creates tabs context that consists of one tab stop - auto decimal
----------------------------------------------------------------------------*/
LSERR InitTabsContextForAutoDecimalTab(PLSTABSCONTEXT plstabscontext, long durAutoDecimalTab)
{
LSCALTBD* pcaltbd;
pcaltbd = plstabscontext->pcaltbd;
Assert(plstabscontext->ccaltbdMax >= 1);
if (!plstabscontext->fTabsInitialized)
{
plstabscontext->icaltbdMac = 1;
pcaltbd->lskt = lsktDecimal;
pcaltbd->ur = durAutoDecimalTab;
pcaltbd->wchTabLeader = 0;
plstabscontext->fTabsInitialized = fTrue;
}
else
{
/* tab is already there because of autonumber */
Assert(plstabscontext->icaltbdMac == 1);
Assert(pcaltbd->lskt == lsktDecimal);
Assert(pcaltbd->ur == durAutoDecimalTab);
}
return lserrNone;
}
/* I T B D M E R G E T A B S */
/*----------------------------------------------------------------------------
%%Function: ItbdMergeTabs
%%Contact: igorzv
Parameters:
plstabscontext - (IN) pointer to tabs context
lstflow - (IN) text flow of the line
plstabs - (IN) tabs array provided by client
fHangingTab - (IN) does paragraph have hanging tab
uaHangingTab - (IN) position of hanging tab
wchHangingTabLeader-(IN) leader for hanging tab
picaltbdMac (OUT) amount of tabs in array
Copies tabs from LSPAP into ptbd[], inserting a hanging tab where
required.
----------------------------------------------------------------------------*/
static LSERR ItbdMergeTabs(PLSTABSCONTEXT plstabscontext, LSTFLOW lstflow,
const LSTABS* plstabs, BOOL fHangingTab,
long uaHangingTab, WCHAR wchHangingTabLeader, DWORD* picaltbdMac)
{
long uaPrevTab, uaCurrentTab;
DWORD itbdOut, itbdIn, itbdLimIn;
LSCALTBD* plscaltbd;
DWORD ccaltbdMax;
LSERR lserr;
/* check that have enough room*/
ccaltbdMax = plstabs->iTabUserDefMac;
if (fHangingTab)
ccaltbdMax++;
if (ccaltbdMax >= plstabscontext->ccaltbdMax)
{
lserr = IncreaseTabsArray(plstabscontext, ccaltbdMax + limCaltbd);
if (lserr != lserrNone)
return lserr;
}
plscaltbd = plstabscontext->pcaltbd;
itbdLimIn = plstabs->iTabUserDefMac;
uaPrevTab = LONG_MAX;
itbdOut = 0;
if (fHangingTab)
{
/* If no user tabs, or hanging tab is before 0th user tab,
* make hanging tab the 0th member of ptbd[].
*/
if (itbdLimIn == 0 || uaHangingTab < plstabs->pTab[0].ua)
{
plscaltbd[0].lskt = lsktLeft;
plscaltbd[0].ur = UrFromUa(lstflow,
&(plstabscontext->plsdocinf->lsdevres), uaHangingTab);
plscaltbd[0].wchTabLeader = wchHangingTabLeader;
plscaltbd[0].fDefault = fFalse;
plscaltbd[0].fHangingTab = fTrue;
uaPrevTab = uaHangingTab;
itbdOut = 1;
}
}
else
{
uaHangingTab = LONG_MAX;
}
/* Copy user defined tabs, checking each time for hanging tab.
*/
for (itbdIn = 0; itbdIn < itbdLimIn; itbdOut++, itbdIn++)
{
uaCurrentTab = plstabs->pTab[itbdIn].ua;
/* If hanging tab is between previous tab and this tab,
* insert hanging tab into ptbd[] here and revisit "this"
* tab again during next loop iteration.
*/
if (uaPrevTab < uaHangingTab && uaHangingTab < uaCurrentTab)
{
plscaltbd[itbdOut].lskt = lsktLeft;
plscaltbd[itbdOut].ur = UrFromUa(lstflow, &(plstabscontext->plsdocinf->lsdevres),
uaHangingTab);
plscaltbd[itbdOut].wchTabLeader = wchHangingTabLeader;
plscaltbd[itbdOut].fDefault = fFalse;
plscaltbd[itbdOut].fHangingTab = fTrue;
itbdIn -= 1;
uaPrevTab = uaHangingTab;
}
else
{
plscaltbd[itbdOut].lskt = plstabs->pTab[itbdIn].lskt;
plscaltbd[itbdOut].ur = UrFromUa(lstflow, &(plstabscontext->plsdocinf->lsdevres),
plstabs->pTab[itbdIn].ua);
plscaltbd[itbdOut].wchTabLeader = plstabs->pTab[itbdIn].wchTabLeader;
plscaltbd[itbdOut].wchCharTab = plstabs->pTab[itbdIn].wchCharTab;
plscaltbd[itbdOut].fDefault = fFalse;
plscaltbd[itbdOut].fHangingTab = fFalse;
uaPrevTab = uaCurrentTab;
}
}
/* If hanging tab is after final user tab, make hanging tab the
* final member of ptbd[]
*/
if (uaPrevTab < uaHangingTab && uaHangingTab < LONG_MAX)
{
plscaltbd[itbdOut].lskt = lsktLeft;
plscaltbd[itbdOut].ur = UrFromUa(lstflow,
&(plstabscontext->plsdocinf->lsdevres), uaHangingTab);
plscaltbd[itbdOut].wchTabLeader = wchHangingTabLeader;
plscaltbd[itbdOut].fDefault = fFalse;
plscaltbd[itbdOut].fHangingTab = fTrue;
itbdOut += 1;
}
*picaltbdMac = itbdOut;
return lserrNone;
}
/* F I N D T A B*/
/*----------------------------------------------------------------------------
%%Function: FindTab
%%Contact: igorzv
Parameters:
plstabscontext - (IN) pointer to tabs context
urPen - (IN) position before this tab
fUseHangingTabAsDefault - (IN) usually hanging tab is used as user defined tab,
but in one case for compstibility with Word97 it's treated as
user default tab
fZeroWidthUserTab- (IN) for compatibility with Word97
picaltbd - (OUT)pointer to a record describing tab stop
pfBreakThroughTab- (OUT)is break through tab occurs
Procedure findes fist tab stop after current pen position. In the case when
it is a default tab stop program adds record to an array of tab stops.
This procedure also resolves breakthrouhtab logic.
----------------------------------------------------------------------------*/
static LSERR FindTab (PLSTABSCONTEXT plstabscontext, long urPen, BOOL fUseHangingTabAsDefault,
BOOL fZeroWidthUserTab,
DWORD* picaltbd, BOOL* pfBreakThroughTab)
{
DWORD icaltbdMac = plstabscontext->icaltbdMac;
LSCALTBD* pcaltbd = plstabscontext->pcaltbd;
long durIncTab, durDelta;
DWORD i;
LSERR lserr;
int iHangingTab = -1;
long urDefaultTab;
long urPenForUserTab = urPen;
*pfBreakThroughTab = fFalse;
if (fZeroWidthUserTab)
urPenForUserTab--;
for (i = 0; i < icaltbdMac &&
(urPenForUserTab >= (pcaltbd[i].ur) /* if fUseHangingTabAsDefault we skip it */
|| (fUseHangingTabAsDefault && pcaltbd[i].fHangingTab));
i++)
{
if (fUseHangingTabAsDefault && pcaltbd[i].fHangingTab)
iHangingTab = i;
}
if (i == icaltbdMac)
{
/* We deleted strange calculation of tab stop which was there due to compatibility with
Word97. Compatibility we are solving when calling this procedure */
durIncTab = plstabscontext->durIncrementalTab;
if (durIncTab == 0)
durIncTab = 1;
durDelta = durIncTab;
if (urPen < 0)
durDelta = 0;
urDefaultTab = ((urPen + durDelta) / durIncTab) * durIncTab;
if (fUseHangingTabAsDefault && iHangingTab != -1 &&
pcaltbd[iHangingTab].ur > urPen &&
pcaltbd[iHangingTab].ur <= urDefaultTab)
{
/* in this case hanging tab is the nearesr default tab */
i = iHangingTab;
}
else
{
icaltbdMac++;
if (icaltbdMac >= plstabscontext->ccaltbdMax)
{
lserr = IncreaseTabsArray(plstabscontext, 0);
if (lserr != lserrNone)
return lserr;
pcaltbd = plstabscontext->pcaltbd;
}
plstabscontext->icaltbdMac = icaltbdMac;
pcaltbd[i].lskt = lsktLeft;
pcaltbd[i].wchTabLeader = 0; /* REVIEW (igorzv) do we need wchSpace as tab leader in this case */
pcaltbd[i].fDefault = fTrue;
pcaltbd[i].fHangingTab = fFalse;
pcaltbd[i].ur = urDefaultTab;
}
}
else
{
if (urPen < plstabscontext->urColumnMax &&
pcaltbd[i].ur >= plstabscontext->urColumnMax)
/* tab we found is user defined behind right margin */
/* it is important to check also that we are not already behind right margin,
opposite can happens because of exceeded right margin */
{
*pfBreakThroughTab = fTrue;
}
}
*picaltbd = i;
return lserrNone;
}
/* I N C R E A S E T A B S A R R A Y*/
/*----------------------------------------------------------------------------
%%Function: IncreaseTabsArray
%%Contact: igorzv
Parameters:
plsc - (IN) ptr to line services context
ccaltbdMaxNew - (IN) new value for array size if 0 then add limCaltbd
Relocate tabs array and set new values in context
----------------------------------------------------------------------------*/
static LSERR IncreaseTabsArray(PLSTABSCONTEXT plstabscontext, DWORD ccaltbdMaxNew)
{
DWORD ccaltbdMax;
if (ccaltbdMaxNew > 0)
ccaltbdMax = ccaltbdMaxNew;
else
ccaltbdMax = plstabscontext->ccaltbdMax + limCaltbd;
/* create new array for tabs */
plstabscontext->pcaltbd = plstabscontext->plscbk->pfnReallocPtr(plstabscontext->pols,
plstabscontext->pcaltbd,
sizeof(LSCALTBD)*ccaltbdMax);
if (plstabscontext->pcaltbd == NULL )
return lserrOutOfMemory;
plstabscontext->ccaltbdMax = ccaltbdMax;
return lserrNone;
}
/* G E T M A R G I N A F T E R B R E A K T H R O U G H T A B*/
/*----------------------------------------------------------------------------
%%Function: GetMarginAfterBreakThroughTab
%%Contact: igorzv
Parameters:
plsc - (IN) ptr to line services context
plsdnTab - (IN) tab which triggered breakthrough tab
purNewMargin - (OUT) new margin because of breakthrough tab
----------------------------------------------------------------------------*/
LSERR GetMarginAfterBreakThroughTab(PLSTABSCONTEXT plstabscontext,
PLSDNODE plsdnTab, long* purNewMargin)
{
LSERR lserr;
long uaNewMargin;
lserr = plstabscontext->plscbk->pfnGetBreakThroughTab(plstabscontext->pols,
UaFromUr(LstflowFromDnode(plsdnTab), &(plstabscontext->plsdocinf->lsdevres),
plstabscontext->urColumnMax),
UaFromUr(LstflowFromDnode(plsdnTab), &(plstabscontext->plsdocinf->lsdevres),
plstabscontext->pcaltbd[plsdnTab->icaltbd].ur),
&uaNewMargin);
if (lserr != lserrNone)
return lserr;
*purNewMargin = UrFromUa(LstflowFromDnode(plsdnTab), &(plstabscontext->plsdocinf->lsdevres),
uaNewMargin);
return lserrNone;
}