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.

750 lines
20 KiB

  1. //
  2. //
  3. // Sapilayr TIP CLearnFromDoc implementation.
  4. //
  5. //
  6. #include "private.h"
  7. #include "sapilayr.h"
  8. #include "learndoc.h"
  9. #include "cregkey.h"
  10. // ----------------------------------------------------------------------------------------------------------
  11. //
  12. // Implementation for CLeanrFromDoc
  13. //
  14. // -----------------------------------------------------------------------------------------------------------
  15. CLearnFromDoc::CLearnFromDoc(CSapiIMX *psi)
  16. {
  17. m_psi = psi;
  18. m_pwszDocBlock = NULL;
  19. _pic = NULL;
  20. _pCSpTask = NULL;
  21. _cpStartRange = NULL;
  22. _cchBlockSize = -1;
  23. _fMoreContent = FALSE;
  24. _fLearnFromDoc = FALSE;
  25. }
  26. CLearnFromDoc::~CLearnFromDoc( )
  27. {
  28. if ( m_pwszDocBlock )
  29. cicMemFree(m_pwszDocBlock);
  30. _ClearDimList( );
  31. };
  32. void CLearnFromDoc::UpdateLearnDocState( )
  33. {
  34. DWORD dw;
  35. GetCompartmentDWORD(m_psi->_tim, GUID_COMPARTMENT_SPEECH_LEARNDOC, &dw, FALSE);
  36. if ( dw != 0 )
  37. _fLearnFromDoc = TRUE;
  38. else
  39. _fLearnFromDoc = FALSE;
  40. if ( _fLearnFromDoc == FALSE )
  41. {
  42. if ( m_pwszDocBlock )
  43. {
  44. cicMemFree(m_pwszDocBlock);
  45. m_pwszDocBlock = NULL;
  46. }
  47. _UpdateRecoContextInterestSet(FALSE);
  48. _ResetDimListFeedState( );
  49. }
  50. }
  51. ULONG CLearnFromDoc::_GetDocBlockSize( )
  52. {
  53. if (_cchBlockSize == (ULONG)-1)
  54. {
  55. CMyRegKey regkey;
  56. if (S_OK == regkey.Open(HKEY_LOCAL_MACHINE, c_szSapilayrKey, KEY_READ))
  57. {
  58. DWORD dw;
  59. if (ERROR_SUCCESS==regkey.QueryValue(dw, c_szDocBlockSize))
  60. {
  61. _cchBlockSize = dw;
  62. }
  63. }
  64. if (_cchBlockSize == (ULONG)-1)
  65. {
  66. _cchBlockSize = SIZE_DOCUMENT_BLOCK;
  67. }
  68. if ( _cchBlockSize < SIZE_FIRST_BLOCK )
  69. _cchBlockSize = SIZE_FIRST_BLOCK; // the first block size is the minimize size.
  70. }
  71. return _cchBlockSize;
  72. }
  73. HRESULT CLearnFromDoc::HandleLearnFromDoc(ITfDocumentMgr *pDim )
  74. {
  75. HRESULT hr = S_OK;
  76. CComPtr<ITfContext> cpic=NULL;
  77. ITfDocumentMgr *dim;
  78. if ( !m_psi )
  79. return E_FAIL;
  80. if ( m_psi->GetFocusIC(&cpic) && (GetLearnFromDoc( ) == TRUE))
  81. {
  82. if ( !pDim )
  83. {
  84. // Try to get the Dim from current focused IC.
  85. hr = cpic->GetDocumentMgr(&dim);
  86. }
  87. else
  88. {
  89. dim = pDim;
  90. dim->AddRef( );
  91. }
  92. if ( !dim )
  93. hr = E_FAIL;
  94. if ( S_OK == hr )
  95. {
  96. // Check to see if this doc has already been fed to SR LM engine.
  97. BOOL fFedAlready = FALSE;
  98. hr = _IsDimAlreadyFed(dim, &fFedAlready);
  99. if ( (S_OK == hr) && !fFedAlready )
  100. {
  101. // Check to if the current doc is ReadOnly or not.
  102. TF_STATUS docStatus;
  103. hr = cpic->GetStatus(&docStatus);
  104. if ( S_OK == hr && !(docStatus.dwStaticFlags & TF_SD_READONLY) )
  105. {
  106. ESDATA esData;
  107. memset(&esData, 0, sizeof(ESDATA));
  108. esData.pUnk = (IUnknown *)dim;
  109. hr = m_psi->_RequestEditSession(ESCB_HANDLE_LEARNFROMDOC,TF_ES_READ, &esData, cpic);
  110. }
  111. }
  112. dim->Release();
  113. }
  114. }
  115. return hr;
  116. }
  117. //+---------------------------------------------------------------------------
  118. //
  119. // CSapiIMX::_HandleLearnFromDoc
  120. //
  121. // Let the speech engine learn from existing document content and have
  122. // more accurate dictation recognition.
  123. // This function will be called when user clicks the speech language bar menu
  124. // and select Learn From Document... item.
  125. //
  126. //---------------------------------------------------------------------------+
  127. HRESULT CLearnFromDoc::_HandleLearnFromDoc(TfEditCookie ec,ITfContext *pic, ITfDocumentMgr *pDim )
  128. {
  129. HRESULT hr = S_OK;
  130. ULONG cchBlock;
  131. BOOL fFeedAlready;
  132. // Get the Dictation Grammar
  133. TraceMsg(TF_GENERAL, "_HandleLearnFromDoc() is called");
  134. if ( m_psi == NULL)
  135. return E_FAIL;
  136. if ( !pDim || !pic )
  137. return E_INVALIDARG;
  138. _pic = pic;
  139. m_psi->GetSpeechTask(&_pCSpTask, FALSE);
  140. if ( _pCSpTask == NULL )
  141. {
  142. // _sptask is not yet initialized, just return.
  143. TraceMsg(TF_GENERAL, "_HandleLearnFromDoc: _pCspTask is NULL");
  144. goto CleanUp;
  145. }
  146. cchBlock = _GetDocBlockSize( );
  147. if ( m_pwszDocBlock == NULL)
  148. {
  149. m_pwszDocBlock = (WCHAR *) cicMemAlloc( (cchBlock+1)*sizeof(WCHAR) );
  150. if (m_pwszDocBlock == NULL)
  151. {
  152. TraceMsg(TF_GENERAL, "_HandleLearnFromDoc: m_pwszDocBlock Out of Memory");
  153. hr = E_OUTOFMEMORY;
  154. goto CleanUp;
  155. }
  156. }
  157. fFeedAlready = FALSE;
  158. hr = _IsDimAlreadyFed(pDim, &fFeedAlready);
  159. if ( (hr != S_OK) || fFeedAlready )
  160. {
  161. // This DIM has already been fed to SR Engine.
  162. // or we got problem to get the feed state for this dim.
  163. // stop here.
  164. goto CleanUp;
  165. }
  166. // Get the range for the document.
  167. _cpStartRange.Release( );
  168. hr = _pic->GetStart(ec, &_cpStartRange);
  169. if ((hr != S_OK) || (_cpStartRange == NULL))
  170. {
  171. TraceMsg(TF_GENERAL, "_HandleLearnFromDoc: _cpStartRange is NULL");
  172. goto CleanUp;
  173. }
  174. // Change the sptask interersting setting
  175. _UpdateRecoContextInterestSet(TRUE);
  176. // This is the first range of the document. just small size of block.
  177. hr = _GetNextRangeContent(ec, SIZE_FIRST_BLOCK);
  178. // Feed to SR Engine.
  179. if ( (hr == S_OK) && _fMoreContent)
  180. {
  181. hr = _FeedContentRangeToSR( );
  182. if ( hr == S_OK)
  183. {
  184. _SetDimFeedState(pDim, TRUE);
  185. }
  186. else
  187. _fMoreContent = FALSE;
  188. }
  189. CleanUp:
  190. return hr;
  191. }
  192. //+---------------------------------------------------------------------------
  193. //
  194. // _GetNextRangeContent
  195. //
  196. // Get the Next Range (block) of document content.
  197. //
  198. // update the flag to indicate if there is effective content
  199. //
  200. // ec: EditCookie
  201. // cchSizeRange: Required block size
  202. //
  203. // At the first time, we just feed a very small size of charcters
  204. // to the Engine so that it will not interfere with current dictation
  205. // handling of engine.
  206. //
  207. // After we get the ADAPTATION notification from Engine, we will feed
  208. // normal size of block ( specified in registry ) to engine.
  209. //
  210. //---------------------------------------------------------------------------+
  211. HRESULT CLearnFromDoc::_GetNextRangeContent(TfEditCookie ec, ULONG cchSizeRange)
  212. {
  213. HRESULT hr = S_OK;
  214. LONG lShift = 0;
  215. TraceMsg(TF_GENERAL, "_GetNextRangeContent is Called");
  216. if ( m_pwszDocBlock == NULL)
  217. {
  218. m_pwszDocBlock = (WCHAR *) cicMemAlloc( (_cchBlockSize+1)*sizeof(WCHAR));
  219. if (m_pwszDocBlock == NULL)
  220. {
  221. TraceMsg(TF_GENERAL, "_GetNextRangeContent: m_pwszDocBlock Out of Memory");
  222. hr = E_OUTOFMEMORY;
  223. return hr;
  224. }
  225. }
  226. // assume there is no more content.
  227. _fMoreContent = FALSE;
  228. // This is Cicero App's Document, we use Cicero interfaces to get the whole document.
  229. // We already get the cpStartRange.
  230. hr = _cpStartRange->GetText(ec, TF_TF_MOVESTART | TF_TF_IGNOREEND, m_pwszDocBlock, cchSizeRange, &_cchContent );
  231. if ( hr!= S_OK ) goto CleanUp;
  232. // If the last character is not a word-break char for English case,
  233. // we don't want to keep this half word for this chunk text, it will go to next
  234. // chunk.
  235. // we just want to shift back some charaters to hit a word-breaker.
  236. WORD prilangid;
  237. prilangid = PRIMARYLANGID(m_psi->GetLangID( ));
  238. if ( prilangid != LANG_JAPANESE && prilangid != LANG_CHINESE )
  239. {
  240. if ( _cchContent > 0 )
  241. {
  242. while ( _cchContent > 0 )
  243. {
  244. if ( iswalpha(m_pwszDocBlock[_cchContent-1]) )
  245. {
  246. m_pwszDocBlock[_cchContent-1] = L'\0';
  247. lShift ++;
  248. _cchContent--;
  249. }
  250. else
  251. break;
  252. }
  253. if ( lShift > 0 )
  254. {
  255. lShift *= ( -1 );
  256. long cch;
  257. _cpStartRange->ShiftEnd(ec, lShift, &cch, NULL);
  258. }
  259. }
  260. }
  261. TraceMsg(TF_GENERAL, "Text Content in Cicero Doc Buffer _cchContent=%d---------------->", _cchContent);
  262. #ifdef DEBUG
  263. {
  264. ULONG x;
  265. for (x=0; x<_cchContent; x++)
  266. {
  267. char szbuf[4];
  268. szbuf[0] = (char)(m_pwszDocBlock[x]);
  269. szbuf[1] = '\0';
  270. OutputDebugString(szbuf);
  271. }
  272. OutputDebugString("\n");
  273. }
  274. #endif
  275. TraceMsg(TF_GENERAL, "Text Content in CiceroDoc Over -------------------!");
  276. if ( _cchContent > 0 || lShift > 0)
  277. // There is more content.
  278. _fMoreContent = TRUE;
  279. else
  280. _fMoreContent = FALSE;
  281. CleanUp:
  282. return hr;
  283. }
  284. //+---------------------------------------------------------------------------
  285. //
  286. // _FeedContentRangeToSR
  287. //
  288. // Feed the current Range (block) of document content to the SR Engine.
  289. //
  290. //---------------------------------------------------------------------------+
  291. HRESULT CLearnFromDoc::_FeedContentRangeToSR( )
  292. {
  293. HRESULT hr = S_OK;
  294. WCHAR *pCoMemText;
  295. CComPtr<ISpRecoContext> cpRecoCtxt;
  296. TraceMsg(TF_GENERAL, "_FeedContentRangeToSR( ) is called");
  297. hr = _pCSpTask->GetSAPIInterface(IID_ISpRecoContext, (void **)&cpRecoCtxt );
  298. if ( (hr != S_OK) || (cpRecoCtxt == NULL) )
  299. return E_FAIL;
  300. // Feed this block of document content to speech dictation.
  301. if ( (_cchContent > 0) && (m_pwszDocBlock != NULL))
  302. {
  303. pCoMemText = (WCHAR *)CoTaskMemAlloc((_cchContent+1)*sizeof(WCHAR));
  304. if ( pCoMemText )
  305. {
  306. wcsncpy(pCoMemText, m_pwszDocBlock, _cchContent);
  307. pCoMemText[_cchContent] = L'\0';
  308. hr = cpRecoCtxt->SetAdaptationData(pCoMemText, _cchContent);
  309. if ( hr != S_OK)
  310. TraceMsg(TF_GENERAL, "_FeedContentRangeToSR: SetAdaptationData Failed");
  311. }
  312. }
  313. return hr;
  314. }
  315. HRESULT CLearnFromDoc::_GetNextRangeEditSession( )
  316. {
  317. HRESULT hr = S_OK;
  318. ESDATA esData;
  319. Assert(m_psi);
  320. memset(&esData, 0, sizeof(ESDATA));
  321. esData.lData1 = (LONG_PTR)_cchBlockSize;
  322. hr = m_psi->_RequestEditSession(ESCB_LEARNDOC_NEXTRANGE, TF_ES_READ, &esData, _pic);
  323. return hr;
  324. }
  325. //+---------------------------------------------------------------------------
  326. //
  327. // _HandleNextRange
  328. //
  329. // Handle the Next Range (block) of document content, get and feed it and
  330. // then update the status.
  331. //
  332. // ec: EditCookie
  333. // cchSizeRange: Required block size
  334. //
  335. //---------------------------------------------------------------------------+
  336. HRESULT CLearnFromDoc::_HandleNextRange(TfEditCookie ec, ULONG cchSizeRange)
  337. {
  338. HRESULT hr = S_OK;
  339. hr = _GetNextRangeContent(ec, cchSizeRange);
  340. if ( hr == S_OK && _fMoreContent)
  341. {
  342. // This next range contains valid content, feed it to SR Engine.
  343. hr = _FeedContentRangeToSR( );
  344. }
  345. if ( (hr != S_OK) || !_fMoreContent )
  346. {
  347. // Error happened or no more content, update the interest set.
  348. hr = _UpdateRecoContextInterestSet(FALSE);
  349. }
  350. return hr;
  351. }
  352. //+-----------------------------------------------------------------------------------------
  353. //
  354. // _UpdateRecoContextInterestSet
  355. //
  356. // Update the RecoContext's interested notification event setting
  357. //
  358. // if fLearnFromDoc is TRUE, we are interested in getting notification of SPEI_ADAPTATION
  359. // if fLearnFromDoc is FALSE, we are not interested in that notification
  360. //
  361. //-------------------------------------------------------------------------------------------+
  362. HRESULT CLearnFromDoc::_UpdateRecoContextInterestSet( BOOL fLearnFromDoc )
  363. {
  364. HRESULT hr = S_OK;
  365. CComPtr<ISpRecoContext> cpRecoCtxt;
  366. ULONGLONG ulInterest = SPFEI(SPEI_SOUND_START) |
  367. SPFEI(SPEI_SOUND_END) |
  368. SPFEI(SPEI_PHRASE_START) |
  369. SPFEI(SPEI_RECOGNITION) |
  370. SPFEI(SPEI_RECO_OTHER_CONTEXT) |
  371. SPFEI(SPEI_HYPOTHESIS) |
  372. SPFEI(SPEI_INTERFERENCE) |
  373. SPFEI(SPEI_FALSE_RECOGNITION);
  374. if ( _pCSpTask == NULL )
  375. return hr;
  376. hr = _pCSpTask->GetSAPIInterface(IID_ISpRecoContext, (void **)&cpRecoCtxt );
  377. if ( (hr != S_OK) || (cpRecoCtxt == NULL) )
  378. return E_FAIL;
  379. if ( fLearnFromDoc )
  380. ulInterest |= SPFEI(SPEI_ADAPTATION);
  381. else
  382. ulInterest &= ~(SPFEI(SPEI_ADAPTATION));
  383. hr = cpRecoCtxt->SetInterest(ulInterest, ulInterest);
  384. return hr;
  385. }
  386. //+-----------------------------------------------------------------------------------------
  387. //
  388. // _AddDimToList
  389. //
  390. // Add a DIM to the dim list, and set the feed state
  391. //
  392. // This function would be called by TIM_CODE_INITDIM callback.
  393. //
  394. //-------------------------------------------------------------------------------------------+
  395. HRESULT CLearnFromDoc::_AddDimToList(ITfDocumentMgr *pDim, BOOL fFed )
  396. {
  397. HRESULT hr = S_OK;
  398. int nCnt = _rgDim.Count();
  399. int i;
  400. BOOL bFound;
  401. DIMREF *dimRef;
  402. TraceMsg(TF_GENERAL, "_AddDimToList is called");
  403. if ( !pDim )
  404. return E_INVALIDARG;
  405. // Check to see if this dim is already added.
  406. bFound = FALSE;
  407. for (i=0; i < nCnt; i++)
  408. {
  409. dimRef = (DIMREF *)_rgDim.Get(i);
  410. if ( dimRef->pDim == pDim )
  411. {
  412. bFound = TRUE;
  413. TraceMsg(TF_GENERAL, "This dim has already been added to the dim list");
  414. break;
  415. }
  416. }
  417. if (bFound)
  418. {
  419. // Set the state.
  420. dimRef->_fFeed = fFed;
  421. }
  422. else
  423. {
  424. dimRef = (DIMREF *)cicMemAllocClear(sizeof(DIMREF));
  425. if ( dimRef == NULL)
  426. return E_OUTOFMEMORY;
  427. dimRef->_fFeed = fFed;
  428. dimRef->pDim = pDim;
  429. if (!_rgDim.Insert(nCnt, 1))
  430. {
  431. cicMemFree(dimRef);
  432. return E_OUTOFMEMORY;
  433. }
  434. pDim->AddRef( );
  435. _rgDim.Set(nCnt, dimRef);
  436. }
  437. return hr;
  438. }
  439. //+-----------------------------------------------------------------------------------------
  440. //
  441. // _RemoveDimFromList
  442. //
  443. // Remove a DIM from the internal dim list, and release the DIM itself.
  444. //
  445. // This function would be called by TIM_CODE_UNINITDIM callback.
  446. //
  447. //-------------------------------------------------------------------------------------------+
  448. HRESULT CLearnFromDoc::_RemoveDimFromList(ITfDocumentMgr *pDim)
  449. {
  450. HRESULT hr = S_OK;
  451. int nCnt = _rgDim.Count();
  452. int i;
  453. DIMREF *dimRef;
  454. TraceMsg(TF_GENERAL, "_RemoveDimFromList is called");
  455. if ( pDim == NULL)
  456. return E_INVALIDARG;
  457. // Check to see if this dim is already added.
  458. for (i=0; i < nCnt; i++)
  459. {
  460. dimRef = (DIMREF *)_rgDim.Get(i);
  461. if ( dimRef->pDim == pDim )
  462. {
  463. // free the DIM.
  464. (dimRef->pDim)->Release( );
  465. dimRef->pDim = NULL;
  466. // Remove it from the list
  467. _rgDim.Remove(i, 1);
  468. // Remove the structure itself.
  469. cicMemFree(dimRef);
  470. break;
  471. }
  472. }
  473. return hr;
  474. }
  475. //+-----------------------------------------------------------------------------------------
  476. //
  477. // _SetDimFeedState
  478. //
  479. // Set the feed state for the specified DIM.
  480. //
  481. // fFed is TRUE means this DIM is already fed to the Engine.
  482. //
  483. //-------------------------------------------------------------------------------------------+
  484. HRESULT CLearnFromDoc::_SetDimFeedState(ITfDocumentMgr *pDim, BOOL fFed )
  485. {
  486. HRESULT hr = S_OK;
  487. int nCnt = _rgDim.Count();
  488. int i;
  489. DIMREF *dimRef;
  490. TraceMsg(TF_GENERAL, "_SetDimFeedState is called");
  491. if ( pDim == NULL)
  492. return E_INVALIDARG;
  493. // Check to see if this dim is already added.
  494. for (i=0; i < nCnt; i++)
  495. {
  496. dimRef = (DIMREF *)_rgDim.Get(i);
  497. if ( dimRef->pDim == pDim )
  498. {
  499. // Set the feed state for this DIM.
  500. dimRef->_fFeed = fFed;
  501. break;
  502. }
  503. }
  504. return hr;
  505. }
  506. //+-----------------------------------------------------------------------------------------
  507. //
  508. // _IsDimAlreadyFed
  509. //
  510. // Check to see if the dim is already fed to the Engine.
  511. //
  512. //-------------------------------------------------------------------------------------------+
  513. HRESULT CLearnFromDoc::_IsDimAlreadyFed(ITfDocumentMgr *pDim, BOOL *fFeed)
  514. {
  515. HRESULT hr = S_OK;
  516. int nCnt = _rgDim.Count();
  517. int i;
  518. DIMREF *dimRef;
  519. TraceMsg(TF_GENERAL, "_IsDimAlreadyFed is called");
  520. if ( (pDim == NULL) || (fFeed == NULL))
  521. return E_INVALIDARG;
  522. *fFeed = FALSE;
  523. // Check to see if this dim is already added.
  524. for (i=0; i < nCnt; i++)
  525. {
  526. dimRef = (DIMREF *)_rgDim.Get(i);
  527. if ( dimRef->pDim == pDim )
  528. {
  529. // Get the feed state for this DIM.
  530. *fFeed = dimRef->_fFeed;
  531. TraceMsg(TF_GENERAL, "IsDimAlreadyFed: pDim=%x, fFeed=%s", (UINT_PTR)pDim, *fFeed ? "TRUE":"FALSE");
  532. break;
  533. }
  534. }
  535. return hr;
  536. }
  537. // CleanUpConsider: above _IsDimAlreadyFed and _SetDimFeedState have similar code, we may supply a new internal base function, and let
  538. // above two functions call it with different param set.
  539. //+-----------------------------------------------------------------------------------------
  540. //
  541. // _ClearDimList
  542. //
  543. // Release all the DIMs in the DIM List, and clear the list itself.
  544. //
  545. //
  546. //-------------------------------------------------------------------------------------------+
  547. HRESULT CLearnFromDoc::_ClearDimList( )
  548. {
  549. HRESULT hr = S_OK;
  550. int nCnt = _rgDim.Count();
  551. int i;
  552. DIMREF *dimRef;
  553. TraceMsg(TF_GENERAL, "_ClearDimList is called");
  554. for (i=0; i < nCnt; i++)
  555. {
  556. dimRef = (DIMREF *)_rgDim.Get(i);
  557. // free the DIM.
  558. if ( dimRef->pDim)
  559. {
  560. (dimRef->pDim)->Release( );
  561. dimRef->pDim = NULL;
  562. }
  563. // Remove it from the list
  564. _rgDim.Remove(i, 1);
  565. // Remove the structure itself.
  566. cicMemFree(dimRef);
  567. }
  568. return hr;
  569. }
  570. //+-----------------------------------------------------------------------------------------
  571. //
  572. // _ResetDimListFeedState
  573. //
  574. // set feed state for all the dims in internal dim list as FALSE
  575. //
  576. // This function would be called when user turns off the Learn from Doc.
  577. //
  578. //-------------------------------------------------------------------------------------------+
  579. HRESULT CLearnFromDoc::_ResetDimListFeedState( )
  580. {
  581. HRESULT hr = S_OK;
  582. int nCnt = _rgDim.Count();
  583. int i;
  584. DIMREF *dimRef;
  585. TraceMsg(TF_GENERAL, "_ResetDimListFeedState is called");
  586. for (i=0; i < nCnt; i++)
  587. {
  588. dimRef = (DIMREF *)_rgDim.Get(i);
  589. // Set the feed state for this DIM as FALSE
  590. dimRef->_fFeed = FALSE;
  591. }
  592. return hr;
  593. }
  594. // CleanUpConsider: above _ClearDimList and _ResetDimListFeedState have similar code, we may supply a new internal base function, and let
  595. // above two functions call it with different param set.