// // 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 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 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 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 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_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 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) { 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 cpPhrase; if (pAlt) { cpPhrase = pAlt; } else { CComPtr 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 0 ) { for (ULONG i=0; i 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 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 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 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; julITNNumElem = 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 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; icReplacements; 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 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; iIndexulITNStart; 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; jcReplacements; 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 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 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 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 cpRangeTemp; CComPtr 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 cpRangeTemp; ULONG ulFirstDelta, ulSecondDelta; ULONG ulFirstTrail, ulSecondTrail; ULONG ulFirstTSpaceRemoved, ulSecondTSpaceRemoved; CStructArray 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 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; im_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 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; iIndexulITNStart >= 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 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 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 cpServicePrv; CComPtr 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; im_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_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 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_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 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} };