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.
3386 lines
103 KiB
3386 lines
103 KiB
//
|
|
// property store class implementation
|
|
//
|
|
|
|
// includes
|
|
#include "private.h"
|
|
#include "globals.h"
|
|
#include "sapilayr.h"
|
|
#include "propstor.h"
|
|
#include "ids.h"
|
|
#include "cicspres.h"
|
|
#include "lmlattic.h"
|
|
|
|
#ifdef DEBUG
|
|
|
|
#include "wchar.h"
|
|
|
|
void DebugPrintOut(WCHAR *pwszStrName, WCHAR *pwszStr)
|
|
{
|
|
|
|
WCHAR wszBuf[80];
|
|
int iLen, i, j;
|
|
|
|
TraceMsg(TF_GENERAL, "the value of %S is", pwszStrName );
|
|
|
|
iLen = wcslen(pwszStr);
|
|
|
|
j = 0;
|
|
for ( i=0; i<iLen; i++)
|
|
{
|
|
if ( (pwszStr[i] < 0x80) && pwszStr[i] > 0x1f )
|
|
{
|
|
wszBuf[j] = pwszStr[i];
|
|
j ++;
|
|
}
|
|
else
|
|
{
|
|
WCHAR buf[8];
|
|
|
|
StringCchPrintfW(buf, ARRAYSIZE(buf), L" %4X ", (int)pwszStr[i] );
|
|
|
|
for ( int x=0; x< (int)wcslen(buf); x++)
|
|
{
|
|
wszBuf[j] = buf[x];
|
|
j++;
|
|
}
|
|
}
|
|
}
|
|
|
|
wszBuf[j] = L'\0';
|
|
|
|
TraceMsg(TF_GENERAL, "%S", wszBuf);
|
|
}
|
|
#endif
|
|
|
|
// ------------------------------------------------------------------------------------------------------------
|
|
// This is a global or standalone function which will be called by CRecoResultWrap and CSapiAlternativeList
|
|
//
|
|
// This function receives the phrase text buffer, this element's display text and display attribute, and
|
|
// previous element's display attribute.
|
|
//
|
|
// On Exit, it will update the phrase text buffer to append this element's text, and return the real offset
|
|
// length value for this element.
|
|
//
|
|
// -------------------------------------------------------------------------------------------------------------
|
|
|
|
HRESULT HandlePhraseElement( CSpDynamicString *pDstr, const WCHAR *pwszTextThis, BYTE bAttrThis, BYTE bAttrPrev, ULONG *pulOffsetThis)
|
|
{
|
|
HRESULT hr=S_OK;
|
|
|
|
ULONG ulPrevLen;
|
|
|
|
if ( !pDstr || !pwszTextThis )
|
|
return E_INVALIDARG;
|
|
|
|
ulPrevLen = pDstr->Length( );
|
|
|
|
if ( (ulPrevLen > 0) && (bAttrThis & SPAF_CONSUME_LEADING_SPACES) )
|
|
{
|
|
// This element wants to remove the trailing spaces of the previous element.
|
|
ULONG ulPrevTrailing = 0;
|
|
|
|
if ( bAttrPrev & SPAF_ONE_TRAILING_SPACE )
|
|
ulPrevTrailing = 1;
|
|
else if ( bAttrPrev & SPAF_TWO_TRAILING_SPACES )
|
|
ulPrevTrailing = 2;
|
|
|
|
if ( ulPrevLen >= ulPrevTrailing )
|
|
{
|
|
ulPrevLen = ulPrevLen - ulPrevTrailing;
|
|
pDstr->TrimToSize(ulPrevLen);
|
|
}
|
|
}
|
|
|
|
if ( pulOffsetThis )
|
|
*pulOffsetThis = ulPrevLen;
|
|
|
|
pDstr->Append(pwszTextThis);
|
|
|
|
if (bAttrThis & SPAF_ONE_TRAILING_SPACE)
|
|
{
|
|
pDstr->Append(L" ");
|
|
}
|
|
else if (bAttrThis & SPAF_TWO_TRAILING_SPACES)
|
|
{
|
|
pDstr->Append(L" ");
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// CRecoResult implementation
|
|
//
|
|
|
|
// ctor
|
|
|
|
CRecoResultWrap::CRecoResultWrap(CSapiIMX *pimx, ULONG ulStartElement, ULONG ulNumElements, ULONG ulNumOfITN)
|
|
{
|
|
m_cRef = 1;
|
|
m_ulStartElement = ulStartElement;
|
|
m_ulNumElements = ulNumElements;
|
|
|
|
// ITN is shown by default if the reco result has it
|
|
// the shown status can change after user goes through
|
|
// correction UI
|
|
//
|
|
|
|
m_ulNumOfITN = ulNumOfITN;
|
|
|
|
m_pimx = pimx;
|
|
|
|
m_pulElementOffsets = NULL;
|
|
m_bstrCurrentText = NULL;
|
|
|
|
m_OffsetDelta = 0;
|
|
m_ulCharsInTrail = 0;
|
|
m_ulTrailSpaceRemoved = 0;
|
|
m_pSerializedRecoResult = NULL;
|
|
|
|
#ifdef DEBUG
|
|
static DWORD s_dbg_Id = 0;
|
|
m_dbg_dwId = s_dbg_Id++;
|
|
#endif // DEBUG
|
|
}
|
|
|
|
CRecoResultWrap::~CRecoResultWrap()
|
|
{
|
|
if (m_pulElementOffsets)
|
|
delete[] m_pulElementOffsets;
|
|
|
|
if (m_bstrCurrentText)
|
|
SysFreeString(m_bstrCurrentText);
|
|
|
|
if (m_rgITNShowState.Count())
|
|
m_rgITNShowState.Clear();
|
|
|
|
if (m_pSerializedRecoResult)
|
|
{
|
|
CoTaskMemFree(m_pSerializedRecoResult);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Init function
|
|
//
|
|
HRESULT CRecoResultWrap::Init(ISpRecoResult *pRecoResult)
|
|
{
|
|
// serialize the given reco result and keep the cotaskmem
|
|
if (m_pSerializedRecoResult != NULL)
|
|
{
|
|
CoTaskMemFree(m_pSerializedRecoResult);
|
|
}
|
|
|
|
Assert(pRecoResult);
|
|
|
|
return pRecoResult->Serialize(&m_pSerializedRecoResult);
|
|
}
|
|
|
|
//
|
|
// GetResult
|
|
//
|
|
HRESULT CRecoResultWrap::GetResult(ISpRecoResult **ppResult)
|
|
{
|
|
if ( m_pSerializedRecoResult == NULL)
|
|
return E_PENDING;
|
|
|
|
// this is a tricky part, we need to access ISpRecoContext
|
|
// and don't want to hold onto it. We get it via CSapiIMX
|
|
// instance which must be always available during session
|
|
//
|
|
Assert(m_pimx);
|
|
|
|
CComPtr<ISpRecoContext> cpRecoCtxt;
|
|
|
|
//
|
|
// GetFunction ensures SAPI is initialized
|
|
//
|
|
HRESULT hr = m_pimx->GetFunction(GUID_NULL, IID_ISpRecoContext, (IUnknown **)&cpRecoCtxt);
|
|
if (S_OK == hr)
|
|
{
|
|
hr = cpRecoCtxt->DeserializeResult(m_pSerializedRecoResult, ppResult);
|
|
}
|
|
|
|
//
|
|
// callar is resposible to release this result object
|
|
//
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// IUnknown
|
|
//
|
|
STDMETHODIMP CRecoResultWrap::QueryInterface(REFIID riid, void **ppvObj)
|
|
{
|
|
HRESULT hr;
|
|
Assert(ppvObj);
|
|
|
|
if (IsEqualIID(riid, IID_IUnknown)
|
|
|| IsEqualIID(riid, IID_PRIV_RESULTWRAP)
|
|
|| IsEqualIID(riid, IID_IServiceProvider))
|
|
{
|
|
*ppvObj = this;
|
|
hr = S_OK;
|
|
this->m_cRef++;
|
|
}
|
|
else
|
|
{
|
|
*ppvObj = NULL;
|
|
hr = E_NOINTERFACE;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CRecoResultWrap::AddRef(void)
|
|
{
|
|
this->m_cRef++;
|
|
return this->m_cRef;
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CRecoResultWrap::Release(void)
|
|
{
|
|
this->m_cRef--;
|
|
if (this->m_cRef > 0)
|
|
{
|
|
return this->m_cRef;
|
|
}
|
|
delete this;
|
|
return 0;
|
|
}
|
|
|
|
// IServiceProvider
|
|
//
|
|
STDMETHODIMP CRecoResultWrap::QueryService(REFGUID guidService, REFIID riid, void** ppv)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
Assert(ppv);
|
|
|
|
if (!IsEqualIID(guidService, GUID_NULL))
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (IsEqualIID(riid, IID_IUnknown))
|
|
{
|
|
*ppv = this;
|
|
hr = S_OK;
|
|
this->m_cRef++;
|
|
}
|
|
else if (IsEqualIID(riid, IID_ISpRecoResult))
|
|
{
|
|
CComPtr<ISpRecoResult> cpResult;
|
|
|
|
hr = GetResult(&cpResult);
|
|
if (S_OK == hr)
|
|
{
|
|
hr = cpResult->QueryInterface(riid, ppv);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*ppv = NULL;
|
|
hr = E_NOINTERFACE;
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
//
|
|
// Clone this RecoResultWrap object.
|
|
//
|
|
HRESULT CRecoResultWrap::Clone(CRecoResultWrap **ppRw)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
CRecoResultWrap *prw;
|
|
CComPtr<ISpRecoResult> cpResult;
|
|
ULONG iIndex;
|
|
|
|
if ( !ppRw ) return E_INVALIDARG;
|
|
|
|
*ppRw = NULL;
|
|
|
|
prw = new CRecoResultWrap(m_pimx, m_ulStartElement, m_ulNumElements, m_ulNumOfITN);
|
|
if ( prw )
|
|
{
|
|
hr = GetResult(&cpResult);
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
hr = prw->Init(cpResult);
|
|
}
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
prw->SetOffsetDelta(m_OffsetDelta);
|
|
prw->SetCharsInTrail(m_ulCharsInTrail);
|
|
prw->SetTrailSpaceRemoved( m_ulTrailSpaceRemoved );
|
|
prw->m_bstrCurrentText = SysAllocString((WCHAR *)m_bstrCurrentText);
|
|
|
|
// Update ITN show-state list .
|
|
|
|
if ( m_ulNumOfITN > 0 )
|
|
{
|
|
SPITNSHOWSTATE *pITNShowState;
|
|
|
|
for (iIndex=0; iIndex<m_ulNumOfITN; iIndex ++ )
|
|
{
|
|
pITNShowState = m_rgITNShowState.GetPtr(iIndex);
|
|
|
|
if ( pITNShowState)
|
|
{
|
|
prw->_InitITNShowState(
|
|
pITNShowState->fITNShown,
|
|
pITNShowState->ulITNStart,
|
|
pITNShowState->ulITNNumElem);
|
|
}
|
|
} // for
|
|
} // if
|
|
|
|
// Update the Offset list for the second range.
|
|
|
|
if (m_pulElementOffsets)
|
|
{
|
|
ULONG ulOffset;
|
|
ULONG ulNumOffset;
|
|
|
|
ulNumOffset = m_ulStartElement+m_ulNumElements;
|
|
|
|
for ( iIndex=0; iIndex <= ulNumOffset; iIndex ++ )
|
|
{
|
|
ulOffset = m_pulElementOffsets[iIndex];
|
|
prw->_SetElementNewOffset(iIndex, ulOffset);
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( S_OK == hr )
|
|
{
|
|
// Return this prw to the caller.
|
|
*ppRw = prw;
|
|
}
|
|
else
|
|
{
|
|
// Something wrong when update the data members.
|
|
// Release the newly created object.
|
|
delete prw;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// _SpeakAudio()
|
|
//
|
|
// synopsis: playback audio based on the elements used in this result object
|
|
//
|
|
HRESULT CRecoResultWrap::_SpeakAudio(ULONG ulStart, ULONG ulcElem)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
CComPtr<ISpRecoResult> cpResult;
|
|
|
|
hr = GetResult(&cpResult);
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
if (ulcElem == 0)
|
|
{
|
|
ulStart = GetStart();
|
|
ulcElem = GetNumElements();
|
|
}
|
|
hr = cpResult->SpeakAudio( ulStart, ulcElem, SPF_ASYNC, NULL);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// _SetElementOffset(ULONG ulElement, ULONG ulNewOffset);
|
|
//
|
|
// This function is to update the offset for some element.
|
|
// It is used after property divide or shrink, some element's
|
|
// length is changed ( by removing trailing spaces etc.).
|
|
//
|
|
|
|
HRESULT CRecoResultWrap::_SetElementNewOffset(ULONG ulElement, ULONG ulNewOffset)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
|
|
if ((ulElement > m_ulStartElement + m_ulNumElements) || (ulElement < m_ulStartElement))
|
|
{
|
|
// This ulElement is not a valid element.
|
|
hr = E_INVALIDARG;
|
|
return hr;
|
|
}
|
|
|
|
if (!m_pulElementOffsets)
|
|
{
|
|
m_pulElementOffsets = new ULONG[m_ulStartElement+m_ulNumElements+1];
|
|
|
|
if ( !m_pulElementOffsets )
|
|
return E_OUTOFMEMORY;
|
|
else
|
|
for ( ULONG i=0; i<m_ulStartElement+m_ulNumElements+1; i++ )
|
|
m_pulElementOffsets[i] = -1;
|
|
}
|
|
|
|
m_pulElementOffsets[ulElement] = ulNewOffset;
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
//
|
|
// _GetElementOffsetCch
|
|
//
|
|
// synopsis: returns the start cch of the given SPPHRASEELEMENT
|
|
//
|
|
ULONG CRecoResultWrap::_GetElementOffsetCch(ULONG ulElement)
|
|
{
|
|
ULONG ulOffset = 0;
|
|
|
|
if (ulElement > m_ulStartElement + m_ulNumElements)
|
|
{
|
|
Assert(m_ulNumElements > 0);
|
|
ulElement = m_ulStartElement + m_ulNumElements;
|
|
}
|
|
else if (ulElement < m_ulStartElement)
|
|
{
|
|
ulElement = m_ulStartElement;
|
|
}
|
|
|
|
if (!m_pulElementOffsets)
|
|
{
|
|
_SetElementOffsetCch(NULL);
|
|
}
|
|
|
|
// m_pulElement could be null at memory stressed situation
|
|
if ( m_pulElementOffsets )
|
|
{
|
|
ulOffset = m_pulElementOffsets[ulElement];
|
|
}
|
|
|
|
return ulOffset;
|
|
}
|
|
|
|
//
|
|
// _SetElementOffsetCch
|
|
//
|
|
// synopsis:
|
|
//
|
|
//
|
|
// Before this function is called, we have to make sure that all the internal ITN show-state list
|
|
// has already been updated for the current phrase.
|
|
//
|
|
// So here we just relay on the correct ITN information to get the right real text string for
|
|
// current phrase and the offsets of all elemenets in this phrase.
|
|
|
|
void CRecoResultWrap::_SetElementOffsetCch(ISpPhraseAlt *pAlt)
|
|
{
|
|
if (m_pulElementOffsets)
|
|
{
|
|
delete[] m_pulElementOffsets;
|
|
|
|
m_pulElementOffsets = NULL;
|
|
}
|
|
|
|
SPPHRASE *pPhrase = NULL;
|
|
HRESULT hr = S_OK;
|
|
|
|
CComPtr<ISpPhrase> cpPhrase;
|
|
|
|
if (pAlt)
|
|
{
|
|
cpPhrase = pAlt;
|
|
}
|
|
else
|
|
{
|
|
CComPtr<ISpRecoResult> cpResult;
|
|
hr = GetResult(&cpResult);
|
|
|
|
//
|
|
// we're called for initialization, use current parent pharse object
|
|
//
|
|
cpPhrase = cpResult;
|
|
}
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
_UpdateInternalText(cpPhrase);
|
|
|
|
hr = cpPhrase->GetPhrase(&pPhrase);
|
|
}
|
|
|
|
ULONG cElements = 0;
|
|
if (S_OK == hr && pPhrase)
|
|
{
|
|
cElements = pPhrase->Rule.ulCountOfElements;
|
|
|
|
// the last colmun (+1) shows offset for the end of the last element
|
|
|
|
if ( m_pulElementOffsets )
|
|
delete[] m_pulElementOffsets;
|
|
|
|
m_pulElementOffsets = new ULONG[cElements+1];
|
|
|
|
if (cElements > 0 && m_pulElementOffsets)
|
|
{
|
|
CSpDynamicString dstr;
|
|
CSpDynamicString dstrReplace;
|
|
|
|
|
|
for (ULONG i = 0; i < cElements; i++ )
|
|
{
|
|
BOOL fInsideITN;
|
|
ULONG ulITNStart, ulITNNumElem;
|
|
|
|
fInsideITN = _CheckITNForElement(pPhrase, i, &ulITNStart, &ulITNNumElem, (CSpDynamicString *)&dstrReplace);
|
|
|
|
if ( fInsideITN )
|
|
{
|
|
m_pulElementOffsets[i] = dstr.Length();
|
|
|
|
// This element is inside an ITN range.
|
|
if ( i == (ulITNStart + ulITNNumElem - 1) )
|
|
{
|
|
// This is the last element of the new ITN.
|
|
// we need to add the replace text to the dstr string
|
|
// so that next non-ITN element will get correct offset.
|
|
|
|
dstr.Append( (WCHAR *)dstrReplace );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pPhrase->pElements[i].pszDisplayText)
|
|
{
|
|
// get cch up to this element.
|
|
// the offset is 0 for elem 0
|
|
//
|
|
const WCHAR *pwszTextThis;
|
|
BYTE bAttrThis = 0;
|
|
BYTE bAttrPrev = 0;
|
|
ULONG ulOffset = 0;
|
|
|
|
pwszTextThis = pPhrase->pElements[i].pszDisplayText;
|
|
bAttrThis = pPhrase->pElements[i].bDisplayAttributes;
|
|
|
|
if ( i > 0 )
|
|
bAttrPrev = pPhrase->pElements[i-1].bDisplayAttributes;
|
|
|
|
HandlePhraseElement( (CSpDynamicString *)&dstr, pwszTextThis, bAttrThis, bAttrPrev,&ulOffset);
|
|
m_pulElementOffsets[i] = ulOffset;
|
|
}
|
|
}
|
|
} // for
|
|
|
|
// store the last columun
|
|
m_pulElementOffsets[cElements] = dstr.Length() - m_ulTrailSpaceRemoved;
|
|
|
|
} // if m_pulElementOffsets
|
|
}
|
|
|
|
if (pPhrase)
|
|
::CoTaskMemFree(pPhrase);
|
|
}
|
|
|
|
//
|
|
// _UpdateInternalText()
|
|
//
|
|
// synopsis: this function updates the internal bstr that covers
|
|
// parent phrase wrapped by our own result object, based on
|
|
// the given phrase object and our internal pointer to
|
|
// the starting element and # of element
|
|
//
|
|
// perf after beta1: consolidate this with _SetElementOffsetCch()
|
|
//
|
|
//
|
|
void CRecoResultWrap::_UpdateInternalText(ISpPhrase *pSpPhrase)
|
|
{
|
|
CSpDynamicString dstrReplace;
|
|
CSpDynamicString dstr;
|
|
CSpDynamicString dstrDelta, dstrTrail;
|
|
ULONG ulLenCurText = 0;
|
|
|
|
if ( m_bstrCurrentText )
|
|
{
|
|
ulLenCurText = wcslen(m_bstrCurrentText);
|
|
|
|
if ( m_OffsetDelta > 0 && m_OffsetDelta <= ulLenCurText )
|
|
{
|
|
dstrDelta.Append(m_bstrCurrentText, m_OffsetDelta);
|
|
}
|
|
|
|
if ( m_ulCharsInTrail > 0 && m_ulCharsInTrail <= ulLenCurText )
|
|
{
|
|
WCHAR *pwszTrail;
|
|
|
|
pwszTrail = m_bstrCurrentText + ulLenCurText - m_ulCharsInTrail ;
|
|
dstrTrail.Append(pwszTrail, m_ulCharsInTrail);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// m_bstrCurrentText doesn't exist, but m_OffsetDelta or m_ulCharsInTrail
|
|
// is not 0, sounds it is not a possible case.
|
|
|
|
// But for safety sake, we just still keep the same number of spaces.
|
|
|
|
if ( m_OffsetDelta > 0 )
|
|
{
|
|
for (ULONG i=0; i<m_OffsetDelta; i++)
|
|
dstrDelta.Append(L" ");
|
|
}
|
|
|
|
if ( m_ulCharsInTrail > 0 )
|
|
{
|
|
for (ULONG i=0; i<m_ulCharsInTrail; i++)
|
|
dstrTrail.Append(L" ");
|
|
}
|
|
}
|
|
|
|
if ( m_ulNumElements == 0 )
|
|
{
|
|
// There is no valid element in this range.
|
|
//
|
|
// Just keep the delta string and Trailing string if they are existing.
|
|
|
|
if ( m_OffsetDelta > 0 )
|
|
dstr.Append( (WCHAR *)dstrDelta );
|
|
|
|
if ( m_ulCharsInTrail > 0)
|
|
dstr.Append((WCHAR *)dstrTrail);
|
|
|
|
if ( m_bstrCurrentText )
|
|
SysFreeString(m_bstrCurrentText);
|
|
|
|
m_bstrCurrentText = SysAllocString((WCHAR *)dstr);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
if ( pSpPhrase == NULL )
|
|
return;
|
|
|
|
// We cannot call pPhrase->GetText( ) to get the real phrase text, because GetText( )
|
|
// assumes all the ITN range have the same show-state, ( ITN or NON_ITN).
|
|
// But there are some cases like some ITN shown as ITN, some other ITN ranges shown as normal
|
|
// text after user selects a candidate.
|
|
//
|
|
|
|
// When the reco wrapper is first generated right after the text is recognized by SR engine,
|
|
// we can call GetText( ) to get the real text of the phrase.
|
|
//
|
|
// After that, user may change it by selecting alternative text.
|
|
|
|
dstr.Clear( );
|
|
|
|
if(m_OffsetDelta > 0)
|
|
{
|
|
// There are some characters which are not part of any elements in the range begining.
|
|
// we need to keep these characters.
|
|
|
|
dstr.Append((WCHAR *)dstrDelta);
|
|
}
|
|
|
|
if (m_bstrCurrentText)
|
|
{
|
|
SysFreeString(m_bstrCurrentText);
|
|
m_bstrCurrentText = NULL;
|
|
}
|
|
|
|
SPPHRASE *pPhrase = NULL;
|
|
|
|
pSpPhrase->GetPhrase(&pPhrase);
|
|
|
|
for (ULONG i = m_ulStartElement; i < m_ulStartElement + m_ulNumElements; i++ )
|
|
{
|
|
BOOL fInsideITN;
|
|
ULONG ulITNStart, ulITNNumElem;
|
|
|
|
fInsideITN = _CheckITNForElement(pPhrase, i, &ulITNStart, &ulITNNumElem, (CSpDynamicString *)&dstrReplace);
|
|
|
|
if ( fInsideITN )
|
|
{
|
|
// This element is inside an ITN range.
|
|
if ( i == (ulITNStart + ulITNNumElem - 1) )
|
|
{
|
|
// This is the last element of the new ITN.
|
|
// we need to add the replace text to the dstr string
|
|
// so that next non-ITN element will get correct offset.
|
|
|
|
dstr.Append( (WCHAR *)dstrReplace );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pPhrase->pElements[i].pszDisplayText)
|
|
{
|
|
const WCHAR *pwszTextThis;
|
|
BYTE bAttrThis = 0;
|
|
BYTE bAttrPrev = 0;
|
|
|
|
pwszTextThis = pPhrase->pElements[i].pszDisplayText;
|
|
bAttrThis = pPhrase->pElements[i].bDisplayAttributes;
|
|
|
|
if ( i > m_ulStartElement )
|
|
bAttrPrev = pPhrase->pElements[i-1].bDisplayAttributes;
|
|
|
|
HandlePhraseElement( (CSpDynamicString *)&dstr, pwszTextThis, bAttrThis, bAttrPrev,NULL);
|
|
}
|
|
}
|
|
} // for
|
|
|
|
if ( m_ulCharsInTrail > 0)
|
|
dstr.Append((WCHAR *)dstrTrail);
|
|
|
|
// If there were some trail spaces removed, we also need to remove the same number of spaces when
|
|
// we try to get the new phrase text.
|
|
|
|
if ( m_ulTrailSpaceRemoved > 0 )
|
|
{
|
|
ULONG ulNewRemoved = 0;
|
|
WCHAR *pwszNewText = (WCHAR *)dstr;
|
|
|
|
ulLenCurText = wcslen(pwszNewText);
|
|
|
|
for (ULONG i=ulLenCurText-1; ((long)i>0) && (ulNewRemoved <= m_ulTrailSpaceRemoved); i-- )
|
|
{
|
|
if ( pwszNewText[i] == L' ' )
|
|
{
|
|
pwszNewText[i] = L'\0';
|
|
ulNewRemoved ++;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
|
|
m_ulTrailSpaceRemoved = ulNewRemoved;
|
|
}
|
|
|
|
// store the last columun
|
|
m_bstrCurrentText = SysAllocString((WCHAR *)dstr);
|
|
|
|
if ( pPhrase )
|
|
::CoTaskMemFree(pPhrase);
|
|
|
|
}
|
|
|
|
BOOL CRecoResultWrap::_CanIgnoreChange(ULONG ich, WCHAR *pszChange, int cch)
|
|
{
|
|
// see if the given text is within tolerable range
|
|
|
|
BOOL bret = FALSE;
|
|
WCHAR *pszCurrentText = NULL;
|
|
|
|
// set up an offset to the current face text
|
|
if (m_bstrCurrentText)
|
|
{
|
|
if (ich < SysStringLen(m_bstrCurrentText))
|
|
{
|
|
pszCurrentText = m_bstrCurrentText;
|
|
pszCurrentText += ich;
|
|
}
|
|
}
|
|
// 1) compare it ignoring the case
|
|
if (pszCurrentText)
|
|
{
|
|
int i = _wcsnicmp(pszCurrentText, pszChange, cch);
|
|
if (i == 0)
|
|
{
|
|
bret = TRUE;
|
|
}
|
|
}
|
|
return bret;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------------//
|
|
//
|
|
// CRecoResultWrap::_RangeHasITN
|
|
//
|
|
// Determine if the partial of phrase between ulStart Element and ulStart+ulcElement -1
|
|
// has ITN.
|
|
//
|
|
// return the ITN number, or 0 if there is no ITN.
|
|
//
|
|
//------------------------------------------------------------------------------------------//
|
|
|
|
ULONG CRecoResultWrap::_RangeHasITN(ULONG ulStartElement, ULONG ulNumElements)
|
|
{
|
|
ULONG ulNumOfITN = 0;
|
|
CComPtr<ISpRecoResult> cpResult;
|
|
HRESULT hr;
|
|
|
|
hr = GetResult(&cpResult);
|
|
|
|
// determine whether this partial result has an ITN
|
|
SPPHRASE *pPhrase;
|
|
if (S_OK == hr)
|
|
hr = cpResult->GetPhrase(&pPhrase);
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
const SPPHRASEREPLACEMENT *pRep = pPhrase->pReplacements;
|
|
for (ULONG ul = 0; ul < pPhrase->cReplacements; ul++)
|
|
{
|
|
// review: we need to verify if this is really a correct way to determine
|
|
// whether the ITN fits in the partial result
|
|
//
|
|
if (pRep->ulFirstElement >= ulStartElement
|
|
&& (pRep->ulFirstElement + pRep->ulCountOfElements) <= (ulStartElement + ulNumElements))
|
|
{
|
|
ulNumOfITN ++;
|
|
}
|
|
pRep++;
|
|
}
|
|
::CoTaskMemFree(pPhrase);
|
|
}
|
|
|
|
return ulNumOfITN;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------------------
|
|
// CRecoResultWrap::_InitITNShowState
|
|
//
|
|
// It will initialize show-state for the given ITN or all the ITNs in this recowrap.
|
|
//
|
|
// Normally this function will be called after the reco wrapper is genertaed.
|
|
//
|
|
// When the reco wrap is first generated after a text is recognized by SR engine, all the
|
|
// ITNs in this reco wrap have the same showstate, it is convinent to set the show state
|
|
// for all the ITNs at one time. in this case, caller could just set both ulITNStart and
|
|
// ulITNNumElements as 0.
|
|
//
|
|
// When the new reco wrap is generated by property divide, shrink or deserialized from
|
|
// IStream, or after an alternative text is selected from candidate window, we cannot
|
|
// assume all the ITNs in the reco wrapper have the same show state. In this case,
|
|
// caller can initialize the show state one ITN by one ITN, it can set ulITNStart and
|
|
// ulITNNumElements to identify this ITN.
|
|
//
|
|
// ------------------------------------------------------------------------------------------
|
|
|
|
HRESULT CRecoResultWrap::_InitITNShowState(BOOL fITNShown, ULONG ulITNStart, ULONG ulITNNumElements )
|
|
{
|
|
|
|
HRESULT hr = S_OK;
|
|
ULONG ulNumOfITN = 0;
|
|
SPPHRASE *pPhrase;
|
|
|
|
TraceMsg(TF_GENERAL, "CRecoResultWrap::_InitITNShowState is called");
|
|
|
|
if ( m_ulNumOfITN == 0 )
|
|
{
|
|
// There is no ITN in this reco wrapper, just return here.
|
|
TraceMsg(TF_GENERAL, "There is no ITN");
|
|
return hr;
|
|
}
|
|
|
|
// The list of SPITNSHOWSTATE is already generated, we just need to set the value for
|
|
// every structure member.
|
|
|
|
if ( (ulITNStart == 0 ) && (ulITNNumElements == 0 ) )
|
|
{
|
|
// All the ITNs in this Reco wrapper have the same show status.
|
|
// we will calculate the every ITN start and end elements based
|
|
// on current reco result phrase.
|
|
|
|
// We want to alloc the structure list.
|
|
|
|
if ( m_rgITNShowState.Count( ) )
|
|
m_rgITNShowState.Clear( );
|
|
|
|
m_rgITNShowState.Append(m_ulNumOfITN);
|
|
|
|
|
|
CComPtr<ISpRecoResult> cpResult;
|
|
|
|
hr = GetResult(&cpResult);
|
|
if (S_OK == hr)
|
|
{
|
|
hr = cpResult->GetPhrase(&pPhrase);
|
|
}
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
const SPPHRASEREPLACEMENT *pRep = pPhrase->pReplacements;
|
|
for (ULONG ul = 0; ul < pPhrase->cReplacements; ul++)
|
|
{
|
|
if (pRep->ulFirstElement >= m_ulStartElement
|
|
&& (pRep->ulFirstElement + pRep->ulCountOfElements) <= (m_ulStartElement + m_ulNumElements))
|
|
{
|
|
// Get an ITN
|
|
|
|
SPITNSHOWSTATE *pITNShowState;
|
|
|
|
if ( pITNShowState = m_rgITNShowState.GetPtr(ulNumOfITN))
|
|
{
|
|
pITNShowState->ulITNStart = pRep->ulFirstElement;
|
|
pITNShowState->ulITNNumElem = pRep->ulCountOfElements;
|
|
pITNShowState->fITNShown = fITNShown;
|
|
}
|
|
|
|
ulNumOfITN ++;
|
|
|
|
if ( ulNumOfITN > m_ulNumOfITN )
|
|
{
|
|
// Something wrong, return here to avoid AV
|
|
break;
|
|
}
|
|
}
|
|
pRep++;
|
|
}
|
|
::CoTaskMemFree(pPhrase);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Set the display status for given ITN.
|
|
// Check to see if this is a valid ITN.
|
|
if ( ulITNNumElements > 0 )
|
|
{
|
|
ULONG ulIndex = 0;
|
|
SPITNSHOWSTATE *pITNShowState = NULL;
|
|
|
|
ulIndex = m_rgITNShowState.Count( );
|
|
|
|
m_rgITNShowState.Append(1);
|
|
|
|
if ( pITNShowState = m_rgITNShowState.GetPtr(ulIndex))
|
|
{
|
|
pITNShowState->ulITNStart = ulITNStart;
|
|
pITNShowState->ulITNNumElem = ulITNNumElements;
|
|
pITNShowState->fITNShown = fITNShown;
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------
|
|
// CRecoResultWrap::_InvertITNShowStateForRange
|
|
//
|
|
// Invert the show state for all the ITNs in the give range ( ulStartElement, to ulNumElements)
|
|
//
|
|
//
|
|
// --------------------------------------------------------------------------------------------
|
|
|
|
HRESULT CRecoResultWrap::_InvertITNShowStateForRange( ULONG ulStartElement, ULONG ulNumElements )
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
TraceMsg(TF_GENERAL,"CRecoResultWrap::_InvertITNShowStateForRange is called, ulStartElement=%d ulNumElements=%d", ulStartElement,ulNumElements);
|
|
if ( m_ulNumOfITN > 0 && ulNumElements > 0 )
|
|
{
|
|
//
|
|
// check to see if there is any ITN inside the given range.
|
|
//
|
|
ULONG ulIndex = 0;
|
|
|
|
for ( ulIndex=0; ulIndex < m_ulNumOfITN; ulIndex ++ )
|
|
{
|
|
SPITNSHOWSTATE *pITNShowState;
|
|
if ( pITNShowState = m_rgITNShowState.GetPtr(ulIndex))
|
|
{
|
|
if ((pITNShowState->ulITNStart >= ulStartElement) &&
|
|
(pITNShowState->ulITNStart + pITNShowState->ulITNNumElem <= ulStartElement + ulNumElements))
|
|
{
|
|
// This ITN is inside the given range, just invert the show state.
|
|
pITNShowState->fITNShown = !pITNShowState->fITNShown;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
// --------------------------------------------------------------------------------------------------
|
|
//
|
|
// CRecoResultWrap::_UpdateStateWithAltPhrase
|
|
//
|
|
// When an Alt phrase is going to used to replace current parent phrase, this method function
|
|
// will update related memeber data, like m_ulNumOfITN, m_ulNumElements, ITN show state list.
|
|
//
|
|
// ---------------------------------------------------------------------------------------------------
|
|
|
|
HRESULT CRecoResultWrap::_UpdateStateWithAltPhrase( ISpPhraseAlt *pSpPhraseAlt )
|
|
{
|
|
|
|
// This code is moved from UpdateInternalText( ).
|
|
// This part code in UpdateInternalText( ) is used only by SetResult( ) when select an alternative from
|
|
// candidate list.
|
|
HRESULT hr = S_OK;
|
|
ULONG ulParentStart;
|
|
ULONG cElements;
|
|
ULONG cElementsInParent;
|
|
|
|
CComPtr<ISpPhraseAlt> cpAlt;
|
|
|
|
TraceMsg(TF_GENERAL,"CRecoResultWrap::_UpdateStateWithAltPhrase is called");
|
|
|
|
if ( pSpPhraseAlt == NULL )
|
|
return E_INVALIDARG;
|
|
|
|
cpAlt=pSpPhraseAlt;
|
|
|
|
hr = cpAlt->GetAltInfo(NULL, &ulParentStart, &cElementsInParent, &cElements);
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
|
|
SPITNSHOWSTATE *pOrgITNShowState = NULL;
|
|
|
|
// case: there is ITN number change.
|
|
//
|
|
// there is element number change.
|
|
|
|
Assert(ulParentStart >= m_ulStartElement);
|
|
Assert(ulParentStart+cElementsInParent <= m_ulStartElement+m_ulNumElements);
|
|
|
|
TraceMsg(TF_GENERAL, "Original Num of ITNs =%d", m_ulNumOfITN);
|
|
|
|
if ( cElements != cElementsInParent )
|
|
{
|
|
// There is element number change.
|
|
// we need to update the start position for all the ITNs which are not in the selection range.
|
|
|
|
for ( ULONG uIndex=0; uIndex < m_ulNumOfITN; uIndex ++)
|
|
{
|
|
SPITNSHOWSTATE *pITNShowState;
|
|
|
|
pITNShowState = m_rgITNShowState.GetPtr(uIndex);
|
|
|
|
if ( pITNShowState && pITNShowState->ulITNStart >= (ulParentStart + cElementsInParent) )
|
|
{
|
|
long newStart;
|
|
|
|
newStart = (long)pITNShowState->ulITNStart + (long)(cElements - cElementsInParent);
|
|
|
|
pITNShowState->ulITNStart = (ULONG)newStart;
|
|
}
|
|
}
|
|
|
|
// set the new element number to reco wrapper.
|
|
|
|
long lNewNumElements = (long)m_ulNumElements + (long)(cElements - cElementsInParent);
|
|
m_ulNumElements = (ULONG)lNewNumElements;
|
|
|
|
}
|
|
|
|
if ( m_ulNumOfITN > 0 )
|
|
{
|
|
pOrgITNShowState = (SPITNSHOWSTATE *) cicMemAllocClear( m_ulNumOfITN * sizeof(SPITNSHOWSTATE) );
|
|
|
|
if ( pOrgITNShowState )
|
|
{
|
|
for (ULONG i=0; i< m_ulNumOfITN; i++ )
|
|
{
|
|
SPITNSHOWSTATE *pITNShowState;
|
|
|
|
pITNShowState = m_rgITNShowState.GetPtr(i);
|
|
|
|
pOrgITNShowState[i].ulITNStart = pITNShowState->ulITNStart;
|
|
pOrgITNShowState[i].ulITNNumElem = pITNShowState->ulITNNumElem;
|
|
pOrgITNShowState[i].fITNShown = pITNShowState->fITNShown;
|
|
}
|
|
}
|
|
else
|
|
hr = E_OUTOFMEMORY;
|
|
|
|
}
|
|
|
|
if ( m_rgITNShowState.Count( ) )
|
|
m_rgITNShowState.Clear( );
|
|
|
|
// Generate a new ITN list for new phrase.
|
|
if ( hr == S_OK )
|
|
{
|
|
SPPHRASE *pPhrase;
|
|
hr = cpAlt->GetPhrase(&pPhrase);
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
const SPPHRASEREPLACEMENT *pRep = pPhrase->pReplacements;
|
|
ULONG ulNumOfITN = 0;
|
|
|
|
for (ULONG ul = 0; ul < pPhrase->cReplacements; ul++)
|
|
{
|
|
ULONG ulITNStart, ulITNNumElem;
|
|
BOOL fITNShown = FALSE;
|
|
|
|
ulITNStart = pRep->ulFirstElement;
|
|
ulITNNumElem = pRep->ulCountOfElements;
|
|
|
|
if ( (ulITNStart >= m_ulStartElement)
|
|
&& ((ulITNStart + ulITNNumElem) <= (m_ulStartElement + m_ulNumElements)) )
|
|
{
|
|
// Get an ITN
|
|
SPITNSHOWSTATE *pITNShowState;
|
|
|
|
m_rgITNShowState.Append(1);
|
|
|
|
pITNShowState = m_rgITNShowState.GetPtr(ulNumOfITN);
|
|
|
|
if ( pITNShowState)
|
|
{
|
|
|
|
// If this ITN is inside the selection range, it show state will be set as TRUE. ITN.
|
|
// Other it will keep the same show state as orgITNShowState.
|
|
|
|
if ( (ulITNStart >= ulParentStart) &&
|
|
((ulITNStart+ulITNNumElem) <= (ulParentStart + cElements)) )
|
|
{
|
|
// This ITN is inside the selection range.
|
|
fITNShown = TRUE;
|
|
}
|
|
else
|
|
{
|
|
// Get the original show state from orgITNShowState
|
|
for ( ULONG j=0; j<m_ulNumOfITN; j ++ )
|
|
{
|
|
if ( (pOrgITNShowState[j].ulITNStart == ulITNStart) &&
|
|
(pOrgITNShowState[j].ulITNNumElem == ulITNNumElem ) )
|
|
{
|
|
fITNShown = pOrgITNShowState[j].fITNShown;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
pITNShowState->ulITNNumElem = ulITNNumElem;
|
|
pITNShowState->ulITNStart = ulITNStart;
|
|
pITNShowState->fITNShown = fITNShown;
|
|
}
|
|
|
|
ulNumOfITN ++;
|
|
}
|
|
|
|
pRep ++;
|
|
}
|
|
|
|
m_ulNumOfITN = ulNumOfITN;
|
|
|
|
TraceMsg(TF_GENERAL, "New Num of ITNs =%d", m_ulNumOfITN);
|
|
|
|
::CoTaskMemFree(pPhrase);
|
|
|
|
}
|
|
}
|
|
|
|
if ( pOrgITNShowState )
|
|
cicMemFree(pOrgITNShowState);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------------//
|
|
//
|
|
// CRecoResultWrap::_GetElementDispAttribute
|
|
//
|
|
// Return the display attribute for the given element, if it is inside of an ITN, and the ITN
|
|
// is showing, return the replacement text's attribute.
|
|
//
|
|
//------------------------------------------------------------------------------------------//
|
|
BYTE CRecoResultWrap::_GetElementDispAttribute(ULONG ulElement)
|
|
{
|
|
SPPHRASE *pPhrase = NULL;
|
|
BYTE bAttr = 0;
|
|
CComPtr<ISpRecoResult> cpResult;
|
|
HRESULT hr;
|
|
|
|
hr = GetResult(&cpResult);
|
|
if (hr == S_OK)
|
|
hr = cpResult->GetPhrase(&pPhrase);
|
|
|
|
if ( hr == S_OK && pPhrase)
|
|
{
|
|
BOOL fInsideITN;
|
|
ULONG ulITNStart, ulITNNumElem;
|
|
|
|
fInsideITN = _CheckITNForElement(NULL, ulElement, &ulITNStart, &ulITNNumElem, NULL);
|
|
|
|
if ( !fInsideITN )
|
|
bAttr = pPhrase->pElements[ulElement].bDisplayAttributes;
|
|
else
|
|
{
|
|
const SPPHRASEREPLACEMENT *pPhrReplace;
|
|
pPhrReplace = pPhrase->pReplacements;
|
|
|
|
if ( pPhrReplace )
|
|
{
|
|
for ( ULONG i=0; i<pPhrase->cReplacements; i++)
|
|
{
|
|
if ( (ulITNStart == pPhrReplace[i].ulFirstElement)
|
|
&& (ulITNNumElem == pPhrReplace[i].ulCountOfElements) )
|
|
{
|
|
bAttr = pPhrReplace[i].bDisplayAttributes;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( pPhrase )
|
|
::CoTaskMemFree(pPhrase);
|
|
|
|
return bAttr;
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------------------//
|
|
//
|
|
// CRecoResultWrap::_CheckITNForElement
|
|
//
|
|
// Determine if the specifed element is inside of an ITN range in the phrase.
|
|
// If it is, the return value would be TRUE, and pulStartElement, pulEndElement will be
|
|
// set as the real start element and num of elements of the ITN range, dstrReplace will hold
|
|
// the replace text string.
|
|
//
|
|
// If the element is not inside an ITN range, return value would be FALSE, all other out
|
|
// parameters will not be set.
|
|
//
|
|
//------------------------------------------------------------------------------------------//
|
|
|
|
BOOL CRecoResultWrap::_CheckITNForElement(SPPHRASE *pPhrase, ULONG ulElement, ULONG *pulITNStart, ULONG *pulITNNumElem, CSpDynamicString *pdstrReplace)
|
|
{
|
|
BOOL fInsideITN = FALSE;
|
|
SPPHRASE *pMyPhrase;
|
|
|
|
pMyPhrase = pPhrase;
|
|
|
|
if ( pMyPhrase == NULL )
|
|
{
|
|
CComPtr<ISpRecoResult> cpResult;
|
|
|
|
HRESULT hr = GetResult(&cpResult);
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
hr = cpResult->GetPhrase(&pMyPhrase);
|
|
}
|
|
|
|
if (S_OK != hr)
|
|
return fInsideITN;
|
|
}
|
|
|
|
if ( m_ulNumOfITN )
|
|
{
|
|
// Check to see if this element is inside an ITN range.
|
|
ULONG ulITNStart;
|
|
ULONG ulITNNumElem;
|
|
|
|
for ( ULONG iIndex=0; iIndex<m_ulNumOfITN; iIndex++ )
|
|
{
|
|
SPITNSHOWSTATE *pITNShowState;
|
|
if ( pITNShowState = m_rgITNShowState.GetPtr(iIndex))
|
|
{
|
|
ulITNStart = pITNShowState->ulITNStart;
|
|
ulITNNumElem = pITNShowState->ulITNNumElem;
|
|
|
|
if ( (ulElement >= ulITNStart) && ( ulElement < ulITNStart + ulITNNumElem) )
|
|
{
|
|
// found this ITN in our internal ITN show state list.
|
|
fInsideITN = pITNShowState->fITNShown;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( fInsideITN )
|
|
{
|
|
if ( pulITNStart )
|
|
*pulITNStart = ulITNStart;
|
|
|
|
if ( pulITNNumElem )
|
|
*pulITNNumElem = ulITNNumElem;
|
|
|
|
if ( pdstrReplace )
|
|
{
|
|
const SPPHRASEREPLACEMENT *pPhrReplace;
|
|
pPhrReplace = pMyPhrase->pReplacements;
|
|
|
|
for ( ULONG j=0; j<pMyPhrase->cReplacements; j++)
|
|
{
|
|
|
|
if ( (ulITNStart == pPhrReplace[j].ulFirstElement)
|
|
&& (ulITNNumElem == pPhrReplace[j].ulCountOfElements) )
|
|
{
|
|
|
|
pdstrReplace->Clear( );
|
|
pdstrReplace->Append(pPhrReplace[j].pszReplacementText);
|
|
|
|
if (pPhrReplace[j].bDisplayAttributes & SPAF_ONE_TRAILING_SPACE)
|
|
pdstrReplace->Append(L" ");
|
|
else if (pPhrReplace[j].bDisplayAttributes & SPAF_TWO_TRAILING_SPACES)
|
|
pdstrReplace->Append(L" ");
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( !pPhrase && pMyPhrase )
|
|
::CoTaskMemFree(pMyPhrase);
|
|
|
|
return fInsideITN;
|
|
}
|
|
|
|
|
|
//
|
|
// CPropStoreRecoResultObject implementation
|
|
//
|
|
|
|
// ctor
|
|
|
|
CPropStoreRecoResultObject::CPropStoreRecoResultObject(CSapiIMX *pimx, ITfRange *pRange)
|
|
{
|
|
m_cpResultWrap = NULL;
|
|
|
|
if ( pRange )
|
|
pRange->Clone(&m_cpRange); // Use a clone range to keep the original Range.
|
|
// It would be useful to handle property shrink and divide.
|
|
else
|
|
m_cpRange = pRange;
|
|
|
|
m_pimx = pimx;
|
|
|
|
m_cRef = 1;
|
|
}
|
|
|
|
// dtor
|
|
CPropStoreRecoResultObject::~CPropStoreRecoResultObject()
|
|
{
|
|
}
|
|
|
|
|
|
// IUnknown
|
|
STDMETHODIMP CPropStoreRecoResultObject::QueryInterface(REFIID riid, void **ppvObj)
|
|
{
|
|
HRESULT hr;
|
|
|
|
Assert(ppvObj);
|
|
|
|
if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_ITfPropertyStore))
|
|
{
|
|
*ppvObj = this;
|
|
hr = S_OK;
|
|
|
|
this->m_cRef++;
|
|
}
|
|
else
|
|
{
|
|
*ppvObj = NULL;
|
|
hr = E_NOINTERFACE;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CPropStoreRecoResultObject::AddRef(void)
|
|
{
|
|
this->m_cRef++;
|
|
|
|
return this->m_cRef;
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CPropStoreRecoResultObject::Release(void)
|
|
{
|
|
this->m_cRef--;
|
|
|
|
if (this->m_cRef > 0)
|
|
{
|
|
return this->m_cRef;
|
|
}
|
|
delete this;
|
|
|
|
return 0;
|
|
}
|
|
|
|
// ITfPropertyStore
|
|
|
|
STDMETHODIMP CPropStoreRecoResultObject::GetType(GUID *pguid)
|
|
{
|
|
HRESULT hr = E_INVALIDARG;
|
|
if (pguid)
|
|
{
|
|
*pguid = GUID_PROP_SAPIRESULTOBJECT;
|
|
hr = S_OK;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CPropStoreRecoResultObject::GetDataType(DWORD *pdwReserved)
|
|
{
|
|
HRESULT hr = E_INVALIDARG;
|
|
if (pdwReserved)
|
|
{
|
|
*pdwReserved = 0;
|
|
hr = S_OK;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CPropStoreRecoResultObject::GetData(VARIANT *pvarValue)
|
|
{
|
|
HRESULT hr = E_INVALIDARG;
|
|
|
|
if (pvarValue)
|
|
{
|
|
QuickVariantInit(pvarValue);
|
|
|
|
if (m_cpResultWrap)
|
|
{
|
|
IUnknown *pUnk;
|
|
|
|
hr = m_cpResultWrap->QueryInterface(IID_IUnknown, (void**)&pUnk);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
pvarValue->vt = VT_UNKNOWN;
|
|
pvarValue->punkVal = pUnk;
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CPropStoreRecoResultObject::OnTextUpdated(DWORD dwFlags, ITfRange *pRange, BOOL *pfAccept)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
*pfAccept = FALSE;
|
|
|
|
Assert(pRange);
|
|
|
|
if (m_pimx->_AcceptRecoResultTextUpdates())
|
|
{
|
|
*pfAccept = TRUE;
|
|
}
|
|
else
|
|
{
|
|
CComPtr<ITfContext> cpic;
|
|
hr = pRange->GetContext(&cpic);
|
|
|
|
if (SUCCEEDED(hr) && cpic)
|
|
{
|
|
CPSRecoEditSession *pes;
|
|
if (pes = new CPSRecoEditSession(this, pRange, cpic))
|
|
{
|
|
pes->_SetEditSessionData(ESCB_PROP_TEXTUPDATE, NULL, 0, (LONG_PTR)dwFlags);
|
|
cpic->RequestEditSession(m_pimx->_GetId(), pes, TF_ES_READ | TF_ES_SYNC, &hr);
|
|
|
|
if ( SUCCEEDED(hr) )
|
|
*pfAccept = (BOOL)pes->_GetRetData( );
|
|
|
|
pes->Release();
|
|
}
|
|
}
|
|
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CPropStoreRecoResultObject::Shrink(ITfRange *pRange, BOOL *pfFree)
|
|
{
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
if (m_pimx->_MasterLMEnabled())
|
|
{
|
|
return S_FALSE; // temporary solution to avoid nested editsessions
|
|
}
|
|
else
|
|
{
|
|
// Shrink this property store to reflect to the new doc Range (pRange).
|
|
|
|
// If the new range contains more than one element of recognized phrase,
|
|
// we just update the property store and keep this property store.
|
|
// *pfFree is set FALSE on exit.
|
|
|
|
// If the new range cannot contain even one complete element of recognized phrase,
|
|
// we just want to discard this property store, let Cicero engine to release this
|
|
// property store.
|
|
// *pfFree is set TRUE on exit.
|
|
|
|
Assert(pRange);
|
|
Assert(pfFree);
|
|
|
|
if ( !pRange || !pfFree )
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
CComPtr<ITfContext> cpic;
|
|
hr = pRange->GetContext(&cpic);
|
|
|
|
if (SUCCEEDED(hr) && cpic)
|
|
{
|
|
CPSRecoEditSession *pes;
|
|
if (pes = new CPSRecoEditSession(this, pRange, cpic))
|
|
{
|
|
pes->_SetEditSessionData(ESCB_PROP_SHRINK, NULL, 0);
|
|
cpic->RequestEditSession(m_pimx->_GetId(), pes, TF_ES_READ | TF_ES_SYNC, &hr);
|
|
|
|
if ( SUCCEEDED(hr) )
|
|
*pfFree = (BOOL)pes->_GetRetData( );
|
|
|
|
pes->Release();
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
STDMETHODIMP CPropStoreRecoResultObject::Divide(ITfRange *pRangeThis, ITfRange *pRangeNew, ITfPropertyStore **ppPropStore)
|
|
{
|
|
|
|
if (m_pimx->_MasterLMEnabled())
|
|
{
|
|
return S_FALSE; // temporary solution to avoid nested editsessions
|
|
}
|
|
else
|
|
{
|
|
// 12/17/1999
|
|
// [dividing a range implementation strategy]
|
|
//
|
|
// - pRangeThis contains the text range *before* the dividing point
|
|
// - pRangeNew contrains the range *after* the dividing point
|
|
// - First, adjust this property store to correctly hold a start element and #of element
|
|
// for pRangeThis
|
|
// - then create a new property store for pRangeNew, which will share the same
|
|
// result blob.
|
|
//
|
|
|
|
// just an experiment to see if cutting the range works.
|
|
// *ppPropStore = NULL;
|
|
Assert(ppPropStore);
|
|
Assert(pRangeThis);
|
|
Assert(pRangeNew);
|
|
|
|
CComPtr<ITfContext> cpic;
|
|
HRESULT hr = pRangeThis->GetContext(&cpic);
|
|
|
|
if (SUCCEEDED(hr) && cpic)
|
|
{
|
|
CPSRecoEditSession *pes;
|
|
if (pes = new CPSRecoEditSession(this, pRangeThis, cpic))
|
|
{
|
|
pes->_SetUnk((IUnknown *)pRangeNew);
|
|
pes->_SetEditSessionData(ESCB_PROP_DIVIDE, NULL, 0);
|
|
cpic->RequestEditSession(m_pimx->_GetId(), pes, TF_ES_READ | TF_ES_SYNC, &hr);
|
|
|
|
if ( SUCCEEDED(hr) )
|
|
*ppPropStore = (ITfPropertyStore *)pes->_GetRetUnknown( );
|
|
|
|
pes->Release();
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
//
|
|
// CPropStoreRecoResultObject::_OnTextUpdated
|
|
//
|
|
// the text has been modified in the document, this function just wants to determine
|
|
// if the property needs to change also.
|
|
// if pfAccept returns TRUE, means the property keep unchanged. ( propbaly it is capitalizing).
|
|
// if pfAccept returns FALSE, means the property needs to be changed to map to the new text ranges.
|
|
//
|
|
// consequently, property dividing or shrinking will be taken by cicero engine.
|
|
//
|
|
HRESULT CPropStoreRecoResultObject::_OnTextUpdated(TfEditCookie ec, DWORD dwFlags, ITfRange *pRange, BOOL *pfAccept)
|
|
{
|
|
// if the change is only about capitalizing we'll ignore the changes
|
|
|
|
Assert(pRange);
|
|
Assert(pfAccept);
|
|
|
|
long cch;
|
|
ULONG ichUpdate = 0;
|
|
TF_HALTCOND hc;
|
|
CComPtr<ITfRange> cpRangeTemp;
|
|
CComPtr<ITfRange> cpPropRangeTemp;
|
|
CRecoResultWrap *pResultWrap;
|
|
|
|
BOOL *pfKeepProp = pfAccept;
|
|
BOOL fCorrection = (dwFlags & TF_TU_CORRECTION);
|
|
|
|
*pfKeepProp = FALSE;
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
pResultWrap = (CRecoResultWrap *)(void *)m_cpResultWrap;
|
|
|
|
// if there's no current text don't try to save the prop
|
|
if (pResultWrap->m_bstrCurrentText == NULL)
|
|
return hr;
|
|
|
|
// did the run change size? we won't accept the change if
|
|
// the run changed size
|
|
if (m_cpRange->Clone(&cpPropRangeTemp) != S_OK)
|
|
{
|
|
hr = E_FAIL;
|
|
return hr;
|
|
}
|
|
|
|
hc.pHaltRange = cpPropRangeTemp;
|
|
hc.aHaltPos = TF_ANCHOR_END;
|
|
hc.dwFlags = 0;
|
|
|
|
if (cpPropRangeTemp->ShiftStart(ec, LONG_MAX, &cch, &hc) != S_OK)
|
|
{
|
|
hr = E_FAIL;
|
|
return hr;
|
|
}
|
|
|
|
if ((ULONG)cch != wcslen(pResultWrap->m_bstrCurrentText))
|
|
return hr;
|
|
|
|
// text has not changed size
|
|
|
|
// correction?
|
|
if (fCorrection)
|
|
{
|
|
*pfKeepProp = TRUE;
|
|
return hr;
|
|
}
|
|
|
|
// everything from here below is about
|
|
// checking for a case change only
|
|
|
|
// calculate the offset of update
|
|
cpPropRangeTemp.Release( );
|
|
hr = m_cpRange->Clone(&cpPropRangeTemp);
|
|
if (S_OK == hr)
|
|
{
|
|
hc.pHaltRange = pRange;
|
|
hc.aHaltPos = TF_ANCHOR_START;
|
|
hc.dwFlags = 0;
|
|
hr = cpPropRangeTemp->ShiftStart(ec, LONG_MAX, &cch, &hc);
|
|
ichUpdate = cch;
|
|
}
|
|
|
|
// calculate cch of the update
|
|
if (S_OK == hr)
|
|
{
|
|
hr = pRange->Clone(&cpRangeTemp);
|
|
}
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
WCHAR *psz;
|
|
|
|
hc.pHaltRange = pRange;
|
|
hc.aHaltPos = TF_ANCHOR_END;
|
|
hc.dwFlags = 0;
|
|
cpRangeTemp->ShiftStart(ec, LONG_MAX, &cch, &hc);
|
|
psz = new WCHAR[cch+1];
|
|
|
|
if (psz)
|
|
{
|
|
if ( S_OK == pRange->GetText(ec, 0, psz, cch, (ULONG *)&cch))
|
|
{
|
|
*pfKeepProp = pResultWrap->_CanIgnoreChange(ichUpdate, psz, cch);
|
|
}
|
|
delete[] psz;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// CPropStoreRecoResultObject::_Divide
|
|
//
|
|
// synopsis : receives edit cookie from edit session
|
|
// so that we can manipulate with ranges
|
|
// to set starting elements/# of elements
|
|
//
|
|
//
|
|
HRESULT CPropStoreRecoResultObject::_Divide(TfEditCookie ec, ITfRange *pR1, ITfRange *pR2, ITfPropertyStore **ppPs)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
CRecoResultWrap *cpResultWrap;
|
|
|
|
long cchFirst = 0; // the number of characters in the first range.
|
|
long cchSecond = 0; // the number of characters in the Second range.
|
|
long cchSecondStart; // Offset of first char in the second range.
|
|
// it starts from the original range's start-point.
|
|
long cchOrgLen; // Number of characters in original text string.
|
|
|
|
int iElementOffsetChanged = 0; // If the end point of the first range
|
|
// is exactly in the last space char of
|
|
// one element, the new offset range for
|
|
// this element in the first range needs
|
|
// to be updated.
|
|
|
|
WCHAR *psz=NULL;
|
|
ULONG iElement;
|
|
ULONG ulStartElement, ulNumElement;
|
|
ULONG ulFirstEndElement, ulNextStartElement;
|
|
ULONG ulFirstStartOffset;
|
|
DIVIDECASE dcDivideCase;
|
|
CComPtr<ITfRange> cpRangeTemp;
|
|
ULONG ulFirstDelta, ulSecondDelta;
|
|
ULONG ulFirstTrail, ulSecondTrail;
|
|
ULONG ulFirstTSpaceRemoved, ulSecondTSpaceRemoved;
|
|
CStructArray<SPITNSHOWSTATE> rgOrgITNShowState;
|
|
|
|
CSpDynamicString dstrOrg, dstrFirst, dstrSecond;
|
|
|
|
TraceMsg(TF_GENERAL, "CPropStoreRecoResultObject::_Divide is called, this=0x%x", (INT_PTR)this);
|
|
|
|
if ( !pR1 || !pR2 )
|
|
return E_INVALIDARG;
|
|
|
|
// Update this property store to keep pR1 instead of the original whole range.
|
|
CComPtr<ITfRange> cpRange;
|
|
hr = pR1->Clone(&cpRange);
|
|
if (S_OK == hr)
|
|
{
|
|
m_cpRange = cpRange;
|
|
}
|
|
else
|
|
return hr;
|
|
|
|
Assert(m_cpRange);
|
|
|
|
// Update m_cpResultWrap for Range1. especially for data member m_bstrCurrentText, m_ulStartElement, m_ulNumElements, ulNumOfITN,
|
|
// and m_pulElementOffsets
|
|
|
|
cpResultWrap = (CRecoResultWrap *)(void *)m_cpResultWrap;
|
|
|
|
if ( cpResultWrap == NULL)
|
|
return E_FAIL;
|
|
|
|
if ( cpResultWrap->m_bstrCurrentText == NULL)
|
|
cpResultWrap->_SetElementOffsetCch(NULL); // To update internal text also.
|
|
|
|
if ( cpResultWrap->m_bstrCurrentText == NULL)
|
|
return E_FAIL;
|
|
|
|
// Initialize the text for the first range and second range.
|
|
dstrOrg.Append(cpResultWrap->m_bstrCurrentText);
|
|
cchOrgLen = wcslen(cpResultWrap->m_bstrCurrentText);
|
|
|
|
if ( cpResultWrap->IsElementOffsetIntialized( ) == FALSE )
|
|
{
|
|
cpResultWrap->_SetElementOffsetCch(NULL);
|
|
}
|
|
|
|
// Calculate how many elements will be in the first Range.
|
|
|
|
hr = pR1->Clone(&cpRangeTemp);
|
|
|
|
if ( hr == S_OK )
|
|
{
|
|
TF_HALTCOND hc;
|
|
|
|
hc.pHaltRange = pR1;
|
|
hc.aHaltPos = TF_ANCHOR_END;
|
|
hc.dwFlags = 0;
|
|
cpRangeTemp->ShiftStart(ec, LONG_MAX, &cchFirst, &hc);
|
|
|
|
if ( cchFirst == 0 )
|
|
return E_FAIL;
|
|
}
|
|
else
|
|
return E_FAIL;
|
|
|
|
// Calculate how many chars are in the second range.
|
|
cpRangeTemp.Release( );
|
|
hr = pR2->Clone(&cpRangeTemp);
|
|
|
|
if ( hr == S_OK )
|
|
{
|
|
TF_HALTCOND hc;
|
|
|
|
hc.pHaltRange = pR2;
|
|
hc.aHaltPos = TF_ANCHOR_END;
|
|
hc.dwFlags = 0;
|
|
cpRangeTemp->ShiftStart(ec, LONG_MAX, &cchSecond, &hc);
|
|
}
|
|
|
|
if ( cchSecond == 0 )
|
|
return E_FAIL;
|
|
|
|
cchSecondStart = cchOrgLen - cchSecond;
|
|
|
|
if ( cchSecondStart < cchFirst )
|
|
{
|
|
// Normally, it is not possible case, but for safety reason, just check it here.
|
|
cchSecondStart = cchFirst;
|
|
}
|
|
|
|
TraceMsg(TF_GENERAL, "cchFirst=%d, cchSecondStart=%d", cchFirst, cchSecondStart);
|
|
|
|
ulStartElement = cpResultWrap->GetStart( );
|
|
ulNumElement = cpResultWrap->GetNumElements( );
|
|
|
|
ulFirstEndElement = ulStartElement + ulNumElement - 1;
|
|
ulNextStartElement = ulStartElement + ulNumElement;
|
|
|
|
ulFirstStartOffset = cpResultWrap->_GetElementOffsetCch(ulStartElement);
|
|
|
|
ulFirstDelta = cpResultWrap->_GetOffsetDelta( );
|
|
ulSecondDelta = 0;
|
|
|
|
ulFirstTrail= 0;
|
|
ulSecondTrail = cpResultWrap->GetCharsInTrail( );
|
|
|
|
ulFirstTSpaceRemoved = 0;
|
|
ulSecondTSpaceRemoved = cpResultWrap->GetTrailSpaceRemoved( );
|
|
|
|
dcDivideCase = DivideNormal;
|
|
|
|
if ( (cchFirst >= cchOrgLen) || (cchSecondStart >= cchOrgLen) )
|
|
{
|
|
// Something is wrong here already.
|
|
// It is better to stop here and return error
|
|
// to avoid any possible crash in the below code!
|
|
return E_FAIL;
|
|
}
|
|
|
|
psz = (WCHAR *)dstrOrg;
|
|
psz += cchSecondStart; // need to account for deleted text as well
|
|
dstrSecond.Append(psz);
|
|
|
|
|
|
psz = (WCHAR *)dstrOrg;
|
|
dstrFirst.Append(psz);
|
|
psz = (WCHAR *)dstrFirst;
|
|
psz[cchFirst] = L'\0';
|
|
|
|
if ( ulNumElement == 0 )
|
|
{
|
|
// There is no any valid element in this range.
|
|
//
|
|
// we just update the m_bstrCurrentText, don't generate a property store
|
|
// for second range.
|
|
dcDivideCase = CurRangeNoElement;
|
|
}
|
|
else
|
|
{
|
|
// At least one element in this property range.
|
|
BOOL fFoundFirstEnd = FALSE;
|
|
BOOL fFoundSecondStart = FALSE;
|
|
|
|
for ( iElement=ulStartElement; iElement < ulStartElement + ulNumElement; iElement++)
|
|
{
|
|
ULONG cchAfterElem_i; // length of text range from StartElement to this element ( include this element).
|
|
ULONG cchToElem_i; // Length of text range from startElement to start of this element. ( exclude this elem).
|
|
|
|
cchAfterElem_i = cpResultWrap->_GetElementOffsetCch(iElement+1) - ulFirstStartOffset + ulFirstDelta;
|
|
cchToElem_i = cpResultWrap->_GetElementOffsetCch(iElement) - ulFirstStartOffset + ulFirstDelta;
|
|
|
|
if ( !fFoundFirstEnd )
|
|
{
|
|
// Try to find First End element and ulFirstTrail for the first range
|
|
if ( cchFirst <= (long)ulFirstDelta )
|
|
{
|
|
// Divide at the point which is not belong to any element of the phrase
|
|
|
|
ulFirstTrail = 0;
|
|
ulFirstDelta = cchFirst;
|
|
dcDivideCase = DivideInDelta;
|
|
fFoundFirstEnd = TRUE;
|
|
|
|
TraceMsg(TF_GENERAL, "The first range is divided inside Delta part");
|
|
}
|
|
else
|
|
{
|
|
if ( cchAfterElem_i == (ULONG)cchFirst )
|
|
{
|
|
// This is the end element for the first range.
|
|
ulFirstEndElement = iElement;
|
|
ulFirstTrail = 0;
|
|
|
|
fFoundFirstEnd = TRUE;
|
|
}
|
|
else if ( cchAfterElem_i > (ULONG)cchFirst )
|
|
{
|
|
if ( ((WCHAR *)dstrOrg)[cchFirst] == L' ')
|
|
{
|
|
// This is also the end elemenet.
|
|
// just divide in at a space char.
|
|
ulFirstEndElement = iElement;
|
|
|
|
ulFirstTrail = 0;
|
|
|
|
// The trailing space is now removed from the original element.
|
|
// we need to update the length for this element. ( update the offset
|
|
// for next element).
|
|
|
|
iElementOffsetChanged = cchAfterElem_i - cchFirst;
|
|
ulFirstTSpaceRemoved = iElementOffsetChanged;
|
|
}
|
|
else
|
|
{
|
|
// check to see if current element is inside an ITN.
|
|
|
|
BOOL fInsideITN;
|
|
ULONG ulITNStart, ulITNNumElem;
|
|
ULONG ulCurElement;
|
|
|
|
fInsideITN = cpResultWrap->_CheckITNForElement(NULL, iElement, &ulITNStart, &ulITNNumElem, NULL);
|
|
|
|
if ( fInsideITN )
|
|
ulCurElement = ulITNStart;
|
|
else
|
|
ulCurElement = iElement;
|
|
|
|
ulFirstEndElement = ulCurElement - 1;
|
|
|
|
// The previous one is EndElement if there is a previous element
|
|
// Discard this element.
|
|
// Divide at a valid element
|
|
|
|
// If divide in the first element, specially handle it.
|
|
if ( ulCurElement == ulStartElement)
|
|
{
|
|
dcDivideCase = DivideInsideFirstElement;
|
|
TraceMsg(TF_GENERAL, "The first range is divided inside the first element");
|
|
}
|
|
|
|
// The first part of this element would become
|
|
// the trail part of the first range.
|
|
ulFirstTrail = (ULONG)cchFirst - cchToElem_i;
|
|
|
|
}
|
|
|
|
fFoundFirstEnd = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( fFoundFirstEnd )
|
|
{
|
|
// Now the data for the first range is completed.
|
|
// we want to find data for the second range.
|
|
|
|
// We want to find the start element and ulSecondDelta for the second range.
|
|
|
|
if ( (long)cchToElem_i >= cchSecondStart )
|
|
{
|
|
// Find the element which is the first element after the start point of second
|
|
// range.
|
|
|
|
ulNextStartElement = iElement;
|
|
ulSecondDelta = cchToElem_i - cchSecondStart;
|
|
|
|
fFoundSecondStart = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
} // for
|
|
|
|
if ( !fFoundFirstEnd )
|
|
{
|
|
// Cannot find the first end element from the above code.
|
|
// it must be divided in the trailing part.
|
|
|
|
// we just want to change the ulCharsInTrail for the first range.
|
|
ULONG ulValidLenInFirstRange;
|
|
|
|
// ulValidLenInFirstRange is the number of Delta chars and valid elements' chars.
|
|
|
|
ulValidLenInFirstRange = cpResultWrap->_GetElementOffsetCch(ulStartElement + ulNumElement) - ulFirstStartOffset + ulFirstDelta;
|
|
|
|
ulFirstTrail = cchFirst - ulValidLenInFirstRange;
|
|
|
|
}
|
|
|
|
if ( !fFoundSecondStart )
|
|
{
|
|
// The second start point must be in Last element or in the Trailing part in the original range.
|
|
// The second range will not contain any valid element.
|
|
|
|
ulSecondTrail = 0;
|
|
ulSecondDelta = cchOrgLen - cchSecondStart;
|
|
|
|
ulNextStartElement = ulStartElement + ulNumElement; // This is not a valid element number in the original
|
|
// range. using this value means there is no valid element
|
|
// in the second range.
|
|
}
|
|
}
|
|
|
|
|
|
TraceMsg(TF_GENERAL, "ulStartElement = %d ulNumElement=%d", ulStartElement, ulNumElement);
|
|
TraceMsg(TF_GENERAL, "ulFirstEndElement = %d ulNextStartElement=%d", ulFirstEndElement, ulNextStartElement);
|
|
TraceMsg(TF_GENERAL, "The First Range text =\"%S\", delta=%d, Trail=%d, TSRemoved=%d", (WCHAR *)dstrFirst, ulFirstDelta, ulFirstTrail, ulFirstTSpaceRemoved);
|
|
TraceMsg(TF_GENERAL, "The second range text =\"%S\", delta=%d, Trail=%d, TSRemoved=%d", (WCHAR *)dstrSecond, ulSecondDelta, ulSecondTrail, ulSecondTSpaceRemoved);
|
|
|
|
if (cpResultWrap->m_bstrCurrentText)
|
|
SysFreeString(cpResultWrap->m_bstrCurrentText);
|
|
|
|
cpResultWrap->m_bstrCurrentText = SysAllocString((WCHAR *)dstrFirst);
|
|
|
|
// Keep the ITN show-state for the second reco wrap use.
|
|
if ( cpResultWrap->m_ulNumOfITN )
|
|
{
|
|
rgOrgITNShowState.Append(cpResultWrap->m_ulNumOfITN);
|
|
|
|
for (ULONG i=0; i<cpResultWrap->m_ulNumOfITN; i++)
|
|
{
|
|
SPITNSHOWSTATE *pITNShowState;
|
|
if ( pITNShowState = rgOrgITNShowState.GetPtr(i))
|
|
{
|
|
SPITNSHOWSTATE *pITNShowStateSource;
|
|
|
|
pITNShowStateSource = cpResultWrap->m_rgITNShowState.GetPtr(i);
|
|
|
|
pITNShowState->ulITNStart = pITNShowStateSource->ulITNStart;
|
|
pITNShowState->ulITNNumElem = pITNShowStateSource->ulITNNumElem;
|
|
pITNShowState->fITNShown = pITNShowStateSource->fITNShown;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Keep the Offset list for second range
|
|
|
|
ULONG *pulOffsetForSecond = NULL;
|
|
|
|
if ( (ulStartElement + ulNumElement - ulNextStartElement) > 0 )
|
|
{
|
|
ULONG ulNextNumOffset;
|
|
|
|
ulNextNumOffset = ulStartElement + ulNumElement - ulNextStartElement + 1;
|
|
|
|
pulOffsetForSecond = new ULONG[ulNextNumOffset];
|
|
|
|
if ( pulOffsetForSecond )
|
|
{
|
|
ULONG i;
|
|
ULONG ulOffset;
|
|
|
|
for ( i=0; i < ulNextNumOffset; i++ )
|
|
{
|
|
ulOffset = cpResultWrap->_GetElementOffsetCch(ulNextStartElement + i );
|
|
pulOffsetForSecond[i] = ulOffset;
|
|
}
|
|
}
|
|
else
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
switch ( dcDivideCase )
|
|
{
|
|
case DivideNormal :
|
|
|
|
ULONG ulNumOfITN;
|
|
ULONG ulFirstNumElement;
|
|
|
|
ulFirstNumElement = ulFirstEndElement - ulStartElement + 1;
|
|
cpResultWrap->SetNumElements(ulFirstNumElement);
|
|
|
|
// update the ITN show-state list.
|
|
if (cpResultWrap->m_ulNumOfITN > 0)
|
|
{
|
|
ulNumOfITN = cpResultWrap->_RangeHasITN(ulStartElement, ulFirstNumElement);
|
|
if ( cpResultWrap->m_ulNumOfITN > ulNumOfITN )
|
|
{
|
|
// There is ITN number change
|
|
// need to remove the ITNs which are not in this range.
|
|
cpResultWrap->m_rgITNShowState.Remove(ulNumOfITN, cpResultWrap->m_ulNumOfITN - ulNumOfITN);
|
|
cpResultWrap->m_ulNumOfITN = ulNumOfITN;
|
|
}
|
|
|
|
}
|
|
|
|
if ( iElementOffsetChanged > 0 )
|
|
{
|
|
// Some trailing spaces are removed from end element of the first range.
|
|
ULONG ulNewOffset;
|
|
|
|
ulNewOffset = cpResultWrap->_GetElementOffsetCch(ulFirstEndElement + 1);
|
|
cpResultWrap->_SetElementNewOffset(ulFirstEndElement + 1, ulNewOffset - iElementOffsetChanged);
|
|
}
|
|
|
|
cpResultWrap->SetCharsInTrail(ulFirstTrail);
|
|
cpResultWrap->SetTrailSpaceRemoved( ulFirstTSpaceRemoved );
|
|
|
|
break;
|
|
|
|
case DivideInsideFirstElement :
|
|
case DivideInDelta :
|
|
|
|
cpResultWrap->SetNumElements(0);
|
|
cpResultWrap->m_ulNumOfITN = 0;
|
|
cpResultWrap->SetOffsetDelta(ulFirstDelta);
|
|
cpResultWrap->SetCharsInTrail(ulFirstTrail);
|
|
cpResultWrap->SetTrailSpaceRemoved( ulFirstTSpaceRemoved );
|
|
|
|
break;
|
|
|
|
case CurRangeNoElement :
|
|
|
|
TraceMsg(TF_GENERAL, "There is no element in original range");
|
|
cpResultWrap->SetNumElements(0);
|
|
cpResultWrap->m_ulNumOfITN = 0;
|
|
break;
|
|
}
|
|
|
|
// Now generate a new properstore for the new range pR2.
|
|
// if the new property store is required.
|
|
|
|
if ( ppPs == NULL )
|
|
return hr;
|
|
|
|
if (dcDivideCase == CurRangeNoElement )
|
|
{
|
|
// there is no any element in the original property range.
|
|
*ppPs = NULL;
|
|
return hr;
|
|
}
|
|
|
|
CPropStoreRecoResultObject *prps = NULL;
|
|
if (S_OK == hr)
|
|
prps = new CPropStoreRecoResultObject(m_pimx, pR2);
|
|
|
|
if (prps)
|
|
{
|
|
hr = prps->QueryInterface(IID_ITfPropertyStore, (void **)ppPs);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
CRecoResultWrap *prw;
|
|
ULONG ulNextNum;
|
|
ULONG ulNumOfITN;
|
|
CComPtr<ISpRecoResult> cpResult;
|
|
|
|
if ( ulNextStartElement >= ulStartElement + ulNumElement)
|
|
{
|
|
// It is divided at the last element of the original range.
|
|
// We will just generate a property store for this Cicero ver 1.0
|
|
// to avoid the original property store be removed by Cicero engine.
|
|
|
|
// FutureConsider: if Cicero changes the logic in the future, we need to change
|
|
// this code as well so that we don't need to generate a property store
|
|
// for this second range.
|
|
|
|
ulNextNum = 0;
|
|
ulNumOfITN = 0;
|
|
ulSecondTSpaceRemoved = 0;
|
|
|
|
}
|
|
else
|
|
{
|
|
ulNextNum = ulStartElement + ulNumElement - ulNextStartElement;
|
|
ulNumOfITN = cpResultWrap->_RangeHasITN(ulNextStartElement, ulNextNum);
|
|
}
|
|
|
|
prw = new CRecoResultWrap(m_pimx, ulNextStartElement, ulNextNum, ulNumOfITN);
|
|
if ( prw != NULL )
|
|
{
|
|
hr = cpResultWrap->GetResult(&cpResult);
|
|
}
|
|
else
|
|
{
|
|
// Check interface pointer ref leak problem.
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
hr = prw->Init(cpResult);
|
|
}
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
prw->SetOffsetDelta(ulSecondDelta);
|
|
prw->SetCharsInTrail(ulSecondTrail);
|
|
prw->SetTrailSpaceRemoved( ulSecondTSpaceRemoved );
|
|
prw->m_bstrCurrentText = SysAllocString((WCHAR *)dstrSecond);
|
|
|
|
// Update ITN show-state list .
|
|
|
|
if ( ulNumOfITN > 0 )
|
|
{
|
|
SPITNSHOWSTATE *pITNShowState;
|
|
ULONG ulOrgNumOfITN;
|
|
|
|
ulOrgNumOfITN = rgOrgITNShowState.Count( );
|
|
|
|
for ( ULONG iIndex=0; iIndex<ulOrgNumOfITN; iIndex ++ )
|
|
{
|
|
pITNShowState = rgOrgITNShowState.GetPtr(iIndex);
|
|
|
|
if ( pITNShowState)
|
|
{
|
|
if ( (pITNShowState->ulITNStart
|
|
>= ulNextStartElement) &&
|
|
(pITNShowState->ulITNStart +
|
|
pITNShowState->ulITNNumElem)
|
|
<= (ulNextStartElement + ulNextNum) )
|
|
{
|
|
prw->_InitITNShowState(
|
|
pITNShowState->fITNShown,
|
|
pITNShowState->ulITNStart,
|
|
pITNShowState->ulITNNumElem);
|
|
}
|
|
}
|
|
} // for
|
|
} // if
|
|
|
|
// Update the Offset list for the second range.
|
|
|
|
if ( (ulNextNum > 0) && pulOffsetForSecond )
|
|
{
|
|
ULONG i;
|
|
ULONG ulOffset;
|
|
|
|
for ( i=0; i <= ulNextNum; i ++ )
|
|
{
|
|
ulOffset = pulOffsetForSecond[i];
|
|
prw->_SetElementNewOffset(ulNextStartElement + i, ulOffset);
|
|
}
|
|
}
|
|
|
|
hr = prps->_InitFromResultWrap(prw);
|
|
}
|
|
prw->Release( );
|
|
}
|
|
prps->Release();
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
if ( rgOrgITNShowState.Count( ) )
|
|
rgOrgITNShowState.Clear( );
|
|
|
|
if ( pulOffsetForSecond )
|
|
delete[] pulOffsetForSecond;
|
|
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// CPropStoreRecoResultObject::_Shrink
|
|
//
|
|
// receive EditCookie from edit session.
|
|
// try to determine the new range's attribute to update the property store or notify
|
|
// the ctf engine to discard it.
|
|
//
|
|
|
|
HRESULT CPropStoreRecoResultObject::_Shrink(TfEditCookie ec, ITfRange *pRange,BOOL *pfFree)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
WCHAR *pwszNewText = NULL;
|
|
long cchNew = 0;
|
|
WCHAR *pwszOrgText = NULL;
|
|
CSpDynamicString dstrOrg;
|
|
long cchOrg = 0;
|
|
CComPtr<ITfRange> cpRangeTemp;
|
|
CRecoResultWrap *cpResultWrap;
|
|
long iStartOffset; // the offset from start of the new range to the
|
|
// original range start point.
|
|
long iLastOffset; // The offset from the last character of new range
|
|
// to the original range start point.
|
|
ULONG ulNewStartElement, ulNewNumElements, ulNewDelta, ulNewTrail, ulNewTSRemoved;
|
|
ULONG ulOrgStartElement, ulOrgNumElements, ulOrgDelta, ulOrgTrail, ulOrgTSRemoved;
|
|
|
|
BOOL fShrinkToWrongPos = FALSE;
|
|
|
|
int iElementOffsetChanged = 0; // If the new range just remove the
|
|
// trailing space of original text,
|
|
// the new valid start and end element
|
|
// will keep unchanged, but the length of
|
|
// new end element is changed.
|
|
|
|
TraceMsg(TF_GENERAL, "CPropStoreRecoResultObject::_Shrink is called, this=0x%x", (INT_PTR)this);
|
|
|
|
if ( !pRange || !pfFree ) return E_INVALIDARG;
|
|
|
|
// Set *pfFree as TRUE intially in case there is an error occuring.
|
|
*pfFree = TRUE;
|
|
|
|
cpResultWrap = (CRecoResultWrap *)(void *)m_cpResultWrap;
|
|
|
|
if ( cpResultWrap == NULL ) return E_FAIL;
|
|
|
|
if ( (WCHAR *)cpResultWrap->m_bstrCurrentText == NULL )
|
|
cpResultWrap->_SetElementOffsetCch(NULL); // To update internal text based.
|
|
|
|
dstrOrg.Append((WCHAR *)cpResultWrap->m_bstrCurrentText);
|
|
pwszOrgText = (WCHAR *)dstrOrg;
|
|
|
|
ulOrgNumElements = cpResultWrap->GetNumElements( );
|
|
ulOrgStartElement = cpResultWrap->GetStart( );
|
|
ulOrgDelta = cpResultWrap->_GetOffsetDelta( );
|
|
ulOrgTrail = cpResultWrap->GetCharsInTrail( );
|
|
ulOrgTSRemoved = cpResultWrap->GetTrailSpaceRemoved( );
|
|
if ( pwszOrgText )
|
|
cchOrg = wcslen(pwszOrgText);
|
|
|
|
if ( (ulOrgNumElements ==0) || (pwszOrgText == NULL) || (cchOrg == 0) )
|
|
{
|
|
// This property store doesn't have resultwrap or doesn't have valid element.
|
|
// let cicero engine free this property store.
|
|
return hr;
|
|
}
|
|
|
|
pwszNewText = new WCHAR[cchOrg+1];
|
|
|
|
// try to get the new text pointed by pRange and character number this text.
|
|
if ( pwszNewText )
|
|
{
|
|
hr = pRange->Clone( &cpRangeTemp );
|
|
if ( hr == S_OK)
|
|
{
|
|
hr = cpRangeTemp->GetText(ec, 0, pwszNewText, cchOrg, (ULONG *)&cchNew);
|
|
}
|
|
|
|
// Get the new range's StartOffset and LastOffset in the original property range.
|
|
|
|
iStartOffset = 0;
|
|
iLastOffset = cchOrg;
|
|
|
|
if ( hr == S_OK && cchNew > 0 )
|
|
{
|
|
long i;
|
|
BOOL fFoundNewString=FALSE;
|
|
|
|
for (i=0; i<=(cchOrg-cchNew); i++)
|
|
{
|
|
WCHAR *pwszOrg;
|
|
|
|
pwszOrg = pwszOrgText + i;
|
|
|
|
if ( wcsncmp(pwszOrg, pwszNewText, cchNew) == 0 )
|
|
{
|
|
// Found the match
|
|
|
|
iStartOffset = i;
|
|
iLastOffset = i + cchNew;
|
|
fFoundNewString = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If we cannot find the new text as the substring in the original property text.
|
|
// It must be shrinked to a wrong place.
|
|
|
|
fShrinkToWrongPos = !fFoundNewString;
|
|
|
|
}
|
|
|
|
}
|
|
else
|
|
hr = E_OUTOFMEMORY;
|
|
|
|
if ( hr != S_OK || fShrinkToWrongPos)
|
|
goto CleanUp;
|
|
|
|
TraceMsg(TF_GENERAL, "Shrink: NewText: cchNew=%d :\"%S\"", cchNew, pwszNewText);
|
|
TraceMsg(TF_GENERAL, "Shrink: OrgText: cchOrg=%d :\"%S\"", cchOrg, pwszOrgText);
|
|
TraceMsg(TF_GENERAL, "Shrink: Org: StartElem=%d NumElem=%d Delta=%d, Trail=%d, TSRemoved=%d", ulOrgStartElement, ulOrgNumElements, ulOrgDelta, ulOrgTrail, ulOrgTSRemoved);
|
|
TraceMsg(TF_GENERAL, "Shrink: iStartOffset=%d, iLastOffset=%d", iStartOffset, iLastOffset);
|
|
|
|
if ( cpResultWrap->IsElementOffsetIntialized( ) == FALSE )
|
|
cpResultWrap->_SetElementOffsetCch(NULL);
|
|
|
|
ulNewStartElement = ulOrgStartElement;
|
|
ulNewDelta = ulOrgDelta;
|
|
ulNewTrail = ulOrgTrail;
|
|
|
|
// Calculate ulNewStartElement and ulNewDelta.
|
|
if ( (ULONG)iStartOffset <= ulOrgDelta )
|
|
{
|
|
ulNewDelta = ulOrgDelta - iStartOffset;
|
|
ulNewStartElement = ulOrgStartElement;
|
|
}
|
|
else
|
|
{
|
|
ULONG iElement;
|
|
ULONG ulOrgStartOffset;
|
|
|
|
ulOrgStartOffset = cpResultWrap->_GetElementOffsetCch(ulOrgStartElement);
|
|
|
|
for ( iElement=ulOrgStartElement; iElement < ulOrgStartElement + ulOrgNumElements; iElement++)
|
|
{
|
|
ULONG ulToElement;
|
|
ULONG ulAfterElement;
|
|
|
|
ulToElement = cpResultWrap->_GetElementOffsetCch(iElement) - ulOrgStartOffset + ulOrgDelta;
|
|
ulAfterElement = cpResultWrap->_GetElementOffsetCch(iElement+1) - ulOrgStartOffset + ulOrgDelta;
|
|
|
|
if ( ulToElement == (ULONG)iStartOffset )
|
|
{
|
|
ulNewStartElement = iElement;
|
|
ulNewDelta = 0;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
if ( (ulToElement < (ULONG)iStartOffset) && (ulAfterElement > (ULONG)iStartOffset))
|
|
{
|
|
ulNewStartElement = iElement + 1;
|
|
ulNewDelta = ulAfterElement - iStartOffset;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Calculate new ulNewNumElements.
|
|
|
|
ulNewNumElements = 0;
|
|
|
|
if ( iLastOffset == cchOrg )
|
|
{
|
|
//
|
|
ULONG ulNewEndElement;
|
|
|
|
// New End is the same as org End.
|
|
ulNewEndElement = ulOrgStartElement + ulOrgNumElements - 1;
|
|
|
|
ulNewNumElements = 1 + ulNewEndElement - ulNewStartElement;
|
|
|
|
}
|
|
else
|
|
{
|
|
long iElement;
|
|
ULONG ulOrgStartOffset;
|
|
ULONG ulOrgEndElement;
|
|
ULONG ulNewEndElement;
|
|
BOOL fFound;
|
|
|
|
ulOrgEndElement = ulOrgStartElement + ulOrgNumElements - 1;
|
|
ulOrgStartOffset = cpResultWrap->_GetElementOffsetCch(ulOrgStartElement);
|
|
|
|
fFound = FALSE;
|
|
for ( iElement=(long)ulOrgEndElement; iElement >= (long)ulOrgStartElement; iElement--)
|
|
{
|
|
ULONG ulToElement;
|
|
ULONG ulAfterElement;
|
|
BOOL fInsideITN;
|
|
ULONG ulITNStart, ulITNNumElem;
|
|
ULONG ulCurElement;
|
|
|
|
ulToElement = cpResultWrap->_GetElementOffsetCch(iElement) - ulOrgStartOffset + ulOrgDelta;
|
|
ulAfterElement = cpResultWrap->_GetElementOffsetCch(iElement+1) - ulOrgStartOffset + ulOrgDelta;
|
|
|
|
if ( iElement == (long)ulOrgEndElement && ( ulAfterElement <= (ULONG)iLastOffset ) )
|
|
{
|
|
// This org last element would be the new last element
|
|
ulNewEndElement = iElement;
|
|
ulNewTrail = (ULONG)iLastOffset - ulAfterElement;
|
|
fFound = TRUE;
|
|
break;
|
|
}
|
|
|
|
fInsideITN = cpResultWrap->_CheckITNForElement(NULL, iElement, &ulITNStart, &ulITNNumElem, NULL);
|
|
|
|
if ( fInsideITN )
|
|
ulCurElement = ulITNStart;
|
|
else
|
|
ulCurElement = iElement;
|
|
|
|
if ( ulToElement == (ULONG)iLastOffset )
|
|
{
|
|
ulNewEndElement = ulCurElement - 1;
|
|
ulNewTrail = 0;
|
|
|
|
fFound = TRUE;
|
|
break;
|
|
}
|
|
|
|
if ( (ulToElement < (ULONG)iLastOffset) && (ulAfterElement > (ULONG)iLastOffset))
|
|
{
|
|
if ( pwszOrgText[iLastOffset] == L' ')
|
|
{
|
|
// The trailing space is now removed from the original element.
|
|
// we need to update the length for this element. ( update the offset
|
|
// for next element).
|
|
|
|
iElementOffsetChanged = ulAfterElement - iLastOffset;
|
|
|
|
if ( fInsideITN )
|
|
ulNewEndElement = ulITNStart + ulITNNumElem - 1;
|
|
else
|
|
ulNewEndElement = iElement;
|
|
|
|
ulNewTrail = 0;
|
|
}
|
|
else
|
|
{
|
|
ulNewEndElement = ulCurElement - 1;
|
|
ulNewTrail = (ULONG)iLastOffset - ulToElement;
|
|
}
|
|
|
|
fFound = TRUE;
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
if ( fFound )
|
|
ulNewNumElements = 1 + ulNewEndElement - ulNewStartElement;
|
|
}
|
|
|
|
ulNewTSRemoved = ulOrgTSRemoved + iElementOffsetChanged;
|
|
|
|
TraceMsg(TF_GENERAL, "Shrink: New: StartElem=%d NumElem=%d Delta=%d, Trail=%d, TSRemoved=%d", ulNewStartElement, ulNewNumElements, ulNewDelta, ulNewTrail, ulNewTSRemoved);
|
|
|
|
// If there is no valid element in the new range, discard this property store
|
|
// otherwise, keep it and update the related data members.
|
|
|
|
if ( ulNewNumElements > 0 )
|
|
{
|
|
ULONG ulNumOfITN;
|
|
|
|
*pfFree = FALSE;
|
|
|
|
CComPtr<ITfRange> cpRange;
|
|
hr = pRange->Clone(&cpRange);
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
m_cpRange = cpRange;
|
|
|
|
cpResultWrap->SetStart(ulNewStartElement);
|
|
cpResultWrap->SetNumElements(ulNewNumElements);
|
|
cpResultWrap->SetOffsetDelta(ulNewDelta);
|
|
cpResultWrap->SetCharsInTrail(ulNewTrail);
|
|
cpResultWrap->SetTrailSpaceRemoved( ulNewTSRemoved );
|
|
|
|
ulNumOfITN = cpResultWrap->_RangeHasITN(ulNewStartElement, ulNewNumElements);
|
|
|
|
cpResultWrap->m_ulNumOfITN = ulNumOfITN;
|
|
|
|
// Update ITN show-state list
|
|
|
|
if ( ulNumOfITN > 0 )
|
|
{
|
|
SPITNSHOWSTATE *pITNShowState;
|
|
ULONG ulOrgNumOfITN;
|
|
|
|
ulOrgNumOfITN = cpResultWrap->m_rgITNShowState.Count( );
|
|
|
|
for ( ULONG iIndex=ulOrgNumOfITN; iIndex>0; iIndex -- )
|
|
{
|
|
pITNShowState = cpResultWrap->m_rgITNShowState.GetPtr(iIndex-1);
|
|
|
|
if ( pITNShowState)
|
|
{
|
|
if ( (pITNShowState->ulITNStart < ulNewStartElement) ||
|
|
(pITNShowState->ulITNStart + pITNShowState->ulITNNumElem) > (ulNewStartElement + ulNewNumElements) )
|
|
{
|
|
// This ITN is not in the new Range
|
|
cpResultWrap->m_rgITNShowState.Remove(iIndex-1, 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
if ( cpResultWrap->m_rgITNShowState.Count( ) )
|
|
cpResultWrap->m_rgITNShowState.Clear( );
|
|
|
|
if ( cpResultWrap->m_bstrCurrentText )
|
|
SysFreeString(cpResultWrap->m_bstrCurrentText);
|
|
|
|
cpResultWrap->m_bstrCurrentText = SysAllocString(pwszNewText);
|
|
|
|
if ( iElementOffsetChanged != 0 )
|
|
{
|
|
ULONG ulNewOffset;
|
|
ULONG ulElemAfterEnd;
|
|
|
|
ulElemAfterEnd = ulNewStartElement + ulNewNumElements;
|
|
ulNewOffset = cpResultWrap->_GetElementOffsetCch(ulElemAfterEnd);
|
|
cpResultWrap->_SetElementNewOffset(ulElemAfterEnd, ulNewOffset - iElementOffsetChanged);
|
|
}
|
|
}
|
|
}
|
|
|
|
CleanUp:
|
|
if ( pwszNewText ) delete[] pwszNewText;
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// CPropStoreRecoResultObject::Clone
|
|
//
|
|
// synopsis : make a new cloned propstore which shares the same SAPI result
|
|
// object as the current class instance
|
|
//
|
|
//
|
|
STDMETHODIMP CPropStoreRecoResultObject::Clone(ITfPropertyStore **ppPropStore)
|
|
{
|
|
HRESULT hr;
|
|
CPropStoreRecoResultObject *prps = new CPropStoreRecoResultObject(m_pimx, m_cpRange);
|
|
if (prps)
|
|
{
|
|
hr = prps->QueryInterface(IID_ITfPropertyStore, (void **)ppPropStore);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
CRecoResultWrap *prw = NULL;
|
|
CRecoResultWrap *pRecoWrapOrg = NULL;
|
|
|
|
if ( m_cpResultWrap )
|
|
{
|
|
hr = m_cpResultWrap->QueryInterface(IID_PRIV_RESULTWRAP, (void **)&pRecoWrapOrg);
|
|
|
|
if ( hr == S_OK )
|
|
{
|
|
hr = pRecoWrapOrg->Clone(&prw);
|
|
}
|
|
|
|
SafeRelease(pRecoWrapOrg);
|
|
|
|
if ( hr == S_OK )
|
|
hr = prps->_InitFromResultWrap(prw);
|
|
|
|
SafeRelease(prw);
|
|
}
|
|
}
|
|
prps->Release();
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CPropStoreRecoResultObject::GetPropertyRangeCreator(CLSID *pclsid)
|
|
{
|
|
HRESULT hr = E_INVALIDARG;
|
|
|
|
if (pclsid)
|
|
{
|
|
*pclsid = CLSID_SapiLayr;
|
|
hr = S_OK;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// CPropStoreRecoResultObject::Serialize
|
|
//
|
|
// synopsis: takes a pointer to an IStream and get the current
|
|
// SAPI result object serialized
|
|
//
|
|
// changes from CResultPropertyStore:
|
|
// Uses SAPI's result object to get the blob
|
|
// serialized. ISpResultObject has to be cloned
|
|
// in order to keep the object alive after
|
|
// 'detaching' it.
|
|
//
|
|
//
|
|
STDMETHODIMP CPropStoreRecoResultObject::Serialize(IStream *pStream, ULONG *pcb)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
|
|
TraceMsg(TF_GENERAL, "Serialize is called, this = 0x%x", (INT_PTR)this);
|
|
|
|
if (m_cpResultWrap && pStream)
|
|
{
|
|
CComPtr<IServiceProvider> cpServicePrv;
|
|
CComPtr<ISpRecoResult> cpRecoResult;
|
|
SPSERIALIZEDRESULT *pResBlock;
|
|
ULONG ulrw1 = 0;
|
|
ULONG ulrw2 = 0;
|
|
|
|
CRecoResultWrap *cpRecoWrap;
|
|
ULONG ulSizeRecoWrap, ulTextNum, ulITNSize;
|
|
ULONG ulOffsetSize, ulOffsetNum;
|
|
RECOWRAPDATA *pRecoWrapData;
|
|
|
|
cpRecoWrap = (CRecoResultWrap *)(void *)m_cpResultWrap;
|
|
|
|
// We want to save m_ulStartElement, m_ulNumElements, m_OffsetDelta, ulNumOfITN, m_bstrCurrentText
|
|
// and a list of ITN show state structure in the RecoResultWrap to the serialized stream.
|
|
|
|
ulTextNum = 0;
|
|
if (cpRecoWrap->m_bstrCurrentText)
|
|
{
|
|
ulTextNum = wcslen(cpRecoWrap->m_bstrCurrentText) + 1; // plus NULL terminator
|
|
}
|
|
|
|
ulITNSize = cpRecoWrap->m_ulNumOfITN * sizeof(SPITNSHOWSTATE);
|
|
|
|
if ( cpRecoWrap->IsElementOffsetIntialized( ) )
|
|
ulOffsetNum = cpRecoWrap->GetNumElements( ) + 1;
|
|
else
|
|
ulOffsetNum = 0;
|
|
|
|
ulOffsetSize = ulOffsetNum * sizeof(ULONG);
|
|
|
|
// Serialiezed data will contain RECOWRAPDATA struct, ITN show-state list, Offset list and m_bstrCurrentText.
|
|
|
|
ulSizeRecoWrap = sizeof(RECOWRAPDATA) + ulITNSize + ulOffsetSize + sizeof(WCHAR) * ulTextNum;
|
|
|
|
pRecoWrapData = (RECOWRAPDATA *)cicMemAllocClear(ulSizeRecoWrap);
|
|
|
|
if (pRecoWrapData)
|
|
{
|
|
WCHAR *pwszText;
|
|
|
|
pRecoWrapData->ulSize = ulSizeRecoWrap;
|
|
pRecoWrapData->ulStartElement = cpRecoWrap->GetStart( );
|
|
pRecoWrapData->ulNumElements = cpRecoWrap->GetNumElements( );
|
|
pRecoWrapData->ulOffsetDelta = cpRecoWrap->_GetOffsetDelta( );
|
|
pRecoWrapData->ulCharsInTrail = cpRecoWrap->GetCharsInTrail( );
|
|
pRecoWrapData->ulTrailSpaceRemoved = cpRecoWrap->GetTrailSpaceRemoved( );
|
|
pRecoWrapData->ulNumOfITN = cpRecoWrap->m_ulNumOfITN;
|
|
pRecoWrapData->ulOffsetNum = ulOffsetNum;
|
|
|
|
// Save the ITN show-state list
|
|
|
|
if ( cpRecoWrap->m_ulNumOfITN > 0 )
|
|
{
|
|
SPITNSHOWSTATE *pITNShowState;
|
|
|
|
pITNShowState = (SPITNSHOWSTATE *)((BYTE *)pRecoWrapData + sizeof(RECOWRAPDATA));
|
|
|
|
for ( ULONG i=0; i<cpRecoWrap->m_ulNumOfITN; i++)
|
|
{
|
|
SPITNSHOWSTATE *pITNShowStateSource;
|
|
|
|
pITNShowStateSource = cpRecoWrap->m_rgITNShowState.GetPtr(i);
|
|
|
|
if ( pITNShowStateSource )
|
|
{
|
|
pITNShowState->fITNShown = pITNShowStateSource->fITNShown;
|
|
pITNShowState->ulITNNumElem = pITNShowStateSource->ulITNNumElem;
|
|
pITNShowState->ulITNStart = pITNShowStateSource->ulITNStart;
|
|
|
|
pITNShowState ++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Save the offset list
|
|
|
|
if ( ulOffsetSize > 0 )
|
|
{
|
|
ULONG *pulOffset;
|
|
|
|
pulOffset = (ULONG *)((BYTE *)pRecoWrapData + sizeof(RECOWRAPDATA) + ulITNSize);
|
|
|
|
for (ULONG i=0; i<ulOffsetNum; i++)
|
|
{
|
|
pulOffset[i] = cpRecoWrap->_GetElementOffsetCch(pRecoWrapData->ulStartElement + i );
|
|
}
|
|
}
|
|
|
|
if (cpRecoWrap->m_bstrCurrentText)
|
|
{
|
|
pwszText = (WCHAR *)((BYTE *)pRecoWrapData + sizeof(RECOWRAPDATA) + ulITNSize + ulOffsetSize);
|
|
StringCchCopyW(pwszText, ulTextNum, cpRecoWrap->m_bstrCurrentText);
|
|
}
|
|
|
|
hr = pStream->Write(
|
|
pRecoWrapData,
|
|
ulSizeRecoWrap, // the number of bytes to copy
|
|
&ulrw1
|
|
);
|
|
|
|
if ( SUCCEEDED(hr) && (ulrw1 == ulSizeRecoWrap))
|
|
{
|
|
|
|
// QI the service provider first then get to the sapi interface
|
|
//
|
|
hr = m_cpResultWrap->QueryInterface(IID_IServiceProvider, (void **)&cpServicePrv);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = cpServicePrv->QueryService(GUID_NULL, IID_ISpRecoResult, (void **)&cpRecoResult);
|
|
}
|
|
|
|
// 'detach' the result to a mem chunk
|
|
//
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = cpRecoResult->Serialize(&pResBlock);
|
|
}
|
|
|
|
// serialize the chunk to the stream
|
|
//
|
|
if (SUCCEEDED(hr) && pResBlock)
|
|
{
|
|
hr = pStream->Write(
|
|
pResBlock,
|
|
(ULONG)pResBlock->ulSerializedSize, // the number of bytes to copy
|
|
&ulrw2
|
|
);
|
|
|
|
if (pcb)
|
|
*pcb = ulrw1 + ulrw2;
|
|
|
|
// no need for the detached mem chunk
|
|
CoTaskMemFree(pResBlock);
|
|
}
|
|
}
|
|
|
|
cicMemFree(pRecoWrapData);
|
|
}
|
|
else
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// CPropStoreRecoResultObject::_InitFromIStream
|
|
//
|
|
// stores IStream copied from param
|
|
//
|
|
HRESULT CPropStoreRecoResultObject::_InitFromIStream(IStream *pStream, int iSize, ISpRecoContext *pRecoCtxt)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
ULONG ulSize = (ULONG)iSize;
|
|
|
|
if (!pStream) return E_INVALIDARG;
|
|
|
|
// alloc the mem chunk for the reco
|
|
// blob
|
|
if ( ulSize == 0 )
|
|
{
|
|
STATSTG stg;
|
|
hr = pStream->Stat(&stg, STATFLAG_NONAME);
|
|
|
|
if (SUCCEEDED(hr))
|
|
ulSize = (int)stg.cbSize.LowPart;
|
|
}
|
|
|
|
// got size from given stream or param
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
|
|
// First We want to get RECOWRAPDATA at the begining of the stream.
|
|
RECOWRAPDATA rwData;
|
|
|
|
hr = pStream->Read(
|
|
&rwData, // the destination buf
|
|
sizeof(RECOWRAPDATA), // the number of bytes to read
|
|
NULL
|
|
);
|
|
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
|
|
ULONG ulITNSize;
|
|
SPITNSHOWSTATE *pITNShowState = NULL;
|
|
|
|
ulITNSize = rwData.ulNumOfITN * sizeof(SPITNSHOWSTATE);
|
|
|
|
if ( ulITNSize > 0 )
|
|
{
|
|
|
|
pITNShowState = (SPITNSHOWSTATE *)cicMemAllocClear(ulITNSize);
|
|
rwData.pITNShowState = pITNShowState;
|
|
|
|
if ( pITNShowState )
|
|
{
|
|
hr = pStream->Read(
|
|
pITNShowState, // the destination buf
|
|
ulITNSize, // the number of bytes to read
|
|
NULL
|
|
);
|
|
}
|
|
else
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
ULONG *pulOffsetElement = NULL;
|
|
ULONG ulOffsetSize, ulOffsetNum;
|
|
|
|
ulOffsetNum = rwData.ulOffsetNum;
|
|
ulOffsetSize = ulOffsetNum * sizeof(ULONG);
|
|
|
|
if ( SUCCEEDED(hr) && ulOffsetSize > 0 )
|
|
{
|
|
pulOffsetElement = (ULONG *) cicMemAllocClear(ulOffsetSize);
|
|
rwData.pulOffset = pulOffsetElement;
|
|
|
|
if ( pulOffsetElement )
|
|
{
|
|
hr = pStream->Read(
|
|
pulOffsetElement, // the destination buf
|
|
ulOffsetSize, // the number of bytes to read
|
|
NULL
|
|
);
|
|
}
|
|
else
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
if ( SUCCEEDED(hr))
|
|
{
|
|
|
|
ULONG ulTextSize;
|
|
WCHAR *pwszText;
|
|
|
|
ulTextSize = rwData.ulSize - sizeof(RECOWRAPDATA) - ulITNSize - ulOffsetSize;
|
|
pwszText = (WCHAR *) cicMemAllocClear(ulTextSize);
|
|
|
|
rwData.pwszText = pwszText;
|
|
|
|
if ( pwszText )
|
|
{
|
|
hr = pStream->Read(
|
|
pwszText, // the destination buf
|
|
ulTextSize, // the number of bytes to read
|
|
NULL
|
|
);
|
|
|
|
if ( SUCCEEDED(hr) )
|
|
{
|
|
// prepare cotaskmem chunk
|
|
SPSERIALIZEDRESULT *pResBlock = (SPSERIALIZEDRESULT *)CoTaskMemAlloc(ulSize - rwData.ulSize + sizeof(ULONG)*4);
|
|
if (pResBlock)
|
|
{
|
|
CComPtr<ISpRecoResult> cpResult;
|
|
|
|
hr = pStream->Read(
|
|
pResBlock, // the destination buf
|
|
ulSize - rwData.ulSize, // the number of bytes to read
|
|
NULL
|
|
);
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
// now create a reco result from the blob data
|
|
hr = pRecoCtxt->DeserializeResult(pResBlock, &cpResult);
|
|
}
|
|
|
|
CoTaskMemFree(pResBlock);
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
_InitFromRecoResult(cpResult, &rwData);
|
|
}
|
|
}
|
|
}
|
|
cicMemFree(pwszText);
|
|
}
|
|
else
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
if ( (hr == S_OK) && (pITNShowState != NULL) )
|
|
cicMemFree(pITNShowState);
|
|
|
|
if ( pulOffsetElement != NULL )
|
|
cicMemFree(pulOffsetElement);
|
|
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CPropStoreRecoResultObject::_InitFromRecoResult(ISpRecoResult *pResult, RECOWRAPDATA *pRecoWrapData)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
ULONG ulStartElement = 0;
|
|
ULONG ulNumElements = 0;
|
|
ULONG ulNumOfITN = 0;
|
|
ULONG ulOffsetNum = 0;
|
|
|
|
m_cpResultWrap.Release();
|
|
|
|
if ( pRecoWrapData == NULL )
|
|
return E_INVALIDARG;
|
|
|
|
// get start/num of elements
|
|
ulStartElement = pRecoWrapData->ulStartElement;
|
|
ulNumElements = pRecoWrapData->ulNumElements;
|
|
ulNumOfITN = pRecoWrapData->ulNumOfITN;
|
|
ulOffsetNum = pRecoWrapData->ulOffsetNum;
|
|
|
|
CRecoResultWrap *prw = new CRecoResultWrap(m_pimx, ulStartElement, ulNumElements, ulNumOfITN);
|
|
|
|
if (prw)
|
|
{
|
|
hr = prw->Init(pResult);
|
|
}
|
|
else
|
|
hr = E_OUTOFMEMORY;
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
m_cpResultWrap = SAFECAST(prw, IUnknown *);
|
|
|
|
prw->SetOffsetDelta(pRecoWrapData->ulOffsetDelta);
|
|
prw->SetCharsInTrail(pRecoWrapData->ulCharsInTrail);
|
|
prw->SetTrailSpaceRemoved(pRecoWrapData->ulTrailSpaceRemoved);
|
|
prw->m_bstrCurrentText = SysAllocString(pRecoWrapData->pwszText);
|
|
|
|
// Update ITN show-state list
|
|
|
|
if ( (ulNumOfITN > 0) && pRecoWrapData->pITNShowState )
|
|
{
|
|
SPITNSHOWSTATE *pITNShowState;
|
|
|
|
pITNShowState = pRecoWrapData->pITNShowState;
|
|
|
|
for ( ULONG i=0; i<ulNumOfITN; i++)
|
|
{
|
|
prw->_InitITNShowState(pITNShowState->fITNShown, pITNShowState->ulITNStart, pITNShowState->ulITNNumElem);
|
|
pITNShowState ++;
|
|
}
|
|
}
|
|
|
|
// Update the element Offset list.
|
|
|
|
if ( (ulOffsetNum > 0) && (pRecoWrapData->pulOffset ))
|
|
{
|
|
|
|
ULONG *pulOffset;
|
|
|
|
pulOffset = pRecoWrapData->pulOffset;
|
|
|
|
for (ULONG i=0; i< ulOffsetNum; i++)
|
|
{
|
|
prw->_SetElementNewOffset(i + ulStartElement, pulOffset[i] );
|
|
}
|
|
}
|
|
|
|
prw->Release();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CPropStoreRecoResultObject::_InitFromResultWrap(IUnknown *pResWrap)
|
|
{
|
|
m_cpResultWrap.Release();
|
|
|
|
m_cpResultWrap = pResWrap;
|
|
|
|
if (m_cpResultWrap)
|
|
{
|
|
return S_OK;
|
|
}
|
|
else
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
|
|
// end of CPropStoreRecoResultObject implementation
|
|
|
|
|
|
//
|
|
// CPropStoreLMLattice implementation
|
|
//
|
|
|
|
// ctor
|
|
CPropStoreLMLattice::CPropStoreLMLattice(CSapiIMX *pimx)
|
|
{
|
|
// init a shared recognition context
|
|
m_cpResultWrap = NULL;
|
|
|
|
m_pimx = pimx;
|
|
|
|
m_cRef = 1;
|
|
}
|
|
|
|
// dtor
|
|
CPropStoreLMLattice::~CPropStoreLMLattice()
|
|
{
|
|
}
|
|
|
|
|
|
// IUnknown
|
|
STDMETHODIMP CPropStoreLMLattice::QueryInterface(REFIID riid, void **ppvObj)
|
|
{
|
|
HRESULT hr;
|
|
|
|
Assert(ppvObj);
|
|
|
|
if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_ITfPropertyStore))
|
|
{
|
|
*ppvObj = this;
|
|
hr = S_OK;
|
|
|
|
this->m_cRef++;
|
|
}
|
|
else
|
|
{
|
|
*ppvObj = NULL;
|
|
hr = E_NOINTERFACE;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CPropStoreLMLattice::AddRef(void)
|
|
{
|
|
this->m_cRef++;
|
|
|
|
return this->m_cRef;
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CPropStoreLMLattice::Release(void)
|
|
{
|
|
this->m_cRef--;
|
|
|
|
if (this->m_cRef > 0)
|
|
{
|
|
return this->m_cRef;
|
|
}
|
|
delete this;
|
|
|
|
return 0;
|
|
}
|
|
|
|
// ITfPropertyStore
|
|
|
|
STDMETHODIMP CPropStoreLMLattice::GetType(GUID *pguid)
|
|
{
|
|
HRESULT hr = E_INVALIDARG;
|
|
if (pguid)
|
|
{
|
|
*pguid = GUID_PROP_LMLATTICE;
|
|
hr = S_OK;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CPropStoreLMLattice::GetDataType(DWORD *pdwReserved)
|
|
{
|
|
HRESULT hr = E_INVALIDARG;
|
|
if (pdwReserved)
|
|
{
|
|
*pdwReserved = 0;
|
|
hr = S_OK;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CPropStoreLMLattice::GetData(VARIANT *pvarValue)
|
|
{
|
|
HRESULT hr = E_INVALIDARG;
|
|
|
|
if (pvarValue)
|
|
{
|
|
QuickVariantInit(pvarValue);
|
|
|
|
if (m_cpResultWrap)
|
|
{
|
|
// return ITfLMLattice object
|
|
// we defer the creation of LMlattice object until
|
|
// the time master LM TIP actually access it
|
|
//
|
|
if (!m_cpLMLattice)
|
|
{
|
|
CLMLattice *pLattice = new CLMLattice(m_pimx, m_cpResultWrap);
|
|
if (pLattice)
|
|
{
|
|
m_cpLMLattice = pLattice;
|
|
pLattice->Release();
|
|
}
|
|
|
|
}
|
|
|
|
if (m_cpLMLattice)
|
|
{
|
|
IUnknown *pUnk = NULL;
|
|
pvarValue->vt = VT_UNKNOWN;
|
|
hr = m_cpLMLattice->QueryInterface(IID_IUnknown, (void**)&pUnk);
|
|
if (S_OK == hr)
|
|
{
|
|
pvarValue->punkVal = pUnk;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CPropStoreLMLattice::OnTextUpdated(DWORD dwFlags, ITfRange *pRange, BOOL *pfAccept)
|
|
{
|
|
*pfAccept = FALSE;
|
|
if (dwFlags & TF_TU_CORRECTION)
|
|
{
|
|
*pfAccept = TRUE;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CPropStoreLMLattice::Shrink(ITfRange *pRange, BOOL *pfFree)
|
|
{
|
|
// could we have something done here?
|
|
*pfFree = TRUE;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CPropStoreLMLattice::Divide(ITfRange *pRangeThis, ITfRange *pRangeNew, ITfPropertyStore **ppPropStore)
|
|
{
|
|
// 12/17/1999
|
|
// [dividing a range implementation strategy]
|
|
//
|
|
// - pRangeThis contains the text range *before* the dividing point
|
|
// - pRangeNew contrains the range *after* the dividing point
|
|
// - First, adjust this property store to correctly hold a start element and #of element
|
|
// for pRangeThis
|
|
// - then create a new property store for pRangeNew, which will share the same
|
|
// result blob.
|
|
//
|
|
|
|
// just an experiment to see if cutting the range works.
|
|
// *ppPropStore = NULL;
|
|
Assert(ppPropStore);
|
|
Assert(pRangeThis);
|
|
Assert(pRangeNew);
|
|
|
|
CComPtr<ITfContext> cpic;
|
|
HRESULT hr = pRangeThis->GetContext(&cpic);
|
|
|
|
if (SUCCEEDED(hr) && cpic)
|
|
{
|
|
CPSLMEditSession *pes;
|
|
if (pes = new CPSLMEditSession(this, pRangeThis, cpic))
|
|
{
|
|
pes->_SetEditSessionData(ESCB_PROP_DIVIDE, NULL, 0);
|
|
pes->_SetUnk((IUnknown *)pRangeNew);
|
|
cpic->RequestEditSession(m_pimx->_GetId(), pes, TF_ES_READ | TF_ES_SYNC, &hr);
|
|
|
|
if ( SUCCEEDED(hr) )
|
|
*ppPropStore = (ITfPropertyStore *)pes->_GetRetUnknown( );
|
|
|
|
pes->Release();
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// CPropStoreLMLattice::_Divide
|
|
//
|
|
// synopsis : receives edit cookie from edit session
|
|
// so that we can manipulate with ranges
|
|
// to set starting elements/# of elements
|
|
//
|
|
//
|
|
HRESULT CPropStoreLMLattice::_Divide(TfEditCookie ec, ITfRange *pR1, ITfRange *pR2, ITfPropertyStore **ppPs)
|
|
{
|
|
// TODO: based on the given ranges, we calculate the offsets of elements and return a new propstore with
|
|
// later half of elements
|
|
|
|
// some clarifications: in case the lattice object has never been accessed, our result wrap object processes
|
|
// ITfPropertyStore::Divide and Shrink for us.
|
|
//
|
|
|
|
return Clone(ppPs);
|
|
}
|
|
|
|
//
|
|
// CPropStoreLMLattice::Clone
|
|
//
|
|
// synopsis : make a new cloned propstore which shares the same SAPI result
|
|
// object as the current class instance
|
|
//
|
|
//
|
|
STDMETHODIMP CPropStoreLMLattice::Clone(ITfPropertyStore **ppPropStore)
|
|
{
|
|
HRESULT hr;
|
|
CPropStoreLMLattice *prps = new CPropStoreLMLattice(m_pimx);
|
|
if (prps)
|
|
{
|
|
hr = prps->QueryInterface(IID_ITfPropertyStore, (void **)ppPropStore);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = prps->_InitFromResultWrap(m_cpResultWrap);
|
|
}
|
|
prps->Release();
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CPropStoreLMLattice::GetPropertyRangeCreator(CLSID *pclsid)
|
|
{
|
|
HRESULT hr = E_INVALIDARG;
|
|
|
|
if (pclsid)
|
|
{
|
|
*pclsid = CLSID_SapiLayr;
|
|
hr = S_OK;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// CPropStoreLMLattice::Serialize
|
|
//
|
|
// synopsis: I don't believe it is very useful to get lattice data
|
|
// persisted to doc file. We can always generate it on the fly
|
|
// from device native blob data
|
|
//
|
|
//
|
|
STDMETHODIMP CPropStoreLMLattice::Serialize(IStream *pStream, ULONG *pcb)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
|
|
HRESULT CPropStoreLMLattice::_InitFromResultWrap(IUnknown *pResWrap)
|
|
{
|
|
m_cpResultWrap.Release();
|
|
|
|
m_cpResultWrap = pResWrap;
|
|
|
|
if (m_cpResultWrap)
|
|
{
|
|
return S_OK;
|
|
}
|
|
else
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
|
|
// private IID for reco result wrapper
|
|
//
|
|
// IID_PRIV_RESULTWRAP
|
|
// b3407713-50d7-4465-97f9-87ad1e752dc5
|
|
//
|
|
const IID IID_PRIV_RESULTWRAP = {
|
|
0xb3407713,
|
|
0x50d7,
|
|
0x4465,
|
|
{0x97, 0xf9, 0x87, 0xad, 0x1e, 0x75, 0x2d, 0xc5}
|
|
};
|