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.

859 lines
23 KiB

  1. //
  2. // Audio playback function impl.
  3. //
  4. //
  5. #include "private.h"
  6. #include "sapilayr.h"
  7. #include "playback.h"
  8. #include "immxutil.h"
  9. #include "propstor.h"
  10. #include "hwxink.h"
  11. //
  12. // ctor/dtor
  13. //
  14. CSapiPlayBack::CSapiPlayBack(CSapiIMX *psi)
  15. {
  16. m_psi = psi;
  17. m_pIC = NULL;
  18. m_cRef = 1;
  19. }
  20. CSapiPlayBack::~CSapiPlayBack()
  21. {
  22. SafeRelease(m_pIC);
  23. }
  24. //
  25. // IUnknown
  26. //
  27. STDMETHODIMP CSapiPlayBack::QueryInterface(REFGUID riid, LPVOID *ppvObj)
  28. {
  29. Assert(ppvObj);
  30. *ppvObj = NULL;
  31. if (IsEqualIID(riid, IID_IUnknown) ||
  32. IsEqualIID(riid, IID_ITfFnPlayBack))
  33. {
  34. *ppvObj = SAFECAST(this, CSapiPlayBack *);
  35. }
  36. if (*ppvObj)
  37. {
  38. AddRef();
  39. return S_OK;
  40. }
  41. return E_NOINTERFACE;
  42. }
  43. STDMETHODIMP_(ULONG) CSapiPlayBack::AddRef(void)
  44. {
  45. return InterlockedIncrement(&m_cRef);
  46. }
  47. STDMETHODIMP_(ULONG) CSapiPlayBack::Release(void)
  48. {
  49. long cr;
  50. cr = InterlockedDecrement(&m_cRef);
  51. Assert(cr >= 0);
  52. if (cr == 0)
  53. {
  54. delete this;
  55. }
  56. return cr;
  57. }
  58. //
  59. // ITfFunction
  60. //
  61. STDMETHODIMP CSapiPlayBack::GetDisplayName(BSTR *pbstrName)
  62. {
  63. HRESULT hr = E_INVALIDARG;
  64. if (pbstrName)
  65. {
  66. *pbstrName = SysAllocString(L"PlayBack Voice");
  67. if (!*pbstrName)
  68. hr = E_OUTOFMEMORY;
  69. else
  70. hr = S_OK;
  71. }
  72. return hr;
  73. }
  74. STDMETHODIMP CSapiPlayBack::IsEnabled(BOOL *pfEnable)
  75. {
  76. *pfEnable = TRUE;
  77. return S_OK;
  78. }
  79. //
  80. // CSapiPlayBack::FindSoundRange
  81. //
  82. // synopsis - finds matching range with sound data for the given
  83. // text range
  84. // callar is responsible for releasing the returned range object
  85. //
  86. HRESULT
  87. CSapiPlayBack::FindSoundRange(TfEditCookie ec, ITfRange *pRange, ITfProperty **ppProp, ITfRange **ppPropRange, ITfRange **ppSndRange)
  88. {
  89. ITfProperty *pProp = NULL;
  90. ITfRange *pPropRange = NULL;
  91. Assert(pRange);
  92. *ppProp = NULL;
  93. HRESULT hr = m_pIC->GetProperty(GUID_PROP_SAPIRESULTOBJECT, &pProp);
  94. if (SUCCEEDED(hr) && pProp)
  95. {
  96. ITfRange *pRangeSize0;
  97. pRange->Clone(&pRangeSize0);
  98. pRangeSize0->Collapse(ec, TF_ANCHOR_START); // findrange would fail if this failed
  99. hr = pProp->FindRange(ec, pRangeSize0, &pPropRange, TF_ANCHOR_START);
  100. pRangeSize0->Release();
  101. *ppProp = pProp;
  102. pProp->AddRef();
  103. }
  104. if (SUCCEEDED(hr) && pPropRange)
  105. {
  106. ITfRange *pRangeForSound;
  107. hr = pPropRange->Clone(&pRangeForSound);
  108. if (ppSndRange && SUCCEEDED(hr))
  109. {
  110. hr = pRangeForSound->Clone(ppSndRange);
  111. pRangeForSound->Release();
  112. }
  113. if (ppPropRange && pPropRange && SUCCEEDED(hr))
  114. {
  115. hr = pPropRange->Clone(ppPropRange);
  116. }
  117. SafeRelease(pPropRange);
  118. }
  119. else
  120. {
  121. if (ppPropRange)
  122. *ppPropRange = NULL;
  123. if (ppSndRange)
  124. *ppSndRange = NULL;
  125. }
  126. SafeRelease(pProp);
  127. return hr;
  128. }
  129. // ITfFnPlayBack
  130. //
  131. //
  132. STDAPI CSapiPlayBack::QueryRange(ITfRange *pRange, ITfRange **ppNewRange, BOOL *pfPlayable)
  133. {
  134. //
  135. // always ok because of TTS.
  136. //
  137. if (ppNewRange)
  138. {
  139. pRange->Clone(ppNewRange);
  140. }
  141. *pfPlayable = TRUE;
  142. return S_OK;
  143. }
  144. // ITfFnPlayBack
  145. //
  146. // play the audio stream attached to the range
  147. // TODO: use TTS if:
  148. // 1) the given range has less text
  149. // then the stream prop
  150. // 2) the audio property is not found
  151. //
  152. STDAPI CSapiPlayBack::Play(ITfRange *pRange)
  153. {
  154. HRESULT hr = E_OUTOFMEMORY;
  155. if ( !m_psi )
  156. return E_FAIL;
  157. SafeRelease(m_pIC);
  158. m_psi->GetFocusIC(&m_pIC);
  159. if (m_pIC)
  160. {
  161. CPlayBackEditSession *pes;
  162. if (pes = new CPlayBackEditSession(this, m_pIC))
  163. {
  164. pes->_SetEditSessionData(ESCB_PLAYBK_PLAYSND, NULL, 0);
  165. pes->_SetRange(pRange);
  166. m_pIC->RequestEditSession(m_psi->_GetId(), pes, TF_ES_READ /*| TF_ES_SYNC */, &hr);
  167. pes->Release();
  168. }
  169. }
  170. return hr;
  171. }
  172. HRESULT CSapiPlayBack::_PlaySound(TfEditCookie ec, ITfRange *pRange)
  173. {
  174. HRESULT hr = S_OK;
  175. LONG l;
  176. ITfRange *pRangeForSound = NULL;
  177. ITfRange *pRangeCurrent = NULL;
  178. BOOL fEmpty;
  179. ULONG ulStart, ulcElem;
  180. // playback one phrase for no selection
  181. pRange->IsEmpty(ec, &fEmpty);
  182. if (fEmpty)
  183. {
  184. ITfProperty *pProp = NULL;
  185. hr = FindSoundRange(ec, pRange, &pProp, NULL, &pRangeForSound);
  186. if (SUCCEEDED(hr) && pRangeForSound)
  187. {
  188. pRange->ShiftStartToRange(ec, pRangeForSound, TF_ANCHOR_START);
  189. pRange->ShiftEndToRange(ec, pRangeForSound, TF_ANCHOR_END);
  190. pRangeForSound->Release();
  191. }
  192. SafeReleaseClear(pProp);
  193. }
  194. // setting up a range object on our own
  195. hr = pRange->Clone(&pRangeCurrent);
  196. while(SUCCEEDED(hr) && pRangeCurrent->IsEmpty(ec, &fEmpty) == S_OK && !fEmpty)
  197. {
  198. ITfProperty *pProp = NULL;
  199. // get the first dictated range
  200. CDictRange *pDictRange = new CDictRange( );
  201. if ( pDictRange )
  202. {
  203. hr = pDictRange->Initialize(ec, m_pIC, pRangeCurrent);
  204. if ( SUCCEEDED(hr) && pDictRange->IsDictRangeFound( ))
  205. {
  206. // Found a dictated range.
  207. pRangeForSound = pDictRange->GetDictRange( );
  208. ulStart = pDictRange->GetStartElem( );
  209. ulcElem = pDictRange->GetNumElem( );
  210. pProp = pDictRange->GetProp( );
  211. // if start anchor of pRangeForSound is larger than start anchor of pRangeCurrent,
  212. // we need to send the text between these two anchors to spVoice first.
  213. hr = pRangeCurrent->CompareStart(ec, pRangeForSound, TF_ANCHOR_START, &l);
  214. if ( SUCCEEDED(hr) && l < 0 )
  215. {
  216. CComPtr<ITfRange> cpRangeText;
  217. hr = pRangeCurrent->Clone(&cpRangeText);
  218. if ( SUCCEEDED(hr) )
  219. {
  220. hr = cpRangeText->ShiftEndToRange(ec, pRangeForSound, TF_ANCHOR_START);
  221. }
  222. if ( SUCCEEDED(hr) )
  223. hr = PlayTextData(ec, cpRangeText);
  224. }
  225. // Then play the audio data.
  226. if (SUCCEEDED(hr) )
  227. {
  228. hr = PlayAudioData(ec, pRangeForSound, pProp, ulStart, ulcElem);
  229. }
  230. }
  231. else
  232. {
  233. // There is no dictated phrase in this range.
  234. // just speak all the rest text at once.
  235. SafeRelease(pRangeForSound);
  236. hr = pRangeCurrent->Clone(&pRangeForSound);
  237. if ( SUCCEEDED(hr) )
  238. hr = PlayTextData(ec, pRangeCurrent);
  239. }
  240. SafeReleaseClear(pProp);
  241. // next range
  242. pRangeCurrent->ShiftStartToRange(ec, pRangeForSound, TF_ANCHOR_END);
  243. pRangeForSound->Release();
  244. delete pDictRange;
  245. }
  246. else
  247. hr = E_OUTOFMEMORY;
  248. } // end of while
  249. SafeRelease(pRangeCurrent);
  250. return hr;
  251. }
  252. //
  253. // CSapiPlayBack::PlayTextData
  254. //
  255. // Playing the text by default voice
  256. //
  257. // This is for Non-Dictated text. pRangeText contains all the Non-Dictated text
  258. const GUID GUID_TS_SERVICE_DATAOBJECT={0x6086fbb5, 0xe225, 0x46ce, {0xa7, 0x70, 0xc1, 0xbb, 0xd3, 0xe0, 0x5d, 0x7b}};
  259. const IID IID_ILineInfo = {0x9C1C5AD5,0xF22F,0x4DE4,{0xB4,0x53,0xA2,0xCC,0x48,0x2E,0x7C,0x33}};
  260. HRESULT CSapiPlayBack::GetInkObjectText(TfEditCookie ec, ITfRange *pRange, BSTR *pbstrWord,UINT *pcchWord)
  261. {
  262. HRESULT hr = S_OK;
  263. CComPtr<IDataObject> cpDataObject;
  264. CComPtr<ILineInfo> cpLineInfo;
  265. if ( !pRange || !pbstrWord || !pcchWord )
  266. return E_FAIL;
  267. *pbstrWord = NULL;
  268. *pcchWord = 0;
  269. hr = pRange->GetEmbedded(ec,
  270. GUID_TS_SERVICE_DATAOBJECT,
  271. IID_IDataObject,
  272. (IUnknown **)&cpDataObject);
  273. if ( hr == S_OK )
  274. {
  275. hr = cpDataObject->QueryInterface(IID_ILineInfo, (void **)&cpLineInfo);
  276. }
  277. if ( hr == S_OK && cpLineInfo)
  278. {
  279. hr = cpLineInfo->TopCandidates(0, pbstrWord, pcchWord, 0, 0);
  280. }
  281. else
  282. {
  283. // it doesn't support ILineInfoi or IDataObject.
  284. // But it is not an error, the code should not terminate here.
  285. hr = S_OK;
  286. }
  287. return hr;
  288. }
  289. HRESULT CSapiPlayBack::PlayTextData(TfEditCookie ec, ITfRange *pRangeText)
  290. {
  291. HRESULT hr = S_OK;
  292. CComPtr<ITfRange> cpRangeCloned;
  293. BOOL fEmpty = TRUE;
  294. CSpDynamicString dstrText;
  295. CSpTask *psp;
  296. WCHAR sz[128];
  297. ULONG iIndex = 0;
  298. ULONG ucch;
  299. if ( m_psi == NULL ) return E_FAIL;
  300. if ( !pRangeText ) return E_INVALIDARG;
  301. hr = pRangeText->Clone(&cpRangeCloned);
  302. // Get the text from the pRangeCloned
  303. while(S_OK == hr && (S_OK == cpRangeCloned->IsEmpty(ec, &fEmpty)) && !fEmpty)
  304. {
  305. WCHAR szEach[2];
  306. BOOL fHitInkObject = FALSE;
  307. BSTR bstr = NULL;
  308. fHitInkObject = FALSE;
  309. hr = cpRangeCloned->GetText(ec, TF_TF_MOVESTART, szEach, ARRAYSIZE(szEach)-1, &ucch);
  310. if (S_OK == hr && ucch > 0)
  311. {
  312. szEach[ucch] = L'\0';
  313. if ( szEach[0] == TF_CHAR_EMBEDDED )
  314. {
  315. // This is an embedded object.
  316. // Check to see if it is Ink Object. currently we support only Inkobject TTSed
  317. CComPtr<ITfRange> cpRangeTmp;
  318. // Shift the start anchor back by 1 char.
  319. hr = cpRangeCloned->Clone(&cpRangeTmp);
  320. if ( hr == S_OK )
  321. {
  322. LONG cch;
  323. hr = cpRangeTmp->ShiftStart(ec, -1, &cch, 0 );
  324. }
  325. if ( hr == S_OK )
  326. hr = GetInkObjectText(ec, cpRangeTmp, &bstr,(UINT *)&ucch);
  327. if ( hr == S_OK && ucch > 0 && bstr)
  328. fHitInkObject = TRUE;
  329. }
  330. if ( fHitInkObject)
  331. {
  332. // Fill the previous text to dstrText.
  333. if ( iIndex > 0 )
  334. {
  335. sz[iIndex] = L'\0';
  336. dstrText.Append(sz);
  337. iIndex = 0;
  338. }
  339. // Fill this Ink Object text
  340. dstrText.Append(bstr);
  341. SysFreeString(bstr);
  342. }
  343. else
  344. {
  345. if ( iIndex >= ARRAYSIZE(sz)-1 )
  346. {
  347. sz[ARRAYSIZE(sz)-1] = L'\0';
  348. dstrText.Append(sz);
  349. iIndex=0;
  350. }
  351. sz[iIndex] = szEach[0];
  352. iIndex ++;
  353. }
  354. }
  355. else
  356. {
  357. // hr is not S_OK or ucch is zero.
  358. // we just want to exit here.
  359. TraceMsg(TF_GENERAL, "PlayTextData: ucch=%d", ucch);
  360. break;
  361. }
  362. }
  363. // Fill the last run of text.
  364. if ( iIndex > 0 )
  365. {
  366. sz[iIndex] = L'\0';
  367. dstrText.Append(sz);
  368. iIndex = 0;
  369. }
  370. // Play the text through TTS service.
  371. if ((hr == S_OK) && dstrText)
  372. {
  373. hr = m_psi->GetSpeechTask(&psp);
  374. if (hr == S_OK)
  375. {
  376. hr = psp->_SpeakText((WCHAR *)dstrText);
  377. psp->Release();
  378. }
  379. }
  380. return hr;
  381. }
  382. //
  383. // CSapiPlayBack::PlayAudioData
  384. //
  385. // Playing the sound by Aduio Data
  386. //
  387. // This is for Dictated text. pRangeAudio keeps the dictated text range.
  388. HRESULT CSapiPlayBack::PlayAudioData(TfEditCookie ec, ITfRange *pRangeAudio, ITfProperty *pProp, ULONG ulStart, ULONG ulcElem )
  389. {
  390. HRESULT hr = S_OK;
  391. CSpTask *psp;
  392. VARIANT var;
  393. if ( m_psi == NULL ) return E_FAIL;
  394. if ( !pRangeAudio || !pProp ) return E_INVALIDARG;
  395. hr = pProp->GetValue(ec, pRangeAudio, &var);
  396. if (S_OK == hr)
  397. {
  398. Assert(var.vt == VT_UNKNOWN);
  399. IUnknown *punk = var.punkVal;
  400. if (punk)
  401. {
  402. // get the wrapper object
  403. CRecoResultWrap *pWrap;
  404. hr = punk->QueryInterface(IID_PRIV_RESULTWRAP, (void **)&pWrap);
  405. if (S_OK == hr)
  406. {
  407. // hr = pWrap->_SpeakAudio(0,0); // better calculate the length accurately
  408. CComPtr<ISpRecoResult> cpResult;
  409. hr = pWrap->GetResult(&cpResult);
  410. if (S_OK == hr)
  411. {
  412. CComPtr<ISpStreamFormat> cpStream;
  413. if ((ulStart == 0) && (ulcElem == 0))
  414. {
  415. // We don't set the start element and number of elems.
  416. // it must be for the whole recoWrap.
  417. ulStart = pWrap->GetStart();
  418. ulcElem = pWrap->GetNumElements();
  419. }
  420. hr = cpResult->GetAudio(ulStart, ulcElem, &cpStream);
  421. if ( S_OK == hr )
  422. {
  423. hr = m_psi->GetSpeechTask(&psp);
  424. if (SUCCEEDED(hr))
  425. {
  426. hr = psp->_SpeakAudio(cpStream);
  427. psp->Release();
  428. }
  429. }
  430. }
  431. pWrap->Release();
  432. }
  433. punk->Release();
  434. }
  435. }
  436. return hr;
  437. }
  438. //
  439. // CSapiPlayBack::GetDataID
  440. //
  441. // This method is incomplete for now, until we figure out
  442. // the usage of dataid
  443. //
  444. HRESULT CSapiPlayBack::GetDataID(BSTR bstrCandXml, int nId, GUID *pguidData)
  445. {
  446. // 1) parse the list and find RANGEDATA
  447. IXMLDOMNodeList *pNList = NULL;
  448. IXMLDOMElement *pElm = NULL;
  449. IXMLDOMNode *pNode;
  450. VARIANT_BOOL bSuccessful;
  451. HRESULT hr = EnsureIXMLDoc();
  452. if (SUCCEEDED(hr))
  453. {
  454. hr = m_pIXMLDoc->loadXML(bstrCandXml, &bSuccessful);
  455. }
  456. // get <RANGEDATA> element
  457. if (SUCCEEDED(hr) && bSuccessful)
  458. {
  459. BSTR bstrRange = SysAllocString(L"RANGEDATA");
  460. if (bstrRange)
  461. {
  462. hr = m_pIXMLDoc->getElementsByTagName(bstrRange, &pNList);
  463. SysFreeString(bstrRange);
  464. }
  465. else
  466. hr = E_OUTOFMEMORY;
  467. }
  468. if (SUCCEEDED(hr) && pNList)
  469. {
  470. if (pNList->nextNode(&pNode) == S_OK)
  471. hr = pNode->QueryInterface(IID_IXMLDOMElement,(void **)&pElm);
  472. pNList->Release();
  473. }
  474. // then <MICROSOFTSPEECH> ...
  475. if (SUCCEEDED(hr) && pElm)
  476. {
  477. BSTR bstrSpchPriv = SysAllocString(L"MICROSOFTSPEECH");
  478. if (bstrSpchPriv)
  479. {
  480. hr = pElm->getElementsByTagName(bstrSpchPriv, &pNList);
  481. SysFreeString(bstrSpchPriv);
  482. }
  483. else
  484. hr = E_OUTOFMEMORY;
  485. pElm->Release();
  486. }
  487. if (SUCCEEDED(hr) && pNList)
  488. {
  489. if (pNList->nextNode(&pNode) == S_OK)
  490. hr = pNode->QueryInterface(IID_IXMLDOMElement,(void **)&pElm);
  491. pNList->Release();
  492. }
  493. // <DATAID>
  494. // right now assuming the speech element
  495. // is put at the level of <rangedata>
  496. // ignoring nId here
  497. if (SUCCEEDED(hr) && pElm)
  498. {
  499. BSTR bstrDataId = SysAllocString(L"DATAID");
  500. if (bstrDataId)
  501. {
  502. hr = pElm->getElementsByTagName(bstrDataId, &pNList);
  503. SysFreeString(bstrDataId);
  504. }
  505. else
  506. hr = E_OUTOFMEMORY;
  507. pElm->Release();
  508. }
  509. // impl later...
  510. // so, here we'll get the real dataid and be done
  511. if (SUCCEEDED(hr) && pNList)
  512. {
  513. pNList->Release();
  514. }
  515. return hr;
  516. }
  517. HRESULT CSapiPlayBack::_PlaySoundSelection(TfEditCookie ec, ITfContext *pic)
  518. {
  519. ITfRange *pSelection;
  520. HRESULT hr = E_FAIL;
  521. if (GetSelectionSimple(ec, pic, &pSelection) == S_OK)
  522. {
  523. hr = _PlaySound(ec, pSelection);
  524. pSelection->Release();
  525. }
  526. return hr;
  527. }
  528. HRESULT CSapiPlayBack::EnsureIXMLDoc(void)
  529. {
  530. HRESULT hr = S_OK;
  531. IXMLDOMDocument *pIXMLDocument;
  532. if (!m_pIXMLDoc)
  533. {
  534. if (SUCCEEDED(hr = CoCreateInstance(CLSID_DOMDocument,
  535. NULL,
  536. CLSCTX_INPROC_SERVER,
  537. IID_IXMLDOMDocument,
  538. (void **) &pIXMLDocument)))
  539. {
  540. m_pIXMLDoc = pIXMLDocument;
  541. }
  542. }
  543. return hr;
  544. }
  545. //
  546. // Implementation for Class CDictRange
  547. //
  548. //
  549. //
  550. // ctor/dtor
  551. //
  552. CDictRange::CDictRange( ) : CBestPropRange( )
  553. {
  554. m_fFoundDictRange = FALSE;
  555. m_pProp = NULL;
  556. m_pDictatRange = NULL;
  557. m_ulStart = 0;
  558. m_ulcElem = 0;
  559. }
  560. CDictRange::~CDictRange( )
  561. {
  562. SafeRelease(m_pProp);
  563. SafeRelease(m_pDictatRange);
  564. }
  565. ITfProperty *CDictRange::GetProp( )
  566. {
  567. if ( m_pProp )
  568. m_pProp->AddRef( );
  569. return m_pProp;
  570. }
  571. ITfRange *CDictRange::GetDictRange( )
  572. {
  573. if ( m_pDictatRange )
  574. m_pDictatRange->AddRef( );
  575. return m_pDictatRange;
  576. }
  577. HRESULT CDictRange::_GetOverlapRange(TfEditCookie ec, ITfRange *pRange1, ITfRange *pRange2, ITfRange **ppOverlapRange)
  578. {
  579. HRESULT hr = E_FAIL;
  580. LONG l1=0;
  581. LONG l2=0;
  582. CComPtr<ITfRange> cpRangeOverlap;
  583. if ( !pRange1 || !pRange2 || !ppOverlapRange )
  584. return E_INVALIDARG;
  585. *ppOverlapRange = NULL;
  586. // Get the overlapping part of pRange and cpPropRange, and then calculate the
  587. // best matched propRange.
  588. hr = pRange1->Clone( &cpRangeOverlap );
  589. if ( SUCCEEDED(hr) )
  590. hr = pRange1->CompareStart(ec, pRange2, TF_ANCHOR_START, &l1);
  591. if ( SUCCEEDED(hr) && l1 < 0 )
  592. {
  593. // Start anchor of pRange1 is before the start anchor of the
  594. // pRange2.
  595. hr = cpRangeOverlap->ShiftStartToRange(ec, pRange2, TF_ANCHOR_START);
  596. }
  597. if ( SUCCEEDED(hr) )
  598. hr = cpRangeOverlap->CompareEnd(ec, pRange2, TF_ANCHOR_END, &l2);
  599. if ( SUCCEEDED(hr) && l2 > 0)
  600. {
  601. // End anchor of cpRangeOverlap is after the end anchor of the pRange2.
  602. hr = cpRangeOverlap->ShiftEndToRange(ec, pRange2, TF_ANCHOR_END);
  603. }
  604. if ( SUCCEEDED(hr) )
  605. hr = cpRangeOverlap->Clone(ppOverlapRange);
  606. return hr;
  607. }
  608. //
  609. // CDictRange::Initialize
  610. //
  611. // synopsis - Get the necessary input parameters, (ec, pic, and pRange),
  612. // and then search the first dictated range inside the given range.
  613. // if it finds the first dictated range, update the related
  614. // data memebers.
  615. //
  616. // if it doesn't find any dictated range, mark as not found.
  617. //
  618. HRESULT CDictRange::Initialize(TfEditCookie ec, ITfContext *pic, ITfRange *pRange)
  619. {
  620. CComPtr<ITfRange> cpPropRange = NULL;
  621. HRESULT hr;
  622. m_fFoundDictRange = FALSE;
  623. m_pProp = NULL;
  624. m_pDictatRange = NULL;
  625. m_ulStart = 0;
  626. m_ulcElem = 0;
  627. if ( !pic || !pRange ) return E_INVALIDARG;
  628. hr = pic->GetProperty(GUID_PROP_SAPIRESULTOBJECT, &m_pProp);
  629. if ( SUCCEEDED(hr) && m_pProp)
  630. {
  631. CComPtr<ITfRange> cpRangeCurrent;
  632. LONG cch;
  633. hr = pRange->Clone(&cpRangeCurrent);
  634. if ( SUCCEEDED(hr) )
  635. {
  636. hr = cpRangeCurrent->Collapse(ec, TF_ANCHOR_START);
  637. }
  638. while ( SUCCEEDED(hr) && !m_fFoundDictRange )
  639. {
  640. cpPropRange.Release( );
  641. hr = m_pProp->FindRange(ec, cpRangeCurrent, &cpPropRange, TF_ANCHOR_START);
  642. if ( SUCCEEDED(hr) && cpPropRange )
  643. {
  644. // Found the first Dictated phrase.
  645. CComPtr<ITfRange> cpRangeOverlap;
  646. // Get the overlapping part of pRange and cpPropRange, and then calculate the
  647. // best matched propRange.
  648. hr = _GetOverlapRange(ec, pRange, cpPropRange, &cpRangeOverlap);
  649. if ( SUCCEEDED(hr) )
  650. {
  651. // Calculate the best matched propRange and ulStart, ulcElem.
  652. cpPropRange.Release( );
  653. hr = _ComputeBestFitPropRange(ec, m_pProp, cpRangeOverlap, &cpPropRange, &m_ulStart, &m_ulcElem);
  654. }
  655. if (SUCCEEDED(hr) && (m_ulcElem > 0))
  656. {
  657. m_fFoundDictRange = TRUE;
  658. }
  659. }
  660. if ( SUCCEEDED(hr) && !m_fFoundDictRange)
  661. {
  662. // cpRangeCurrent shift forward one character. and try again.
  663. hr = cpRangeCurrent->ShiftStart(ec, 1, &cch, NULL);
  664. if ( SUCCEEDED(hr) && (cch==0))
  665. {
  666. // Hit a region or the end of doc.
  667. // check to see if there is more region.
  668. BOOL fNoRegion = TRUE;
  669. hr = cpRangeCurrent->ShiftStartRegion(ec, TF_SD_FORWARD, &fNoRegion);
  670. if ( fNoRegion )
  671. {
  672. TraceMsg(TF_GENERAL, "Reach to end of doc");
  673. break;
  674. }
  675. else
  676. TraceMsg(TF_GENERAL, "Shift over to another region!");
  677. }
  678. if ( SUCCEEDED(hr) )
  679. {
  680. // check to see if cpRangeCurrent is beyond of the pRange.
  681. hr = pRange->CompareEnd(ec, cpRangeCurrent, TF_ANCHOR_END, &cch);
  682. if ( SUCCEEDED(hr) && cch <= 0 )
  683. {
  684. // cpRangeCurrent Now is beyond of specified range, exit while statement.
  685. TraceMsg(TF_GENERAL, "reach to the end of original range");
  686. break;
  687. }
  688. }
  689. }
  690. }
  691. }
  692. if (SUCCEEDED(hr) && cpPropRange && m_fFoundDictRange)
  693. {
  694. hr = cpPropRange->Clone(&m_pDictatRange);
  695. }
  696. return hr;
  697. }