You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
483 lines
14 KiB
483 lines
14 KiB
//
|
|
// normal.cpp
|
|
//
|
|
|
|
#include "private.h"
|
|
#include "normal.h"
|
|
#include "txtcache.h"
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// GetTextComplete
|
|
//
|
|
// Wrapper for GetText that keeps asking until the input buffers are full.
|
|
//----------------------------------------------------------------------------
|
|
|
|
HRESULT GetTextComplete(ITextStoreACP *ptsi, LONG acpStart, LONG acpEnd,
|
|
WCHAR *pchPlain, ULONG cchPlainReq,
|
|
ULONG *pcchPlainOut, TS_RUNINFO *prgRunInfo, ULONG ulRunInfoReq, ULONG *pulRunInfoOut,
|
|
LONG *pacpNext)
|
|
{
|
|
ULONG cchPlainOut;
|
|
ULONG ulRunInfoOut;
|
|
BOOL fNoMoreSpace;
|
|
HRESULT hr;
|
|
|
|
fNoMoreSpace = FALSE;
|
|
|
|
*pcchPlainOut = 0;
|
|
*pulRunInfoOut = 0;
|
|
|
|
while (TRUE)
|
|
{
|
|
Perf_IncCounter(PERF_NORM_GETTEXTCOMPLETE);
|
|
|
|
hr = CProcessTextCache::GetText(ptsi, acpStart, acpEnd, pchPlain, cchPlainReq, &cchPlainOut,
|
|
prgRunInfo, ulRunInfoReq, &ulRunInfoOut, pacpNext);
|
|
|
|
if (hr != S_OK)
|
|
break;
|
|
|
|
if (cchPlainOut == 0 && ulRunInfoOut == 0)
|
|
break; // eod
|
|
|
|
if (cchPlainReq > 0 && cchPlainOut > 0)
|
|
{
|
|
cchPlainReq -= cchPlainOut;
|
|
*pcchPlainOut += cchPlainOut;
|
|
|
|
if (cchPlainReq == 0)
|
|
{
|
|
fNoMoreSpace = TRUE;
|
|
}
|
|
else
|
|
{
|
|
pchPlain += cchPlainOut;
|
|
}
|
|
}
|
|
if (ulRunInfoReq > 0)
|
|
{
|
|
Assert(ulRunInfoOut > 0 && prgRunInfo->uCount > 0); // app bug?
|
|
|
|
if (ulRunInfoOut == 0)
|
|
break; // woah, app bug, avoid infinite loop
|
|
|
|
ulRunInfoReq -= ulRunInfoOut;
|
|
*pulRunInfoOut += ulRunInfoOut;
|
|
|
|
if (ulRunInfoReq == 0)
|
|
{
|
|
fNoMoreSpace = TRUE;
|
|
}
|
|
else
|
|
{
|
|
prgRunInfo += ulRunInfoOut;
|
|
}
|
|
}
|
|
|
|
if (fNoMoreSpace)
|
|
break; // buffers full
|
|
|
|
if (*pacpNext == acpEnd)
|
|
break; // got it all
|
|
|
|
acpStart = *pacpNext;
|
|
Assert(acpStart < acpEnd);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// PlainTextOffset
|
|
//
|
|
// NB: current implementation always skips hidden text.
|
|
//----------------------------------------------------------------------------
|
|
|
|
HRESULT PlainTextOffset(ITextStoreACP *ptsi, LONG ichAppBase, LONG iAppOffset, LONG *piPlainOffset)
|
|
{
|
|
BOOL fNeg;
|
|
HRESULT hr;
|
|
ULONG uRunInfoLen;
|
|
ULONG cch;
|
|
ULONG cchPlain;
|
|
TS_RUNINFO *pri;
|
|
TS_RUNINFO *priStop;
|
|
TS_RUNINFO rgRunInfo[32];
|
|
|
|
*piPlainOffset = 0;
|
|
|
|
if (iAppOffset == 0)
|
|
return S_OK;
|
|
|
|
fNeg = FALSE;
|
|
|
|
if (iAppOffset < 0)
|
|
{
|
|
fNeg = TRUE;
|
|
ichAppBase += iAppOffset;
|
|
iAppOffset = -iAppOffset;
|
|
}
|
|
|
|
cchPlain = 0;
|
|
|
|
do
|
|
{
|
|
Perf_IncCounter(PERF_PTO_GETTEXT);
|
|
|
|
hr = CProcessTextCache::GetText(ptsi, ichAppBase, ichAppBase + iAppOffset, NULL, 0, &cch,
|
|
rgRunInfo, ARRAYSIZE(rgRunInfo), &uRunInfoLen, &ichAppBase);
|
|
|
|
if (hr != S_OK)
|
|
goto Exit;
|
|
|
|
if (uRunInfoLen == 0 || rgRunInfo[0].uCount == 0)
|
|
{
|
|
Assert(0); // this should never happen, it means cicero is referencing a position past end-of-doc
|
|
hr = E_UNEXPECTED;
|
|
goto Exit;
|
|
}
|
|
|
|
cch = 0;
|
|
pri = rgRunInfo;
|
|
priStop = rgRunInfo + uRunInfoLen;
|
|
|
|
while (pri < priStop)
|
|
{
|
|
if (pri->type == TS_RT_PLAIN)
|
|
{
|
|
cchPlain += pri->uCount;
|
|
}
|
|
iAppOffset -= pri->uCount;
|
|
pri++;
|
|
}
|
|
|
|
} while (iAppOffset > 0);
|
|
|
|
*piPlainOffset = fNeg ? -(LONG)cchPlain : cchPlain;
|
|
|
|
hr = S_OK;
|
|
|
|
Exit:
|
|
return hr;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// AppTextOffsetForward
|
|
//
|
|
// piAppOffset, on return, points just past the iPlainOffset plain char or eod.
|
|
// Use AppTextOffsetNorm for a normalized return value!
|
|
//
|
|
// Returns S_FALSE if clipped due to bod or eod.
|
|
//----------------------------------------------------------------------------
|
|
|
|
inline IsPlainRun(TS_RUNINFO *pri, BOOL fSkipHidden)
|
|
{
|
|
return (pri->type == TS_RT_PLAIN ||
|
|
(!fSkipHidden && pri->type == TS_RT_HIDDEN));
|
|
}
|
|
|
|
HRESULT AppTextOffsetForward(ITextStoreACP *ptsi, LONG ichAppBase, LONG iPlainOffset, LONG *piAppOffset, DWORD dwFlags)
|
|
{
|
|
LONG acpStart;
|
|
LONG acpEnd;
|
|
HRESULT hr;
|
|
ULONG uRunInfoLen;
|
|
ULONG cch;
|
|
ULONG cchRead;
|
|
ULONG cchACP;
|
|
TS_RUNINFO *pri;
|
|
TS_RUNINFO *priStop;
|
|
ULONG i;
|
|
WCHAR *pch;
|
|
TS_RUNINFO rgRunInfo[32];
|
|
WCHAR ach[256];
|
|
BOOL fIgnoreRegions = (dwFlags & ATO_IGNORE_REGIONS);
|
|
BOOL fSkipHidden = (dwFlags & ATO_SKIP_HIDDEN);
|
|
|
|
Perf_IncCounter(PERF_ATOF_COUNTER);
|
|
|
|
Assert(iPlainOffset > 0);
|
|
Assert(*piAppOffset == 0);
|
|
|
|
cchACP = 0;
|
|
// Issue: use TsSF_REGIONS
|
|
cchRead = (ULONG)(fIgnoreRegions ? 0 : ARRAYSIZE(ach)); // we only need text if looking for regions
|
|
|
|
do
|
|
{
|
|
acpStart = ichAppBase;
|
|
acpEnd = (ichAppBase + iPlainOffset) < 0 ? LONG_MAX : ichAppBase + iPlainOffset;
|
|
Assert(acpEnd >= acpStart);
|
|
|
|
Perf_IncCounter(PERF_ATOF_GETTEXT_COUNTER);
|
|
|
|
hr = CProcessTextCache::GetText(ptsi, acpStart, acpEnd, ach, cchRead, &cch,
|
|
rgRunInfo, ARRAYSIZE(rgRunInfo), &uRunInfoLen, &acpEnd);
|
|
|
|
if (hr != S_OK)
|
|
{
|
|
Assert(0);
|
|
goto Exit;
|
|
}
|
|
|
|
if (uRunInfoLen == 0) // hit eod?
|
|
{
|
|
hr = S_FALSE;
|
|
break;
|
|
}
|
|
|
|
pri = rgRunInfo;
|
|
priStop = rgRunInfo + uRunInfoLen;
|
|
pch = &ach[0];
|
|
|
|
while (pri != priStop)
|
|
{
|
|
Assert(pri->uCount > 0); // runs should always be at least one char long
|
|
|
|
// scan for region boundary if necessary
|
|
if (!fIgnoreRegions && pri->type != TS_RT_OPAQUE)
|
|
{
|
|
if (IsPlainRun(pri, fSkipHidden))
|
|
{
|
|
// run is plain or hidden text (and we want to count hidden text)
|
|
for (i=0; i<pri->uCount; i++)
|
|
{
|
|
if (*pch == TS_CHAR_REGION)
|
|
{
|
|
// we hit a region boundary, pull out!
|
|
cchACP += i;
|
|
hr = S_FALSE; // for normalization
|
|
goto ExitOK;
|
|
}
|
|
pch++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// run is hidden text, which we want to skip over
|
|
pch += pri->uCount;
|
|
}
|
|
}
|
|
|
|
cchACP += pri->uCount;
|
|
if (IsPlainRun(pri, fSkipHidden))
|
|
{
|
|
iPlainOffset -= pri->uCount;
|
|
}
|
|
ichAppBase += pri->uCount;
|
|
pri++;
|
|
}
|
|
}
|
|
while (iPlainOffset != 0);
|
|
|
|
ExitOK:
|
|
*piAppOffset = cchACP;
|
|
|
|
Exit:
|
|
return hr;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// AppTextOffsetBackward
|
|
//
|
|
// piAppOffset, on return, points just past the iPlainOffset plain char or eod.
|
|
// Use AppTextOffsetNorm for a normalized return value!
|
|
//
|
|
// Returns S_FALSE if clipped due to bod or eod.
|
|
//----------------------------------------------------------------------------
|
|
|
|
HRESULT AppTextOffsetBackward(ITextStoreACP *ptsi, LONG ichAppBase, LONG iPlainOffset, LONG *piAppOffset, DWORD dwFlags)
|
|
{
|
|
LONG acpStart;
|
|
LONG acpEnd;
|
|
LONG acpEndOut;
|
|
HRESULT hr;
|
|
ULONG uRunInfoLen;
|
|
ULONG cch;
|
|
ULONG cchRead;
|
|
ULONG cchACP;
|
|
TS_RUNINFO *pri;
|
|
TS_RUNINFO *priStop;
|
|
ULONG i;
|
|
TS_RUNINFO rgRunInfo[32];
|
|
WCHAR *pch;
|
|
WCHAR ach[256];
|
|
BOOL fIgnoreRegions = (dwFlags & ATO_IGNORE_REGIONS);
|
|
BOOL fSkipHidden = (dwFlags & ATO_SKIP_HIDDEN);
|
|
|
|
Assert(iPlainOffset < 0);
|
|
Assert(*piAppOffset == 0);
|
|
|
|
cchACP = 0;
|
|
// Issue: use TsSF_REGIONS
|
|
cchRead = (ULONG)(fIgnoreRegions ? 0 : ARRAYSIZE(ach)); // we only need text if looking for regions
|
|
|
|
do
|
|
{
|
|
Assert(iPlainOffset < 0); // if this is >= 0, we or the app messed up the formatting run count
|
|
|
|
acpStart = ichAppBase + (fIgnoreRegions ? iPlainOffset : max(iPlainOffset, -(LONG)ARRAYSIZE(ach)));
|
|
acpStart = max(acpStart, 0); // handle top-of-doc collisions
|
|
acpEnd = ichAppBase;
|
|
Assert(acpEnd >= acpStart);
|
|
|
|
hr = GetTextComplete(ptsi, acpStart, acpEnd, ach, cchRead, &cch,
|
|
rgRunInfo, ARRAYSIZE(rgRunInfo), &uRunInfoLen, &acpEndOut);
|
|
|
|
if (hr != S_OK)
|
|
{
|
|
Assert(0);
|
|
goto Exit;
|
|
}
|
|
|
|
if (uRunInfoLen == 0) // hit eod?
|
|
{
|
|
hr = S_FALSE;
|
|
break;
|
|
}
|
|
|
|
// it's possible the GetText above didn't return everything we asked for....
|
|
// this happens when our format buffer isn't large enough
|
|
if (acpEndOut != acpEnd)
|
|
{
|
|
// so let's be conservative and ask for something we know should succeed
|
|
acpStart = ichAppBase - ARRAYSIZE(rgRunInfo);
|
|
|
|
Assert(acpStart >= 0); // the prev GetText should have succeeded if there were fewer chars than we're asking for now....
|
|
Assert(acpEnd - acpStart < -iPlainOffset); // again, we shouldn't get this far if we already asked for fewer chars
|
|
Assert(ARRAYSIZE(rgRunInfo) < ARRAYSIZE(ach)); // want to ask for the max we can handle in the worst case
|
|
Assert(acpEnd == ichAppBase);
|
|
|
|
hr = GetTextComplete(ptsi, acpStart, acpEnd, ach, cchRead, &cch,
|
|
rgRunInfo, ARRAYSIZE(rgRunInfo), &uRunInfoLen, &acpEndOut);
|
|
|
|
if (hr != S_OK)
|
|
{
|
|
Assert(0);
|
|
goto Exit;
|
|
}
|
|
|
|
if (uRunInfoLen == 0) // hit eod?
|
|
{
|
|
Assert(0); // this should never happen, because the original call for more chars returned non-zero!
|
|
goto Exit;
|
|
}
|
|
|
|
Assert(acpEnd == acpEndOut);
|
|
}
|
|
|
|
pri = rgRunInfo + uRunInfoLen - 1;
|
|
priStop = rgRunInfo - 1;
|
|
pch = &ach[cch-1];
|
|
|
|
while (pri != priStop)
|
|
{
|
|
Assert(pri->uCount > 0); // runs should always be at least one char long
|
|
|
|
// scan for region boundary if necessary
|
|
if (!fIgnoreRegions && pri->type != TS_RT_OPAQUE)
|
|
{
|
|
if (IsPlainRun(pri, fSkipHidden))
|
|
{
|
|
// run is plain or hidden text (and we want to count hidden text)
|
|
for (i=0; i<pri->uCount; i++)
|
|
{
|
|
if (*pch == TS_CHAR_REGION)
|
|
{
|
|
// we hit a region boundary, pull out!
|
|
cchACP += i;
|
|
hr = S_FALSE; // for normalization
|
|
goto ExitOK;
|
|
}
|
|
pch--;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// run is hidden text, which we want to skip over
|
|
pch -= pri->uCount;
|
|
}
|
|
}
|
|
|
|
cchACP += pri->uCount;
|
|
if (IsPlainRun(pri, fSkipHidden))
|
|
{
|
|
iPlainOffset += (LONG)pri->uCount;
|
|
}
|
|
ichAppBase -= (LONG)pri->uCount;
|
|
pri--;
|
|
}
|
|
|
|
// also check for top-of-doc
|
|
if (ichAppBase == 0)
|
|
{
|
|
hr = S_FALSE;
|
|
break;
|
|
}
|
|
|
|
} while (iPlainOffset != 0);
|
|
|
|
ExitOK:
|
|
*piAppOffset = -(LONG)cchACP;
|
|
|
|
Exit:
|
|
return hr;
|
|
}
|
|
|
|
#ifdef UNUSED
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// AppTextOffsetNorm
|
|
//
|
|
// Returns a normalized acp offset that spans the specificed number of plain chars --
|
|
// so the return offset is just short of (lPlainOffset + 1), or at eod. Returns
|
|
// S_FALSE if the initial call to AppTextOffset gets clipped because of eod.
|
|
//----------------------------------------------------------------------------
|
|
|
|
HRESULT AppTextOffsetNorm(ITextStoreACP *ptsi, LONG acpAppBase, LONG lPlainOffset, LONG *plAppOffset)
|
|
{
|
|
HRESULT hr;
|
|
|
|
Perf_IncCounter(PERF_ATON_COUNTER);
|
|
|
|
// if caller wants a neg offset, return value is already
|
|
// guarenteed normalized -- just before a plain text char.
|
|
// Otherwise, ask for the offset of the next char, then
|
|
// step back one char.
|
|
if ((lPlainOffset < LONG_MAX) && (lPlainOffset >= 0))
|
|
{
|
|
lPlainOffset++;
|
|
}
|
|
|
|
hr = AppTextOffset(ptsi, acpAppBase, lPlainOffset, plAppOffset, FALSE);
|
|
|
|
if (*plAppOffset > 0)
|
|
{
|
|
if ((lPlainOffset < LONG_MAX) && (hr == S_OK)) // could be S_FALSE if hit eod
|
|
{
|
|
// step back, and we're normalized
|
|
(*plAppOffset)--;
|
|
}
|
|
}
|
|
else if (*plAppOffset < 0)
|
|
{
|
|
// if we moved backwards there's only one case to
|
|
// worry about: if we hit a region boundary. Then
|
|
// we need to normalize.
|
|
if (hr == S_FALSE)
|
|
{
|
|
*plAppOffset = Normalize(ptsi, acpAppBase + *plAppOffset) - acpAppBase;
|
|
}
|
|
}
|
|
|
|
#ifndef PERF_DUMP
|
|
Assert(*plAppOffset == Normalize(ptsi, acpAppBase + *plAppOffset) - acpAppBase);
|
|
#endif
|
|
|
|
return hr;
|
|
}
|
|
#endif // UNUSED
|