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

  1. //
  2. // property store class implementation
  3. //
  4. // includes
  5. #include "private.h"
  6. #include "globals.h"
  7. #include "sapilayr.h"
  8. #include "propstor.h"
  9. #include "ids.h"
  10. #include "cicspres.h"
  11. #include "lmlattic.h"
  12. #ifdef DEBUG
  13. #include "wchar.h"
  14. void DebugPrintOut(WCHAR *pwszStrName, WCHAR *pwszStr)
  15. {
  16. WCHAR wszBuf[80];
  17. int iLen, i, j;
  18. TraceMsg(TF_GENERAL, "the value of %S is", pwszStrName );
  19. iLen = wcslen(pwszStr);
  20. j = 0;
  21. for ( i=0; i<iLen; i++)
  22. {
  23. if ( (pwszStr[i] < 0x80) && pwszStr[i] > 0x1f )
  24. {
  25. wszBuf[j] = pwszStr[i];
  26. j ++;
  27. }
  28. else
  29. {
  30. WCHAR buf[8];
  31. StringCchPrintfW(buf, ARRAYSIZE(buf), L" %4X ", (int)pwszStr[i] );
  32. for ( int x=0; x< (int)wcslen(buf); x++)
  33. {
  34. wszBuf[j] = buf[x];
  35. j++;
  36. }
  37. }
  38. }
  39. wszBuf[j] = L'\0';
  40. TraceMsg(TF_GENERAL, "%S", wszBuf);
  41. }
  42. #endif
  43. // ------------------------------------------------------------------------------------------------------------
  44. // This is a global or standalone function which will be called by CRecoResultWrap and CSapiAlternativeList
  45. //
  46. // This function receives the phrase text buffer, this element's display text and display attribute, and
  47. // previous element's display attribute.
  48. //
  49. // On Exit, it will update the phrase text buffer to append this element's text, and return the real offset
  50. // length value for this element.
  51. //
  52. // -------------------------------------------------------------------------------------------------------------
  53. HRESULT HandlePhraseElement( CSpDynamicString *pDstr, const WCHAR *pwszTextThis, BYTE bAttrThis, BYTE bAttrPrev, ULONG *pulOffsetThis)
  54. {
  55. HRESULT hr=S_OK;
  56. ULONG ulPrevLen;
  57. if ( !pDstr || !pwszTextThis )
  58. return E_INVALIDARG;
  59. ulPrevLen = pDstr->Length( );
  60. if ( (ulPrevLen > 0) && (bAttrThis & SPAF_CONSUME_LEADING_SPACES) )
  61. {
  62. // This element wants to remove the trailing spaces of the previous element.
  63. ULONG ulPrevTrailing = 0;
  64. if ( bAttrPrev & SPAF_ONE_TRAILING_SPACE )
  65. ulPrevTrailing = 1;
  66. else if ( bAttrPrev & SPAF_TWO_TRAILING_SPACES )
  67. ulPrevTrailing = 2;
  68. if ( ulPrevLen >= ulPrevTrailing )
  69. {
  70. ulPrevLen = ulPrevLen - ulPrevTrailing;
  71. pDstr->TrimToSize(ulPrevLen);
  72. }
  73. }
  74. if ( pulOffsetThis )
  75. *pulOffsetThis = ulPrevLen;
  76. pDstr->Append(pwszTextThis);
  77. if (bAttrThis & SPAF_ONE_TRAILING_SPACE)
  78. {
  79. pDstr->Append(L" ");
  80. }
  81. else if (bAttrThis & SPAF_TWO_TRAILING_SPACES)
  82. {
  83. pDstr->Append(L" ");
  84. }
  85. return hr;
  86. }
  87. //
  88. // CRecoResult implementation
  89. //
  90. // ctor
  91. CRecoResultWrap::CRecoResultWrap(CSapiIMX *pimx, ULONG ulStartElement, ULONG ulNumElements, ULONG ulNumOfITN)
  92. {
  93. m_cRef = 1;
  94. m_ulStartElement = ulStartElement;
  95. m_ulNumElements = ulNumElements;
  96. // ITN is shown by default if the reco result has it
  97. // the shown status can change after user goes through
  98. // correction UI
  99. //
  100. m_ulNumOfITN = ulNumOfITN;
  101. m_pimx = pimx;
  102. m_pulElementOffsets = NULL;
  103. m_bstrCurrentText = NULL;
  104. m_OffsetDelta = 0;
  105. m_ulCharsInTrail = 0;
  106. m_ulTrailSpaceRemoved = 0;
  107. m_pSerializedRecoResult = NULL;
  108. #ifdef DEBUG
  109. static DWORD s_dbg_Id = 0;
  110. m_dbg_dwId = s_dbg_Id++;
  111. #endif // DEBUG
  112. }
  113. CRecoResultWrap::~CRecoResultWrap()
  114. {
  115. if (m_pulElementOffsets)
  116. delete[] m_pulElementOffsets;
  117. if (m_bstrCurrentText)
  118. SysFreeString(m_bstrCurrentText);
  119. if (m_rgITNShowState.Count())
  120. m_rgITNShowState.Clear();
  121. if (m_pSerializedRecoResult)
  122. {
  123. CoTaskMemFree(m_pSerializedRecoResult);
  124. }
  125. }
  126. //
  127. // Init function
  128. //
  129. HRESULT CRecoResultWrap::Init(ISpRecoResult *pRecoResult)
  130. {
  131. // serialize the given reco result and keep the cotaskmem
  132. if (m_pSerializedRecoResult != NULL)
  133. {
  134. CoTaskMemFree(m_pSerializedRecoResult);
  135. }
  136. Assert(pRecoResult);
  137. return pRecoResult->Serialize(&m_pSerializedRecoResult);
  138. }
  139. //
  140. // GetResult
  141. //
  142. HRESULT CRecoResultWrap::GetResult(ISpRecoResult **ppResult)
  143. {
  144. if ( m_pSerializedRecoResult == NULL)
  145. return E_PENDING;
  146. // this is a tricky part, we need to access ISpRecoContext
  147. // and don't want to hold onto it. We get it via CSapiIMX
  148. // instance which must be always available during session
  149. //
  150. Assert(m_pimx);
  151. CComPtr<ISpRecoContext> cpRecoCtxt;
  152. //
  153. // GetFunction ensures SAPI is initialized
  154. //
  155. HRESULT hr = m_pimx->GetFunction(GUID_NULL, IID_ISpRecoContext, (IUnknown **)&cpRecoCtxt);
  156. if (S_OK == hr)
  157. {
  158. hr = cpRecoCtxt->DeserializeResult(m_pSerializedRecoResult, ppResult);
  159. }
  160. //
  161. // callar is resposible to release this result object
  162. //
  163. return hr;
  164. }
  165. //
  166. // IUnknown
  167. //
  168. STDMETHODIMP CRecoResultWrap::QueryInterface(REFIID riid, void **ppvObj)
  169. {
  170. HRESULT hr;
  171. Assert(ppvObj);
  172. if (IsEqualIID(riid, IID_IUnknown)
  173. || IsEqualIID(riid, IID_PRIV_RESULTWRAP)
  174. || IsEqualIID(riid, IID_IServiceProvider))
  175. {
  176. *ppvObj = this;
  177. hr = S_OK;
  178. this->m_cRef++;
  179. }
  180. else
  181. {
  182. *ppvObj = NULL;
  183. hr = E_NOINTERFACE;
  184. }
  185. return hr;
  186. }
  187. STDMETHODIMP_(ULONG) CRecoResultWrap::AddRef(void)
  188. {
  189. this->m_cRef++;
  190. return this->m_cRef;
  191. }
  192. STDMETHODIMP_(ULONG) CRecoResultWrap::Release(void)
  193. {
  194. this->m_cRef--;
  195. if (this->m_cRef > 0)
  196. {
  197. return this->m_cRef;
  198. }
  199. delete this;
  200. return 0;
  201. }
  202. // IServiceProvider
  203. //
  204. STDMETHODIMP CRecoResultWrap::QueryService(REFGUID guidService, REFIID riid, void** ppv)
  205. {
  206. HRESULT hr = S_OK;
  207. Assert(ppv);
  208. if (!IsEqualIID(guidService, GUID_NULL))
  209. {
  210. hr = E_FAIL;
  211. }
  212. if (SUCCEEDED(hr))
  213. {
  214. if (IsEqualIID(riid, IID_IUnknown))
  215. {
  216. *ppv = this;
  217. hr = S_OK;
  218. this->m_cRef++;
  219. }
  220. else if (IsEqualIID(riid, IID_ISpRecoResult))
  221. {
  222. CComPtr<ISpRecoResult> cpResult;
  223. hr = GetResult(&cpResult);
  224. if (S_OK == hr)
  225. {
  226. hr = cpResult->QueryInterface(riid, ppv);
  227. }
  228. }
  229. else
  230. {
  231. *ppv = NULL;
  232. hr = E_NOINTERFACE;
  233. }
  234. }
  235. return hr;
  236. }
  237. //
  238. // Clone this RecoResultWrap object.
  239. //
  240. HRESULT CRecoResultWrap::Clone(CRecoResultWrap **ppRw)
  241. {
  242. HRESULT hr = S_OK;
  243. CRecoResultWrap *prw;
  244. CComPtr<ISpRecoResult> cpResult;
  245. ULONG iIndex;
  246. if ( !ppRw ) return E_INVALIDARG;
  247. *ppRw = NULL;
  248. prw = new CRecoResultWrap(m_pimx, m_ulStartElement, m_ulNumElements, m_ulNumOfITN);
  249. if ( prw )
  250. {
  251. hr = GetResult(&cpResult);
  252. if (S_OK == hr)
  253. {
  254. hr = prw->Init(cpResult);
  255. }
  256. if (S_OK == hr)
  257. {
  258. prw->SetOffsetDelta(m_OffsetDelta);
  259. prw->SetCharsInTrail(m_ulCharsInTrail);
  260. prw->SetTrailSpaceRemoved( m_ulTrailSpaceRemoved );
  261. prw->m_bstrCurrentText = SysAllocString((WCHAR *)m_bstrCurrentText);
  262. // Update ITN show-state list .
  263. if ( m_ulNumOfITN > 0 )
  264. {
  265. SPITNSHOWSTATE *pITNShowState;
  266. for (iIndex=0; iIndex<m_ulNumOfITN; iIndex ++ )
  267. {
  268. pITNShowState = m_rgITNShowState.GetPtr(iIndex);
  269. if ( pITNShowState)
  270. {
  271. prw->_InitITNShowState(
  272. pITNShowState->fITNShown,
  273. pITNShowState->ulITNStart,
  274. pITNShowState->ulITNNumElem);
  275. }
  276. } // for
  277. } // if
  278. // Update the Offset list for the second range.
  279. if (m_pulElementOffsets)
  280. {
  281. ULONG ulOffset;
  282. ULONG ulNumOffset;
  283. ulNumOffset = m_ulStartElement+m_ulNumElements;
  284. for ( iIndex=0; iIndex <= ulNumOffset; iIndex ++ )
  285. {
  286. ulOffset = m_pulElementOffsets[iIndex];
  287. prw->_SetElementNewOffset(iIndex, ulOffset);
  288. }
  289. }
  290. }
  291. if ( S_OK == hr )
  292. {
  293. // Return this prw to the caller.
  294. *ppRw = prw;
  295. }
  296. else
  297. {
  298. // Something wrong when update the data members.
  299. // Release the newly created object.
  300. delete prw;
  301. }
  302. }
  303. else
  304. {
  305. hr = E_OUTOFMEMORY;
  306. }
  307. return hr;
  308. }
  309. //
  310. // _SpeakAudio()
  311. //
  312. // synopsis: playback audio based on the elements used in this result object
  313. //
  314. HRESULT CRecoResultWrap::_SpeakAudio(ULONG ulStart, ULONG ulcElem)
  315. {
  316. HRESULT hr = E_FAIL;
  317. CComPtr<ISpRecoResult> cpResult;
  318. hr = GetResult(&cpResult);
  319. if (S_OK == hr)
  320. {
  321. if (ulcElem == 0)
  322. {
  323. ulStart = GetStart();
  324. ulcElem = GetNumElements();
  325. }
  326. hr = cpResult->SpeakAudio( ulStart, ulcElem, SPF_ASYNC, NULL);
  327. }
  328. return hr;
  329. }
  330. //
  331. // _SetElementOffset(ULONG ulElement, ULONG ulNewOffset);
  332. //
  333. // This function is to update the offset for some element.
  334. // It is used after property divide or shrink, some element's
  335. // length is changed ( by removing trailing spaces etc.).
  336. //
  337. HRESULT CRecoResultWrap::_SetElementNewOffset(ULONG ulElement, ULONG ulNewOffset)
  338. {
  339. HRESULT hr = S_OK;
  340. if ((ulElement > m_ulStartElement + m_ulNumElements) || (ulElement < m_ulStartElement))
  341. {
  342. // This ulElement is not a valid element.
  343. hr = E_INVALIDARG;
  344. return hr;
  345. }
  346. if (!m_pulElementOffsets)
  347. {
  348. m_pulElementOffsets = new ULONG[m_ulStartElement+m_ulNumElements+1];
  349. if ( !m_pulElementOffsets )
  350. return E_OUTOFMEMORY;
  351. else
  352. for ( ULONG i=0; i<m_ulStartElement+m_ulNumElements+1; i++ )
  353. m_pulElementOffsets[i] = -1;
  354. }
  355. m_pulElementOffsets[ulElement] = ulNewOffset;
  356. return hr;
  357. }
  358. //
  359. // _GetElementOffsetCch
  360. //
  361. // synopsis: returns the start cch of the given SPPHRASEELEMENT
  362. //
  363. ULONG CRecoResultWrap::_GetElementOffsetCch(ULONG ulElement)
  364. {
  365. ULONG ulOffset = 0;
  366. if (ulElement > m_ulStartElement + m_ulNumElements)
  367. {
  368. Assert(m_ulNumElements > 0);
  369. ulElement = m_ulStartElement + m_ulNumElements;
  370. }
  371. else if (ulElement < m_ulStartElement)
  372. {
  373. ulElement = m_ulStartElement;
  374. }
  375. if (!m_pulElementOffsets)
  376. {
  377. _SetElementOffsetCch(NULL);
  378. }
  379. // m_pulElement could be null at memory stressed situation
  380. if ( m_pulElementOffsets )
  381. {
  382. ulOffset = m_pulElementOffsets[ulElement];
  383. }
  384. return ulOffset;
  385. }
  386. //
  387. // _SetElementOffsetCch
  388. //
  389. // synopsis:
  390. //
  391. //
  392. // Before this function is called, we have to make sure that all the internal ITN show-state list
  393. // has already been updated for the current phrase.
  394. //
  395. // So here we just relay on the correct ITN information to get the right real text string for
  396. // current phrase and the offsets of all elemenets in this phrase.
  397. void CRecoResultWrap::_SetElementOffsetCch(ISpPhraseAlt *pAlt)
  398. {
  399. if (m_pulElementOffsets)
  400. {
  401. delete[] m_pulElementOffsets;
  402. m_pulElementOffsets = NULL;
  403. }
  404. SPPHRASE *pPhrase = NULL;
  405. HRESULT hr = S_OK;
  406. CComPtr<ISpPhrase> cpPhrase;
  407. if (pAlt)
  408. {
  409. cpPhrase = pAlt;
  410. }
  411. else
  412. {
  413. CComPtr<ISpRecoResult> cpResult;
  414. hr = GetResult(&cpResult);
  415. //
  416. // we're called for initialization, use current parent pharse object
  417. //
  418. cpPhrase = cpResult;
  419. }
  420. if (S_OK == hr)
  421. {
  422. _UpdateInternalText(cpPhrase);
  423. hr = cpPhrase->GetPhrase(&pPhrase);
  424. }
  425. ULONG cElements = 0;
  426. if (S_OK == hr && pPhrase)
  427. {
  428. cElements = pPhrase->Rule.ulCountOfElements;
  429. // the last colmun (+1) shows offset for the end of the last element
  430. if ( m_pulElementOffsets )
  431. delete[] m_pulElementOffsets;
  432. m_pulElementOffsets = new ULONG[cElements+1];
  433. if (cElements > 0 && m_pulElementOffsets)
  434. {
  435. CSpDynamicString dstr;
  436. CSpDynamicString dstrReplace;
  437. for (ULONG i = 0; i < cElements; i++ )
  438. {
  439. BOOL fInsideITN;
  440. ULONG ulITNStart, ulITNNumElem;
  441. fInsideITN = _CheckITNForElement(pPhrase, i, &ulITNStart, &ulITNNumElem, (CSpDynamicString *)&dstrReplace);
  442. if ( fInsideITN )
  443. {
  444. m_pulElementOffsets[i] = dstr.Length();
  445. // This element is inside an ITN range.
  446. if ( i == (ulITNStart + ulITNNumElem - 1) )
  447. {
  448. // This is the last element of the new ITN.
  449. // we need to add the replace text to the dstr string
  450. // so that next non-ITN element will get correct offset.
  451. dstr.Append( (WCHAR *)dstrReplace );
  452. }
  453. }
  454. else
  455. {
  456. if (pPhrase->pElements[i].pszDisplayText)
  457. {
  458. // get cch up to this element.
  459. // the offset is 0 for elem 0
  460. //
  461. const WCHAR *pwszTextThis;
  462. BYTE bAttrThis = 0;
  463. BYTE bAttrPrev = 0;
  464. ULONG ulOffset = 0;
  465. pwszTextThis = pPhrase->pElements[i].pszDisplayText;
  466. bAttrThis = pPhrase->pElements[i].bDisplayAttributes;
  467. if ( i > 0 )
  468. bAttrPrev = pPhrase->pElements[i-1].bDisplayAttributes;
  469. HandlePhraseElement( (CSpDynamicString *)&dstr, pwszTextThis, bAttrThis, bAttrPrev,&ulOffset);
  470. m_pulElementOffsets[i] = ulOffset;
  471. }
  472. }
  473. } // for
  474. // store the last columun
  475. m_pulElementOffsets[cElements] = dstr.Length() - m_ulTrailSpaceRemoved;
  476. } // if m_pulElementOffsets
  477. }
  478. if (pPhrase)
  479. ::CoTaskMemFree(pPhrase);
  480. }
  481. //
  482. // _UpdateInternalText()
  483. //
  484. // synopsis: this function updates the internal bstr that covers
  485. // parent phrase wrapped by our own result object, based on
  486. // the given phrase object and our internal pointer to
  487. // the starting element and # of element
  488. //
  489. // perf after beta1: consolidate this with _SetElementOffsetCch()
  490. //
  491. //
  492. void CRecoResultWrap::_UpdateInternalText(ISpPhrase *pSpPhrase)
  493. {
  494. CSpDynamicString dstrReplace;
  495. CSpDynamicString dstr;
  496. CSpDynamicString dstrDelta, dstrTrail;
  497. ULONG ulLenCurText = 0;
  498. if ( m_bstrCurrentText )
  499. {
  500. ulLenCurText = wcslen(m_bstrCurrentText);
  501. if ( m_OffsetDelta > 0 && m_OffsetDelta <= ulLenCurText )
  502. {
  503. dstrDelta.Append(m_bstrCurrentText, m_OffsetDelta);
  504. }
  505. if ( m_ulCharsInTrail > 0 && m_ulCharsInTrail <= ulLenCurText )
  506. {
  507. WCHAR *pwszTrail;
  508. pwszTrail = m_bstrCurrentText + ulLenCurText - m_ulCharsInTrail ;
  509. dstrTrail.Append(pwszTrail, m_ulCharsInTrail);
  510. }
  511. }
  512. else
  513. {
  514. // m_bstrCurrentText doesn't exist, but m_OffsetDelta or m_ulCharsInTrail
  515. // is not 0, sounds it is not a possible case.
  516. // But for safety sake, we just still keep the same number of spaces.
  517. if ( m_OffsetDelta > 0 )
  518. {
  519. for (ULONG i=0; i<m_OffsetDelta; i++)
  520. dstrDelta.Append(L" ");
  521. }
  522. if ( m_ulCharsInTrail > 0 )
  523. {
  524. for (ULONG i=0; i<m_ulCharsInTrail; i++)
  525. dstrTrail.Append(L" ");
  526. }
  527. }
  528. if ( m_ulNumElements == 0 )
  529. {
  530. // There is no valid element in this range.
  531. //
  532. // Just keep the delta string and Trailing string if they are existing.
  533. if ( m_OffsetDelta > 0 )
  534. dstr.Append( (WCHAR *)dstrDelta );
  535. if ( m_ulCharsInTrail > 0)
  536. dstr.Append((WCHAR *)dstrTrail);
  537. if ( m_bstrCurrentText )
  538. SysFreeString(m_bstrCurrentText);
  539. m_bstrCurrentText = SysAllocString((WCHAR *)dstr);
  540. return;
  541. }
  542. if ( pSpPhrase == NULL )
  543. return;
  544. // We cannot call pPhrase->GetText( ) to get the real phrase text, because GetText( )
  545. // assumes all the ITN range have the same show-state, ( ITN or NON_ITN).
  546. // But there are some cases like some ITN shown as ITN, some other ITN ranges shown as normal
  547. // text after user selects a candidate.
  548. //
  549. // When the reco wrapper is first generated right after the text is recognized by SR engine,
  550. // we can call GetText( ) to get the real text of the phrase.
  551. //
  552. // After that, user may change it by selecting alternative text.
  553. dstr.Clear( );
  554. if(m_OffsetDelta > 0)
  555. {
  556. // There are some characters which are not part of any elements in the range begining.
  557. // we need to keep these characters.
  558. dstr.Append((WCHAR *)dstrDelta);
  559. }
  560. if (m_bstrCurrentText)
  561. {
  562. SysFreeString(m_bstrCurrentText);
  563. m_bstrCurrentText = NULL;
  564. }
  565. SPPHRASE *pPhrase = NULL;
  566. pSpPhrase->GetPhrase(&pPhrase);
  567. for (ULONG i = m_ulStartElement; i < m_ulStartElement + m_ulNumElements; i++ )
  568. {
  569. BOOL fInsideITN;
  570. ULONG ulITNStart, ulITNNumElem;
  571. fInsideITN = _CheckITNForElement(pPhrase, i, &ulITNStart, &ulITNNumElem, (CSpDynamicString *)&dstrReplace);
  572. if ( fInsideITN )
  573. {
  574. // This element is inside an ITN range.
  575. if ( i == (ulITNStart + ulITNNumElem - 1) )
  576. {
  577. // This is the last element of the new ITN.
  578. // we need to add the replace text to the dstr string
  579. // so that next non-ITN element will get correct offset.
  580. dstr.Append( (WCHAR *)dstrReplace );
  581. }
  582. }
  583. else
  584. {
  585. if (pPhrase->pElements[i].pszDisplayText)
  586. {
  587. const WCHAR *pwszTextThis;
  588. BYTE bAttrThis = 0;
  589. BYTE bAttrPrev = 0;
  590. pwszTextThis = pPhrase->pElements[i].pszDisplayText;
  591. bAttrThis = pPhrase->pElements[i].bDisplayAttributes;
  592. if ( i > m_ulStartElement )
  593. bAttrPrev = pPhrase->pElements[i-1].bDisplayAttributes;
  594. HandlePhraseElement( (CSpDynamicString *)&dstr, pwszTextThis, bAttrThis, bAttrPrev,NULL);
  595. }
  596. }
  597. } // for
  598. if ( m_ulCharsInTrail > 0)
  599. dstr.Append((WCHAR *)dstrTrail);
  600. // If there were some trail spaces removed, we also need to remove the same number of spaces when
  601. // we try to get the new phrase text.
  602. if ( m_ulTrailSpaceRemoved > 0 )
  603. {
  604. ULONG ulNewRemoved = 0;
  605. WCHAR *pwszNewText = (WCHAR *)dstr;
  606. ulLenCurText = wcslen(pwszNewText);
  607. for (ULONG i=ulLenCurText-1; ((long)i>0) && (ulNewRemoved <= m_ulTrailSpaceRemoved); i-- )
  608. {
  609. if ( pwszNewText[i] == L' ' )
  610. {
  611. pwszNewText[i] = L'\0';
  612. ulNewRemoved ++;
  613. }
  614. else
  615. break;
  616. }
  617. m_ulTrailSpaceRemoved = ulNewRemoved;
  618. }
  619. // store the last columun
  620. m_bstrCurrentText = SysAllocString((WCHAR *)dstr);
  621. if ( pPhrase )
  622. ::CoTaskMemFree(pPhrase);
  623. }
  624. BOOL CRecoResultWrap::_CanIgnoreChange(ULONG ich, WCHAR *pszChange, int cch)
  625. {
  626. // see if the given text is within tolerable range
  627. BOOL bret = FALSE;
  628. WCHAR *pszCurrentText = NULL;
  629. // set up an offset to the current face text
  630. if (m_bstrCurrentText)
  631. {
  632. if (ich < SysStringLen(m_bstrCurrentText))
  633. {
  634. pszCurrentText = m_bstrCurrentText;
  635. pszCurrentText += ich;
  636. }
  637. }
  638. // 1) compare it ignoring the case
  639. if (pszCurrentText)
  640. {
  641. int i = _wcsnicmp(pszCurrentText, pszChange, cch);
  642. if (i == 0)
  643. {
  644. bret = TRUE;
  645. }
  646. }
  647. return bret;
  648. }
  649. //------------------------------------------------------------------------------------------//
  650. //
  651. // CRecoResultWrap::_RangeHasITN
  652. //
  653. // Determine if the partial of phrase between ulStart Element and ulStart+ulcElement -1
  654. // has ITN.
  655. //
  656. // return the ITN number, or 0 if there is no ITN.
  657. //
  658. //------------------------------------------------------------------------------------------//
  659. ULONG CRecoResultWrap::_RangeHasITN(ULONG ulStartElement, ULONG ulNumElements)
  660. {
  661. ULONG ulNumOfITN = 0;
  662. CComPtr<ISpRecoResult> cpResult;
  663. HRESULT hr;
  664. hr = GetResult(&cpResult);
  665. // determine whether this partial result has an ITN
  666. SPPHRASE *pPhrase;
  667. if (S_OK == hr)
  668. hr = cpResult->GetPhrase(&pPhrase);
  669. if (S_OK == hr)
  670. {
  671. const SPPHRASEREPLACEMENT *pRep = pPhrase->pReplacements;
  672. for (ULONG ul = 0; ul < pPhrase->cReplacements; ul++)
  673. {
  674. // review: we need to verify if this is really a correct way to determine
  675. // whether the ITN fits in the partial result
  676. //
  677. if (pRep->ulFirstElement >= ulStartElement
  678. && (pRep->ulFirstElement + pRep->ulCountOfElements) <= (ulStartElement + ulNumElements))
  679. {
  680. ulNumOfITN ++;
  681. }
  682. pRep++;
  683. }
  684. ::CoTaskMemFree(pPhrase);
  685. }
  686. return ulNumOfITN;
  687. }
  688. // -----------------------------------------------------------------------------------------
  689. // CRecoResultWrap::_InitITNShowState
  690. //
  691. // It will initialize show-state for the given ITN or all the ITNs in this recowrap.
  692. //
  693. // Normally this function will be called after the reco wrapper is genertaed.
  694. //
  695. // When the reco wrap is first generated after a text is recognized by SR engine, all the
  696. // ITNs in this reco wrap have the same showstate, it is convinent to set the show state
  697. // for all the ITNs at one time. in this case, caller could just set both ulITNStart and
  698. // ulITNNumElements as 0.
  699. //
  700. // When the new reco wrap is generated by property divide, shrink or deserialized from
  701. // IStream, or after an alternative text is selected from candidate window, we cannot
  702. // assume all the ITNs in the reco wrapper have the same show state. In this case,
  703. // caller can initialize the show state one ITN by one ITN, it can set ulITNStart and
  704. // ulITNNumElements to identify this ITN.
  705. //
  706. // ------------------------------------------------------------------------------------------
  707. HRESULT CRecoResultWrap::_InitITNShowState(BOOL fITNShown, ULONG ulITNStart, ULONG ulITNNumElements )
  708. {
  709. HRESULT hr = S_OK;
  710. ULONG ulNumOfITN = 0;
  711. SPPHRASE *pPhrase;
  712. TraceMsg(TF_GENERAL, "CRecoResultWrap::_InitITNShowState is called");
  713. if ( m_ulNumOfITN == 0 )
  714. {
  715. // There is no ITN in this reco wrapper, just return here.
  716. TraceMsg(TF_GENERAL, "There is no ITN");
  717. return hr;
  718. }
  719. // The list of SPITNSHOWSTATE is already generated, we just need to set the value for
  720. // every structure member.
  721. if ( (ulITNStart == 0 ) && (ulITNNumElements == 0 ) )
  722. {
  723. // All the ITNs in this Reco wrapper have the same show status.
  724. // we will calculate the every ITN start and end elements based
  725. // on current reco result phrase.
  726. // We want to alloc the structure list.
  727. if ( m_rgITNShowState.Count( ) )
  728. m_rgITNShowState.Clear( );
  729. m_rgITNShowState.Append(m_ulNumOfITN);
  730. CComPtr<ISpRecoResult> cpResult;
  731. hr = GetResult(&cpResult);
  732. if (S_OK == hr)
  733. {
  734. hr = cpResult->GetPhrase(&pPhrase);
  735. }
  736. if (S_OK == hr)
  737. {
  738. const SPPHRASEREPLACEMENT *pRep = pPhrase->pReplacements;
  739. for (ULONG ul = 0; ul < pPhrase->cReplacements; ul++)
  740. {
  741. if (pRep->ulFirstElement >= m_ulStartElement
  742. && (pRep->ulFirstElement + pRep->ulCountOfElements) <= (m_ulStartElement + m_ulNumElements))
  743. {
  744. // Get an ITN
  745. SPITNSHOWSTATE *pITNShowState;
  746. if ( pITNShowState = m_rgITNShowState.GetPtr(ulNumOfITN))
  747. {
  748. pITNShowState->ulITNStart = pRep->ulFirstElement;
  749. pITNShowState->ulITNNumElem = pRep->ulCountOfElements;
  750. pITNShowState->fITNShown = fITNShown;
  751. }
  752. ulNumOfITN ++;
  753. if ( ulNumOfITN > m_ulNumOfITN )
  754. {
  755. // Something wrong, return here to avoid AV
  756. break;
  757. }
  758. }
  759. pRep++;
  760. }
  761. ::CoTaskMemFree(pPhrase);
  762. }
  763. }
  764. else
  765. {
  766. // Set the display status for given ITN.
  767. // Check to see if this is a valid ITN.
  768. if ( ulITNNumElements > 0 )
  769. {
  770. ULONG ulIndex = 0;
  771. SPITNSHOWSTATE *pITNShowState = NULL;
  772. ulIndex = m_rgITNShowState.Count( );
  773. m_rgITNShowState.Append(1);
  774. if ( pITNShowState = m_rgITNShowState.GetPtr(ulIndex))
  775. {
  776. pITNShowState->ulITNStart = ulITNStart;
  777. pITNShowState->ulITNNumElem = ulITNNumElements;
  778. pITNShowState->fITNShown = fITNShown;
  779. }
  780. }
  781. }
  782. return hr;
  783. }
  784. // --------------------------------------------------------------------------------------------
  785. // CRecoResultWrap::_InvertITNShowStateForRange
  786. //
  787. // Invert the show state for all the ITNs in the give range ( ulStartElement, to ulNumElements)
  788. //
  789. //
  790. // --------------------------------------------------------------------------------------------
  791. HRESULT CRecoResultWrap::_InvertITNShowStateForRange( ULONG ulStartElement, ULONG ulNumElements )
  792. {
  793. HRESULT hr = S_OK;
  794. TraceMsg(TF_GENERAL,"CRecoResultWrap::_InvertITNShowStateForRange is called, ulStartElement=%d ulNumElements=%d", ulStartElement,ulNumElements);
  795. if ( m_ulNumOfITN > 0 && ulNumElements > 0 )
  796. {
  797. //
  798. // check to see if there is any ITN inside the given range.
  799. //
  800. ULONG ulIndex = 0;
  801. for ( ulIndex=0; ulIndex < m_ulNumOfITN; ulIndex ++ )
  802. {
  803. SPITNSHOWSTATE *pITNShowState;
  804. if ( pITNShowState = m_rgITNShowState.GetPtr(ulIndex))
  805. {
  806. if ((pITNShowState->ulITNStart >= ulStartElement) &&
  807. (pITNShowState->ulITNStart + pITNShowState->ulITNNumElem <= ulStartElement + ulNumElements))
  808. {
  809. // This ITN is inside the given range, just invert the show state.
  810. pITNShowState->fITNShown = !pITNShowState->fITNShown;
  811. }
  812. }
  813. }
  814. }
  815. return hr;
  816. }
  817. // --------------------------------------------------------------------------------------------------
  818. //
  819. // CRecoResultWrap::_UpdateStateWithAltPhrase
  820. //
  821. // When an Alt phrase is going to used to replace current parent phrase, this method function
  822. // will update related memeber data, like m_ulNumOfITN, m_ulNumElements, ITN show state list.
  823. //
  824. // ---------------------------------------------------------------------------------------------------
  825. HRESULT CRecoResultWrap::_UpdateStateWithAltPhrase( ISpPhraseAlt *pSpPhraseAlt )
  826. {
  827. // This code is moved from UpdateInternalText( ).
  828. // This part code in UpdateInternalText( ) is used only by SetResult( ) when select an alternative from
  829. // candidate list.
  830. HRESULT hr = S_OK;
  831. ULONG ulParentStart;
  832. ULONG cElements;
  833. ULONG cElementsInParent;
  834. CComPtr<ISpPhraseAlt> cpAlt;
  835. TraceMsg(TF_GENERAL,"CRecoResultWrap::_UpdateStateWithAltPhrase is called");
  836. if ( pSpPhraseAlt == NULL )
  837. return E_INVALIDARG;
  838. cpAlt=pSpPhraseAlt;
  839. hr = cpAlt->GetAltInfo(NULL, &ulParentStart, &cElementsInParent, &cElements);
  840. if (S_OK == hr)
  841. {
  842. SPITNSHOWSTATE *pOrgITNShowState = NULL;
  843. // case: there is ITN number change.
  844. //
  845. // there is element number change.
  846. Assert(ulParentStart >= m_ulStartElement);
  847. Assert(ulParentStart+cElementsInParent <= m_ulStartElement+m_ulNumElements);
  848. TraceMsg(TF_GENERAL, "Original Num of ITNs =%d", m_ulNumOfITN);
  849. if ( cElements != cElementsInParent )
  850. {
  851. // There is element number change.
  852. // we need to update the start position for all the ITNs which are not in the selection range.
  853. for ( ULONG uIndex=0; uIndex < m_ulNumOfITN; uIndex ++)
  854. {
  855. SPITNSHOWSTATE *pITNShowState;
  856. pITNShowState = m_rgITNShowState.GetPtr(uIndex);
  857. if ( pITNShowState && pITNShowState->ulITNStart >= (ulParentStart + cElementsInParent) )
  858. {
  859. long newStart;
  860. newStart = (long)pITNShowState->ulITNStart + (long)(cElements - cElementsInParent);
  861. pITNShowState->ulITNStart = (ULONG)newStart;
  862. }
  863. }
  864. // set the new element number to reco wrapper.
  865. long lNewNumElements = (long)m_ulNumElements + (long)(cElements - cElementsInParent);
  866. m_ulNumElements = (ULONG)lNewNumElements;
  867. }
  868. if ( m_ulNumOfITN > 0 )
  869. {
  870. pOrgITNShowState = (SPITNSHOWSTATE *) cicMemAllocClear( m_ulNumOfITN * sizeof(SPITNSHOWSTATE) );
  871. if ( pOrgITNShowState )
  872. {
  873. for (ULONG i=0; i< m_ulNumOfITN; i++ )
  874. {
  875. SPITNSHOWSTATE *pITNShowState;
  876. pITNShowState = m_rgITNShowState.GetPtr(i);
  877. pOrgITNShowState[i].ulITNStart = pITNShowState->ulITNStart;
  878. pOrgITNShowState[i].ulITNNumElem = pITNShowState->ulITNNumElem;
  879. pOrgITNShowState[i].fITNShown = pITNShowState->fITNShown;
  880. }
  881. }
  882. else
  883. hr = E_OUTOFMEMORY;
  884. }
  885. if ( m_rgITNShowState.Count( ) )
  886. m_rgITNShowState.Clear( );
  887. // Generate a new ITN list for new phrase.
  888. if ( hr == S_OK )
  889. {
  890. SPPHRASE *pPhrase;
  891. hr = cpAlt->GetPhrase(&pPhrase);
  892. if (S_OK == hr)
  893. {
  894. const SPPHRASEREPLACEMENT *pRep = pPhrase->pReplacements;
  895. ULONG ulNumOfITN = 0;
  896. for (ULONG ul = 0; ul < pPhrase->cReplacements; ul++)
  897. {
  898. ULONG ulITNStart, ulITNNumElem;
  899. BOOL fITNShown = FALSE;
  900. ulITNStart = pRep->ulFirstElement;
  901. ulITNNumElem = pRep->ulCountOfElements;
  902. if ( (ulITNStart >= m_ulStartElement)
  903. && ((ulITNStart + ulITNNumElem) <= (m_ulStartElement + m_ulNumElements)) )
  904. {
  905. // Get an ITN
  906. SPITNSHOWSTATE *pITNShowState;
  907. m_rgITNShowState.Append(1);
  908. pITNShowState = m_rgITNShowState.GetPtr(ulNumOfITN);
  909. if ( pITNShowState)
  910. {
  911. // If this ITN is inside the selection range, it show state will be set as TRUE. ITN.
  912. // Other it will keep the same show state as orgITNShowState.
  913. if ( (ulITNStart >= ulParentStart) &&
  914. ((ulITNStart+ulITNNumElem) <= (ulParentStart + cElements)) )
  915. {
  916. // This ITN is inside the selection range.
  917. fITNShown = TRUE;
  918. }
  919. else
  920. {
  921. // Get the original show state from orgITNShowState
  922. for ( ULONG j=0; j<m_ulNumOfITN; j ++ )
  923. {
  924. if ( (pOrgITNShowState[j].ulITNStart == ulITNStart) &&
  925. (pOrgITNShowState[j].ulITNNumElem == ulITNNumElem ) )
  926. {
  927. fITNShown = pOrgITNShowState[j].fITNShown;
  928. break;
  929. }
  930. }
  931. }
  932. pITNShowState->ulITNNumElem = ulITNNumElem;
  933. pITNShowState->ulITNStart = ulITNStart;
  934. pITNShowState->fITNShown = fITNShown;
  935. }
  936. ulNumOfITN ++;
  937. }
  938. pRep ++;
  939. }
  940. m_ulNumOfITN = ulNumOfITN;
  941. TraceMsg(TF_GENERAL, "New Num of ITNs =%d", m_ulNumOfITN);
  942. ::CoTaskMemFree(pPhrase);
  943. }
  944. }
  945. if ( pOrgITNShowState )
  946. cicMemFree(pOrgITNShowState);
  947. }
  948. return hr;
  949. }
  950. //------------------------------------------------------------------------------------------//
  951. //
  952. // CRecoResultWrap::_GetElementDispAttribute
  953. //
  954. // Return the display attribute for the given element, if it is inside of an ITN, and the ITN
  955. // is showing, return the replacement text's attribute.
  956. //
  957. //------------------------------------------------------------------------------------------//
  958. BYTE CRecoResultWrap::_GetElementDispAttribute(ULONG ulElement)
  959. {
  960. SPPHRASE *pPhrase = NULL;
  961. BYTE bAttr = 0;
  962. CComPtr<ISpRecoResult> cpResult;
  963. HRESULT hr;
  964. hr = GetResult(&cpResult);
  965. if (hr == S_OK)
  966. hr = cpResult->GetPhrase(&pPhrase);
  967. if ( hr == S_OK && pPhrase)
  968. {
  969. BOOL fInsideITN;
  970. ULONG ulITNStart, ulITNNumElem;
  971. fInsideITN = _CheckITNForElement(NULL, ulElement, &ulITNStart, &ulITNNumElem, NULL);
  972. if ( !fInsideITN )
  973. bAttr = pPhrase->pElements[ulElement].bDisplayAttributes;
  974. else
  975. {
  976. const SPPHRASEREPLACEMENT *pPhrReplace;
  977. pPhrReplace = pPhrase->pReplacements;
  978. if ( pPhrReplace )
  979. {
  980. for ( ULONG i=0; i<pPhrase->cReplacements; i++)
  981. {
  982. if ( (ulITNStart == pPhrReplace[i].ulFirstElement)
  983. && (ulITNNumElem == pPhrReplace[i].ulCountOfElements) )
  984. {
  985. bAttr = pPhrReplace[i].bDisplayAttributes;
  986. break;
  987. }
  988. }
  989. }
  990. }
  991. }
  992. if ( pPhrase )
  993. ::CoTaskMemFree(pPhrase);
  994. return bAttr;
  995. }
  996. //------------------------------------------------------------------------------------------//
  997. //
  998. // CRecoResultWrap::_CheckITNForElement
  999. //
  1000. // Determine if the specifed element is inside of an ITN range in the phrase.
  1001. // If it is, the return value would be TRUE, and pulStartElement, pulEndElement will be
  1002. // set as the real start element and num of elements of the ITN range, dstrReplace will hold
  1003. // the replace text string.
  1004. //
  1005. // If the element is not inside an ITN range, return value would be FALSE, all other out
  1006. // parameters will not be set.
  1007. //
  1008. //------------------------------------------------------------------------------------------//
  1009. BOOL CRecoResultWrap::_CheckITNForElement(SPPHRASE *pPhrase, ULONG ulElement, ULONG *pulITNStart, ULONG *pulITNNumElem, CSpDynamicString *pdstrReplace)
  1010. {
  1011. BOOL fInsideITN = FALSE;
  1012. SPPHRASE *pMyPhrase;
  1013. pMyPhrase = pPhrase;
  1014. if ( pMyPhrase == NULL )
  1015. {
  1016. CComPtr<ISpRecoResult> cpResult;
  1017. HRESULT hr = GetResult(&cpResult);
  1018. if (S_OK == hr)
  1019. {
  1020. hr = cpResult->GetPhrase(&pMyPhrase);
  1021. }
  1022. if (S_OK != hr)
  1023. return fInsideITN;
  1024. }
  1025. if ( m_ulNumOfITN )
  1026. {
  1027. // Check to see if this element is inside an ITN range.
  1028. ULONG ulITNStart;
  1029. ULONG ulITNNumElem;
  1030. for ( ULONG iIndex=0; iIndex<m_ulNumOfITN; iIndex++ )
  1031. {
  1032. SPITNSHOWSTATE *pITNShowState;
  1033. if ( pITNShowState = m_rgITNShowState.GetPtr(iIndex))
  1034. {
  1035. ulITNStart = pITNShowState->ulITNStart;
  1036. ulITNNumElem = pITNShowState->ulITNNumElem;
  1037. if ( (ulElement >= ulITNStart) && ( ulElement < ulITNStart + ulITNNumElem) )
  1038. {
  1039. // found this ITN in our internal ITN show state list.
  1040. fInsideITN = pITNShowState->fITNShown;
  1041. break;
  1042. }
  1043. }
  1044. }
  1045. if ( fInsideITN )
  1046. {
  1047. if ( pulITNStart )
  1048. *pulITNStart = ulITNStart;
  1049. if ( pulITNNumElem )
  1050. *pulITNNumElem = ulITNNumElem;
  1051. if ( pdstrReplace )
  1052. {
  1053. const SPPHRASEREPLACEMENT *pPhrReplace;
  1054. pPhrReplace = pMyPhrase->pReplacements;
  1055. for ( ULONG j=0; j<pMyPhrase->cReplacements; j++)
  1056. {
  1057. if ( (ulITNStart == pPhrReplace[j].ulFirstElement)
  1058. && (ulITNNumElem == pPhrReplace[j].ulCountOfElements) )
  1059. {
  1060. pdstrReplace->Clear( );
  1061. pdstrReplace->Append(pPhrReplace[j].pszReplacementText);
  1062. if (pPhrReplace[j].bDisplayAttributes & SPAF_ONE_TRAILING_SPACE)
  1063. pdstrReplace->Append(L" ");
  1064. else if (pPhrReplace[j].bDisplayAttributes & SPAF_TWO_TRAILING_SPACES)
  1065. pdstrReplace->Append(L" ");
  1066. break;
  1067. }
  1068. }
  1069. }
  1070. }
  1071. }
  1072. if ( !pPhrase && pMyPhrase )
  1073. ::CoTaskMemFree(pMyPhrase);
  1074. return fInsideITN;
  1075. }
  1076. //
  1077. // CPropStoreRecoResultObject implementation
  1078. //
  1079. // ctor
  1080. CPropStoreRecoResultObject::CPropStoreRecoResultObject(CSapiIMX *pimx, ITfRange *pRange)
  1081. {
  1082. m_cpResultWrap = NULL;
  1083. if ( pRange )
  1084. pRange->Clone(&m_cpRange); // Use a clone range to keep the original Range.
  1085. // It would be useful to handle property shrink and divide.
  1086. else
  1087. m_cpRange = pRange;
  1088. m_pimx = pimx;
  1089. m_cRef = 1;
  1090. }
  1091. // dtor
  1092. CPropStoreRecoResultObject::~CPropStoreRecoResultObject()
  1093. {
  1094. }
  1095. // IUnknown
  1096. STDMETHODIMP CPropStoreRecoResultObject::QueryInterface(REFIID riid, void **ppvObj)
  1097. {
  1098. HRESULT hr;
  1099. Assert(ppvObj);
  1100. if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_ITfPropertyStore))
  1101. {
  1102. *ppvObj = this;
  1103. hr = S_OK;
  1104. this->m_cRef++;
  1105. }
  1106. else
  1107. {
  1108. *ppvObj = NULL;
  1109. hr = E_NOINTERFACE;
  1110. }
  1111. return hr;
  1112. }
  1113. STDMETHODIMP_(ULONG) CPropStoreRecoResultObject::AddRef(void)
  1114. {
  1115. this->m_cRef++;
  1116. return this->m_cRef;
  1117. }
  1118. STDMETHODIMP_(ULONG) CPropStoreRecoResultObject::Release(void)
  1119. {
  1120. this->m_cRef--;
  1121. if (this->m_cRef > 0)
  1122. {
  1123. return this->m_cRef;
  1124. }
  1125. delete this;
  1126. return 0;
  1127. }
  1128. // ITfPropertyStore
  1129. STDMETHODIMP CPropStoreRecoResultObject::GetType(GUID *pguid)
  1130. {
  1131. HRESULT hr = E_INVALIDARG;
  1132. if (pguid)
  1133. {
  1134. *pguid = GUID_PROP_SAPIRESULTOBJECT;
  1135. hr = S_OK;
  1136. }
  1137. return hr;
  1138. }
  1139. STDMETHODIMP CPropStoreRecoResultObject::GetDataType(DWORD *pdwReserved)
  1140. {
  1141. HRESULT hr = E_INVALIDARG;
  1142. if (pdwReserved)
  1143. {
  1144. *pdwReserved = 0;
  1145. hr = S_OK;
  1146. }
  1147. return hr;
  1148. }
  1149. STDMETHODIMP CPropStoreRecoResultObject::GetData(VARIANT *pvarValue)
  1150. {
  1151. HRESULT hr = E_INVALIDARG;
  1152. if (pvarValue)
  1153. {
  1154. QuickVariantInit(pvarValue);
  1155. if (m_cpResultWrap)
  1156. {
  1157. IUnknown *pUnk;
  1158. hr = m_cpResultWrap->QueryInterface(IID_IUnknown, (void**)&pUnk);
  1159. if (SUCCEEDED(hr))
  1160. {
  1161. pvarValue->vt = VT_UNKNOWN;
  1162. pvarValue->punkVal = pUnk;
  1163. hr = S_OK;
  1164. }
  1165. }
  1166. }
  1167. return hr;
  1168. }
  1169. STDMETHODIMP CPropStoreRecoResultObject::OnTextUpdated(DWORD dwFlags, ITfRange *pRange, BOOL *pfAccept)
  1170. {
  1171. HRESULT hr = S_OK;
  1172. *pfAccept = FALSE;
  1173. Assert(pRange);
  1174. if (m_pimx->_AcceptRecoResultTextUpdates())
  1175. {
  1176. *pfAccept = TRUE;
  1177. }
  1178. else
  1179. {
  1180. CComPtr<ITfContext> cpic;
  1181. hr = pRange->GetContext(&cpic);
  1182. if (SUCCEEDED(hr) && cpic)
  1183. {
  1184. CPSRecoEditSession *pes;
  1185. if (pes = new CPSRecoEditSession(this, pRange, cpic))
  1186. {
  1187. pes->_SetEditSessionData(ESCB_PROP_TEXTUPDATE, NULL, 0, (LONG_PTR)dwFlags);
  1188. cpic->RequestEditSession(m_pimx->_GetId(), pes, TF_ES_READ | TF_ES_SYNC, &hr);
  1189. if ( SUCCEEDED(hr) )
  1190. *pfAccept = (BOOL)pes->_GetRetData( );
  1191. pes->Release();
  1192. }
  1193. }
  1194. }
  1195. return hr;
  1196. }
  1197. STDMETHODIMP CPropStoreRecoResultObject::Shrink(ITfRange *pRange, BOOL *pfFree)
  1198. {
  1199. HRESULT hr = S_OK;
  1200. if (m_pimx->_MasterLMEnabled())
  1201. {
  1202. return S_FALSE; // temporary solution to avoid nested editsessions
  1203. }
  1204. else
  1205. {
  1206. // Shrink this property store to reflect to the new doc Range (pRange).
  1207. // If the new range contains more than one element of recognized phrase,
  1208. // we just update the property store and keep this property store.
  1209. // *pfFree is set FALSE on exit.
  1210. // If the new range cannot contain even one complete element of recognized phrase,
  1211. // we just want to discard this property store, let Cicero engine to release this
  1212. // property store.
  1213. // *pfFree is set TRUE on exit.
  1214. Assert(pRange);
  1215. Assert(pfFree);
  1216. if ( !pRange || !pfFree )
  1217. {
  1218. return E_INVALIDARG;
  1219. }
  1220. CComPtr<ITfContext> cpic;
  1221. hr = pRange->GetContext(&cpic);
  1222. if (SUCCEEDED(hr) && cpic)
  1223. {
  1224. CPSRecoEditSession *pes;
  1225. if (pes = new CPSRecoEditSession(this, pRange, cpic))
  1226. {
  1227. pes->_SetEditSessionData(ESCB_PROP_SHRINK, NULL, 0);
  1228. cpic->RequestEditSession(m_pimx->_GetId(), pes, TF_ES_READ | TF_ES_SYNC, &hr);
  1229. if ( SUCCEEDED(hr) )
  1230. *pfFree = (BOOL)pes->_GetRetData( );
  1231. pes->Release();
  1232. }
  1233. }
  1234. return hr;
  1235. }
  1236. }
  1237. STDMETHODIMP CPropStoreRecoResultObject::Divide(ITfRange *pRangeThis, ITfRange *pRangeNew, ITfPropertyStore **ppPropStore)
  1238. {
  1239. if (m_pimx->_MasterLMEnabled())
  1240. {
  1241. return S_FALSE; // temporary solution to avoid nested editsessions
  1242. }
  1243. else
  1244. {
  1245. // 12/17/1999
  1246. // [dividing a range implementation strategy]
  1247. //
  1248. // - pRangeThis contains the text range *before* the dividing point
  1249. // - pRangeNew contrains the range *after* the dividing point
  1250. // - First, adjust this property store to correctly hold a start element and #of element
  1251. // for pRangeThis
  1252. // - then create a new property store for pRangeNew, which will share the same
  1253. // result blob.
  1254. //
  1255. // just an experiment to see if cutting the range works.
  1256. // *ppPropStore = NULL;
  1257. Assert(ppPropStore);
  1258. Assert(pRangeThis);
  1259. Assert(pRangeNew);
  1260. CComPtr<ITfContext> cpic;
  1261. HRESULT hr = pRangeThis->GetContext(&cpic);
  1262. if (SUCCEEDED(hr) && cpic)
  1263. {
  1264. CPSRecoEditSession *pes;
  1265. if (pes = new CPSRecoEditSession(this, pRangeThis, cpic))
  1266. {
  1267. pes->_SetUnk((IUnknown *)pRangeNew);
  1268. pes->_SetEditSessionData(ESCB_PROP_DIVIDE, NULL, 0);
  1269. cpic->RequestEditSession(m_pimx->_GetId(), pes, TF_ES_READ | TF_ES_SYNC, &hr);
  1270. if ( SUCCEEDED(hr) )
  1271. *ppPropStore = (ITfPropertyStore *)pes->_GetRetUnknown( );
  1272. pes->Release();
  1273. }
  1274. }
  1275. return hr;
  1276. }
  1277. }
  1278. //
  1279. // CPropStoreRecoResultObject::_OnTextUpdated
  1280. //
  1281. // the text has been modified in the document, this function just wants to determine
  1282. // if the property needs to change also.
  1283. // if pfAccept returns TRUE, means the property keep unchanged. ( propbaly it is capitalizing).
  1284. // if pfAccept returns FALSE, means the property needs to be changed to map to the new text ranges.
  1285. //
  1286. // consequently, property dividing or shrinking will be taken by cicero engine.
  1287. //
  1288. HRESULT CPropStoreRecoResultObject::_OnTextUpdated(TfEditCookie ec, DWORD dwFlags, ITfRange *pRange, BOOL *pfAccept)
  1289. {
  1290. // if the change is only about capitalizing we'll ignore the changes
  1291. Assert(pRange);
  1292. Assert(pfAccept);
  1293. long cch;
  1294. ULONG ichUpdate = 0;
  1295. TF_HALTCOND hc;
  1296. CComPtr<ITfRange> cpRangeTemp;
  1297. CComPtr<ITfRange> cpPropRangeTemp;
  1298. CRecoResultWrap *pResultWrap;
  1299. BOOL *pfKeepProp = pfAccept;
  1300. BOOL fCorrection = (dwFlags & TF_TU_CORRECTION);
  1301. *pfKeepProp = FALSE;
  1302. HRESULT hr = S_OK;
  1303. pResultWrap = (CRecoResultWrap *)(void *)m_cpResultWrap;
  1304. // if there's no current text don't try to save the prop
  1305. if (pResultWrap->m_bstrCurrentText == NULL)
  1306. return hr;
  1307. // did the run change size? we won't accept the change if
  1308. // the run changed size
  1309. if (m_cpRange->Clone(&cpPropRangeTemp) != S_OK)
  1310. {
  1311. hr = E_FAIL;
  1312. return hr;
  1313. }
  1314. hc.pHaltRange = cpPropRangeTemp;
  1315. hc.aHaltPos = TF_ANCHOR_END;
  1316. hc.dwFlags = 0;
  1317. if (cpPropRangeTemp->ShiftStart(ec, LONG_MAX, &cch, &hc) != S_OK)
  1318. {
  1319. hr = E_FAIL;
  1320. return hr;
  1321. }
  1322. if ((ULONG)cch != wcslen(pResultWrap->m_bstrCurrentText))
  1323. return hr;
  1324. // text has not changed size
  1325. // correction?
  1326. if (fCorrection)
  1327. {
  1328. *pfKeepProp = TRUE;
  1329. return hr;
  1330. }
  1331. // everything from here below is about
  1332. // checking for a case change only
  1333. // calculate the offset of update
  1334. cpPropRangeTemp.Release( );
  1335. hr = m_cpRange->Clone(&cpPropRangeTemp);
  1336. if (S_OK == hr)
  1337. {
  1338. hc.pHaltRange = pRange;
  1339. hc.aHaltPos = TF_ANCHOR_START;
  1340. hc.dwFlags = 0;
  1341. hr = cpPropRangeTemp->ShiftStart(ec, LONG_MAX, &cch, &hc);
  1342. ichUpdate = cch;
  1343. }
  1344. // calculate cch of the update
  1345. if (S_OK == hr)
  1346. {
  1347. hr = pRange->Clone(&cpRangeTemp);
  1348. }
  1349. if (S_OK == hr)
  1350. {
  1351. WCHAR *psz;
  1352. hc.pHaltRange = pRange;
  1353. hc.aHaltPos = TF_ANCHOR_END;
  1354. hc.dwFlags = 0;
  1355. cpRangeTemp->ShiftStart(ec, LONG_MAX, &cch, &hc);
  1356. psz = new WCHAR[cch+1];
  1357. if (psz)
  1358. {
  1359. if ( S_OK == pRange->GetText(ec, 0, psz, cch, (ULONG *)&cch))
  1360. {
  1361. *pfKeepProp = pResultWrap->_CanIgnoreChange(ichUpdate, psz, cch);
  1362. }
  1363. delete[] psz;
  1364. }
  1365. }
  1366. return hr;
  1367. }
  1368. //
  1369. // CPropStoreRecoResultObject::_Divide
  1370. //
  1371. // synopsis : receives edit cookie from edit session
  1372. // so that we can manipulate with ranges
  1373. // to set starting elements/# of elements
  1374. //
  1375. //
  1376. HRESULT CPropStoreRecoResultObject::_Divide(TfEditCookie ec, ITfRange *pR1, ITfRange *pR2, ITfPropertyStore **ppPs)
  1377. {
  1378. HRESULT hr = S_OK;
  1379. CRecoResultWrap *cpResultWrap;
  1380. long cchFirst = 0; // the number of characters in the first range.
  1381. long cchSecond = 0; // the number of characters in the Second range.
  1382. long cchSecondStart; // Offset of first char in the second range.
  1383. // it starts from the original range's start-point.
  1384. long cchOrgLen; // Number of characters in original text string.
  1385. int iElementOffsetChanged = 0; // If the end point of the first range
  1386. // is exactly in the last space char of
  1387. // one element, the new offset range for
  1388. // this element in the first range needs
  1389. // to be updated.
  1390. WCHAR *psz=NULL;
  1391. ULONG iElement;
  1392. ULONG ulStartElement, ulNumElement;
  1393. ULONG ulFirstEndElement, ulNextStartElement;
  1394. ULONG ulFirstStartOffset;
  1395. DIVIDECASE dcDivideCase;
  1396. CComPtr<ITfRange> cpRangeTemp;
  1397. ULONG ulFirstDelta, ulSecondDelta;
  1398. ULONG ulFirstTrail, ulSecondTrail;
  1399. ULONG ulFirstTSpaceRemoved, ulSecondTSpaceRemoved;
  1400. CStructArray<SPITNSHOWSTATE> rgOrgITNShowState;
  1401. CSpDynamicString dstrOrg, dstrFirst, dstrSecond;
  1402. TraceMsg(TF_GENERAL, "CPropStoreRecoResultObject::_Divide is called, this=0x%x", (INT_PTR)this);
  1403. if ( !pR1 || !pR2 )
  1404. return E_INVALIDARG;
  1405. // Update this property store to keep pR1 instead of the original whole range.
  1406. CComPtr<ITfRange> cpRange;
  1407. hr = pR1->Clone(&cpRange);
  1408. if (S_OK == hr)
  1409. {
  1410. m_cpRange = cpRange;
  1411. }
  1412. else
  1413. return hr;
  1414. Assert(m_cpRange);
  1415. // Update m_cpResultWrap for Range1. especially for data member m_bstrCurrentText, m_ulStartElement, m_ulNumElements, ulNumOfITN,
  1416. // and m_pulElementOffsets
  1417. cpResultWrap = (CRecoResultWrap *)(void *)m_cpResultWrap;
  1418. if ( cpResultWrap == NULL)
  1419. return E_FAIL;
  1420. if ( cpResultWrap->m_bstrCurrentText == NULL)
  1421. cpResultWrap->_SetElementOffsetCch(NULL); // To update internal text also.
  1422. if ( cpResultWrap->m_bstrCurrentText == NULL)
  1423. return E_FAIL;
  1424. // Initialize the text for the first range and second range.
  1425. dstrOrg.Append(cpResultWrap->m_bstrCurrentText);
  1426. cchOrgLen = wcslen(cpResultWrap->m_bstrCurrentText);
  1427. if ( cpResultWrap->IsElementOffsetIntialized( ) == FALSE )
  1428. {
  1429. cpResultWrap->_SetElementOffsetCch(NULL);
  1430. }
  1431. // Calculate how many elements will be in the first Range.
  1432. hr = pR1->Clone(&cpRangeTemp);
  1433. if ( hr == S_OK )
  1434. {
  1435. TF_HALTCOND hc;
  1436. hc.pHaltRange = pR1;
  1437. hc.aHaltPos = TF_ANCHOR_END;
  1438. hc.dwFlags = 0;
  1439. cpRangeTemp->ShiftStart(ec, LONG_MAX, &cchFirst, &hc);
  1440. if ( cchFirst == 0 )
  1441. return E_FAIL;
  1442. }
  1443. else
  1444. return E_FAIL;
  1445. // Calculate how many chars are in the second range.
  1446. cpRangeTemp.Release( );
  1447. hr = pR2->Clone(&cpRangeTemp);
  1448. if ( hr == S_OK )
  1449. {
  1450. TF_HALTCOND hc;
  1451. hc.pHaltRange = pR2;
  1452. hc.aHaltPos = TF_ANCHOR_END;
  1453. hc.dwFlags = 0;
  1454. cpRangeTemp->ShiftStart(ec, LONG_MAX, &cchSecond, &hc);
  1455. }
  1456. if ( cchSecond == 0 )
  1457. return E_FAIL;
  1458. cchSecondStart = cchOrgLen - cchSecond;
  1459. if ( cchSecondStart < cchFirst )
  1460. {
  1461. // Normally, it is not possible case, but for safety reason, just check it here.
  1462. cchSecondStart = cchFirst;
  1463. }
  1464. TraceMsg(TF_GENERAL, "cchFirst=%d, cchSecondStart=%d", cchFirst, cchSecondStart);
  1465. ulStartElement = cpResultWrap->GetStart( );
  1466. ulNumElement = cpResultWrap->GetNumElements( );
  1467. ulFirstEndElement = ulStartElement + ulNumElement - 1;
  1468. ulNextStartElement = ulStartElement + ulNumElement;
  1469. ulFirstStartOffset = cpResultWrap->_GetElementOffsetCch(ulStartElement);
  1470. ulFirstDelta = cpResultWrap->_GetOffsetDelta( );
  1471. ulSecondDelta = 0;
  1472. ulFirstTrail= 0;
  1473. ulSecondTrail = cpResultWrap->GetCharsInTrail( );
  1474. ulFirstTSpaceRemoved = 0;
  1475. ulSecondTSpaceRemoved = cpResultWrap->GetTrailSpaceRemoved( );
  1476. dcDivideCase = DivideNormal;
  1477. if ( (cchFirst >= cchOrgLen) || (cchSecondStart >= cchOrgLen) )
  1478. {
  1479. // Something is wrong here already.
  1480. // It is better to stop here and return error
  1481. // to avoid any possible crash in the below code!
  1482. return E_FAIL;
  1483. }
  1484. psz = (WCHAR *)dstrOrg;
  1485. psz += cchSecondStart; // need to account for deleted text as well
  1486. dstrSecond.Append(psz);
  1487. psz = (WCHAR *)dstrOrg;
  1488. dstrFirst.Append(psz);
  1489. psz = (WCHAR *)dstrFirst;
  1490. psz[cchFirst] = L'\0';
  1491. if ( ulNumElement == 0 )
  1492. {
  1493. // There is no any valid element in this range.
  1494. //
  1495. // we just update the m_bstrCurrentText, don't generate a property store
  1496. // for second range.
  1497. dcDivideCase = CurRangeNoElement;
  1498. }
  1499. else
  1500. {
  1501. // At least one element in this property range.
  1502. BOOL fFoundFirstEnd = FALSE;
  1503. BOOL fFoundSecondStart = FALSE;
  1504. for ( iElement=ulStartElement; iElement < ulStartElement + ulNumElement; iElement++)
  1505. {
  1506. ULONG cchAfterElem_i; // length of text range from StartElement to this element ( include this element).
  1507. ULONG cchToElem_i; // Length of text range from startElement to start of this element. ( exclude this elem).
  1508. cchAfterElem_i = cpResultWrap->_GetElementOffsetCch(iElement+1) - ulFirstStartOffset + ulFirstDelta;
  1509. cchToElem_i = cpResultWrap->_GetElementOffsetCch(iElement) - ulFirstStartOffset + ulFirstDelta;
  1510. if ( !fFoundFirstEnd )
  1511. {
  1512. // Try to find First End element and ulFirstTrail for the first range
  1513. if ( cchFirst <= (long)ulFirstDelta )
  1514. {
  1515. // Divide at the point which is not belong to any element of the phrase
  1516. ulFirstTrail = 0;
  1517. ulFirstDelta = cchFirst;
  1518. dcDivideCase = DivideInDelta;
  1519. fFoundFirstEnd = TRUE;
  1520. TraceMsg(TF_GENERAL, "The first range is divided inside Delta part");
  1521. }
  1522. else
  1523. {
  1524. if ( cchAfterElem_i == (ULONG)cchFirst )
  1525. {
  1526. // This is the end element for the first range.
  1527. ulFirstEndElement = iElement;
  1528. ulFirstTrail = 0;
  1529. fFoundFirstEnd = TRUE;
  1530. }
  1531. else if ( cchAfterElem_i > (ULONG)cchFirst )
  1532. {
  1533. if ( ((WCHAR *)dstrOrg)[cchFirst] == L' ')
  1534. {
  1535. // This is also the end elemenet.
  1536. // just divide in at a space char.
  1537. ulFirstEndElement = iElement;
  1538. ulFirstTrail = 0;
  1539. // The trailing space is now removed from the original element.
  1540. // we need to update the length for this element. ( update the offset
  1541. // for next element).
  1542. iElementOffsetChanged = cchAfterElem_i - cchFirst;
  1543. ulFirstTSpaceRemoved = iElementOffsetChanged;
  1544. }
  1545. else
  1546. {
  1547. // check to see if current element is inside an ITN.
  1548. BOOL fInsideITN;
  1549. ULONG ulITNStart, ulITNNumElem;
  1550. ULONG ulCurElement;
  1551. fInsideITN = cpResultWrap->_CheckITNForElement(NULL, iElement, &ulITNStart, &ulITNNumElem, NULL);
  1552. if ( fInsideITN )
  1553. ulCurElement = ulITNStart;
  1554. else
  1555. ulCurElement = iElement;
  1556. ulFirstEndElement = ulCurElement - 1;
  1557. // The previous one is EndElement if there is a previous element
  1558. // Discard this element.
  1559. // Divide at a valid element
  1560. // If divide in the first element, specially handle it.
  1561. if ( ulCurElement == ulStartElement)
  1562. {
  1563. dcDivideCase = DivideInsideFirstElement;
  1564. TraceMsg(TF_GENERAL, "The first range is divided inside the first element");
  1565. }
  1566. // The first part of this element would become
  1567. // the trail part of the first range.
  1568. ulFirstTrail = (ULONG)cchFirst - cchToElem_i;
  1569. }
  1570. fFoundFirstEnd = TRUE;
  1571. }
  1572. }
  1573. }
  1574. if ( fFoundFirstEnd )
  1575. {
  1576. // Now the data for the first range is completed.
  1577. // we want to find data for the second range.
  1578. // We want to find the start element and ulSecondDelta for the second range.
  1579. if ( (long)cchToElem_i >= cchSecondStart )
  1580. {
  1581. // Find the element which is the first element after the start point of second
  1582. // range.
  1583. ulNextStartElement = iElement;
  1584. ulSecondDelta = cchToElem_i - cchSecondStart;
  1585. fFoundSecondStart = TRUE;
  1586. break;
  1587. }
  1588. }
  1589. } // for
  1590. if ( !fFoundFirstEnd )
  1591. {
  1592. // Cannot find the first end element from the above code.
  1593. // it must be divided in the trailing part.
  1594. // we just want to change the ulCharsInTrail for the first range.
  1595. ULONG ulValidLenInFirstRange;
  1596. // ulValidLenInFirstRange is the number of Delta chars and valid elements' chars.
  1597. ulValidLenInFirstRange = cpResultWrap->_GetElementOffsetCch(ulStartElement + ulNumElement) - ulFirstStartOffset + ulFirstDelta;
  1598. ulFirstTrail = cchFirst - ulValidLenInFirstRange;
  1599. }
  1600. if ( !fFoundSecondStart )
  1601. {
  1602. // The second start point must be in Last element or in the Trailing part in the original range.
  1603. // The second range will not contain any valid element.
  1604. ulSecondTrail = 0;
  1605. ulSecondDelta = cchOrgLen - cchSecondStart;
  1606. ulNextStartElement = ulStartElement + ulNumElement; // This is not a valid element number in the original
  1607. // range. using this value means there is no valid element
  1608. // in the second range.
  1609. }
  1610. }
  1611. TraceMsg(TF_GENERAL, "ulStartElement = %d ulNumElement=%d", ulStartElement, ulNumElement);
  1612. TraceMsg(TF_GENERAL, "ulFirstEndElement = %d ulNextStartElement=%d", ulFirstEndElement, ulNextStartElement);
  1613. TraceMsg(TF_GENERAL, "The First Range text =\"%S\", delta=%d, Trail=%d, TSRemoved=%d", (WCHAR *)dstrFirst, ulFirstDelta, ulFirstTrail, ulFirstTSpaceRemoved);
  1614. TraceMsg(TF_GENERAL, "The second range text =\"%S\", delta=%d, Trail=%d, TSRemoved=%d", (WCHAR *)dstrSecond, ulSecondDelta, ulSecondTrail, ulSecondTSpaceRemoved);
  1615. if (cpResultWrap->m_bstrCurrentText)
  1616. SysFreeString(cpResultWrap->m_bstrCurrentText);
  1617. cpResultWrap->m_bstrCurrentText = SysAllocString((WCHAR *)dstrFirst);
  1618. // Keep the ITN show-state for the second reco wrap use.
  1619. if ( cpResultWrap->m_ulNumOfITN )
  1620. {
  1621. rgOrgITNShowState.Append(cpResultWrap->m_ulNumOfITN);
  1622. for (ULONG i=0; i<cpResultWrap->m_ulNumOfITN; i++)
  1623. {
  1624. SPITNSHOWSTATE *pITNShowState;
  1625. if ( pITNShowState = rgOrgITNShowState.GetPtr(i))
  1626. {
  1627. SPITNSHOWSTATE *pITNShowStateSource;
  1628. pITNShowStateSource = cpResultWrap->m_rgITNShowState.GetPtr(i);
  1629. pITNShowState->ulITNStart = pITNShowStateSource->ulITNStart;
  1630. pITNShowState->ulITNNumElem = pITNShowStateSource->ulITNNumElem;
  1631. pITNShowState->fITNShown = pITNShowStateSource->fITNShown;
  1632. }
  1633. }
  1634. }
  1635. // Keep the Offset list for second range
  1636. ULONG *pulOffsetForSecond = NULL;
  1637. if ( (ulStartElement + ulNumElement - ulNextStartElement) > 0 )
  1638. {
  1639. ULONG ulNextNumOffset;
  1640. ulNextNumOffset = ulStartElement + ulNumElement - ulNextStartElement + 1;
  1641. pulOffsetForSecond = new ULONG[ulNextNumOffset];
  1642. if ( pulOffsetForSecond )
  1643. {
  1644. ULONG i;
  1645. ULONG ulOffset;
  1646. for ( i=0; i < ulNextNumOffset; i++ )
  1647. {
  1648. ulOffset = cpResultWrap->_GetElementOffsetCch(ulNextStartElement + i );
  1649. pulOffsetForSecond[i] = ulOffset;
  1650. }
  1651. }
  1652. else
  1653. hr = E_OUTOFMEMORY;
  1654. }
  1655. switch ( dcDivideCase )
  1656. {
  1657. case DivideNormal :
  1658. ULONG ulNumOfITN;
  1659. ULONG ulFirstNumElement;
  1660. ulFirstNumElement = ulFirstEndElement - ulStartElement + 1;
  1661. cpResultWrap->SetNumElements(ulFirstNumElement);
  1662. // update the ITN show-state list.
  1663. if (cpResultWrap->m_ulNumOfITN > 0)
  1664. {
  1665. ulNumOfITN = cpResultWrap->_RangeHasITN(ulStartElement, ulFirstNumElement);
  1666. if ( cpResultWrap->m_ulNumOfITN > ulNumOfITN )
  1667. {
  1668. // There is ITN number change
  1669. // need to remove the ITNs which are not in this range.
  1670. cpResultWrap->m_rgITNShowState.Remove(ulNumOfITN, cpResultWrap->m_ulNumOfITN - ulNumOfITN);
  1671. cpResultWrap->m_ulNumOfITN = ulNumOfITN;
  1672. }
  1673. }
  1674. if ( iElementOffsetChanged > 0 )
  1675. {
  1676. // Some trailing spaces are removed from end element of the first range.
  1677. ULONG ulNewOffset;
  1678. ulNewOffset = cpResultWrap->_GetElementOffsetCch(ulFirstEndElement + 1);
  1679. cpResultWrap->_SetElementNewOffset(ulFirstEndElement + 1, ulNewOffset - iElementOffsetChanged);
  1680. }
  1681. cpResultWrap->SetCharsInTrail(ulFirstTrail);
  1682. cpResultWrap->SetTrailSpaceRemoved( ulFirstTSpaceRemoved );
  1683. break;
  1684. case DivideInsideFirstElement :
  1685. case DivideInDelta :
  1686. cpResultWrap->SetNumElements(0);
  1687. cpResultWrap->m_ulNumOfITN = 0;
  1688. cpResultWrap->SetOffsetDelta(ulFirstDelta);
  1689. cpResultWrap->SetCharsInTrail(ulFirstTrail);
  1690. cpResultWrap->SetTrailSpaceRemoved( ulFirstTSpaceRemoved );
  1691. break;
  1692. case CurRangeNoElement :
  1693. TraceMsg(TF_GENERAL, "There is no element in original range");
  1694. cpResultWrap->SetNumElements(0);
  1695. cpResultWrap->m_ulNumOfITN = 0;
  1696. break;
  1697. }
  1698. // Now generate a new properstore for the new range pR2.
  1699. // if the new property store is required.
  1700. if ( ppPs == NULL )
  1701. return hr;
  1702. if (dcDivideCase == CurRangeNoElement )
  1703. {
  1704. // there is no any element in the original property range.
  1705. *ppPs = NULL;
  1706. return hr;
  1707. }
  1708. CPropStoreRecoResultObject *prps = NULL;
  1709. if (S_OK == hr)
  1710. prps = new CPropStoreRecoResultObject(m_pimx, pR2);
  1711. if (prps)
  1712. {
  1713. hr = prps->QueryInterface(IID_ITfPropertyStore, (void **)ppPs);
  1714. if (SUCCEEDED(hr))
  1715. {
  1716. CRecoResultWrap *prw;
  1717. ULONG ulNextNum;
  1718. ULONG ulNumOfITN;
  1719. CComPtr<ISpRecoResult> cpResult;
  1720. if ( ulNextStartElement >= ulStartElement + ulNumElement)
  1721. {
  1722. // It is divided at the last element of the original range.
  1723. // We will just generate a property store for this Cicero ver 1.0
  1724. // to avoid the original property store be removed by Cicero engine.
  1725. // FutureConsider: if Cicero changes the logic in the future, we need to change
  1726. // this code as well so that we don't need to generate a property store
  1727. // for this second range.
  1728. ulNextNum = 0;
  1729. ulNumOfITN = 0;
  1730. ulSecondTSpaceRemoved = 0;
  1731. }
  1732. else
  1733. {
  1734. ulNextNum = ulStartElement + ulNumElement - ulNextStartElement;
  1735. ulNumOfITN = cpResultWrap->_RangeHasITN(ulNextStartElement, ulNextNum);
  1736. }
  1737. prw = new CRecoResultWrap(m_pimx, ulNextStartElement, ulNextNum, ulNumOfITN);
  1738. if ( prw != NULL )
  1739. {
  1740. hr = cpResultWrap->GetResult(&cpResult);
  1741. }
  1742. else
  1743. {
  1744. // Check interface pointer ref leak problem.
  1745. return E_OUTOFMEMORY;
  1746. }
  1747. if (S_OK == hr)
  1748. {
  1749. hr = prw->Init(cpResult);
  1750. }
  1751. if (S_OK == hr)
  1752. {
  1753. prw->SetOffsetDelta(ulSecondDelta);
  1754. prw->SetCharsInTrail(ulSecondTrail);
  1755. prw->SetTrailSpaceRemoved( ulSecondTSpaceRemoved );
  1756. prw->m_bstrCurrentText = SysAllocString((WCHAR *)dstrSecond);
  1757. // Update ITN show-state list .
  1758. if ( ulNumOfITN > 0 )
  1759. {
  1760. SPITNSHOWSTATE *pITNShowState;
  1761. ULONG ulOrgNumOfITN;
  1762. ulOrgNumOfITN = rgOrgITNShowState.Count( );
  1763. for ( ULONG iIndex=0; iIndex<ulOrgNumOfITN; iIndex ++ )
  1764. {
  1765. pITNShowState = rgOrgITNShowState.GetPtr(iIndex);
  1766. if ( pITNShowState)
  1767. {
  1768. if ( (pITNShowState->ulITNStart
  1769. >= ulNextStartElement) &&
  1770. (pITNShowState->ulITNStart +
  1771. pITNShowState->ulITNNumElem)
  1772. <= (ulNextStartElement + ulNextNum) )
  1773. {
  1774. prw->_InitITNShowState(
  1775. pITNShowState->fITNShown,
  1776. pITNShowState->ulITNStart,
  1777. pITNShowState->ulITNNumElem);
  1778. }
  1779. }
  1780. } // for
  1781. } // if
  1782. // Update the Offset list for the second range.
  1783. if ( (ulNextNum > 0) && pulOffsetForSecond )
  1784. {
  1785. ULONG i;
  1786. ULONG ulOffset;
  1787. for ( i=0; i <= ulNextNum; i ++ )
  1788. {
  1789. ulOffset = pulOffsetForSecond[i];
  1790. prw->_SetElementNewOffset(ulNextStartElement + i, ulOffset);
  1791. }
  1792. }
  1793. hr = prps->_InitFromResultWrap(prw);
  1794. }
  1795. prw->Release( );
  1796. }
  1797. prps->Release();
  1798. }
  1799. else
  1800. {
  1801. hr = E_OUTOFMEMORY;
  1802. }
  1803. if ( rgOrgITNShowState.Count( ) )
  1804. rgOrgITNShowState.Clear( );
  1805. if ( pulOffsetForSecond )
  1806. delete[] pulOffsetForSecond;
  1807. return hr;
  1808. }
  1809. //
  1810. // CPropStoreRecoResultObject::_Shrink
  1811. //
  1812. // receive EditCookie from edit session.
  1813. // try to determine the new range's attribute to update the property store or notify
  1814. // the ctf engine to discard it.
  1815. //
  1816. HRESULT CPropStoreRecoResultObject::_Shrink(TfEditCookie ec, ITfRange *pRange,BOOL *pfFree)
  1817. {
  1818. HRESULT hr = S_OK;
  1819. WCHAR *pwszNewText = NULL;
  1820. long cchNew = 0;
  1821. WCHAR *pwszOrgText = NULL;
  1822. CSpDynamicString dstrOrg;
  1823. long cchOrg = 0;
  1824. CComPtr<ITfRange> cpRangeTemp;
  1825. CRecoResultWrap *cpResultWrap;
  1826. long iStartOffset; // the offset from start of the new range to the
  1827. // original range start point.
  1828. long iLastOffset; // The offset from the last character of new range
  1829. // to the original range start point.
  1830. ULONG ulNewStartElement, ulNewNumElements, ulNewDelta, ulNewTrail, ulNewTSRemoved;
  1831. ULONG ulOrgStartElement, ulOrgNumElements, ulOrgDelta, ulOrgTrail, ulOrgTSRemoved;
  1832. BOOL fShrinkToWrongPos = FALSE;
  1833. int iElementOffsetChanged = 0; // If the new range just remove the
  1834. // trailing space of original text,
  1835. // the new valid start and end element
  1836. // will keep unchanged, but the length of
  1837. // new end element is changed.
  1838. TraceMsg(TF_GENERAL, "CPropStoreRecoResultObject::_Shrink is called, this=0x%x", (INT_PTR)this);
  1839. if ( !pRange || !pfFree ) return E_INVALIDARG;
  1840. // Set *pfFree as TRUE intially in case there is an error occuring.
  1841. *pfFree = TRUE;
  1842. cpResultWrap = (CRecoResultWrap *)(void *)m_cpResultWrap;
  1843. if ( cpResultWrap == NULL ) return E_FAIL;
  1844. if ( (WCHAR *)cpResultWrap->m_bstrCurrentText == NULL )
  1845. cpResultWrap->_SetElementOffsetCch(NULL); // To update internal text based.
  1846. dstrOrg.Append((WCHAR *)cpResultWrap->m_bstrCurrentText);
  1847. pwszOrgText = (WCHAR *)dstrOrg;
  1848. ulOrgNumElements = cpResultWrap->GetNumElements( );
  1849. ulOrgStartElement = cpResultWrap->GetStart( );
  1850. ulOrgDelta = cpResultWrap->_GetOffsetDelta( );
  1851. ulOrgTrail = cpResultWrap->GetCharsInTrail( );
  1852. ulOrgTSRemoved = cpResultWrap->GetTrailSpaceRemoved( );
  1853. if ( pwszOrgText )
  1854. cchOrg = wcslen(pwszOrgText);
  1855. if ( (ulOrgNumElements ==0) || (pwszOrgText == NULL) || (cchOrg == 0) )
  1856. {
  1857. // This property store doesn't have resultwrap or doesn't have valid element.
  1858. // let cicero engine free this property store.
  1859. return hr;
  1860. }
  1861. pwszNewText = new WCHAR[cchOrg+1];
  1862. // try to get the new text pointed by pRange and character number this text.
  1863. if ( pwszNewText )
  1864. {
  1865. hr = pRange->Clone( &cpRangeTemp );
  1866. if ( hr == S_OK)
  1867. {
  1868. hr = cpRangeTemp->GetText(ec, 0, pwszNewText, cchOrg, (ULONG *)&cchNew);
  1869. }
  1870. // Get the new range's StartOffset and LastOffset in the original property range.
  1871. iStartOffset = 0;
  1872. iLastOffset = cchOrg;
  1873. if ( hr == S_OK && cchNew > 0 )
  1874. {
  1875. long i;
  1876. BOOL fFoundNewString=FALSE;
  1877. for (i=0; i<=(cchOrg-cchNew); i++)
  1878. {
  1879. WCHAR *pwszOrg;
  1880. pwszOrg = pwszOrgText + i;
  1881. if ( wcsncmp(pwszOrg, pwszNewText, cchNew) == 0 )
  1882. {
  1883. // Found the match
  1884. iStartOffset = i;
  1885. iLastOffset = i + cchNew;
  1886. fFoundNewString = TRUE;
  1887. break;
  1888. }
  1889. }
  1890. // If we cannot find the new text as the substring in the original property text.
  1891. // It must be shrinked to a wrong place.
  1892. fShrinkToWrongPos = !fFoundNewString;
  1893. }
  1894. }
  1895. else
  1896. hr = E_OUTOFMEMORY;
  1897. if ( hr != S_OK || fShrinkToWrongPos)
  1898. goto CleanUp;
  1899. TraceMsg(TF_GENERAL, "Shrink: NewText: cchNew=%d :\"%S\"", cchNew, pwszNewText);
  1900. TraceMsg(TF_GENERAL, "Shrink: OrgText: cchOrg=%d :\"%S\"", cchOrg, pwszOrgText);
  1901. TraceMsg(TF_GENERAL, "Shrink: Org: StartElem=%d NumElem=%d Delta=%d, Trail=%d, TSRemoved=%d", ulOrgStartElement, ulOrgNumElements, ulOrgDelta, ulOrgTrail, ulOrgTSRemoved);
  1902. TraceMsg(TF_GENERAL, "Shrink: iStartOffset=%d, iLastOffset=%d", iStartOffset, iLastOffset);
  1903. if ( cpResultWrap->IsElementOffsetIntialized( ) == FALSE )
  1904. cpResultWrap->_SetElementOffsetCch(NULL);
  1905. ulNewStartElement = ulOrgStartElement;
  1906. ulNewDelta = ulOrgDelta;
  1907. ulNewTrail = ulOrgTrail;
  1908. // Calculate ulNewStartElement and ulNewDelta.
  1909. if ( (ULONG)iStartOffset <= ulOrgDelta )
  1910. {
  1911. ulNewDelta = ulOrgDelta - iStartOffset;
  1912. ulNewStartElement = ulOrgStartElement;
  1913. }
  1914. else
  1915. {
  1916. ULONG iElement;
  1917. ULONG ulOrgStartOffset;
  1918. ulOrgStartOffset = cpResultWrap->_GetElementOffsetCch(ulOrgStartElement);
  1919. for ( iElement=ulOrgStartElement; iElement < ulOrgStartElement + ulOrgNumElements; iElement++)
  1920. {
  1921. ULONG ulToElement;
  1922. ULONG ulAfterElement;
  1923. ulToElement = cpResultWrap->_GetElementOffsetCch(iElement) - ulOrgStartOffset + ulOrgDelta;
  1924. ulAfterElement = cpResultWrap->_GetElementOffsetCch(iElement+1) - ulOrgStartOffset + ulOrgDelta;
  1925. if ( ulToElement == (ULONG)iStartOffset )
  1926. {
  1927. ulNewStartElement = iElement;
  1928. ulNewDelta = 0;
  1929. break;
  1930. }
  1931. else
  1932. {
  1933. if ( (ulToElement < (ULONG)iStartOffset) && (ulAfterElement > (ULONG)iStartOffset))
  1934. {
  1935. ulNewStartElement = iElement + 1;
  1936. ulNewDelta = ulAfterElement - iStartOffset;
  1937. break;
  1938. }
  1939. }
  1940. }
  1941. }
  1942. // Calculate new ulNewNumElements.
  1943. ulNewNumElements = 0;
  1944. if ( iLastOffset == cchOrg )
  1945. {
  1946. //
  1947. ULONG ulNewEndElement;
  1948. // New End is the same as org End.
  1949. ulNewEndElement = ulOrgStartElement + ulOrgNumElements - 1;
  1950. ulNewNumElements = 1 + ulNewEndElement - ulNewStartElement;
  1951. }
  1952. else
  1953. {
  1954. long iElement;
  1955. ULONG ulOrgStartOffset;
  1956. ULONG ulOrgEndElement;
  1957. ULONG ulNewEndElement;
  1958. BOOL fFound;
  1959. ulOrgEndElement = ulOrgStartElement + ulOrgNumElements - 1;
  1960. ulOrgStartOffset = cpResultWrap->_GetElementOffsetCch(ulOrgStartElement);
  1961. fFound = FALSE;
  1962. for ( iElement=(long)ulOrgEndElement; iElement >= (long)ulOrgStartElement; iElement--)
  1963. {
  1964. ULONG ulToElement;
  1965. ULONG ulAfterElement;
  1966. BOOL fInsideITN;
  1967. ULONG ulITNStart, ulITNNumElem;
  1968. ULONG ulCurElement;
  1969. ulToElement = cpResultWrap->_GetElementOffsetCch(iElement) - ulOrgStartOffset + ulOrgDelta;
  1970. ulAfterElement = cpResultWrap->_GetElementOffsetCch(iElement+1) - ulOrgStartOffset + ulOrgDelta;
  1971. if ( iElement == (long)ulOrgEndElement && ( ulAfterElement <= (ULONG)iLastOffset ) )
  1972. {
  1973. // This org last element would be the new last element
  1974. ulNewEndElement = iElement;
  1975. ulNewTrail = (ULONG)iLastOffset - ulAfterElement;
  1976. fFound = TRUE;
  1977. break;
  1978. }
  1979. fInsideITN = cpResultWrap->_CheckITNForElement(NULL, iElement, &ulITNStart, &ulITNNumElem, NULL);
  1980. if ( fInsideITN )
  1981. ulCurElement = ulITNStart;
  1982. else
  1983. ulCurElement = iElement;
  1984. if ( ulToElement == (ULONG)iLastOffset )
  1985. {
  1986. ulNewEndElement = ulCurElement - 1;
  1987. ulNewTrail = 0;
  1988. fFound = TRUE;
  1989. break;
  1990. }
  1991. if ( (ulToElement < (ULONG)iLastOffset) && (ulAfterElement > (ULONG)iLastOffset))
  1992. {
  1993. if ( pwszOrgText[iLastOffset] == L' ')
  1994. {
  1995. // The trailing space is now removed from the original element.
  1996. // we need to update the length for this element. ( update the offset
  1997. // for next element).
  1998. iElementOffsetChanged = ulAfterElement - iLastOffset;
  1999. if ( fInsideITN )
  2000. ulNewEndElement = ulITNStart + ulITNNumElem - 1;
  2001. else
  2002. ulNewEndElement = iElement;
  2003. ulNewTrail = 0;
  2004. }
  2005. else
  2006. {
  2007. ulNewEndElement = ulCurElement - 1;
  2008. ulNewTrail = (ULONG)iLastOffset - ulToElement;
  2009. }
  2010. fFound = TRUE;
  2011. break;
  2012. }
  2013. }
  2014. if ( fFound )
  2015. ulNewNumElements = 1 + ulNewEndElement - ulNewStartElement;
  2016. }
  2017. ulNewTSRemoved = ulOrgTSRemoved + iElementOffsetChanged;
  2018. TraceMsg(TF_GENERAL, "Shrink: New: StartElem=%d NumElem=%d Delta=%d, Trail=%d, TSRemoved=%d", ulNewStartElement, ulNewNumElements, ulNewDelta, ulNewTrail, ulNewTSRemoved);
  2019. // If there is no valid element in the new range, discard this property store
  2020. // otherwise, keep it and update the related data members.
  2021. if ( ulNewNumElements > 0 )
  2022. {
  2023. ULONG ulNumOfITN;
  2024. *pfFree = FALSE;
  2025. CComPtr<ITfRange> cpRange;
  2026. hr = pRange->Clone(&cpRange);
  2027. if (S_OK == hr)
  2028. {
  2029. m_cpRange = cpRange;
  2030. cpResultWrap->SetStart(ulNewStartElement);
  2031. cpResultWrap->SetNumElements(ulNewNumElements);
  2032. cpResultWrap->SetOffsetDelta(ulNewDelta);
  2033. cpResultWrap->SetCharsInTrail(ulNewTrail);
  2034. cpResultWrap->SetTrailSpaceRemoved( ulNewTSRemoved );
  2035. ulNumOfITN = cpResultWrap->_RangeHasITN(ulNewStartElement, ulNewNumElements);
  2036. cpResultWrap->m_ulNumOfITN = ulNumOfITN;
  2037. // Update ITN show-state list
  2038. if ( ulNumOfITN > 0 )
  2039. {
  2040. SPITNSHOWSTATE *pITNShowState;
  2041. ULONG ulOrgNumOfITN;
  2042. ulOrgNumOfITN = cpResultWrap->m_rgITNShowState.Count( );
  2043. for ( ULONG iIndex=ulOrgNumOfITN; iIndex>0; iIndex -- )
  2044. {
  2045. pITNShowState = cpResultWrap->m_rgITNShowState.GetPtr(iIndex-1);
  2046. if ( pITNShowState)
  2047. {
  2048. if ( (pITNShowState->ulITNStart < ulNewStartElement) ||
  2049. (pITNShowState->ulITNStart + pITNShowState->ulITNNumElem) > (ulNewStartElement + ulNewNumElements) )
  2050. {
  2051. // This ITN is not in the new Range
  2052. cpResultWrap->m_rgITNShowState.Remove(iIndex-1, 1);
  2053. }
  2054. }
  2055. }
  2056. }
  2057. else
  2058. if ( cpResultWrap->m_rgITNShowState.Count( ) )
  2059. cpResultWrap->m_rgITNShowState.Clear( );
  2060. if ( cpResultWrap->m_bstrCurrentText )
  2061. SysFreeString(cpResultWrap->m_bstrCurrentText);
  2062. cpResultWrap->m_bstrCurrentText = SysAllocString(pwszNewText);
  2063. if ( iElementOffsetChanged != 0 )
  2064. {
  2065. ULONG ulNewOffset;
  2066. ULONG ulElemAfterEnd;
  2067. ulElemAfterEnd = ulNewStartElement + ulNewNumElements;
  2068. ulNewOffset = cpResultWrap->_GetElementOffsetCch(ulElemAfterEnd);
  2069. cpResultWrap->_SetElementNewOffset(ulElemAfterEnd, ulNewOffset - iElementOffsetChanged);
  2070. }
  2071. }
  2072. }
  2073. CleanUp:
  2074. if ( pwszNewText ) delete[] pwszNewText;
  2075. return hr;
  2076. }
  2077. //
  2078. // CPropStoreRecoResultObject::Clone
  2079. //
  2080. // synopsis : make a new cloned propstore which shares the same SAPI result
  2081. // object as the current class instance
  2082. //
  2083. //
  2084. STDMETHODIMP CPropStoreRecoResultObject::Clone(ITfPropertyStore **ppPropStore)
  2085. {
  2086. HRESULT hr;
  2087. CPropStoreRecoResultObject *prps = new CPropStoreRecoResultObject(m_pimx, m_cpRange);
  2088. if (prps)
  2089. {
  2090. hr = prps->QueryInterface(IID_ITfPropertyStore, (void **)ppPropStore);
  2091. if (SUCCEEDED(hr))
  2092. {
  2093. CRecoResultWrap *prw = NULL;
  2094. CRecoResultWrap *pRecoWrapOrg = NULL;
  2095. if ( m_cpResultWrap )
  2096. {
  2097. hr = m_cpResultWrap->QueryInterface(IID_PRIV_RESULTWRAP, (void **)&pRecoWrapOrg);
  2098. if ( hr == S_OK )
  2099. {
  2100. hr = pRecoWrapOrg->Clone(&prw);
  2101. }
  2102. SafeRelease(pRecoWrapOrg);
  2103. if ( hr == S_OK )
  2104. hr = prps->_InitFromResultWrap(prw);
  2105. SafeRelease(prw);
  2106. }
  2107. }
  2108. prps->Release();
  2109. }
  2110. else
  2111. {
  2112. hr = E_OUTOFMEMORY;
  2113. }
  2114. return hr;
  2115. }
  2116. STDMETHODIMP CPropStoreRecoResultObject::GetPropertyRangeCreator(CLSID *pclsid)
  2117. {
  2118. HRESULT hr = E_INVALIDARG;
  2119. if (pclsid)
  2120. {
  2121. *pclsid = CLSID_SapiLayr;
  2122. hr = S_OK;
  2123. }
  2124. return hr;
  2125. }
  2126. //
  2127. // CPropStoreRecoResultObject::Serialize
  2128. //
  2129. // synopsis: takes a pointer to an IStream and get the current
  2130. // SAPI result object serialized
  2131. //
  2132. // changes from CResultPropertyStore:
  2133. // Uses SAPI's result object to get the blob
  2134. // serialized. ISpResultObject has to be cloned
  2135. // in order to keep the object alive after
  2136. // 'detaching' it.
  2137. //
  2138. //
  2139. STDMETHODIMP CPropStoreRecoResultObject::Serialize(IStream *pStream, ULONG *pcb)
  2140. {
  2141. HRESULT hr = E_FAIL;
  2142. TraceMsg(TF_GENERAL, "Serialize is called, this = 0x%x", (INT_PTR)this);
  2143. if (m_cpResultWrap && pStream)
  2144. {
  2145. CComPtr<IServiceProvider> cpServicePrv;
  2146. CComPtr<ISpRecoResult> cpRecoResult;
  2147. SPSERIALIZEDRESULT *pResBlock;
  2148. ULONG ulrw1 = 0;
  2149. ULONG ulrw2 = 0;
  2150. CRecoResultWrap *cpRecoWrap;
  2151. ULONG ulSizeRecoWrap, ulTextNum, ulITNSize;
  2152. ULONG ulOffsetSize, ulOffsetNum;
  2153. RECOWRAPDATA *pRecoWrapData;
  2154. cpRecoWrap = (CRecoResultWrap *)(void *)m_cpResultWrap;
  2155. // We want to save m_ulStartElement, m_ulNumElements, m_OffsetDelta, ulNumOfITN, m_bstrCurrentText
  2156. // and a list of ITN show state structure in the RecoResultWrap to the serialized stream.
  2157. ulTextNum = 0;
  2158. if (cpRecoWrap->m_bstrCurrentText)
  2159. {
  2160. ulTextNum = wcslen(cpRecoWrap->m_bstrCurrentText) + 1; // plus NULL terminator
  2161. }
  2162. ulITNSize = cpRecoWrap->m_ulNumOfITN * sizeof(SPITNSHOWSTATE);
  2163. if ( cpRecoWrap->IsElementOffsetIntialized( ) )
  2164. ulOffsetNum = cpRecoWrap->GetNumElements( ) + 1;
  2165. else
  2166. ulOffsetNum = 0;
  2167. ulOffsetSize = ulOffsetNum * sizeof(ULONG);
  2168. // Serialiezed data will contain RECOWRAPDATA struct, ITN show-state list, Offset list and m_bstrCurrentText.
  2169. ulSizeRecoWrap = sizeof(RECOWRAPDATA) + ulITNSize + ulOffsetSize + sizeof(WCHAR) * ulTextNum;
  2170. pRecoWrapData = (RECOWRAPDATA *)cicMemAllocClear(ulSizeRecoWrap);
  2171. if (pRecoWrapData)
  2172. {
  2173. WCHAR *pwszText;
  2174. pRecoWrapData->ulSize = ulSizeRecoWrap;
  2175. pRecoWrapData->ulStartElement = cpRecoWrap->GetStart( );
  2176. pRecoWrapData->ulNumElements = cpRecoWrap->GetNumElements( );
  2177. pRecoWrapData->ulOffsetDelta = cpRecoWrap->_GetOffsetDelta( );
  2178. pRecoWrapData->ulCharsInTrail = cpRecoWrap->GetCharsInTrail( );
  2179. pRecoWrapData->ulTrailSpaceRemoved = cpRecoWrap->GetTrailSpaceRemoved( );
  2180. pRecoWrapData->ulNumOfITN = cpRecoWrap->m_ulNumOfITN;
  2181. pRecoWrapData->ulOffsetNum = ulOffsetNum;
  2182. // Save the ITN show-state list
  2183. if ( cpRecoWrap->m_ulNumOfITN > 0 )
  2184. {
  2185. SPITNSHOWSTATE *pITNShowState;
  2186. pITNShowState = (SPITNSHOWSTATE *)((BYTE *)pRecoWrapData + sizeof(RECOWRAPDATA));
  2187. for ( ULONG i=0; i<cpRecoWrap->m_ulNumOfITN; i++)
  2188. {
  2189. SPITNSHOWSTATE *pITNShowStateSource;
  2190. pITNShowStateSource = cpRecoWrap->m_rgITNShowState.GetPtr(i);
  2191. if ( pITNShowStateSource )
  2192. {
  2193. pITNShowState->fITNShown = pITNShowStateSource->fITNShown;
  2194. pITNShowState->ulITNNumElem = pITNShowStateSource->ulITNNumElem;
  2195. pITNShowState->ulITNStart = pITNShowStateSource->ulITNStart;
  2196. pITNShowState ++;
  2197. }
  2198. }
  2199. }
  2200. // Save the offset list
  2201. if ( ulOffsetSize > 0 )
  2202. {
  2203. ULONG *pulOffset;
  2204. pulOffset = (ULONG *)((BYTE *)pRecoWrapData + sizeof(RECOWRAPDATA) + ulITNSize);
  2205. for (ULONG i=0; i<ulOffsetNum; i++)
  2206. {
  2207. pulOffset[i] = cpRecoWrap->_GetElementOffsetCch(pRecoWrapData->ulStartElement + i );
  2208. }
  2209. }
  2210. if (cpRecoWrap->m_bstrCurrentText)
  2211. {
  2212. pwszText = (WCHAR *)((BYTE *)pRecoWrapData + sizeof(RECOWRAPDATA) + ulITNSize + ulOffsetSize);
  2213. StringCchCopyW(pwszText, ulTextNum, cpRecoWrap->m_bstrCurrentText);
  2214. }
  2215. hr = pStream->Write(
  2216. pRecoWrapData,
  2217. ulSizeRecoWrap, // the number of bytes to copy
  2218. &ulrw1
  2219. );
  2220. if ( SUCCEEDED(hr) && (ulrw1 == ulSizeRecoWrap))
  2221. {
  2222. // QI the service provider first then get to the sapi interface
  2223. //
  2224. hr = m_cpResultWrap->QueryInterface(IID_IServiceProvider, (void **)&cpServicePrv);
  2225. if (SUCCEEDED(hr))
  2226. {
  2227. hr = cpServicePrv->QueryService(GUID_NULL, IID_ISpRecoResult, (void **)&cpRecoResult);
  2228. }
  2229. // 'detach' the result to a mem chunk
  2230. //
  2231. if (SUCCEEDED(hr))
  2232. {
  2233. hr = cpRecoResult->Serialize(&pResBlock);
  2234. }
  2235. // serialize the chunk to the stream
  2236. //
  2237. if (SUCCEEDED(hr) && pResBlock)
  2238. {
  2239. hr = pStream->Write(
  2240. pResBlock,
  2241. (ULONG)pResBlock->ulSerializedSize, // the number of bytes to copy
  2242. &ulrw2
  2243. );
  2244. if (pcb)
  2245. *pcb = ulrw1 + ulrw2;
  2246. // no need for the detached mem chunk
  2247. CoTaskMemFree(pResBlock);
  2248. }
  2249. }
  2250. cicMemFree(pRecoWrapData);
  2251. }
  2252. else
  2253. hr = E_OUTOFMEMORY;
  2254. }
  2255. return hr;
  2256. }
  2257. //
  2258. // CPropStoreRecoResultObject::_InitFromIStream
  2259. //
  2260. // stores IStream copied from param
  2261. //
  2262. HRESULT CPropStoreRecoResultObject::_InitFromIStream(IStream *pStream, int iSize, ISpRecoContext *pRecoCtxt)
  2263. {
  2264. HRESULT hr = S_OK;
  2265. ULONG ulSize = (ULONG)iSize;
  2266. if (!pStream) return E_INVALIDARG;
  2267. // alloc the mem chunk for the reco
  2268. // blob
  2269. if ( ulSize == 0 )
  2270. {
  2271. STATSTG stg;
  2272. hr = pStream->Stat(&stg, STATFLAG_NONAME);
  2273. if (SUCCEEDED(hr))
  2274. ulSize = (int)stg.cbSize.LowPart;
  2275. }
  2276. // got size from given stream or param
  2277. if (SUCCEEDED(hr))
  2278. {
  2279. // First We want to get RECOWRAPDATA at the begining of the stream.
  2280. RECOWRAPDATA rwData;
  2281. hr = pStream->Read(
  2282. &rwData, // the destination buf
  2283. sizeof(RECOWRAPDATA), // the number of bytes to read
  2284. NULL
  2285. );
  2286. if ( SUCCEEDED(hr) )
  2287. {
  2288. ULONG ulITNSize;
  2289. SPITNSHOWSTATE *pITNShowState = NULL;
  2290. ulITNSize = rwData.ulNumOfITN * sizeof(SPITNSHOWSTATE);
  2291. if ( ulITNSize > 0 )
  2292. {
  2293. pITNShowState = (SPITNSHOWSTATE *)cicMemAllocClear(ulITNSize);
  2294. rwData.pITNShowState = pITNShowState;
  2295. if ( pITNShowState )
  2296. {
  2297. hr = pStream->Read(
  2298. pITNShowState, // the destination buf
  2299. ulITNSize, // the number of bytes to read
  2300. NULL
  2301. );
  2302. }
  2303. else
  2304. hr = E_OUTOFMEMORY;
  2305. }
  2306. ULONG *pulOffsetElement = NULL;
  2307. ULONG ulOffsetSize, ulOffsetNum;
  2308. ulOffsetNum = rwData.ulOffsetNum;
  2309. ulOffsetSize = ulOffsetNum * sizeof(ULONG);
  2310. if ( SUCCEEDED(hr) && ulOffsetSize > 0 )
  2311. {
  2312. pulOffsetElement = (ULONG *) cicMemAllocClear(ulOffsetSize);
  2313. rwData.pulOffset = pulOffsetElement;
  2314. if ( pulOffsetElement )
  2315. {
  2316. hr = pStream->Read(
  2317. pulOffsetElement, // the destination buf
  2318. ulOffsetSize, // the number of bytes to read
  2319. NULL
  2320. );
  2321. }
  2322. else
  2323. hr = E_OUTOFMEMORY;
  2324. }
  2325. if ( SUCCEEDED(hr))
  2326. {
  2327. ULONG ulTextSize;
  2328. WCHAR *pwszText;
  2329. ulTextSize = rwData.ulSize - sizeof(RECOWRAPDATA) - ulITNSize - ulOffsetSize;
  2330. pwszText = (WCHAR *) cicMemAllocClear(ulTextSize);
  2331. rwData.pwszText = pwszText;
  2332. if ( pwszText )
  2333. {
  2334. hr = pStream->Read(
  2335. pwszText, // the destination buf
  2336. ulTextSize, // the number of bytes to read
  2337. NULL
  2338. );
  2339. if ( SUCCEEDED(hr) )
  2340. {
  2341. // prepare cotaskmem chunk
  2342. SPSERIALIZEDRESULT *pResBlock = (SPSERIALIZEDRESULT *)CoTaskMemAlloc(ulSize - rwData.ulSize + sizeof(ULONG)*4);
  2343. if (pResBlock)
  2344. {
  2345. CComPtr<ISpRecoResult> cpResult;
  2346. hr = pStream->Read(
  2347. pResBlock, // the destination buf
  2348. ulSize - rwData.ulSize, // the number of bytes to read
  2349. NULL
  2350. );
  2351. if (S_OK == hr)
  2352. {
  2353. // now create a reco result from the blob data
  2354. hr = pRecoCtxt->DeserializeResult(pResBlock, &cpResult);
  2355. }
  2356. CoTaskMemFree(pResBlock);
  2357. if (S_OK == hr)
  2358. {
  2359. _InitFromRecoResult(cpResult, &rwData);
  2360. }
  2361. }
  2362. }
  2363. cicMemFree(pwszText);
  2364. }
  2365. else
  2366. hr = E_OUTOFMEMORY;
  2367. }
  2368. if ( (hr == S_OK) && (pITNShowState != NULL) )
  2369. cicMemFree(pITNShowState);
  2370. if ( pulOffsetElement != NULL )
  2371. cicMemFree(pulOffsetElement);
  2372. }
  2373. else
  2374. {
  2375. hr = E_OUTOFMEMORY;
  2376. }
  2377. }
  2378. return hr;
  2379. }
  2380. HRESULT CPropStoreRecoResultObject::_InitFromRecoResult(ISpRecoResult *pResult, RECOWRAPDATA *pRecoWrapData)
  2381. {
  2382. HRESULT hr = S_OK;
  2383. ULONG ulStartElement = 0;
  2384. ULONG ulNumElements = 0;
  2385. ULONG ulNumOfITN = 0;
  2386. ULONG ulOffsetNum = 0;
  2387. m_cpResultWrap.Release();
  2388. if ( pRecoWrapData == NULL )
  2389. return E_INVALIDARG;
  2390. // get start/num of elements
  2391. ulStartElement = pRecoWrapData->ulStartElement;
  2392. ulNumElements = pRecoWrapData->ulNumElements;
  2393. ulNumOfITN = pRecoWrapData->ulNumOfITN;
  2394. ulOffsetNum = pRecoWrapData->ulOffsetNum;
  2395. CRecoResultWrap *prw = new CRecoResultWrap(m_pimx, ulStartElement, ulNumElements, ulNumOfITN);
  2396. if (prw)
  2397. {
  2398. hr = prw->Init(pResult);
  2399. }
  2400. else
  2401. hr = E_OUTOFMEMORY;
  2402. if (S_OK == hr)
  2403. {
  2404. m_cpResultWrap = SAFECAST(prw, IUnknown *);
  2405. prw->SetOffsetDelta(pRecoWrapData->ulOffsetDelta);
  2406. prw->SetCharsInTrail(pRecoWrapData->ulCharsInTrail);
  2407. prw->SetTrailSpaceRemoved(pRecoWrapData->ulTrailSpaceRemoved);
  2408. prw->m_bstrCurrentText = SysAllocString(pRecoWrapData->pwszText);
  2409. // Update ITN show-state list
  2410. if ( (ulNumOfITN > 0) && pRecoWrapData->pITNShowState )
  2411. {
  2412. SPITNSHOWSTATE *pITNShowState;
  2413. pITNShowState = pRecoWrapData->pITNShowState;
  2414. for ( ULONG i=0; i<ulNumOfITN; i++)
  2415. {
  2416. prw->_InitITNShowState(pITNShowState->fITNShown, pITNShowState->ulITNStart, pITNShowState->ulITNNumElem);
  2417. pITNShowState ++;
  2418. }
  2419. }
  2420. // Update the element Offset list.
  2421. if ( (ulOffsetNum > 0) && (pRecoWrapData->pulOffset ))
  2422. {
  2423. ULONG *pulOffset;
  2424. pulOffset = pRecoWrapData->pulOffset;
  2425. for (ULONG i=0; i< ulOffsetNum; i++)
  2426. {
  2427. prw->_SetElementNewOffset(i + ulStartElement, pulOffset[i] );
  2428. }
  2429. }
  2430. prw->Release();
  2431. }
  2432. return hr;
  2433. }
  2434. HRESULT CPropStoreRecoResultObject::_InitFromResultWrap(IUnknown *pResWrap)
  2435. {
  2436. m_cpResultWrap.Release();
  2437. m_cpResultWrap = pResWrap;
  2438. if (m_cpResultWrap)
  2439. {
  2440. return S_OK;
  2441. }
  2442. else
  2443. return E_INVALIDARG;
  2444. }
  2445. // end of CPropStoreRecoResultObject implementation
  2446. //
  2447. // CPropStoreLMLattice implementation
  2448. //
  2449. // ctor
  2450. CPropStoreLMLattice::CPropStoreLMLattice(CSapiIMX *pimx)
  2451. {
  2452. // init a shared recognition context
  2453. m_cpResultWrap = NULL;
  2454. m_pimx = pimx;
  2455. m_cRef = 1;
  2456. }
  2457. // dtor
  2458. CPropStoreLMLattice::~CPropStoreLMLattice()
  2459. {
  2460. }
  2461. // IUnknown
  2462. STDMETHODIMP CPropStoreLMLattice::QueryInterface(REFIID riid, void **ppvObj)
  2463. {
  2464. HRESULT hr;
  2465. Assert(ppvObj);
  2466. if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_ITfPropertyStore))
  2467. {
  2468. *ppvObj = this;
  2469. hr = S_OK;
  2470. this->m_cRef++;
  2471. }
  2472. else
  2473. {
  2474. *ppvObj = NULL;
  2475. hr = E_NOINTERFACE;
  2476. }
  2477. return hr;
  2478. }
  2479. STDMETHODIMP_(ULONG) CPropStoreLMLattice::AddRef(void)
  2480. {
  2481. this->m_cRef++;
  2482. return this->m_cRef;
  2483. }
  2484. STDMETHODIMP_(ULONG) CPropStoreLMLattice::Release(void)
  2485. {
  2486. this->m_cRef--;
  2487. if (this->m_cRef > 0)
  2488. {
  2489. return this->m_cRef;
  2490. }
  2491. delete this;
  2492. return 0;
  2493. }
  2494. // ITfPropertyStore
  2495. STDMETHODIMP CPropStoreLMLattice::GetType(GUID *pguid)
  2496. {
  2497. HRESULT hr = E_INVALIDARG;
  2498. if (pguid)
  2499. {
  2500. *pguid = GUID_PROP_LMLATTICE;
  2501. hr = S_OK;
  2502. }
  2503. return hr;
  2504. }
  2505. STDMETHODIMP CPropStoreLMLattice::GetDataType(DWORD *pdwReserved)
  2506. {
  2507. HRESULT hr = E_INVALIDARG;
  2508. if (pdwReserved)
  2509. {
  2510. *pdwReserved = 0;
  2511. hr = S_OK;
  2512. }
  2513. return hr;
  2514. }
  2515. STDMETHODIMP CPropStoreLMLattice::GetData(VARIANT *pvarValue)
  2516. {
  2517. HRESULT hr = E_INVALIDARG;
  2518. if (pvarValue)
  2519. {
  2520. QuickVariantInit(pvarValue);
  2521. if (m_cpResultWrap)
  2522. {
  2523. // return ITfLMLattice object
  2524. // we defer the creation of LMlattice object until
  2525. // the time master LM TIP actually access it
  2526. //
  2527. if (!m_cpLMLattice)
  2528. {
  2529. CLMLattice *pLattice = new CLMLattice(m_pimx, m_cpResultWrap);
  2530. if (pLattice)
  2531. {
  2532. m_cpLMLattice = pLattice;
  2533. pLattice->Release();
  2534. }
  2535. }
  2536. if (m_cpLMLattice)
  2537. {
  2538. IUnknown *pUnk = NULL;
  2539. pvarValue->vt = VT_UNKNOWN;
  2540. hr = m_cpLMLattice->QueryInterface(IID_IUnknown, (void**)&pUnk);
  2541. if (S_OK == hr)
  2542. {
  2543. pvarValue->punkVal = pUnk;
  2544. }
  2545. }
  2546. else
  2547. {
  2548. hr = E_OUTOFMEMORY;
  2549. }
  2550. }
  2551. }
  2552. return hr;
  2553. }
  2554. STDMETHODIMP CPropStoreLMLattice::OnTextUpdated(DWORD dwFlags, ITfRange *pRange, BOOL *pfAccept)
  2555. {
  2556. *pfAccept = FALSE;
  2557. if (dwFlags & TF_TU_CORRECTION)
  2558. {
  2559. *pfAccept = TRUE;
  2560. }
  2561. return S_OK;
  2562. }
  2563. STDMETHODIMP CPropStoreLMLattice::Shrink(ITfRange *pRange, BOOL *pfFree)
  2564. {
  2565. // could we have something done here?
  2566. *pfFree = TRUE;
  2567. return S_OK;
  2568. }
  2569. STDMETHODIMP CPropStoreLMLattice::Divide(ITfRange *pRangeThis, ITfRange *pRangeNew, ITfPropertyStore **ppPropStore)
  2570. {
  2571. // 12/17/1999
  2572. // [dividing a range implementation strategy]
  2573. //
  2574. // - pRangeThis contains the text range *before* the dividing point
  2575. // - pRangeNew contrains the range *after* the dividing point
  2576. // - First, adjust this property store to correctly hold a start element and #of element
  2577. // for pRangeThis
  2578. // - then create a new property store for pRangeNew, which will share the same
  2579. // result blob.
  2580. //
  2581. // just an experiment to see if cutting the range works.
  2582. // *ppPropStore = NULL;
  2583. Assert(ppPropStore);
  2584. Assert(pRangeThis);
  2585. Assert(pRangeNew);
  2586. CComPtr<ITfContext> cpic;
  2587. HRESULT hr = pRangeThis->GetContext(&cpic);
  2588. if (SUCCEEDED(hr) && cpic)
  2589. {
  2590. CPSLMEditSession *pes;
  2591. if (pes = new CPSLMEditSession(this, pRangeThis, cpic))
  2592. {
  2593. pes->_SetEditSessionData(ESCB_PROP_DIVIDE, NULL, 0);
  2594. pes->_SetUnk((IUnknown *)pRangeNew);
  2595. cpic->RequestEditSession(m_pimx->_GetId(), pes, TF_ES_READ | TF_ES_SYNC, &hr);
  2596. if ( SUCCEEDED(hr) )
  2597. *ppPropStore = (ITfPropertyStore *)pes->_GetRetUnknown( );
  2598. pes->Release();
  2599. }
  2600. }
  2601. return hr;
  2602. }
  2603. //
  2604. // CPropStoreLMLattice::_Divide
  2605. //
  2606. // synopsis : receives edit cookie from edit session
  2607. // so that we can manipulate with ranges
  2608. // to set starting elements/# of elements
  2609. //
  2610. //
  2611. HRESULT CPropStoreLMLattice::_Divide(TfEditCookie ec, ITfRange *pR1, ITfRange *pR2, ITfPropertyStore **ppPs)
  2612. {
  2613. // TODO: based on the given ranges, we calculate the offsets of elements and return a new propstore with
  2614. // later half of elements
  2615. // some clarifications: in case the lattice object has never been accessed, our result wrap object processes
  2616. // ITfPropertyStore::Divide and Shrink for us.
  2617. //
  2618. return Clone(ppPs);
  2619. }
  2620. //
  2621. // CPropStoreLMLattice::Clone
  2622. //
  2623. // synopsis : make a new cloned propstore which shares the same SAPI result
  2624. // object as the current class instance
  2625. //
  2626. //
  2627. STDMETHODIMP CPropStoreLMLattice::Clone(ITfPropertyStore **ppPropStore)
  2628. {
  2629. HRESULT hr;
  2630. CPropStoreLMLattice *prps = new CPropStoreLMLattice(m_pimx);
  2631. if (prps)
  2632. {
  2633. hr = prps->QueryInterface(IID_ITfPropertyStore, (void **)ppPropStore);
  2634. if (SUCCEEDED(hr))
  2635. {
  2636. hr = prps->_InitFromResultWrap(m_cpResultWrap);
  2637. }
  2638. prps->Release();
  2639. }
  2640. else
  2641. {
  2642. hr = E_OUTOFMEMORY;
  2643. }
  2644. return hr;
  2645. }
  2646. STDMETHODIMP CPropStoreLMLattice::GetPropertyRangeCreator(CLSID *pclsid)
  2647. {
  2648. HRESULT hr = E_INVALIDARG;
  2649. if (pclsid)
  2650. {
  2651. *pclsid = CLSID_SapiLayr;
  2652. hr = S_OK;
  2653. }
  2654. return hr;
  2655. }
  2656. //
  2657. // CPropStoreLMLattice::Serialize
  2658. //
  2659. // synopsis: I don't believe it is very useful to get lattice data
  2660. // persisted to doc file. We can always generate it on the fly
  2661. // from device native blob data
  2662. //
  2663. //
  2664. STDMETHODIMP CPropStoreLMLattice::Serialize(IStream *pStream, ULONG *pcb)
  2665. {
  2666. return E_NOTIMPL;
  2667. }
  2668. HRESULT CPropStoreLMLattice::_InitFromResultWrap(IUnknown *pResWrap)
  2669. {
  2670. m_cpResultWrap.Release();
  2671. m_cpResultWrap = pResWrap;
  2672. if (m_cpResultWrap)
  2673. {
  2674. return S_OK;
  2675. }
  2676. else
  2677. return E_INVALIDARG;
  2678. }
  2679. // private IID for reco result wrapper
  2680. //
  2681. // IID_PRIV_RESULTWRAP
  2682. // b3407713-50d7-4465-97f9-87ad1e752dc5
  2683. //
  2684. const IID IID_PRIV_RESULTWRAP = {
  2685. 0xb3407713,
  2686. 0x50d7,
  2687. 0x4465,
  2688. {0x97, 0xf9, 0x87, 0xad, 0x1e, 0x75, 0x2d, 0xc5}
  2689. };