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.

3043 lines
99 KiB

  1. //
  2. // reconv.cpp
  3. //
  4. #include "private.h"
  5. #include "globals.h"
  6. #include "sapilayr.h"
  7. #include "fnrecon.h"
  8. #include "immxutil.h"
  9. #include "candlist.h"
  10. #include "propstor.h"
  11. #include "catutil.h"
  12. #include "osver.h"
  13. #include "mui.h"
  14. #include "tsattrs.h"
  15. #include "cregkey.h"
  16. #include <htmlhelp.h>
  17. #include "TabletTip_i.c"
  18. #include "spgrmr.h"
  19. HRESULT HandlePhraseElement( CSpDynamicString *pDstr, const WCHAR *pwszTextThis, BYTE bAttrThis, BYTE bAttrPrev, ULONG *pulOffsetThis);
  20. //////////////////////////////////////////////////////////////////////////////
  21. //
  22. // CSapiAlternativeList
  23. //
  24. //////////////////////////////////////////////////////////////////////////////
  25. // ctor/dtor
  26. CSapiAlternativeList::CSapiAlternativeList(LANGID langid, ITfRange *pRange, ULONG ulMaxCandChars)
  27. {
  28. m_nItem = 0;
  29. m_ulStart = 0;
  30. m_ulcElem = 0;
  31. m_cAlt = 0;
  32. m_ppAlt = NULL;
  33. m_langid = langid;
  34. m_cpRange = pRange;
  35. m_fFirstAltInCandidate = FALSE;
  36. m_fNoAlternate = FALSE;
  37. m_iFakeAlternate = NO_FAKEALT;
  38. m_MaxCandChars = ulMaxCandChars;
  39. m_ulIndexSelect = 0;
  40. }
  41. CSapiAlternativeList::~CSapiAlternativeList()
  42. {
  43. UINT i;
  44. for (i = 0; i < m_cAlt; i++)
  45. {
  46. if (NULL != m_ppAlt[i])
  47. {
  48. m_ppAlt[i]->Release();
  49. }
  50. }
  51. if (m_prgLMAlternates)
  52. {
  53. int nItem = m_prgLMAlternates->Count();
  54. for (i = 0; i < (UINT)nItem; i++)
  55. {
  56. CLMAlternates *plmalt = m_prgLMAlternates->Get(i);
  57. if (plmalt)
  58. delete plmalt;
  59. }
  60. delete m_prgLMAlternates;
  61. }
  62. if (m_ppAlt)
  63. cicMemFree(m_ppAlt);
  64. if (m_rgElemUsed.Count())
  65. {
  66. for ( i=0; i<(UINT)m_rgElemUsed.Count(); i++)
  67. {
  68. SPELEMENTUSED *pElemUsed;
  69. pElemUsed = m_rgElemUsed.GetPtr(i);
  70. if ( pElemUsed && pElemUsed->pwszAltText)
  71. {
  72. cicMemFree(pElemUsed->pwszAltText);
  73. pElemUsed->pwszAltText = NULL;
  74. }
  75. }
  76. m_rgElemUsed.Clear();
  77. }
  78. }
  79. //
  80. // AddLMAlternates
  81. //
  82. HRESULT CSapiAlternativeList::AddLMAlternates(CLMAlternates *pLMAlt)
  83. {
  84. HRESULT hr = E_FAIL;
  85. BOOL fFoundADup = FALSE;
  86. if (!pLMAlt)
  87. return E_INVALIDARG;
  88. if (!m_prgLMAlternates)
  89. {
  90. m_prgLMAlternates = new CPtrArray<CLMAlternates>;
  91. }
  92. if (m_prgLMAlternates)
  93. {
  94. int iIdx = m_prgLMAlternates->Count();
  95. // need to find a dup
  96. for (int i = 0; i < iIdx && !fFoundADup ; i++)
  97. {
  98. CLMAlternates *plma = m_prgLMAlternates->Get(i);
  99. if (plma)
  100. {
  101. WCHAR *pszStored = new WCHAR[plma->GetLen()+1];
  102. WCHAR *pszAdding = new WCHAR[pLMAlt->GetLen()+1];
  103. if (pszStored && pszAdding)
  104. {
  105. plma->GetString(pszStored, plma->GetLen()+1);
  106. pLMAlt->GetString(pszAdding, pLMAlt->GetLen()+1);
  107. if (!wcscmp(pszAdding, pszStored))
  108. {
  109. fFoundADup = TRUE;
  110. }
  111. }
  112. if (pszStored)
  113. delete[] pszStored;
  114. if (pszAdding)
  115. delete[] pszAdding;
  116. }
  117. }
  118. if (!fFoundADup && pLMAlt)
  119. {
  120. if (!m_prgLMAlternates->Insert(iIdx, 1))
  121. return E_OUTOFMEMORY;
  122. m_prgLMAlternates->Set(iIdx, pLMAlt);
  123. hr = S_OK;
  124. }
  125. }
  126. return hr;
  127. }
  128. // SetPhraseAlt
  129. //
  130. // synopsis: receive an alternates list as a param and copy the alternates
  131. // to the array which this class internally maintains.
  132. // Additionally, a pointer to the reco result wrapper
  133. // is maintained per CSapiAlternativeList class instance.
  134. //
  135. // params pResWrap - a pointer to the wrapper object
  136. // ppAlt - a pointer to the array of phrases that the callar has alloced
  137. // cAlt - is passed in with the # of real SAPI alternates
  138. // ulStart - index to the start element in the parent phrase
  139. // culElem - # of minimum elements used (will get replaced) in the parent phrase.
  140. //
  141. HRESULT CSapiAlternativeList::SetPhraseAlt(CRecoResultWrap *pResWrap, ISpPhraseAlt **ppAlt, ULONG cAlt, ULONG ulStart, ULONG ulcElem, WCHAR *pwszParent)
  142. {
  143. // setup the info for used elements in parent phrase
  144. // these are useful for the 0 index (ITN) alternate
  145. HRESULT hr = S_OK;
  146. SPPHRASE *pParentPhrase = NULL;
  147. CSpDynamicString dstr;
  148. if ( !pResWrap || !ppAlt || !pwszParent )
  149. return E_INVALIDARG;
  150. m_ulStart = ulStart;
  151. m_ulcElem = ulcElem;
  152. m_fFirstAltInCandidate = FALSE;
  153. m_fNoAlternate = FALSE;
  154. m_iFakeAlternate = NO_FAKEALT;
  155. // alloc the struct for the used element info
  156. m_rgElemUsed.Append(cAlt);
  157. for ( int i=0; i<m_rgElemUsed.Count( ); i++)
  158. {
  159. SPELEMENTUSED *pElemUsed;
  160. if ( pElemUsed = m_rgElemUsed.GetPtr(i))
  161. {
  162. pElemUsed->pwszAltText = NULL;
  163. }
  164. }
  165. // comptr releases on the previous object
  166. // at this indirection
  167. //
  168. Assert(pResWrap);
  169. m_cpwrp = pResWrap;
  170. if (m_ppAlt)
  171. {
  172. for (UINT i = 0; i < m_cAlt; i++)
  173. {
  174. if (NULL != m_ppAlt[i])
  175. {
  176. m_ppAlt[i]->Release();
  177. }
  178. }
  179. cicMemFree(m_ppAlt);
  180. m_ppAlt = NULL;
  181. }
  182. m_ppAlt = (ISpPhraseAlt **)cicMemAlloc(sizeof(*ppAlt)*cAlt);
  183. if (!m_ppAlt)
  184. return E_OUTOFMEMORY;
  185. Assert(ppAlt);
  186. #ifdef DONTUSE
  187. // Get the current select text in parent phrase.
  188. CComPtr<IServiceProvider> cpServicePrv;
  189. CComPtr<ISpRecoResult> cpResult;
  190. hr = m_cpwrp->QueryInterface(IID_IServiceProvider, (void **)&cpServicePrv);
  191. if ( S_OK == hr )
  192. hr = cpServicePrv->QueryService(GUID_NULL, IID_ISpRecoResult, (void **)&cpResult);
  193. if (S_OK == hr)
  194. {
  195. CSpDynamicString dstrReplace;
  196. cpResult->GetPhrase(&pParentPhrase);
  197. for (ULONG i = m_ulStart; i < m_ulStart + m_ulcElem; i++ )
  198. {
  199. BOOL fInsideITN;
  200. ULONG ulITNStart, ulITNNumElem;
  201. fInsideITN = m_cpwrp->_CheckITNForElement(pParentPhrase, i, &ulITNStart, &ulITNNumElem, (CSpDynamicString *)&dstrReplace);
  202. if ( fInsideITN )
  203. {
  204. // This element is inside an ITN range.
  205. if ( i == (ulITNStart + ulITNNumElem - 1) )
  206. {
  207. // This is the last element of the new ITN.
  208. // we need to add the replace text to the dstr string
  209. // so that next non-ITN element will get correct offset.
  210. dstr.Append( (WCHAR *)dstrReplace );
  211. }
  212. }
  213. else
  214. {
  215. if (pParentPhrase->pElements[i].pszDisplayText)
  216. {
  217. const WCHAR *pwszTextThis;
  218. BYTE bAttrThis = 0;
  219. BYTE bAttrPrev = 0;
  220. pwszTextThis = pParentPhrase->pElements[i].pszDisplayText;
  221. bAttrThis = pParentPhrase->pElements[i].bDisplayAttributes;
  222. if ( i > m_ulStart )
  223. bAttrPrev = pParentPhrase->pElements[i-1].bDisplayAttributes;
  224. HandlePhraseElement( (CSpDynamicString *)&dstr, pwszTextThis, bAttrThis, bAttrPrev,NULL);
  225. }
  226. }
  227. } // for
  228. pwszParent = (WCHAR *)dstr;
  229. if (pParentPhrase)
  230. CoTaskMemFree(pParentPhrase);
  231. }
  232. #endif
  233. UINT j=0;
  234. if ( pwszParent )
  235. {
  236. ULONG ulRecoWrpStart, ulRecoWrpNumElements;
  237. WCHAR *pwszFakeAlt = NULL; // This is for Capitalized string for parent phrase.
  238. if ( iswalpha(pwszParent[0]) )
  239. {
  240. int iStrLen = wcslen(pwszParent);
  241. pwszFakeAlt = (WCHAR *)cicMemAlloc((iStrLen+1) * sizeof(WCHAR));
  242. if ( pwszFakeAlt )
  243. {
  244. WCHAR wch;
  245. wch = pwszParent[0];
  246. StringCchCopyW(pwszFakeAlt, iStrLen+1, pwszParent);
  247. if ( iswlower(wch) )
  248. pwszFakeAlt[0] = towupper(wch);
  249. else
  250. pwszFakeAlt[0] = towlower(wch);
  251. int iLen = wcslen(pwszFakeAlt);
  252. if ( (iLen > 0) && (pwszFakeAlt[iLen-1] < 0x20) )
  253. pwszFakeAlt[iLen-1] = L'\0';
  254. }
  255. }
  256. ulRecoWrpStart = pResWrap->GetStart( );
  257. ulRecoWrpNumElements = pResWrap->GetNumElements( );
  258. ULONG ValidParentStart, ValidParentEnd; // Point to the valid parent element range which could be matched by
  259. // the alternative phrase.
  260. int ShiftDelta = 2; // We just want to shift the valid parent element range by ShiftDelta elements from current
  261. // start and end element in parent phrase.
  262. // ie, ulStart - 3, ulEnd + 3, if they are in the valid range of the reco wrapper.
  263. ValidParentStart = ulRecoWrpStart;
  264. if ( ((int)ulStart - ShiftDelta) > (int)ulRecoWrpStart )
  265. ValidParentStart = ulStart - ShiftDelta;
  266. ValidParentEnd = ulRecoWrpStart + ulRecoWrpNumElements - 1;
  267. if ( ((int)ulStart + (int)ulcElem -1 + ShiftDelta) < (int)ValidParentEnd )
  268. ValidParentEnd = ulStart + ulcElem - 1 + ShiftDelta;
  269. CComPtr<ISpRecoResult> cpResult;
  270. pResWrap->GetResult(&cpResult);
  271. cpResult->GetPhrase(&pParentPhrase);
  272. for (UINT i = 0; (i < cAlt) && (j < cAlt) && *ppAlt; i++, ppAlt++)
  273. {
  274. SPPHRASE *pPhrases = NULL;
  275. ULONG ulcElements = 0;
  276. ULONG ulParentStart = 0;
  277. ULONG ulcParentElements = 0;
  278. ULONG ulLeadSpaceRemoved = 0;
  279. // Assume the first alt phrase is exactly same as the parent phrase.
  280. // practically it is true so far.
  281. // if it is not true in the future, we may need to change logical here!!!
  282. (*ppAlt)->GetPhrase(&pPhrases);
  283. (*ppAlt)->GetAltInfo(NULL, &ulParentStart, &ulcParentElements, &ulcElements);
  284. if ( (ulParentStart >= ValidParentStart) && ( ulParentStart+ulcParentElements -1 <= ValidParentEnd) )
  285. {
  286. WCHAR *pwszAlt = (WCHAR *)cicMemAllocClear((m_MaxCandChars+1)*sizeof(WCHAR));
  287. if ( !pwszAlt )
  288. {
  289. hr = E_OUTOFMEMORY;
  290. break;
  291. }
  292. BOOL fAddToCandidateList = FALSE;
  293. BOOL fControlCharsInAltPhrase = FALSE;
  294. // Add code to skip start and end elements which are the same as parent phrase's elements.
  295. UINT ulSkipStartWords = 0;
  296. UINT ulSkipEndWords = 0;
  297. for (UINT k = ulParentStart; k < ulStart; k++)
  298. {
  299. if (_wcsicmp(pPhrases->pElements[k].pszDisplayText, pParentPhrase->pElements[k].pszDisplayText) == 0)
  300. {
  301. // Matching pre-word in alternate. This is redundant.
  302. ulSkipStartWords ++;
  303. }
  304. else
  305. {
  306. // Do not match. Stop processing.
  307. break;
  308. }
  309. }
  310. for (UINT k = ulParentStart + ulcParentElements - 1; k >= ulStart + ulcElem ; k--)
  311. {
  312. // Count backwards in alternate phrase.
  313. UINT l = ulParentStart + ulcElements - ((ulParentStart + ulcParentElements) - k);
  314. if (_wcsicmp(pPhrases->pElements[l].pszDisplayText, pParentPhrase->pElements[k].pszDisplayText) == 0)
  315. {
  316. ulSkipEndWords ++;
  317. }
  318. else
  319. {
  320. // Do not match. Stop processing.
  321. break;
  322. }
  323. }
  324. ulParentStart += ulSkipStartWords;
  325. ulcElements -= ulSkipStartWords + ulSkipEndWords;
  326. ulcParentElements -= ulSkipStartWords + ulSkipEndWords;
  327. hr = GetAlternativeText(*ppAlt, pPhrases, (i ==0 ? TRUE : FALSE), ulParentStart, ulcElements, pwszAlt, m_MaxCandChars, &ulLeadSpaceRemoved);
  328. if ( S_OK == hr )
  329. {
  330. for ( ULONG iIndex =0; iIndex < wcslen(pwszAlt); iIndex++ )
  331. {
  332. if ( pwszAlt[iIndex] < 0x20 )
  333. {
  334. fControlCharsInAltPhrase = TRUE;
  335. break;
  336. }
  337. }
  338. }
  339. BOOL fNotDupAlt = TRUE;
  340. if ( S_OK == hr && pwszAlt )
  341. {
  342. fNotDupAlt = _wcsicmp(pwszAlt, pwszParent);
  343. if ( fNotDupAlt && (j>0) )
  344. {
  345. SPELEMENTUSED *pElemUsed;
  346. for (UINT x=0; x<j; x++ )
  347. {
  348. if ( pElemUsed = m_rgElemUsed.GetPtr(x))
  349. fNotDupAlt = _wcsicmp(pwszAlt, pElemUsed->pwszAltText);
  350. if ( !fNotDupAlt )
  351. break;
  352. }
  353. }
  354. }
  355. if ((S_OK == hr) && !fControlCharsInAltPhrase && (pwszAlt[0] != L'\0') && fNotDupAlt)
  356. {
  357. // This is different item from the parent, it should be inserted to the canidate list.
  358. // initialize the AltCached item
  359. if ( (i > 0) || (pResWrap->_RangeHasITN(ulParentStart, ulcParentElements) > 0 ) )
  360. {
  361. SPELEMENTUSED *pElemUsed;
  362. if ( pElemUsed = m_rgElemUsed.GetPtr(j))
  363. {
  364. pElemUsed->ulParentStart = ulParentStart;
  365. pElemUsed->ulcParentElements = ulcParentElements;
  366. pElemUsed->ulcElements = ulcElements;
  367. pElemUsed->pwszAltText = pwszAlt;
  368. pElemUsed->ulLeadSpaceRemoved = ulLeadSpaceRemoved;
  369. m_ppAlt[j] = *ppAlt;
  370. m_ppAlt[j]->AddRef();
  371. j ++;
  372. if ( i == 0 )
  373. {
  374. // The first Alt phrase is also in the canidate list. the selectedd range must contain ITN.
  375. m_fFirstAltInCandidate = TRUE;
  376. }
  377. fAddToCandidateList = TRUE;
  378. }
  379. }
  380. }
  381. if ( fAddToCandidateList == FALSE )
  382. {
  383. // Same string. or GetAlternativeText returns Error.
  384. // don't insert it into the candidate list.
  385. // Release the alloced memory
  386. cicMemFree(pwszAlt);
  387. }
  388. // Handle Faked Alternate
  389. if ((i == 0) && (pwszFakeAlt != NULL))
  390. {
  391. // This is the parent phrase, Only the first character is capitalized.
  392. SPELEMENTUSED *pElemUsed;
  393. if ( pElemUsed = m_rgElemUsed.GetPtr(j))
  394. {
  395. pElemUsed->ulParentStart = ulParentStart;
  396. pElemUsed->ulcParentElements = ulcParentElements;
  397. pElemUsed->ulcElements = ulcElements;
  398. pElemUsed->pwszAltText = pwszFakeAlt;
  399. pElemUsed->ulLeadSpaceRemoved = 0;
  400. m_iFakeAlternate = j;
  401. m_ppAlt[j] = *ppAlt;
  402. m_ppAlt[j]->AddRef();
  403. j ++;
  404. }
  405. }
  406. }
  407. if (pPhrases)
  408. CoTaskMemFree( pPhrases );
  409. }
  410. if (pParentPhrase)
  411. {
  412. CoTaskMemFree(pParentPhrase);
  413. }
  414. if ( pwszFakeAlt && (m_iFakeAlternate == NO_FAKEALT) )
  415. cicMemFree(pwszFakeAlt);
  416. }
  417. m_cAlt = j;
  418. if ( S_OK == hr )
  419. {
  420. if ( m_cAlt == 0 )
  421. {
  422. // There is no available Alternate.
  423. // Just show string "No Alternate" in the candidate window.
  424. m_fNoAlternate = TRUE;
  425. SPELEMENTUSED *pElemUsed;
  426. WCHAR *pwszNoAlt=(WCHAR *)cicMemAllocClear(m_MaxCandChars*sizeof(WCHAR));
  427. if ( (pElemUsed = m_rgElemUsed.GetPtr(0)) && pwszNoAlt )
  428. {
  429. pElemUsed->ulParentStart = m_ulStart;
  430. pElemUsed->ulcParentElements = m_ulcElem;
  431. pElemUsed->ulcElements = m_ulcElem;
  432. pElemUsed->ulLeadSpaceRemoved = 0;
  433. CicLoadStringWrapW(g_hInst, IDS_NO_ALTERNATE, pwszNoAlt, m_MaxCandChars);
  434. pElemUsed->pwszAltText = pwszNoAlt;
  435. CComPtr<IServiceProvider> cpServicePrv;
  436. CComPtr<ISpRecoResult> cpResult;
  437. hr = m_cpwrp->QueryInterface(IID_IServiceProvider, (void **)&cpServicePrv);
  438. if ( S_OK == hr )
  439. hr = cpServicePrv->QueryService(GUID_NULL, IID_ISpRecoResult, (void **)&cpResult);
  440. if ( (hr == S_OK) && cpResult )
  441. {
  442. m_ppAlt[0] = (ISpPhraseAlt *)(ISpRecoResult *)cpResult;
  443. m_ppAlt[0]->AddRef();
  444. }
  445. m_cAlt = 1;
  446. }
  447. }
  448. }
  449. else
  450. {
  451. // Release all the allocated memeory and AltCached Items in this function.
  452. UINT i;
  453. if (m_ppAlt)
  454. {
  455. for (i = 0; i < m_cAlt; i++)
  456. {
  457. m_ppAlt[i]->Release();
  458. m_ppAlt[i] = NULL;
  459. }
  460. cicMemFree(m_ppAlt);
  461. m_ppAlt = NULL;
  462. }
  463. if (m_rgElemUsed.Count())
  464. {
  465. for ( i=0; i<(UINT)m_rgElemUsed.Count(); i++)
  466. {
  467. SPELEMENTUSED *pElemUsed;
  468. pElemUsed = m_rgElemUsed.GetPtr(i);
  469. if ( pElemUsed && pElemUsed->pwszAltText)
  470. {
  471. cicMemFree(pElemUsed->pwszAltText);
  472. pElemUsed->pwszAltText = NULL;
  473. }
  474. }
  475. m_rgElemUsed.Clear();
  476. }
  477. m_cAlt = 0;
  478. }
  479. return hr;
  480. }
  481. // GetNumItem
  482. //
  483. //
  484. int CSapiAlternativeList::GetNumItem(void)
  485. {
  486. if (!m_nItem)
  487. m_nItem = m_cAlt;
  488. if ( m_prgLMAlternates )
  489. {
  490. return m_nItem + m_prgLMAlternates->Count();
  491. }
  492. else
  493. return m_nItem;
  494. }
  495. HRESULT CSapiAlternativeList::_ProcessTrailingSpaces(SPPHRASE *pPhrases, ULONG ulNextElem, WCHAR *pwszAlt)
  496. {
  497. HRESULT hr = S_OK;
  498. ULONG ulSize;
  499. BOOL fRemoveTrail;
  500. if ( !pwszAlt || !pPhrases)
  501. return E_INVALIDARG;
  502. if ( ulNextElem >= pPhrases->Rule.ulCountOfElements)
  503. {
  504. // NextElement is not a valid element
  505. return hr;
  506. }
  507. if ( pPhrases->pElements[ulNextElem].bDisplayAttributes & SPAF_CONSUME_LEADING_SPACES )
  508. fRemoveTrail = TRUE;
  509. else
  510. fRemoveTrail = FALSE;
  511. if ( !fRemoveTrail )
  512. return hr;
  513. ulSize = wcslen(pwszAlt);
  514. for ( ULONG i=ulSize; i>0; i-- )
  515. {
  516. if ( (pwszAlt[i-1] != L' ') && (pwszAlt[i-1] != L'\t') )
  517. break;
  518. pwszAlt[i-1] = L'\0';
  519. }
  520. return hr;
  521. }
  522. HRESULT CSapiAlternativeList::GetAlternativeText(ISpPhraseAlt *pAlt,SPPHRASE *pPhrases, BOOL fFirstAlt, ULONG ulStartElem, ULONG ulNumElems, WCHAR *pwszAlt, int cchAlt, ULONG *pulLeadSpaceRemoved)
  523. {
  524. HRESULT hr = S_OK;
  525. CSpDynamicString sds;
  526. ULONG ulLeadSpaceRemoved = 0;
  527. if ( !pPhrases || !pwszAlt || !cchAlt || !pulLeadSpaceRemoved)
  528. return E_INVALIDARG;
  529. if ( !pAlt )
  530. return E_INVALIDARG;
  531. // We assume the first phrase in the AltPhrase list is exactly same as the parent phrase.
  532. // Specially handle it when it contains ITN.
  533. if ((m_cpwrp->m_ulNumOfITN > 0) && fFirstAlt)
  534. {
  535. // the ITN is always index 0
  536. //
  537. CSpDynamicString dstr;
  538. //
  539. // this seems confusing but fITNShown indicates whether the ITN
  540. // is displayed on the doc.
  541. // We no longer use fITNShown to inidicates the ITN show state.
  542. // Because there are may be more ITNs in one phrase, and every ITN may have
  543. // different show state on the doc.
  544. // Now we use an ITNSHOWSTATE list to keep the display state for individual ITN.
  545. // So we have to include non-ITN in the alternates if the ITN is on the doc,
  546. // and include the ITN in the alternates if the non-ITN is on the doc.
  547. //
  548. ULONG ulRepCount = 0;
  549. if (pPhrases->Rule.ulCountOfElements > 0)
  550. {
  551. for (UINT i = 0; i < pPhrases->Rule.ulCountOfElements; i++ )
  552. {
  553. if (i >= ulStartElem && i < ulStartElem + ulNumElems)
  554. {
  555. ULONG ulITNStart, ulITNNumElem;
  556. BOOL fITNShown = FALSE;
  557. BOOL fInsideITN = FALSE;
  558. // Check to see if this element is inside an ITN,
  559. // and if this ITN is shown up in the current Doc.
  560. for ( ulRepCount=0; ulRepCount<pPhrases->cReplacements; ulRepCount++)
  561. {
  562. ulITNStart = pPhrases->pReplacements[ulRepCount].ulFirstElement;
  563. ulITNNumElem = pPhrases->pReplacements[ulRepCount].ulCountOfElements;
  564. if ( (i == ulITNStart) && ((i + ulITNNumElem) <= pPhrases->Rule.ulCountOfElements))
  565. {
  566. // This element is in an ITN.
  567. fInsideITN = TRUE;
  568. // check if this ITN is shown as ITN in current DOC
  569. for ( ULONG iIndex=0; iIndex < m_cpwrp->m_ulNumOfITN; iIndex ++ )
  570. {
  571. SPITNSHOWSTATE *pITNShowState;
  572. pITNShowState = m_cpwrp->m_rgITNShowState.GetPtr(iIndex);
  573. if ( pITNShowState )
  574. {
  575. if ( (pITNShowState->ulITNStart == ulITNStart)
  576. && (pITNShowState->ulITNNumElem == ulITNNumElem) )
  577. {
  578. fITNShown = pITNShowState->fITNShown;
  579. break;
  580. }
  581. }
  582. }
  583. break;
  584. }
  585. }
  586. // use ITN version for an alternate when it is not shown in the parent
  587. BOOL fUseITN = fInsideITN && !fITNShown;
  588. if ( fUseITN && (ulRepCount < pPhrases->cReplacements) )
  589. {
  590. sds.Append(pPhrases->pReplacements[ulRepCount].pszReplacementText);
  591. i += pPhrases->pReplacements[ulRepCount].ulCountOfElements - 1;
  592. if (pPhrases->pReplacements[ulRepCount].bDisplayAttributes & SPAF_ONE_TRAILING_SPACE)
  593. {
  594. sds.Append(L" ");
  595. }
  596. else if (pPhrases->pReplacements[ulRepCount].bDisplayAttributes & SPAF_TWO_TRAILING_SPACES)
  597. {
  598. sds.Append(L" ");
  599. }
  600. }
  601. else
  602. {
  603. const WCHAR *pwszTextThis;
  604. BYTE bAttrThis = 0;
  605. BYTE bAttrPrev = 0;
  606. pwszTextThis = pPhrases->pElements[i].pszDisplayText;
  607. bAttrThis = pPhrases->pElements[i].bDisplayAttributes;
  608. if ( i > m_ulStart )
  609. bAttrPrev = pPhrases->pElements[i-1].bDisplayAttributes;
  610. HandlePhraseElement( (CSpDynamicString *)&sds, pwszTextThis, bAttrThis, bAttrPrev,NULL);
  611. }
  612. }
  613. }
  614. }
  615. }
  616. else
  617. {
  618. // This is not the first Altphrase.
  619. // Or even if it is the first Altphrase, but there is no ITN in this phrase
  620. ULONG ulcElements = 0;
  621. ULONG ulParentStart = 0;
  622. ULONG ulcParentElements = 0;
  623. if (pPhrases->Rule.ulCountOfElements > 0)
  624. {
  625. //
  626. // If the start element is not the first element in parent phrase,
  627. // and it has SPAF_CONSUME_LEADING_SPACES attr in parent phrase,
  628. // but this start element doesn't have SPAF_CONSUME_LEADING_SPACES in
  629. // the alternate phrase,
  630. // in this case, we need to add leading space in this alternate text.
  631. // the number of spaces can be found from previous element's attribute,
  632. // or the replacement's attribute if the previous phrase is displayed with
  633. // replacement text.
  634. //
  635. // Example here, if you dictate: "this is a test, this is good example."
  636. //
  637. // element test has one trail space.
  638. // element "," has SPAF_CONSUME_LEADING_SPACES.
  639. //
  640. // When you select "," to get alternate, the alternate could be "of,", "to,"
  641. // in the alternate phrase, the start element doesn't have SPAF_CONSUME_LEADING_SPACES
  642. // attr. in this case, the alternate text needs to add one space, so the text would be " of,"
  643. // " to,". otherwise, when user selects this alternate, the new text would be like
  644. // "this is a testof,....". ( there is no space between test and of ).
  645. //
  646. if ( ulStartElem > m_cpwrp->GetStart( ) )
  647. {
  648. BYTE bAttrParent, bAttrPrevParent;
  649. BYTE bAttrAlternate;
  650. ULONG ulPrevSpace = 0;
  651. bAttrParent = m_cpwrp->_GetElementDispAttribute(ulStartElem);
  652. bAttrPrevParent = m_cpwrp->_GetElementDispAttribute(ulStartElem - 1);
  653. bAttrAlternate = pPhrases->pElements[ulStartElem].bDisplayAttributes;
  654. if ( bAttrPrevParent & SPAF_ONE_TRAILING_SPACE )
  655. ulPrevSpace = 1;
  656. else if ( bAttrPrevParent & SPAF_TWO_TRAILING_SPACES )
  657. ulPrevSpace = 2;
  658. if ( (bAttrParent & SPAF_CONSUME_LEADING_SPACES) &&
  659. !(bAttrAlternate & SPAF_CONSUME_LEADING_SPACES) &&
  660. ulPrevSpace > 0)
  661. {
  662. // Add the required spaces for the previous element
  663. // which was removed before when parent phrase showed up.
  664. sds.Append( (ulPrevSpace == 1 ? L" " : L" ") );
  665. }
  666. if ( !(bAttrParent & SPAF_CONSUME_LEADING_SPACES) &&
  667. (bAttrAlternate & SPAF_CONSUME_LEADING_SPACES) &&
  668. ulPrevSpace > 0 )
  669. {
  670. // the previous element's trailing space needs to be
  671. // removed if it is selected.
  672. ulLeadSpaceRemoved = ulPrevSpace;
  673. }
  674. }
  675. /*
  676. // This code block tries to get Non-ITN form text for the alternate.
  677. // Yakima engine changed design to require ITN form text must be shown up in
  678. // candidate window.
  679. //
  680. // So this part of code is replaced by the below code block.
  681. //
  682. for (UINT i = 0; i < pPhrases->Rule.ulCountOfElements; i++ )
  683. {
  684. if (i >= ulStartElem && i < ulStartElem + ulNumElems)
  685. {
  686. const WCHAR *pwszTextThis;
  687. BYTE bAttrThis = 0;
  688. BYTE bAttrPrev = 0;
  689. pwszTextThis = pPhrases->pElements[i].pszDisplayText;
  690. bAttrThis = pPhrases->pElements[i].bDisplayAttributes;
  691. if ( i > ulParentStart )
  692. bAttrPrev = pPhrases->pElements[i-1].bDisplayAttributes;
  693. HandlePhraseElement( (CSpDynamicString *)&sds, pwszTextThis, bAttrThis, bAttrPrev,NULL);
  694. }
  695. }
  696. */
  697. BYTE bAttr = 0;
  698. CSpDynamicString sdsAltText;
  699. if ( pAlt->GetText(ulStartElem, ulNumElems, TRUE, &sdsAltText, &bAttr) == S_OK )
  700. {
  701. if (bAttr & SPAF_ONE_TRAILING_SPACE)
  702. {
  703. sdsAltText.Append(L" ");
  704. }
  705. else if (bAttr & SPAF_TWO_TRAILING_SPACES)
  706. {
  707. sdsAltText.Append(L" ");
  708. }
  709. }
  710. if ( sdsAltText )
  711. sds.Append(sdsAltText);
  712. }
  713. }
  714. if (sds)
  715. {
  716. _ProcessTrailingSpaces(pPhrases, ulStartElem + ulNumElems, (WCHAR *)sds);
  717. int TextLen;
  718. TextLen = wcslen( (WCHAR *)sds);
  719. if (TextLen > cchAlt )
  720. {
  721. // There is not enough buffer to hold this alternate text.
  722. // Set the first element as NULL to indicate this situation.
  723. pwszAlt[0] = L'\0';
  724. }
  725. else
  726. {
  727. // The passed buffer can hold all the alternate text.
  728. wcsncpy(pwszAlt, sds, TextLen);
  729. pwszAlt[TextLen] = L'\0';
  730. }
  731. }
  732. if ( pulLeadSpaceRemoved )
  733. *pulLeadSpaceRemoved = ulLeadSpaceRemoved;
  734. return hr;
  735. }
  736. HRESULT CSapiAlternativeList::GetProbability(int nId, int * pnPrb)
  737. {
  738. HRESULT hr = E_INVALIDARG;
  739. //
  740. // bogus for now
  741. //
  742. if(pnPrb && nId >= 0)
  743. {
  744. if ( nId < m_nItem)
  745. {
  746. *pnPrb = 10 - nId;
  747. }
  748. else if ( m_prgLMAlternates
  749. && (nId - m_nItem) < m_prgLMAlternates->Count())
  750. {
  751. // we'll be able to get something from LM
  752. // but not normalized for now anyway
  753. *pnPrb = 1;
  754. }
  755. hr = S_OK;
  756. }
  757. return hr;
  758. }
  759. HRESULT CSapiAlternativeList::GetCachedAltInfo
  760. (
  761. ULONG nId,
  762. ULONG *pulParentStart,
  763. ULONG *pulcParentElements,
  764. ULONG *pulcElements,
  765. WCHAR **ppwszText,
  766. ULONG *pulLeadSpaceRemoved
  767. )
  768. {
  769. if (nId < m_cAlt)
  770. {
  771. SPELEMENTUSED *pElemUsed;
  772. if ( pElemUsed = m_rgElemUsed.GetPtr(nId))
  773. {
  774. if (pulParentStart)
  775. *pulParentStart = pElemUsed->ulParentStart;
  776. if (pulcParentElements)
  777. *pulcParentElements = pElemUsed->ulcParentElements;
  778. if (pulcElements)
  779. *pulcElements = pElemUsed->ulcElements;
  780. if ( ppwszText)
  781. *ppwszText = pElemUsed->pwszAltText;
  782. if (pulLeadSpaceRemoved )
  783. *pulLeadSpaceRemoved = pElemUsed->ulLeadSpaceRemoved;
  784. }
  785. }
  786. return S_OK;
  787. }
  788. void CSapiAlternativeList::_Commit(ULONG nIdx, ISpRecoResult *pRecoResult)
  789. {
  790. if ((m_iFakeAlternate != NO_FAKEALT) && (m_iFakeAlternate == (int)nIdx))
  791. {
  792. // This is for Faked Alternate.
  793. // Don't change any thing.
  794. // just return here.
  795. return;
  796. }
  797. if (m_cpwrp->m_ulNumOfITN > 0)
  798. {
  799. // if we now have the ITN shown as the recognized text, it's swapped with non-ITN
  800. // if we have non-ITN shown as the recognized text, it's swapped with the ITN
  801. // We should change the show state only for the replaced range,
  802. // ( not for all the phrase if uses doesn't select the whole phrase.
  803. if ((nIdx == 0) && _IsFirstAltInCandidate() )
  804. {
  805. // the ITN alternate has been chosen.
  806. //
  807. // we need to invert the show state for all the ITN inside the selection range.
  808. m_cpwrp->_InvertITNShowStateForRange(m_ulStart, m_ulcElem);
  809. // we don't have to commit this to SR engine but
  810. // we need to recalculate the character offsets for
  811. // SR elements using the current pharse (set NULL)
  812. //
  813. m_cpwrp->_SetElementOffsetCch(NULL);
  814. return;
  815. }
  816. }
  817. // if a non-SR candidate (such as LM's) is chosen,
  818. // nIdx would be >= m_cAlt and we don't have to
  819. // tell SR about that.
  820. if(nIdx < m_cAlt)
  821. {
  822. // Need to update the real ITN show state list
  823. // and then save the real text and get the offset for
  824. // all the elements.
  825. HRESULT hr = m_cpwrp->_UpdateStateWithAltPhrase(m_ppAlt[nIdx]);
  826. if ( S_OK == hr )
  827. {
  828. // Offset value should be based on ITN display status.
  829. // revise character offsets using the alternate phrase
  830. m_cpwrp->_SetElementOffsetCch(m_ppAlt[nIdx]);
  831. }
  832. if (S_OK == hr)
  833. {
  834. hr = m_ppAlt[nIdx]->Commit();
  835. }
  836. // we need to invalidate the result object too
  837. if (S_OK == hr)
  838. {
  839. hr = m_cpwrp->Init(pRecoResult);
  840. }
  841. }
  842. }
  843. //+---------------------------------------------------------------------------
  844. //
  845. // _GetUIFont()
  846. //
  847. // synopsis: get appropriate logfont based on
  848. // the current langid assigned to the alternativelist
  849. //
  850. // return : TRUE if there's a specific logfont to the langid
  851. // FALSE if no logfont data is available for the langid
  852. //
  853. //+---------------------------------------------------------------------------
  854. BOOL CSapiAlternativeList::_GetUIFont(BOOL fVerticalWriting, LOGFONTW *plf)
  855. {
  856. // other languages will follow later
  857. //
  858. const WCHAR c_szFontJPW2K[] = L"Microsoft Sans Serif";
  859. const WCHAR c_szFontJPOTHER[] = L"MS P Gothic";
  860. const WCHAR c_szFontJPNVert[] = L"@MS Gothic";
  861. const WCHAR c_szFontJPNVertWin9x[] = L"@\xFF2D\xFF33 \xFF30\x30B4\x30B7\x30C3\x30AF"; // @MS P Gothic
  862. const WCHAR c_szFontCHS[] = L"SimSum";
  863. const WCHAR c_szFontCHSVert[] = L"@SimSun";
  864. const WCHAR c_szFontCHSVertLoc[] = L"@\x5b8b\x4f53";
  865. Assert(plf);
  866. int iDpi = 96;
  867. int iPoint = 0;
  868. HDC hdc = CreateIC("DISPLAY", NULL, NULL, NULL);
  869. if (hdc)
  870. {
  871. iDpi = GetDeviceCaps(hdc, LOGPIXELSY);
  872. DeleteDC(hdc);
  873. }
  874. else
  875. goto err_exit;
  876. switch(PRIMARYLANGID(m_langid))
  877. {
  878. case LANG_JAPANESE:
  879. iPoint = 12; // Satori uses 12 point font
  880. if ( !fVerticalWriting )
  881. {
  882. wcsncpy(plf->lfFaceName,
  883. IsOnNT5() ? c_szFontJPW2K : c_szFontJPOTHER,
  884. ARRAYSIZE(plf->lfFaceName));
  885. }
  886. else
  887. {
  888. wcsncpy(plf->lfFaceName,
  889. IsOn98() ? c_szFontJPNVertWin9x : c_szFontJPNVert,
  890. ARRAYSIZE(plf->lfFaceName));
  891. }
  892. // don't bother to call GetLocaleInfo() for now
  893. plf->lfCharSet = SHIFTJIS_CHARSET;
  894. break;
  895. case LANG_CHINESE:
  896. iPoint = 9;
  897. if ( !fVerticalWriting )
  898. {
  899. wcsncpy(plf->lfFaceName, c_szFontCHS, ARRAYSIZE(plf->lfFaceName));
  900. }
  901. else
  902. {
  903. wcsncpy(plf->lfFaceName,
  904. IsOnNT5() ? c_szFontCHSVert : c_szFontCHSVertLoc,
  905. ARRAYSIZE(plf->lfFaceName));
  906. }
  907. // don't bother to call GetLocaleInfo() for now
  908. plf->lfCharSet = GB2312_CHARSET;
  909. break;
  910. default:
  911. break;
  912. }
  913. if (iPoint > 0)
  914. plf->lfHeight = -iPoint * iDpi / 72;
  915. err_exit:
  916. return iPoint > 0;
  917. }
  918. //////////////////////////////////////////////////////////////////////////////
  919. //
  920. // CFunction
  921. //
  922. //////////////////////////////////////////////////////////////////////////////
  923. //+---------------------------------------------------------------------------
  924. //
  925. // ctor
  926. //
  927. //----------------------------------------------------------------------------
  928. CFunction::CFunction(CSapiIMX *pImx)
  929. {
  930. m_pImx = pImx;
  931. if (m_pImx)
  932. m_pImx->AddRef();
  933. m_cRef = 1;
  934. }
  935. //+---------------------------------------------------------------------------
  936. //
  937. // dtor
  938. //
  939. //----------------------------------------------------------------------------
  940. CFunction::~CFunction()
  941. {
  942. SafeRelease(m_pImx);
  943. }
  944. //+---------------------------------------------------------------------------
  945. //
  946. // CFunction::GetFocusedTarget
  947. //
  948. //----------------------------------------------------------------------------
  949. BOOL CFunction::GetFocusedTarget(TfEditCookie ec, ITfContext *pic, ITfRange *pRange, BOOL bAdjust, ITfRange **ppRangeTmp)
  950. {
  951. ITfRange *pRangeTmp = NULL;
  952. ITfRange *pRangeTmp2 = NULL;
  953. IEnumTfRanges *pEnumTrack = NULL;
  954. BOOL bRet = FALSE;
  955. BOOL fWholeDoc = FALSE;
  956. if (!pRange)
  957. {
  958. fWholeDoc = TRUE;
  959. if (FAILED(GetRangeForWholeDoc(ec, pic, &pRange)))
  960. return FALSE;
  961. }
  962. if (bAdjust)
  963. {
  964. //
  965. // multi owner and PF_FOCUS range support.
  966. //
  967. if (FAILED(AdjustRangeByTextOwner(ec, pic,
  968. pRange,
  969. &pRangeTmp2,
  970. CLSID_SapiLayr)))
  971. goto Exit;
  972. GUID rgGuid[2];
  973. rgGuid[0] = GUID_ATTR_SAPI_INPUT;
  974. rgGuid[1] = GUID_ATTR_SAPI_GREENBAR;
  975. if (FAILED(AdjustRangeByAttribute(m_pImx->_GetLibTLS(),
  976. ec, pic,
  977. pRangeTmp2,
  978. &pRangeTmp,
  979. rgGuid, ARRAYSIZE(rgGuid))))
  980. goto Exit;
  981. }
  982. else
  983. {
  984. pRange->Clone(&pRangeTmp);
  985. }
  986. ITfRange *pPropRange;
  987. ITfReadOnlyProperty *pProp;
  988. //
  989. // check if there is an intersection of PF_FOCUS range and owned range.
  990. // if there is no such range, we return FALSE.
  991. //
  992. if (FAILED(EnumTrackTextAndFocus(ec, pic, pRangeTmp, &pProp, &pEnumTrack)))
  993. goto Exit;
  994. while(pEnumTrack->Next(1, &pPropRange, 0) == S_OK)
  995. {
  996. if (IsOwnerAndFocus(m_pImx->_GetLibTLS(), ec, CLSID_SapiLayr, pProp, pPropRange))
  997. bRet = TRUE;
  998. pPropRange->Release();
  999. }
  1000. pProp->Release();
  1001. if (bRet)
  1002. {
  1003. *ppRangeTmp = pRangeTmp;
  1004. (*ppRangeTmp)->AddRef();
  1005. }
  1006. Exit:
  1007. SafeRelease(pEnumTrack);
  1008. SafeRelease(pRangeTmp);
  1009. SafeRelease(pRangeTmp2);
  1010. if (fWholeDoc)
  1011. pRange->Release();
  1012. return bRet;
  1013. }
  1014. HRESULT CFunction::_GetLangIdFromRange(TfEditCookie ec, ITfContext *pic, ITfRange *pRange, LANGID *plangid)
  1015. {
  1016. HRESULT hr;
  1017. ITfProperty *pProp;
  1018. LANGID langid;
  1019. // get langid from the given range
  1020. if (SUCCEEDED(hr = pic->GetProperty(GUID_PROP_LANGID, &pProp)))
  1021. {
  1022. GetLangIdPropertyData(ec, pProp, pRange, &langid);
  1023. pProp->Release();
  1024. }
  1025. if (SUCCEEDED(hr) && plangid)
  1026. *plangid = langid;
  1027. return hr;
  1028. }
  1029. //////////////////////////////////////////////////////////////////////////////
  1030. //
  1031. // CFnReconversion
  1032. //
  1033. //////////////////////////////////////////////////////////////////////////////
  1034. //+---------------------------------------------------------------------------
  1035. //
  1036. // IUnknown
  1037. //
  1038. //----------------------------------------------------------------------------
  1039. STDAPI CFnReconversion::QueryInterface(REFIID riid, void **ppvObj)
  1040. {
  1041. *ppvObj = NULL;
  1042. if (IsEqualIID(riid, IID_IUnknown) ||
  1043. IsEqualIID(riid, IID_ITfFnReconversion))
  1044. {
  1045. *ppvObj = SAFECAST(this, CFnReconversion *);
  1046. }
  1047. if (*ppvObj)
  1048. {
  1049. AddRef();
  1050. return S_OK;
  1051. }
  1052. return E_NOINTERFACE;
  1053. }
  1054. STDAPI_(ULONG) CFnReconversion::AddRef()
  1055. {
  1056. return InterlockedIncrement(&m_cRef);
  1057. }
  1058. STDAPI_(ULONG) CFnReconversion::Release()
  1059. {
  1060. long cr;
  1061. cr = InterlockedDecrement(&m_cRef);
  1062. Assert(cr >= 0);
  1063. if (cr == 0)
  1064. {
  1065. delete this;
  1066. }
  1067. return cr;
  1068. }
  1069. //+---------------------------------------------------------------------------
  1070. //
  1071. // ctor
  1072. //
  1073. //----------------------------------------------------------------------------
  1074. CFnReconversion::CFnReconversion(CSapiIMX *psi) : CFunction(psi) , CMasterLMWrap(psi), CBestPropRange( )
  1075. {
  1076. m_psal = NULL;
  1077. // initialize with the current profile langid
  1078. m_langid = m_pImx->GetLangID();
  1079. // m_MaxCandChars = 0;
  1080. }
  1081. //+---------------------------------------------------------------------------
  1082. //
  1083. // dtor
  1084. //
  1085. //----------------------------------------------------------------------------
  1086. CFnReconversion::~CFnReconversion()
  1087. {
  1088. if (m_psal)
  1089. {
  1090. delete m_psal;
  1091. }
  1092. }
  1093. //+---------------------------------------------------------------------------
  1094. //
  1095. // dtor
  1096. //
  1097. //----------------------------------------------------------------------------
  1098. STDAPI CFnReconversion::GetDisplayName(BSTR *pbstrName)
  1099. {
  1100. *pbstrName = SysAllocString(L"Reconversion");
  1101. return S_OK;
  1102. }
  1103. //+---------------------------------------------------------------------------
  1104. //
  1105. // CFnReconversion::QueryRange
  1106. //
  1107. //----------------------------------------------------------------------------
  1108. HRESULT CFnReconversion::QueryRange(ITfRange *pRange, ITfRange **ppRange, BOOL *pfConvertable)
  1109. {
  1110. CFnRecvEditSession *pes;
  1111. CComPtr<ITfContext> cpic;
  1112. HRESULT hr = E_FAIL;
  1113. if (ppRange == NULL || pfConvertable == NULL || pRange == NULL)
  1114. return E_INVALIDARG;
  1115. *ppRange = NULL;
  1116. *pfConvertable = FALSE;
  1117. // call MasterLM when it's available
  1118. //
  1119. _EnsureMasterLM(m_langid);
  1120. if (m_cpMasterLM)
  1121. {
  1122. hr = m_cpMasterLM->QueryRange( pRange, ppRange, pfConvertable );
  1123. return hr;
  1124. }
  1125. if (SUCCEEDED(pRange->GetContext(&cpic)))
  1126. {
  1127. hr = E_OUTOFMEMORY;
  1128. if (pes = new CFnRecvEditSession(this, pRange, cpic))
  1129. {
  1130. pes->_SetEditSessionData(ESCB_RECONV_QUERYRECONV,NULL, 0);
  1131. cpic->RequestEditSession(m_pImx->_GetId(), pes, TF_ES_READ | TF_ES_SYNC, &hr);
  1132. if ( SUCCEEDED(hr) )
  1133. *ppRange = (ITfRange *)pes->_GetRetUnknown( );
  1134. pes->Release();
  1135. }
  1136. *pfConvertable = (hr == S_OK);
  1137. if (hr == S_FALSE)
  1138. {
  1139. hr = S_OK;
  1140. }
  1141. }
  1142. return hr;
  1143. }
  1144. //+---------------------------------------------------------------------------
  1145. //
  1146. // CFnReconversion::GetReconversion
  1147. //
  1148. //----------------------------------------------------------------------------
  1149. STDAPI CFnReconversion::GetReconversion(ITfRange *pRange, ITfCandidateList **ppCandList)
  1150. {
  1151. CFnRecvEditSession *pes;
  1152. ITfContext *pic;
  1153. HRESULT hr = E_FAIL;
  1154. // Call master LM when it's available!
  1155. //
  1156. //
  1157. Assert(pRange);
  1158. _EnsureMasterLM(m_langid);
  1159. if (m_cpMasterLM)
  1160. {
  1161. return m_cpMasterLM->GetReconversion( pRange, ppCandList);
  1162. }
  1163. if (FAILED(pRange->GetContext(&pic)))
  1164. goto Exit;
  1165. hr = E_OUTOFMEMORY;
  1166. if (pes = new CFnRecvEditSession(this, pRange,pic) )
  1167. {
  1168. pes->_SetEditSessionData(ESCB_RECONV_GETRECONV,NULL, 0);
  1169. pic->RequestEditSession(m_pImx->_GetId(), pes, TF_ES_READ | TF_ES_SYNC, &hr);
  1170. if (SUCCEEDED(hr))
  1171. *ppCandList = (ITfCandidateList *)pes->_GetRetUnknown( );
  1172. pes->Release();
  1173. }
  1174. pic->Release();
  1175. Exit:
  1176. return hr;
  1177. }
  1178. //+---------------------------------------------------------------------------
  1179. //
  1180. // CFnReconversion::_QueryReconversion
  1181. //
  1182. //----------------------------------------------------------------------------
  1183. HRESULT CFnReconversion::_QueryReconversion(TfEditCookie ec, ITfContext *pic, ITfRange *pRange, ITfRange **ppNewRange)
  1184. {
  1185. Assert(pic);
  1186. Assert(pRange);
  1187. Assert(ppNewRange);
  1188. Assert(*ppNewRange == NULL);
  1189. CComPtr<ITfProperty> cpProp ;
  1190. HRESULT hr = pic->GetProperty(GUID_PROP_SAPIRESULTOBJECT, &cpProp);
  1191. if (SUCCEEDED(hr) && cpProp)
  1192. {
  1193. CComPtr<ITfRange> cpBestPropRange;
  1194. if (S_OK == hr)
  1195. {
  1196. hr = _ComputeBestFitPropRange(ec, cpProp, pRange, &cpBestPropRange, NULL, NULL);
  1197. }
  1198. // adjust start element and num elements
  1199. if (S_OK == hr)
  1200. {
  1201. if (ppNewRange)
  1202. {
  1203. // TODO: this adjustment has to be done per element not phrase
  1204. *ppNewRange = cpBestPropRange;
  1205. (*ppNewRange)->AddRef();
  1206. }
  1207. }
  1208. }
  1209. return hr;
  1210. }
  1211. //+---------------------------------------------------------------------------
  1212. //
  1213. // CFnReconversion::_GetSapilayrEngineInstance
  1214. //
  1215. //----------------------------------------------------------------------------
  1216. HRESULT CFnReconversion::_GetSapilayrEngineInstance(ISpRecognizer **ppRecoEngine)
  1217. {
  1218. #ifdef _WIN64
  1219. return E_NOTIMPL;
  1220. #else
  1221. HRESULT hr = E_FAIL;
  1222. CComPtr<ITfFnGetSAPIObject> cpGetSAPI;
  1223. // we shouldn't release this until we terminate ourselves
  1224. // so we don't use comptr here
  1225. hr = m_pImx->GetFunction(GUID_NULL, IID_ITfFnGetSAPIObject, (IUnknown **)&cpGetSAPI);
  1226. if (S_OK == hr)
  1227. {
  1228. hr = cpGetSAPI->Get(GETIF_RECOGNIZERNOINIT, (IUnknown **)ppRecoEngine);
  1229. }
  1230. return hr;
  1231. #endif
  1232. }
  1233. //+---------------------------------------------------------------------------
  1234. //
  1235. // CFnReconversion::_GetReconversion
  1236. //
  1237. //----------------------------------------------------------------------------
  1238. HRESULT CFnReconversion::_GetReconversion(TfEditCookie ec, ITfContext *pic, ITfRange *pRange, ITfCandidateList **ppCandList, BOOL fDisableEngine /*=FALSE*/)
  1239. {
  1240. WCHAR *pszText = NULL;
  1241. CCandidateList *pCandList = NULL;
  1242. CComPtr<ITfProperty> cpProp;
  1243. HRESULT hr;
  1244. BOOL fEmpty;
  1245. if(!pRange)
  1246. return E_FAIL;
  1247. Assert(m_pImx);
  1248. ULONG cAlt = m_pImx->_GetMaxAlternates();
  1249. if (pRange->IsEmpty(ec, &fEmpty) != S_OK || fEmpty)
  1250. return E_FAIL;
  1251. // GUID_PROP_SAPIRESULTOBJECT really gets us
  1252. // a wrapper object of ISpRecoResult
  1253. //
  1254. hr = pic->GetProperty(GUID_PROP_SAPIRESULTOBJECT, &cpProp);
  1255. if (S_OK != hr)
  1256. return S_FALSE;
  1257. // try to reuse the candidate object if user opens it twice
  1258. // on the same range using the same function instance
  1259. //
  1260. if (m_psal && m_psal->IsSameRange(pRange, ec))
  1261. {
  1262. CComPtr<ITfRange> cpPropRangeTemp;
  1263. hr = cpProp->FindRange(ec, pRange, &cpPropRangeTemp, TF_ANCHOR_START);
  1264. if (S_OK == hr)
  1265. hr = GetCandidateForRange(m_psal, pic, pRange, ppCandList) ;
  1266. }
  1267. else
  1268. {
  1269. // get langid from the range property
  1270. LANGID langid;
  1271. if (FAILED(_GetLangIdFromRange(ec, pic, pRange, &langid)) || (langid == 0))
  1272. {
  1273. langid = GetUserDefaultLangID();
  1274. }
  1275. _SetCurrentLangID(langid);
  1276. if (m_psal)
  1277. {
  1278. delete m_psal;
  1279. m_psal = NULL;
  1280. }
  1281. CSapiAlternativeList *psal = new CSapiAlternativeList(langid, pRange, _GetMaxCandidateChars( ));
  1282. if (psal)
  1283. {
  1284. m_psal = psal;
  1285. }
  1286. if (SUCCEEDED(hr) && cpProp && psal)
  1287. {
  1288. CComPtr<ITfRange> cpPropRange;
  1289. hr = cpProp->FindRange(ec, pRange, &cpPropRange, TF_ANCHOR_START);
  1290. CComPtr<IUnknown> cpunk;
  1291. // this punk points to the wrapper
  1292. if (S_OK == hr)
  1293. hr = GetUnknownPropertyData(ec, cpProp, cpPropRange, &cpunk);
  1294. if ((hr == S_OK) && cpunk)
  1295. {
  1296. CSpTask *psp;
  1297. hr = m_pImx->GetSpeechTask(&psp);
  1298. if (SUCCEEDED(hr))
  1299. {
  1300. CRecoResultWrap *pResWrap;
  1301. hr = cpunk->QueryInterface(IID_PRIV_RESULTWRAP, (void **)&pResWrap);
  1302. if (S_OK == hr)
  1303. {
  1304. CComPtr<ITfRange> cpBestPropRange;
  1305. ISpPhraseAlt **ppAlt = (ISpPhraseAlt **)cicMemAlloc(cAlt*sizeof(ISpPhraseAlt *));
  1306. if (!ppAlt)
  1307. {
  1308. hr = E_OUTOFMEMORY;
  1309. }
  1310. else
  1311. {
  1312. ULONG ulStart, ulcElem;
  1313. hr = _ComputeBestFitPropRange(ec, cpProp, pRange, &cpBestPropRange, &ulStart, &ulcElem);
  1314. if (S_OK == hr)
  1315. {
  1316. m_cpRecoResult.Release();
  1317. CComPtr<ISpRecognizer> cpEngine;
  1318. _GetSapilayrEngineInstance(&cpEngine);
  1319. if (fDisableEngine && cpEngine)
  1320. {
  1321. // We stop the engine deliberately here (whether active or not - cannot afford to check as
  1322. // we may get blocked by SAPI). This forces the engine into a synchronization since the audio
  1323. // stops and we are guaranteed to be able to display the candidate list object
  1324. // This particular scenario (fDisableEngine == TRUE) occurs with a Word right-click context
  1325. // request for alternates. The normal 'display alternates list' scenario is handled in the
  1326. // _Reconvert() call since it requires the engine to be re-enabled after the alternates list
  1327. // is display to avoid it blocking until the engine hears silence.
  1328. cpEngine->SetRecoState(SPRST_INACTIVE_WITH_PURGE);
  1329. }
  1330. hr = psp->GetAlternates(pResWrap, ulStart, ulcElem, ppAlt, &cAlt, &m_cpRecoResult);
  1331. if (fDisableEngine && cpEngine)
  1332. {
  1333. // If the microphone is supposed to be open, we now restart the engine.
  1334. if (m_pImx->GetOnOff())
  1335. {
  1336. // We need to restart the engine now that we are fully initialized.
  1337. cpEngine->SetRecoState(SPRST_ACTIVE);
  1338. }
  1339. }
  1340. }
  1341. if ( S_OK == hr )
  1342. {
  1343. // Before we call SetPhraseAlt( ), we need to get the current parent text covered
  1344. // by cpBestPropRange.
  1345. WCHAR *pwszParent = NULL;
  1346. CComPtr<ITfRange> cpParentRange;
  1347. long cchChunck = 128;
  1348. hr = cpBestPropRange->Clone(&cpParentRange);
  1349. if ( S_OK == hr )
  1350. {
  1351. long cch;
  1352. int iNumOfChunck=1;
  1353. pwszParent = (WCHAR *) cicMemAllocClear((cchChunck+1) * sizeof(WCHAR) );
  1354. if ( pwszParent )
  1355. {
  1356. hr = cpParentRange->GetText(ec, TF_TF_MOVESTART, pwszParent, (ULONG)cchChunck, (ULONG *)&cch);
  1357. if ( (S_OK == hr) && ( cch > 0 ) )
  1358. pwszParent[cch] = L'\0';
  1359. }
  1360. else
  1361. hr = E_OUTOFMEMORY;
  1362. while ( (S_OK == hr) && (cch == cchChunck))
  1363. {
  1364. long iNewSize;
  1365. iNewSize = ((iNumOfChunck+1) * cchChunck + 1 ) * sizeof(WCHAR);
  1366. pwszParent = (WCHAR *)cicMemReAlloc(pwszParent, iNewSize);
  1367. if ( pwszParent )
  1368. {
  1369. WCHAR *pwszNewPosition;
  1370. pwszNewPosition = pwszParent + iNumOfChunck * cchChunck;
  1371. hr = cpParentRange->GetText(ec, TF_TF_MOVESTART, pwszNewPosition, (ULONG)cchChunck, (ULONG *)&cch);
  1372. if ( (S_OK == hr) && ( cch > 0 ) )
  1373. pwszNewPosition[cch] = L'\0';
  1374. }
  1375. else
  1376. {
  1377. hr = E_OUTOFMEMORY;
  1378. }
  1379. iNumOfChunck ++;
  1380. }
  1381. }
  1382. if (S_OK == hr)
  1383. {
  1384. // this is to store the obtained alternate phrases
  1385. // to CSapiAlternativeList class instance
  1386. //
  1387. hr = psal->SetPhraseAlt(pResWrap, ppAlt, cAlt, ulStart, ulcElem, pwszParent);
  1388. }
  1389. if ( pwszParent )
  1390. cicMemFree(pwszParent);
  1391. }
  1392. if ((hr == S_OK) && ppAlt)
  1393. {
  1394. for (UINT i = 0; i < cAlt; i++)
  1395. {
  1396. if (NULL != ppAlt[i])
  1397. {
  1398. ppAlt[i]->Release();
  1399. ppAlt[i] = NULL;
  1400. }
  1401. }
  1402. }
  1403. if ( ppAlt )
  1404. cicMemFree(ppAlt);
  1405. }
  1406. pResWrap->Release();
  1407. // get this alternative list processed by external LM
  1408. //
  1409. if (S_OK == hr)
  1410. {
  1411. Assert(cpBestPropRange);
  1412. hr = GetCandidateForRange(psal, pic, cpBestPropRange, ppCandList) ;
  1413. }
  1414. }
  1415. psp->Release();
  1416. }
  1417. }
  1418. }
  1419. }
  1420. return hr;
  1421. }
  1422. HRESULT CFnReconversion::GetCandidateForRange(CSapiAlternativeList *psal, ITfContext *pic, ITfRange *pRange, ITfCandidateList **ppCandList)
  1423. {
  1424. Assert(psal);
  1425. if ( !psal || !pic || !pRange || !ppCandList )
  1426. return E_INVALIDARG;
  1427. HRESULT hr = S_OK;
  1428. int nItem = psal->GetNumItem();
  1429. WCHAR *pwszAlt = NULL;
  1430. CCandidateList *pCandList = new CCandidateList(SetResult, pic, pRange, SetOption);
  1431. if (!pCandList)
  1432. {
  1433. hr = E_OUTOFMEMORY;
  1434. }
  1435. else
  1436. {
  1437. int nPrb;
  1438. for (int i = 0; SUCCEEDED(hr) && i < nItem ; i++)
  1439. {
  1440. psal->GetCachedAltInfo(i, NULL, NULL,NULL, &pwszAlt);
  1441. if ( pwszAlt )
  1442. {
  1443. hr = psal->GetProbability(i, &nPrb);
  1444. if (SUCCEEDED(hr))
  1445. {
  1446. // note CSapiAlternateveList has exactly same life span as CFnReconversion
  1447. pCandList->AddString(pwszAlt, m_langid, psal, this, NULL);
  1448. }
  1449. }
  1450. }
  1451. // Add menu options here.
  1452. HICON hIcon = NULL;
  1453. WCHAR wzTmp[MAX_PATH];
  1454. wzTmp[0] = 0;
  1455. CicLoadStringWrapW(g_hInst, IDS_REPLAY, wzTmp, MAX_PATH);
  1456. if (wzTmp[0] != 0)
  1457. {
  1458. hIcon = (HICON)LoadImage(g_hInst, MAKEINTRESOURCE(ID_ICON_TTSPLAY), IMAGE_ICON, 16, 16, 0);
  1459. pCandList->AddOption(wzTmp, m_langid, NULL, this, NULL, OPTION_REPLAY, hIcon ? hIcon : NULL, NULL);
  1460. }
  1461. wzTmp[0] = 0;
  1462. CicLoadStringWrapW(g_hInst, IDS_DELETE, wzTmp, MAX_PATH);
  1463. if (wzTmp[0] != 0)
  1464. {
  1465. hIcon = (HICON)LoadImage(g_hInstSpgrmr,
  1466. MAKEINTRESOURCE(IDI_SPTIP_DELETEICON),
  1467. IMAGE_ICON, 16, 16, 0);
  1468. pCandList->AddOption(wzTmp, m_langid, NULL, this, NULL, OPTION_DELETE, hIcon ? hIcon : NULL, NULL);
  1469. }
  1470. if (GetSystemMetrics(SM_TABLETPC) > 0)
  1471. {
  1472. BOOL fDisplayRedo = TRUE;
  1473. DWORD dw = 0;
  1474. CMyRegKey regkey;
  1475. if (S_OK == regkey.Open(HKEY_LOCAL_MACHINE, c_szSapilayrKey, KEY_READ ) )
  1476. {
  1477. if (ERROR_SUCCESS == regkey.QueryValue(dw, TEXT("DisableRewrite")))
  1478. {
  1479. if (dw == 1)
  1480. {
  1481. fDisplayRedo = FALSE;
  1482. }
  1483. }
  1484. }
  1485. if (fDisplayRedo)
  1486. {
  1487. wzTmp[0] = 0;
  1488. CicLoadStringWrapW(g_hInst, IDS_REDO, wzTmp, MAX_PATH);
  1489. if (wzTmp[0] != 0)
  1490. {
  1491. pCandList->AddOption(wzTmp, m_langid, NULL, this, NULL, OPTION_REDO, NULL, NULL);
  1492. }
  1493. }
  1494. }
  1495. hr = pCandList->QueryInterface(IID_ITfCandidateList, (void **)ppCandList);
  1496. pCandList->Release();
  1497. }
  1498. return hr;
  1499. }
  1500. //+---------------------------------------------------------------------------
  1501. //
  1502. // CFnReconversion::Reconvert
  1503. //
  1504. //----------------------------------------------------------------------------
  1505. STDAPI CFnReconversion::Reconvert(ITfRange *pRange)
  1506. {
  1507. CFnRecvEditSession *pes;
  1508. ITfContext *pic;
  1509. HRESULT hr = E_FAIL;
  1510. Assert(pRange);
  1511. if (FAILED(pRange->GetContext(&pic)))
  1512. goto Exit;
  1513. hr = E_OUTOFMEMORY;
  1514. if (pes = new CFnRecvEditSession(this, pRange, pic))
  1515. {
  1516. BOOL fCallLMReconvert = FALSE;
  1517. pes->_SetEditSessionData(ESCB_RECONV_RECONV, NULL, 0);
  1518. pic->RequestEditSession(m_pImx->_GetId(), pes, TF_ES_READWRITE | TF_ES_ASYNC, &hr);
  1519. if ( SUCCEEDED(hr) )
  1520. fCallLMReconvert = (BOOL)pes->_GetRetData( );
  1521. if (hr == S_OK && fCallLMReconvert)
  1522. {
  1523. // need to call LM reconvert
  1524. Assert(m_cpMasterLM != NULL);
  1525. hr = m_cpMasterLM->Reconvert(pRange);
  1526. }
  1527. pes->Release();
  1528. }
  1529. pic->Release();
  1530. Exit:
  1531. return hr;
  1532. }
  1533. //+---------------------------------------------------------------------------
  1534. //
  1535. // CFnReconversion::_Reconvert
  1536. //
  1537. //----------------------------------------------------------------------------
  1538. HRESULT CFnReconversion::_Reconvert(TfEditCookie ec, ITfContext *pic, ITfRange *pRange, BOOL *pfCallLMReconvert)
  1539. {
  1540. ITfCandidateList *pCandList;
  1541. HRESULT hr;
  1542. CSpTask *psp = NULL;
  1543. // Call master LM when it's available!
  1544. //
  1545. // For voice playback, we need to do a little more.
  1546. // we have to call QueryRange first and determine the
  1547. // length of playback here.
  1548. //
  1549. *pfCallLMReconvert = FALSE;
  1550. _EnsureMasterLM(m_langid);
  1551. if (m_cpMasterLM)
  1552. {
  1553. // playback the whole range for now
  1554. //
  1555. CComPtr<ITfProperty> cpProp;
  1556. hr = pic->GetProperty(GUID_PROP_SAPIRESULTOBJECT, &cpProp);
  1557. if (S_OK == hr)
  1558. {
  1559. CComPtr<ITfRange> cpPropRange;
  1560. CComPtr<IUnknown> cpunk;
  1561. hr = cpProp->FindRange(ec, pRange, &cpPropRange, TF_ANCHOR_START);
  1562. if (S_OK == hr)
  1563. {
  1564. hr = GetUnknownPropertyData(ec, cpProp, cpPropRange, &cpunk);
  1565. }
  1566. if (S_OK == hr)
  1567. {
  1568. CRecoResultWrap *pwrap = (CRecoResultWrap *)(void *)cpunk;
  1569. pwrap->_SpeakAudio(0, 0);
  1570. }
  1571. }
  1572. // after exiting this edit session, caller needs to call m_cpMasterLM->Reconvert
  1573. *pfCallLMReconvert = TRUE;
  1574. return S_OK;
  1575. }
  1576. CComPtr<ISpRecognizer> cpEngine;
  1577. _GetSapilayrEngineInstance(&cpEngine);
  1578. if (cpEngine)
  1579. {
  1580. // We stop the engine deliberately here (whether active or not - cannot afford to check as
  1581. // we may get blocked by SAPI). This forces the engine into a synchronization since the audio
  1582. // stops and we are guaranteed to be able to display the candidate list object
  1583. cpEngine->SetRecoState(SPRST_INACTIVE_WITH_PURGE);
  1584. }
  1585. if (S_OK != (hr = _GetReconversion(ec, pic, pRange, &pCandList)))
  1586. return hr;
  1587. // voice playback
  1588. if ( m_pImx->_EnablePlaybackWhileCandUIOpen( ) )
  1589. {
  1590. if (m_psal)
  1591. m_psal->_Speak();
  1592. }
  1593. hr = ShowCandidateList(ec, pic, pRange, pCandList);
  1594. if (cpEngine)
  1595. {
  1596. // If the microphone is supposed to be open, we now restart the engine.
  1597. if (m_pImx->GetOnOff())
  1598. {
  1599. // We need to restart the engine now that we are fully initialized.
  1600. cpEngine->SetRecoState(SPRST_ACTIVE);
  1601. }
  1602. }
  1603. pCandList->Release();
  1604. return hr;
  1605. }
  1606. //+---------------------------------------------------------------------------
  1607. //
  1608. // CFnReconversion::ShowCandidateList
  1609. //
  1610. //----------------------------------------------------------------------------
  1611. HRESULT CFnReconversion::ShowCandidateList(TfEditCookie ec, ITfContext *pic, ITfRange *pRange, ITfCandidateList *pCandList)
  1612. {
  1613. // Determine if current range is vertical writing
  1614. CComPtr<ITfReadOnlyProperty> cpProperty;
  1615. VARIANT var;
  1616. BOOL fVertical = FALSE;
  1617. ULONG lDirection = 0;
  1618. if ( pic->GetAppProperty(TSATTRID_Text_VerticalWriting, &cpProperty) == S_OK )
  1619. {
  1620. if (cpProperty->GetValue(ec, pRange, &var) == S_OK )
  1621. {
  1622. fVertical = var.boolVal;
  1623. }
  1624. }
  1625. // Get the current text orientation.
  1626. cpProperty.Release( );
  1627. if ( pic->GetAppProperty(TSATTRID_Text_Orientation, &cpProperty) == S_OK )
  1628. {
  1629. if (cpProperty->GetValue(ec, pRange, &var) == S_OK )
  1630. {
  1631. lDirection = var.lVal;
  1632. }
  1633. }
  1634. // During the speech tip activation time, we just want to create candidateui object once for perf improvement.
  1635. if ( m_pImx->_pCandUIEx == NULL )
  1636. {
  1637. CoCreateInstance(CLSID_TFCandidateUI, NULL, CLSCTX_INPROC_SERVER, IID_ITfCandidateUI, (void**)&m_pImx->_pCandUIEx);
  1638. }
  1639. if ( m_pImx->_pCandUIEx )
  1640. {
  1641. ITfDocumentMgr *pdim;
  1642. if (SUCCEEDED(m_pImx->GetFocusDIM(&pdim)))
  1643. {
  1644. m_pImx->_pCandUIEx->SetClientId(m_pImx->_GetId());
  1645. ITfCandUIAutoFilterEventSink *pCuiFes = new CCandUIFilterEventSink(this, pic, m_pImx->_pCandUIEx);
  1646. CComPtr<ITfCandUIFnAutoFilter> cpFnFilter;
  1647. if (S_OK == m_pImx->_pCandUIEx->GetFunction(IID_ITfCandUIFnAutoFilter, (IUnknown **)&cpFnFilter))
  1648. {
  1649. cpFnFilter->Advise(pCuiFes);
  1650. cpFnFilter->Enable(TRUE);
  1651. }
  1652. //
  1653. // set the right font size for Japanese and Chinese case
  1654. //
  1655. CComPtr<ITfCandUICandString> cpITfCandUIObj;
  1656. if (S_OK == m_pImx->_pCandUIEx->GetUIObject(IID_ITfCandUICandString, (IUnknown **)&cpITfCandUIObj))
  1657. {
  1658. Assert(m_psal); // this shouldn't fail
  1659. if (m_psal)
  1660. {
  1661. LOGFONTW lf = {0};
  1662. if (m_psal->_GetUIFont(fVertical, &lf))
  1663. {
  1664. cpITfCandUIObj->SetFont(&lf);
  1665. }
  1666. }
  1667. }
  1668. //
  1669. // Set the candidate Ui window's style.
  1670. //
  1671. // Speech TIP always uses drop-down candidat window.
  1672. //
  1673. CComPtr<ITfCandUIFnUIConfig> cpFnUIConfig;
  1674. if (S_OK == m_pImx->_pCandUIEx->GetFunction(IID_ITfCandUIFnUIConfig, (IUnknown **)&cpFnUIConfig))
  1675. {
  1676. CANDUISTYLE style;
  1677. style = CANDUISTY_LIST;
  1678. cpFnUIConfig->SetUIStyle(pic, style);
  1679. }
  1680. //
  1681. // Set the candidate UI window's direction.
  1682. //
  1683. CComPtr<ITfCandUICandWindow> cpUICandWnd;
  1684. if ( S_OK == m_pImx->_pCandUIEx->GetUIObject(IID_ITfCandUICandWindow, (IUnknown **)&cpUICandWnd) )
  1685. {
  1686. CANDUIUIDIRECTION dwOption = CANDUIDIR_TOPTOBOTTOM;
  1687. switch ( lDirection )
  1688. {
  1689. case 900 : // Text direction Bottom to Top
  1690. dwOption = CANDUIDIR_LEFTTORIGHT;
  1691. break;
  1692. case 1800 : // Text direction Right to Left.
  1693. dwOption = CANDUIDIR_BOTTOMTOTOP;
  1694. break;
  1695. case 2700 : // Text direction Top to Bottom.
  1696. dwOption = dwOption = CANDUIDIR_RIGHTTOLEFT;
  1697. break;
  1698. default :
  1699. dwOption = CANDUIDIR_TOPTOBOTTOM;
  1700. break;
  1701. }
  1702. cpUICandWnd->SetUIDirection(dwOption);
  1703. }
  1704. m_pImx->_pCandUIEx->SetCandidateList(pCandList);
  1705. // Before Open Candidate UI window, we want to save the current IP
  1706. m_pImx->_SaveCorrectOrgIP(ec, pic);
  1707. m_pImx->_pCandUIEx->OpenCandidateUI(NULL, pdim, ec, pRange);
  1708. pCuiFes->Release();
  1709. pdim->Release();
  1710. }
  1711. }
  1712. return S_OK;
  1713. }
  1714. //+---------------------------------------------------------------------------
  1715. //
  1716. // CFnReconversion::SetResult
  1717. //
  1718. //----------------------------------------------------------------------------
  1719. HRESULT CFnReconversion::SetResult(ITfContext *pic, ITfRange *pRange, CCandidateString *pCand, TfCandidateResult imcr)
  1720. {
  1721. BSTR bstr;
  1722. HRESULT hr = S_OK;
  1723. CFnReconversion *pReconv = (CFnReconversion *)(pCand->_punk);
  1724. if ((imcr == CAND_FINALIZED) || (imcr == CAND_SELECTED))
  1725. {
  1726. pCand->GetString(&bstr);
  1727. // TODO: here we have to re-calc the range based on the strart element points
  1728. // which are indicated from AltInfo.
  1729. ULONG ulParentStart = 0;
  1730. ULONG ulcParentElements = 0;
  1731. ULONG ulIndex = 0;
  1732. ULONG cchParentStart = 0;
  1733. ULONG cchParentReplace = 0;
  1734. BOOL fNoAlternate = FALSE;
  1735. CSapiAlternativeList *psal = (CSapiAlternativeList *)pCand->_pv;
  1736. pCand->GetIndex(&ulIndex);
  1737. if (psal)
  1738. {
  1739. CRecoResultWrap *cpRecoWrap;
  1740. ULONG ulStartElement;
  1741. ULONG ulLeadSpaceRemoved = 0;
  1742. // save current selection index.
  1743. psal->_SaveCurrentSelectionIndex(ulIndex);
  1744. cpRecoWrap = psal->GetResultWrap();
  1745. ulStartElement = cpRecoWrap->GetStart( );
  1746. psal->GetCachedAltInfo(ulIndex, &ulParentStart, &ulcParentElements, NULL, NULL, &ulLeadSpaceRemoved);
  1747. cchParentStart = cpRecoWrap->_GetElementOffsetCch(ulParentStart);
  1748. cchParentReplace = cpRecoWrap->_GetElementOffsetCch(ulParentStart + ulcParentElements) - cchParentStart;
  1749. cchParentStart = cchParentStart - cpRecoWrap->_GetElementOffsetCch(ulStartElement) + cpRecoWrap->_GetOffsetDelta( );
  1750. if ( ulLeadSpaceRemoved > 0 && cchParentStart > ulLeadSpaceRemoved)
  1751. {
  1752. cchParentStart -= ulLeadSpaceRemoved;
  1753. cchParentReplace += ulLeadSpaceRemoved;
  1754. }
  1755. fNoAlternate = psal->_IsNoAlternate( );
  1756. if ( !fNoAlternate )
  1757. {
  1758. hr = pReconv->m_pImx->SetReplaceSelection(pRange, cchParentStart, cchParentReplace, pic);
  1759. if ( (SUCCEEDED(hr)) && (imcr == CAND_FINALIZED) )
  1760. {
  1761. if ( ulParentStart + ulcParentElements == ulStartElement + cpRecoWrap->GetNumElements( ) )
  1762. {
  1763. // The parent selection contains the last element.
  1764. // if its trailing spaces were removed before, we also want to
  1765. // remove the same number of trailing spaces in the new alternative text.
  1766. // We already considered this case during _Commit( ).
  1767. // We just need to update the result text which wil be injected.
  1768. ULONG ulTSRemoved;
  1769. ulTSRemoved = cpRecoWrap->GetTrailSpaceRemoved( );
  1770. if ( ulTSRemoved > 0 )
  1771. {
  1772. ULONG ulTextLen;
  1773. ULONG ulRemovedNew = 0;
  1774. ulTextLen = wcslen(bstr);
  1775. for ( ULONG i=ulTextLen-1; (int)i>=0 && ulRemovedNew <= ulTSRemoved; i-- )
  1776. {
  1777. if ( bstr[i] == L' ' )
  1778. {
  1779. bstr[i] = L'\0';
  1780. ulRemovedNew ++;
  1781. }
  1782. else
  1783. break;
  1784. }
  1785. if ( ulRemovedNew < ulTSRemoved )
  1786. cpRecoWrap->SetTrailSpaceRemoved( ulRemovedNew );
  1787. }
  1788. }
  1789. pReconv->_Commit(pCand);
  1790. // If the first element in this RecoWrap is updated by the new alternate
  1791. // speech tip needs to check if this new alternate wants to
  1792. // consume the leading space or if extra space is required to add
  1793. // between this phrase and previous phrase.
  1794. //
  1795. BOOL bHandleLeadingSpace = (ulParentStart == ulStartElement) ? TRUE : FALSE;
  1796. hr = pReconv->m_pImx->InjectAlternateText(bstr, pReconv->m_langid, pic, bHandleLeadingSpace);
  1797. //
  1798. // Update the Selection grammar's text buffer.
  1799. //
  1800. if ( SUCCEEDED(hr) && pReconv->m_pImx )
  1801. {
  1802. CSpTask *psp = NULL;
  1803. (pReconv->m_pImx)->GetSpeechTask(&psp);
  1804. if ( psp )
  1805. {
  1806. hr = psp->_UpdateSelectGramTextBufWhenStatusChanged( );
  1807. psp->Release( );
  1808. }
  1809. }
  1810. //
  1811. }
  1812. }
  1813. }
  1814. SysFreeString(bstr);
  1815. }
  1816. // close candidate UI if it's still there
  1817. if (imcr == CAND_FINALIZED || imcr == CAND_CANCELED)
  1818. {
  1819. // Just close the candidate UI, don't release the object, so that the object keeps alive while the
  1820. // speech tip is activated, this is for performance improvement.
  1821. pReconv->m_pImx->CloseCandUI( );
  1822. if ( imcr == CAND_CANCELED )
  1823. {
  1824. // Just release the stored IP to avoid memory leak.
  1825. // Don't restore it according to the new spec so that
  1826. // user can continue to dictate new text over the selection.
  1827. //
  1828. // If we find this is not a good Usuability,
  1829. // we can change it back to the original behavior.
  1830. pReconv->m_pImx->_ReleaseCorrectOrgIP( );
  1831. }
  1832. }
  1833. return hr;
  1834. }
  1835. //+---------------------------------------------------------------------------
  1836. //
  1837. // CFnReconversion::GetTabletTip
  1838. //
  1839. //----------------------------------------------------------------------------
  1840. HRESULT CFnReconversion::GetTabletTip(void)
  1841. {
  1842. HRESULT hr = S_OK;
  1843. CComPtr<IUnknown> cpunk;
  1844. if (m_cpTabletTip)
  1845. {
  1846. m_cpTabletTip = NULL; // Releases our reference.
  1847. }
  1848. hr = CoCreateInstance(CLSID_UIHost, NULL, CLSCTX_LOCAL_SERVER, IID_IUnknown, (void **) &cpunk);
  1849. if (SUCCEEDED(hr))
  1850. {
  1851. hr = cpunk->QueryInterface(IID_ITipWindow, (void **) &m_cpTabletTip);
  1852. }
  1853. return hr;
  1854. }
  1855. //+---------------------------------------------------------------------------
  1856. //
  1857. // CFnReconversion::SetOption
  1858. //
  1859. //----------------------------------------------------------------------------
  1860. HRESULT CFnReconversion::SetOption(ITfContext *pic, ITfRange *pRange, CCandidateString *pCand, TfCandidateResult imcr)
  1861. {
  1862. HRESULT hr = S_OK;
  1863. CFnReconversion *pReconv = (CFnReconversion *)(pCand->_punk);
  1864. if (imcr == CAND_FINALIZED)
  1865. {
  1866. ULONG ulID = 0;
  1867. pCand->GetID(&ulID);
  1868. switch (ulID)
  1869. {
  1870. case OPTION_REPLAY:
  1871. {
  1872. // Replay audio. Do not close candidate list.
  1873. CSapiAlternativeList *psal;
  1874. psal = pReconv->GetCSapiAlternativeList( );
  1875. if ( psal )
  1876. {
  1877. psal->_Speak( );
  1878. }
  1879. break;
  1880. }
  1881. case OPTION_DELETE: // Delete from dictinary...
  1882. {
  1883. // Close candidate UI.
  1884. pReconv->m_pImx->_pCandUIEx->CloseCandidateUI();
  1885. // Delete the current selection in the document.
  1886. pReconv->m_pImx->HandleKey(VK_DELETE);
  1887. break;
  1888. }
  1889. case OPTION_REDO: // Tablet PC specific option.
  1890. {
  1891. // Close candidate UI.
  1892. pReconv->m_pImx->_pCandUIEx->CloseCandidateUI();
  1893. if (pReconv->m_cpTabletTip == NULL)
  1894. {
  1895. pReconv->GetTabletTip();
  1896. }
  1897. if (pReconv->m_cpTabletTip)
  1898. {
  1899. hr = pReconv->m_cpTabletTip->ShowWnd(VARIANT_TRUE);
  1900. if (FAILED(hr))
  1901. {
  1902. // Reget TabletTip and try a second time in case the previous instance was killed.
  1903. hr = pReconv->GetTabletTip();
  1904. if (SUCCEEDED(hr))
  1905. {
  1906. hr = pReconv->m_cpTabletTip->ShowWnd(VARIANT_TRUE);
  1907. }
  1908. }
  1909. }
  1910. break;
  1911. }
  1912. }
  1913. }
  1914. // close candidate UI if it's still there
  1915. if (imcr == CAND_CANCELED)
  1916. {
  1917. if (pReconv->m_pImx->_pCandUIEx)
  1918. {
  1919. pReconv->m_pImx->_pCandUIEx->CloseCandidateUI();
  1920. }
  1921. }
  1922. return hr;
  1923. }
  1924. //
  1925. // _Commit
  1926. //
  1927. // synopisis: accept the candidate string as the final selection and
  1928. // let SR know the decision has been made
  1929. //
  1930. void CFnReconversion::_Commit(CCandidateString *pcand)
  1931. {
  1932. ULONG nIdx;
  1933. Assert(pcand);
  1934. if (S_OK == pcand->GetIndex(&nIdx))
  1935. {
  1936. // let CSapiAlternativeList class do the real work
  1937. if (m_psal)
  1938. m_psal->_Commit(nIdx, m_cpRecoResult);
  1939. // we no longer need to hold the reco result
  1940. m_cpRecoResult.Release();
  1941. }
  1942. }
  1943. ULONG CBestPropRange::_GetMaxCandidateChars( )
  1944. {
  1945. if ( m_MaxCandChars == 0 )
  1946. {
  1947. // it is not initialized
  1948. CMyRegKey regkey;
  1949. DWORD dw = MAX_CANDIDATE_CHARS;
  1950. if (S_OK == regkey.Open(HKEY_LOCAL_MACHINE, c_szSapilayrKey, KEY_READ))
  1951. {
  1952. regkey.QueryValue(dw, c_szMaxCandChars);
  1953. }
  1954. if ( (dw > MAX_CANDIDATE_CHARS) || (dw == 0) )
  1955. dw = MAX_CANDIDATE_CHARS;
  1956. m_MaxCandChars = dw;
  1957. }
  1958. return m_MaxCandChars;
  1959. }
  1960. //
  1961. // FindVisiblePropertyRange
  1962. //
  1963. // Searches for a property range, skipping over any "empty" (containing only hidden text)
  1964. // property spans along the way.
  1965. //
  1966. // We can encounter hidden property spans (zero-length from a tip's point of view) if the
  1967. // user marks some dictated text as hidden in word.
  1968. HRESULT FindVisiblePropertyRange(TfEditCookie ec, ITfProperty *pProperty, ITfRange *pTestRange, ITfRange **ppPropertyRange)
  1969. {
  1970. BOOL fEmpty;
  1971. HRESULT hr;
  1972. while (TRUE)
  1973. {
  1974. hr = pProperty->FindRange(ec, pTestRange, ppPropertyRange, TF_ANCHOR_START);
  1975. if (hr != S_OK)
  1976. break;
  1977. if ((*ppPropertyRange)->IsEmpty(ec, &fEmpty) != S_OK)
  1978. {
  1979. hr = E_FAIL;
  1980. break;
  1981. }
  1982. if (!fEmpty)
  1983. break;
  1984. // found an empty property span
  1985. // this means it contains only hidden text, so skip it
  1986. if (pTestRange->ShiftStartToRange(ec, *ppPropertyRange, TF_ANCHOR_END) != S_OK)
  1987. {
  1988. hr = E_FAIL;
  1989. break;
  1990. }
  1991. (*ppPropertyRange)->Release();
  1992. }
  1993. if (hr != S_OK)
  1994. {
  1995. *ppPropertyRange = NULL;
  1996. }
  1997. return hr;
  1998. }
  1999. //
  2000. // _ComputeBestFitPropRange
  2001. //
  2002. // synopsis: returns the range that includes at least one SPPHRASE element
  2003. // which also includes the specified (incoming) range
  2004. // *pulStart should include the start element used in the reco result
  2005. // *pulcElem should include the # of elements used
  2006. //
  2007. HRESULT CBestPropRange::_ComputeBestFitPropRange
  2008. (
  2009. TfEditCookie ec,
  2010. ITfProperty *pProp,
  2011. ITfRange *pRangeIn,
  2012. ITfRange **ppBestPropRange,
  2013. ULONG *pulStart,
  2014. ULONG *pulcElem
  2015. )
  2016. {
  2017. HRESULT hr = E_FAIL;
  2018. CComPtr<ITfRange> cpPropRange ;
  2019. CComPtr<IUnknown> cpunk;
  2020. ULONG ucch;
  2021. BOOL fBeyondPropRange = FALSE;
  2022. TraceMsg(TF_GENERAL, "_ComputeBestFitPropRange is called");
  2023. // find the reco result with a span that includes the given range
  2024. Assert(pProp);
  2025. Assert(pRangeIn);
  2026. CComPtr<ITfRange> cpRange ;
  2027. hr = pRangeIn->Clone(&cpRange);
  2028. if (S_OK == hr)
  2029. {
  2030. hr = FindVisiblePropertyRange(ec, pProp, cpRange, &cpPropRange);
  2031. }
  2032. if ( hr == S_FALSE )
  2033. {
  2034. // if this is not a selection and the IP is at the last position of this region, we just try to reconvert on the possible previous
  2035. // dictated phrase.
  2036. BOOL fTryPreviousPhrase = FALSE;
  2037. BOOL fEmpty = FALSE;
  2038. // Add code here to check it meets the condition.
  2039. if ( S_OK == cpRange->IsEmpty(ec, &fEmpty) && fEmpty )
  2040. {
  2041. CComPtr<ITfRange> cpRangeTmp;
  2042. if ( S_OK == cpRange->Clone(&cpRangeTmp) )
  2043. {
  2044. LONG cch = 0;
  2045. if ( (S_OK == cpRangeTmp->ShiftStart(ec, 1, &cch, NULL)) && (cch < 1) )
  2046. {
  2047. // it is at the end of a region or entire document.
  2048. fTryPreviousPhrase = TRUE;
  2049. }
  2050. }
  2051. }
  2052. if ( fTryPreviousPhrase )
  2053. {
  2054. LONG cch;
  2055. hr = cpRange->ShiftStart(ec, -1, &cch, NULL);
  2056. if ( hr == S_OK )
  2057. {
  2058. hr = cpRange->Collapse(ec, TF_ANCHOR_START);
  2059. }
  2060. if ( hr == S_OK )
  2061. {
  2062. hr = FindVisiblePropertyRange(ec, pProp, cpRange, &cpPropRange);
  2063. }
  2064. }
  2065. }
  2066. // get a wrapper for the prop range
  2067. if (S_OK == hr)
  2068. {
  2069. hr = GetUnknownPropertyData(ec, pProp, cpPropRange, &cpunk);
  2070. }
  2071. if ((hr == S_OK) && cpunk)
  2072. {
  2073. // first calculate the # of chars upto the start anchor of the given range
  2074. //
  2075. CComPtr<ITfRange> cpClonedPropRange;
  2076. if (S_OK == hr)
  2077. {
  2078. hr = cpPropRange->Clone(&cpClonedPropRange);
  2079. }
  2080. if (S_OK == hr)
  2081. {
  2082. hr = cpClonedPropRange->ShiftEndToRange(ec, cpRange, TF_ANCHOR_START);
  2083. }
  2084. ULONG ulCchToSelection = 0;
  2085. ULONG ulCchInSelection = 0;
  2086. BOOL fEmpty;
  2087. if (S_OK == hr)
  2088. {
  2089. CSpDynamicString dstr;
  2090. while(S_OK == hr && (S_OK == cpClonedPropRange->IsEmpty(ec, &fEmpty)) && !fEmpty)
  2091. {
  2092. WCHAR sz[64];
  2093. hr = cpClonedPropRange->GetText(ec, TF_TF_MOVESTART, sz, ARRAYSIZE(sz)-1, &ucch);
  2094. if (S_OK == hr)
  2095. {
  2096. sz[ucch] = L'\0';
  2097. dstr.Append(sz);
  2098. }
  2099. }
  2100. ulCchToSelection = dstr.Length();
  2101. }
  2102. // then calc the # of chars upto the end anchor of the given range
  2103. if(S_OK == hr)
  2104. {
  2105. hr = cpRange->IsEmpty(ec, &fEmpty);
  2106. if (S_OK == hr && !fEmpty)
  2107. {
  2108. CComPtr<ITfRange> cpClonedGivenRange;
  2109. hr = cpRange->Clone(&cpClonedGivenRange);
  2110. // compare the end of the given range and proprange,
  2111. // if the given range goes beyond proprange, snap it
  2112. // within the proprange
  2113. if (S_OK == hr)
  2114. {
  2115. LONG lResult;
  2116. hr = cpClonedGivenRange->CompareEnd(ec, cpPropRange, TF_ANCHOR_END, &lResult);
  2117. if (S_OK == hr && lResult > 0)
  2118. {
  2119. // the end of the given range is beyond the proprange
  2120. // we need to snap it before getting text
  2121. hr = cpClonedGivenRange->ShiftEndToRange(ec, cpPropRange, TF_ANCHOR_END);
  2122. fBeyondPropRange = TRUE;
  2123. }
  2124. // now we get the text we use to calc the # of elements
  2125. CSpDynamicString dstr;
  2126. while(S_OK == hr && (S_OK == cpClonedGivenRange->IsEmpty(ec, &fEmpty)) && !fEmpty)
  2127. {
  2128. WCHAR sz[64];
  2129. hr = cpClonedGivenRange->GetText(ec, TF_TF_MOVESTART, sz, ARRAYSIZE(sz)-1, &ucch);
  2130. if (S_OK == hr)
  2131. {
  2132. sz[ucch] = L'\0';
  2133. dstr.Append(sz);
  2134. }
  2135. }
  2136. ulCchInSelection = dstr.Length();
  2137. // If there are some spaces in the beginning of the selection,
  2138. // we need to shift the start of the selection to the next non-space character.
  2139. if ( ulCchInSelection > 0 )
  2140. {
  2141. WCHAR *pStr;
  2142. pStr = (WCHAR *)dstr;
  2143. while ( (*pStr == L' ') || (*pStr == L'\t'))
  2144. {
  2145. ulCchInSelection --;
  2146. ulCchToSelection ++;
  2147. pStr ++;
  2148. }
  2149. if ( *pStr == L'\0' )
  2150. {
  2151. // This selection contains only spaces. no other non-space character.
  2152. // we don't want to get alternate for this selection.
  2153. // just return here.
  2154. if (ppBestPropRange != NULL )
  2155. *ppBestPropRange = NULL;
  2156. if ( pulStart != NULL )
  2157. *pulStart = 0;
  2158. if (pulcElem != NULL )
  2159. *pulcElem = 0;
  2160. return S_FALSE;
  2161. }
  2162. }
  2163. }
  2164. }
  2165. }
  2166. // get the result object cpunk points to our wrapper object
  2167. CComPtr<IServiceProvider> cpServicePrv;
  2168. CComPtr<ISpRecoResult> cpResult;
  2169. SPPHRASE *pPhrases = NULL;
  2170. CRecoResultWrap *pResWrap = NULL;
  2171. if (S_OK == hr)
  2172. {
  2173. hr = cpunk->QueryInterface(IID_IServiceProvider, (void **)&cpServicePrv);
  2174. }
  2175. // get result object
  2176. if (S_OK == hr)
  2177. {
  2178. hr = cpServicePrv->QueryService(GUID_NULL, IID_ISpRecoResult, (void **)&cpResult);
  2179. }
  2180. // now we can see how many elements we can use
  2181. if (S_OK == hr)
  2182. {
  2183. hr = cpResult->GetPhrase(&pPhrases);
  2184. }
  2185. if (S_OK == hr)
  2186. {
  2187. hr = cpunk->QueryInterface(IID_PRIV_RESULTWRAP, (void **)&pResWrap);
  2188. }
  2189. if (S_OK == hr && pPhrases)
  2190. {
  2191. // calc the start anchor of the new range
  2192. #ifdef NOUSEELEMENTOFFSET
  2193. CSpDynamicString dstr;
  2194. #endif
  2195. long cchToElem_i = 0;
  2196. long cchAfterElem_i = 0;
  2197. BOOL fStartFound = FALSE;
  2198. ULONG i;
  2199. ULONG ulNumElements;
  2200. CComPtr<ITfRange> cpNewRange;
  2201. hr = cpRange->Clone(&cpNewRange);
  2202. if ( fBeyondPropRange )
  2203. {
  2204. hr = cpNewRange->ShiftEndToRange(ec, cpPropRange, TF_ANCHOR_END);
  2205. }
  2206. if ( ulCchInSelection > _GetMaxCandidateChars( ) )
  2207. {
  2208. // If the selection has more than MaxCandidate Chars, we need to shift the range end
  2209. // to left so that it contains at most MaxCandidate Chars in the selection.
  2210. long cch;
  2211. cch = (long)_GetMaxCandidateChars( ) - (long)ulCchInSelection;
  2212. ulCchInSelection = _GetMaxCandidateChars( );
  2213. cpNewRange->ShiftEnd(ec, cch, &cch, NULL);
  2214. }
  2215. ulNumElements = pResWrap->GetNumElements();
  2216. // get start element and # of elements via wrapper object
  2217. if ((S_OK == hr) && ulNumElements > 0 )
  2218. {
  2219. ULONG ulStart;
  2220. ULONG ulEnd;
  2221. ULONG ulOffsetStart;
  2222. ULONG ulDelta;
  2223. ulStart = pResWrap->GetStart();
  2224. ulEnd = ulStart + pResWrap->GetNumElements() - 1;
  2225. ulDelta = pResWrap->_GetOffsetDelta( );
  2226. ulOffsetStart = pResWrap->_GetElementOffsetCch(ulStart);
  2227. for (i = ulStart; i <= ulEnd; i++ )
  2228. {
  2229. #ifdef NOUSEELEMENTOFFSET
  2230. // CleanupConsider: replace this logic with pResWrap->GetElementOffsets(i)
  2231. // where we cache the calculated offsets
  2232. //
  2233. if (pPhrases->pElements[i].pszDisplayText)
  2234. {
  2235. cchToElem_i = dstr.Length();
  2236. dstr.Append(pPhrases->pElements[i].pszDisplayText);
  2237. if (pPhrases->pElements[i].bDisplayAttributes & SPAF_ONE_TRAILING_SPACE)
  2238. {
  2239. dstr.Append(L" ");
  2240. }
  2241. else if (pPhrases->pElements[i].bDisplayAttributes & SPAF_TWO_TRAILING_SPACES)
  2242. {
  2243. dstr.Append(L" ");
  2244. }
  2245. cchAfterElem_i = dstr.Length();
  2246. }
  2247. else
  2248. break;
  2249. #else
  2250. // when i < # of elements, it's guaranteed that we have n = i + 1
  2251. //
  2252. cchToElem_i = pResWrap->_GetElementOffsetCch(i) - ulOffsetStart + ulDelta;
  2253. cchAfterElem_i = pResWrap->_GetElementOffsetCch(i+1) - ulOffsetStart + ulDelta;
  2254. #endif
  2255. if ( ulCchInSelection == 0 )
  2256. {
  2257. // we need to specially handle this case that no character is in selection
  2258. // user just puts a cursor right before a character.
  2259. // We just want to find out which element would contain this IP.
  2260. // and then shift anchors to this element's start and end position.
  2261. if ( (ULONG)cchAfterElem_i > ulCchToSelection )
  2262. {
  2263. // This element is the right element to contain this IP.
  2264. long cch;
  2265. // this is usually reverse shifting
  2266. // Shift the start anchor to this element's start position.
  2267. cpNewRange->ShiftStart(ec, cchToElem_i - ulCchToSelection, &cch, NULL);
  2268. // store the starting element used
  2269. TraceMsg(TF_GENERAL, "Start element = %d", i);
  2270. if (pulStart)
  2271. {
  2272. *pulStart = i;
  2273. }
  2274. // Shift the end anchor to this element's end position.
  2275. cpNewRange->ShiftEnd(ec,
  2276. cchAfterElem_i - ulCchToSelection,
  2277. &cch, NULL);
  2278. TraceMsg(TF_GENERAL, "End Element = %d", i);
  2279. break;
  2280. }
  2281. }
  2282. else
  2283. {
  2284. // 1) shift the start anchor of prop range based on the element offsets of
  2285. // the result object, comparing it with the start anchor (ulCchToSelection)
  2286. // of the given range
  2287. // - choose the start elements that is right before the start anchor.
  2288. //
  2289. if ((ULONG)cchAfterElem_i > ulCchToSelection && !fStartFound)
  2290. {
  2291. long cch;
  2292. // this is usually reverse shifting
  2293. cpNewRange->ShiftStart(ec, cchToElem_i - ulCchToSelection, &cch, NULL);
  2294. // store the starting element used
  2295. TraceMsg(TF_GENERAL, "Start element = %d", i);
  2296. if (pulStart)
  2297. {
  2298. *pulStart = i;
  2299. }
  2300. fStartFound = TRUE;
  2301. }
  2302. // 2) shift the end anchor of prop range based on the the element offset
  2303. // and the # of elements of result object,
  2304. // comparing it with the end anchor of the given range (ulCchToSelection+ulCchInSelection)
  2305. // - the element so the span ends right after the end anchor of the given range.
  2306. //
  2307. if ((ULONG)cchAfterElem_i >= ulCchToSelection + ulCchInSelection)
  2308. {
  2309. long cch;
  2310. if ( ulCchInSelection >= _GetMaxCandidateChars( ) )
  2311. {
  2312. // The selection contains MaxCand chars, we should make sure the char number
  2313. // in new proprange less than MaxCand.
  2314. if ( (ULONG)cchAfterElem_i > ulCchToSelection + ulCchInSelection )
  2315. {
  2316. // if keeping this element, the total char number will larger than MaxCand.
  2317. // So use the previous element as the last element.
  2318. if ( i > ulStart ) // This conditon should always be true.
  2319. {
  2320. i--;
  2321. cchAfterElem_i = pResWrap->_GetElementOffsetCch(i+1) - ulOffsetStart + ulDelta;
  2322. }
  2323. }
  2324. }
  2325. cpNewRange->ShiftEnd(ec,
  2326. cchAfterElem_i - (ulCchToSelection + ulCchInSelection),
  2327. &cch, NULL);
  2328. TraceMsg(TF_GENERAL, "End Element = %d", i);
  2329. break;
  2330. }
  2331. }
  2332. }
  2333. if (pulcElem && pulStart)
  2334. {
  2335. // we need to check if the current selection contains any ITN range.
  2336. // If it contains ITN range, we need to change the start and num of elements if
  2337. // the start element or end element is inside an ITN range.
  2338. BOOL fInsideITN;
  2339. ULONG ulITNStart, ulITNNumElem;
  2340. ULONG ulEndElem;
  2341. ulEndElem = i; // Current end element
  2342. if ( i > ulEnd )
  2343. ulEndElem = ulEnd;
  2344. fInsideITN = pResWrap->_CheckITNForElement(NULL, *pulStart, &ulITNStart, &ulITNNumElem, NULL );
  2345. if ( fInsideITN && (ulITNStart < *pulStart) )
  2346. *pulStart = ulITNStart;
  2347. fInsideITN = pResWrap->_CheckITNForElement(NULL, ulEndElem, &ulITNStart, &ulITNNumElem, NULL );
  2348. if ( fInsideITN && ulEndElem < (ulITNStart + ulITNNumElem - 1) )
  2349. ulEndElem = ulITNStart + ulITNNumElem - 1;
  2350. *pulcElem = ulEndElem - *pulStart + 1;
  2351. TraceMsg(TF_GENERAL, "Final Best Range: start=%d num=%d", *pulStart, *pulcElem);
  2352. }
  2353. }
  2354. CoTaskMemFree( pPhrases );
  2355. if ( ulNumElements > 0 )
  2356. {
  2357. Assert(cpNewRange);
  2358. Assert(ppBestPropRange);
  2359. *ppBestPropRange = cpNewRange;
  2360. (*ppBestPropRange)->AddRef();
  2361. }
  2362. else
  2363. hr = S_FALSE;
  2364. }
  2365. SafeRelease(pResWrap);
  2366. }
  2367. return hr;
  2368. }
  2369. //
  2370. //
  2371. // CCandUIFilterEventSink
  2372. //
  2373. //
  2374. STDMETHODIMP CCandUIFilterEventSink::QueryInterface(REFIID riid, void **ppvObj)
  2375. {
  2376. *ppvObj = NULL;
  2377. if (IsEqualIID(riid, IID_IUnknown) ||
  2378. IsEqualIID(riid, IID_ITfCandUIAutoFilterEventSink))
  2379. {
  2380. *ppvObj = SAFECAST(this, CCandUIFilterEventSink *);
  2381. }
  2382. if (*ppvObj)
  2383. {
  2384. AddRef();
  2385. return S_OK;
  2386. }
  2387. return E_NOINTERFACE;
  2388. }
  2389. STDMETHODIMP_(ULONG) CCandUIFilterEventSink::AddRef(void)
  2390. {
  2391. return ++m_cRef;
  2392. }
  2393. STDMETHODIMP_(ULONG) CCandUIFilterEventSink::Release(void)
  2394. {
  2395. long cr;
  2396. cr = --m_cRef;
  2397. Assert(cr >= 0);
  2398. if (cr == 0)
  2399. {
  2400. delete this;
  2401. }
  2402. return cr;
  2403. }
  2404. HRESULT CCandUIFilterEventSink::OnFilterEvent(CANDUIFILTEREVENT ev)
  2405. {
  2406. HRESULT hr = S_OK;
  2407. // Temporally comment out the below code to fix bug 4777.
  2408. //
  2409. // To fully support the specification of filter feature, we need to change more code
  2410. // in SetFilterString( ) to use the correct parent range in current document so that the filter
  2411. // string is injected to a correct position.
  2412. //
  2413. // We also want to change code to restore the original document text when the canidate UI is
  2414. // cancelled.
  2415. //
  2416. //
  2417. // if (ev == CANDUIFEV_UPDATED)
  2418. if ( ev == CANDUIFEV_NONMATCH )
  2419. {
  2420. // When we got non-matching notification, we need to inject the previous filter string to the document.
  2421. if (m_pfnReconv)
  2422. {
  2423. Assert(m_pfnReconv);
  2424. Assert(m_pfnReconv->m_pImx);
  2425. ESDATA esData;
  2426. memset(&esData, 0, sizeof(ESDATA));
  2427. esData.pUnk = (IUnknown *)m_pCandUI;
  2428. m_pfnReconv->m_pImx->_RequestEditSession(ESCB_UPDATEFILTERSTR,TF_ES_READWRITE, &esData, m_pic);
  2429. }
  2430. }
  2431. return hr; // looks like S_OK is expected anyways
  2432. }
  2433. /* this filter event is no longer used.
  2434. HRESULT CCandUIFilterEventSink::OnAddCharToFilterStringEvent(CANDUIFILTEREVENT ev, WCHAR wch, int nItemVisible, BOOL *bEten)
  2435. {
  2436. HRESULT hr = S_OK;
  2437. if ( (bEten == NULL) || (ev != CANDUIFEV_ADDCHARTOFILTER))
  2438. return E_INVALIDARG;
  2439. *bEten = FALSE;
  2440. if ( nItemVisible == 0 )
  2441. {
  2442. if ( (wch <= L'9') && (wch >= L'1') )
  2443. {
  2444. // we need to select the speified candidate text.
  2445. // if candidate UI is open, we need to select the right alternate.
  2446. if ( m_pCandUI )
  2447. {
  2448. ULONG ulIndex;
  2449. ulIndex = wch - L'0';
  2450. m_pCandUI->ProcessCommand(CANDUICMD_SELECTLINE, ulIndex);
  2451. }
  2452. *bEten = TRUE;
  2453. }
  2454. else if ( wch == L' ' )
  2455. {
  2456. if ( m_pCandUI )
  2457. {
  2458. m_pCandUI->ProcessCommand(CANDUICMD_MOVESELNEXT, 0);
  2459. }
  2460. *bEten = TRUE;
  2461. }
  2462. }
  2463. return hr;
  2464. }
  2465. */