Leaked source code of windows server 2003
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

//
// 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}
};