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.

1791 lines
50 KiB

  1. //
  2. // command.cpp
  3. // This file contains methods related to C&C grammars' commands handling.
  4. //
  5. //
  6. #include "private.h"
  7. #include "globals.h"
  8. #include "sapilayr.h"
  9. #include "nui.h"
  10. #include "cresstr.h"
  11. #define LB_ID_Correction 200
  12. #define LB_ID_Microphone 201
  13. #define LB_ID_VoiceCmd 202
  14. #define Select_ID_SELECT 1
  15. #define Select_ID_DELETE 2
  16. #define Select_ID_CORRECT 3
  17. #define Select_ID_SELTHROUGH 4
  18. #define Select_ID_DELTHROUGH 5
  19. #define Select_ID_UNSELECT 6
  20. #define Select_ID_SELECTPREV 7
  21. #define Select_ID_SELECTNEXT 8
  22. #define Select_ID_CORRECTPREV 9
  23. #define Select_ID_CORRECTNEXT 10
  24. #define Select_ID_SELSENTENCE 11
  25. #define Select_ID_SELPARAGRAPH 12
  26. #define Select_ID_SELWORD 13
  27. #define Select_ID_SelectAll 14
  28. #define Select_ID_DeletePhrase 15 // Scratch That
  29. #define Select_ID_Convert 16
  30. #define Select_ID_SelectThat 17
  31. #define Select_ID_Finalize 18
  32. #define Navigate_ID_INSERTBEFORE 20
  33. #define Navigate_ID_INSERTAFTER 21
  34. #define Navigate_ID_Go_To_Bottom 22
  35. #define Navigate_ID_Go_To_Top 23
  36. #define Navigate_ID_Move_Home 24
  37. #define Navigate_ID_Move_End 25
  38. #define Edit_ID_Undo 30
  39. #define Edit_ID_Cut 31
  40. #define Edit_ID_Copy 32
  41. #define Edit_ID_Paste 33
  42. #define Keyboard_ID_Move_Up 40
  43. #define Keyboard_ID_Move_Down 41
  44. #define Keyboard_ID_Move_Left 42
  45. #define Keyboard_ID_Move_Right 43
  46. #define Keyboard_ID_Page_Up 44
  47. #define Keyboard_ID_Page_Down 45
  48. #define Keyboard_ID_Tab 46
  49. #define Keyboard_ID_Enter 47
  50. #define Keyboard_ID_Backspace 48
  51. #define Keyboard_ID_Delete 49
  52. #define Keyboard_ID_SpaceBar 50
  53. #define Case_ID_CapIt 70
  54. #define Case_ID_AllCaps 71
  55. #define Case_ID_NoCaps 72
  56. #define Case_ID_CapThat 73
  57. #define Case_ID_AllCapsThat 74
  58. #define Case_ID_NoCapsThat 75
  59. //
  60. // CSpTask::_DoCommand
  61. //
  62. // review: the rulename may need to be localizable?
  63. //
  64. HRESULT CSpTask::_DoCommand(ULONGLONG ullGramId, SPPHRASE *pPhrase, LANGID langid)
  65. {
  66. HRESULT hr = S_OK;
  67. TraceMsg(TF_GENERAL, "_DoCommand is called");
  68. if ( pPhrase->Rule.pszName )
  69. {
  70. switch (ullGramId)
  71. {
  72. case GRAM_ID_URLSPELL:
  73. case GRAM_ID_CCDICT:
  74. TraceMsg(TF_GENERAL, "Grammar is GRAM_ID_CCDICT");
  75. if (wcscmp(pPhrase->Rule.pszName, c_szDictTBRule) == 0)
  76. {
  77. hr = _HandleDictCmdGrammar(pPhrase, langid);
  78. }
  79. else
  80. hr = _HandleModeBiasCmd(pPhrase, langid);
  81. break;
  82. case GRAM_ID_CMDSHARED:
  83. TraceMsg(TF_SAPI_PERF, "Grammar is GRAM_ID_CMDSHARED");
  84. hr = _HandleShardCmdGrammar(pPhrase, langid);
  85. break;
  86. /* case GRAM_ID_NUMMODE:
  87. TraceMsg(TF_GENERAL, "Grammar is GRAM_ID_NUMMODE");
  88. hr = _HandleNumModeGrammar(pPhrase, langid);
  89. break;
  90. */
  91. case GRAM_ID_TBCMD:
  92. TraceMsg(TF_GENERAL, "Grammar is GRAM_ID_TBCMD");
  93. hr = _HandleToolBarGrammar(pPhrase, langid);
  94. break;
  95. case GRID_INTEGER_STANDALONE:
  96. TraceMsg(TF_GENERAL, "Grammar is GRID_INTEGER_STANDALONE");
  97. hr = _HandleNumITNGrammar(pPhrase, langid);
  98. break;
  99. case GRAM_ID_SPELLING:
  100. TraceMsg(TF_GENERAL, "Grammar is GRAM_ID_SPELLING");
  101. hr = _HandleSpellGrammar(pPhrase, langid);
  102. break;
  103. default:
  104. break;
  105. }
  106. if (SUCCEEDED(hr) && m_pime && m_pime->IsFocusFullAware(m_pime->_tim))
  107. {
  108. // If this is a Cicero full aware application,
  109. // we need to finalize the composition after the text has
  110. // been handled ( changed ) successfully for this command.
  111. hr = m_pime->_FinalizeComposition();
  112. }
  113. // Feeding context to the dictation grammar if it is in diction mode.
  114. if ( SUCCEEDED(hr) && m_pime && m_pime->GetDICTATIONSTAT_DictOnOff() )
  115. m_pime->_SetCurrentIPtoSR();
  116. }
  117. return hr;
  118. }
  119. //+---------------------------------------------------------------------------
  120. //
  121. // _ShowCommandOnBalloon
  122. //
  123. // Show the command text from currnet Phrase to the Balloon
  124. //----------------------------------------------------------------------------
  125. void CSpTask::_ShowCommandOnBalloon(SPPHRASE *pPhrase)
  126. {
  127. Assert(pPhrase);
  128. if (m_pime->GetSpeechUIServer())
  129. {
  130. ULONG ulStartElem, ulNumElems;
  131. CSpDynamicString dstr;
  132. ulStartElem = pPhrase->Rule.ulFirstElement;
  133. ulNumElems = pPhrase->Rule.ulCountOfElements;
  134. for (ULONG i = ulStartElem; i < ulStartElem + ulNumElems; i++ )
  135. {
  136. if (pPhrase->pElements[i].pszDisplayText)
  137. {
  138. BYTE bAttr = 0;
  139. bAttr = pPhrase->pElements[i].bDisplayAttributes;
  140. dstr.Append(pPhrase->pElements[i].pszDisplayText);
  141. if (bAttr & SPAF_ONE_TRAILING_SPACE)
  142. {
  143. dstr.Append(L" ");
  144. }
  145. else if (bAttr & SPAF_TWO_TRAILING_SPACES)
  146. {
  147. dstr.Append(L" ");
  148. }
  149. }
  150. }
  151. m_pime->GetSpeechUIServer()->UpdateBalloon(TF_LB_BALLOON_RECO, (WCHAR *)dstr, -1);
  152. }
  153. }
  154. HRESULT CSpTask::_HandleModeBiasCmd(SPPHRASE *pPhrase, LANGID langid)
  155. {
  156. HRESULT hr = S_OK;
  157. if (wcscmp(pPhrase->Rule.pszName, c_szDynUrlHist) == 0
  158. || wcscmp(pPhrase->Rule.pszName, c_szStaticUrlHist) == 0
  159. || wcscmp(pPhrase->Rule.pszName, c_szStaticUrlSpell) == 0 )
  160. {
  161. // at this moment it's pretty simple, we just handle the first element
  162. // for recognition
  163. //
  164. if ( pPhrase->pProperties && pPhrase->pProperties[0].pszValue)
  165. {
  166. if (wcscmp( pPhrase->pProperties[0].pszValue, L"dict") != 0)
  167. {
  168. hr = m_pime->InjectModebiasText(pPhrase->pProperties[0].pszValue, langid);
  169. }
  170. else
  171. {
  172. ULONG ulStartElem, ulNumElems;
  173. CSpDynamicString dstr;
  174. ulStartElem = pPhrase->Rule.ulFirstElement;
  175. ulNumElems = pPhrase->Rule.ulCountOfElements;
  176. for (ULONG i = ulStartElem; i < ulStartElem + ulNumElems; i++ )
  177. {
  178. if (pPhrase->pElements[i].pszDisplayText)
  179. {
  180. dstr.Append(pPhrase->pElements[i].pszDisplayText);
  181. }
  182. }
  183. hr = m_pime->InjectModebiasText(dstr, langid);
  184. }
  185. }
  186. }
  187. return hr;
  188. }
  189. //+---------------------------------------------------------------------------
  190. //
  191. // _HandleDictCmdGrammar
  192. //
  193. // Handle all the commands in grammar dictcmd.xml
  194. //
  195. //----------------------------------------------------------------------------
  196. HRESULT CSpTask::_HandleDictCmdGrammar(SPPHRASE *pPhrase, LANGID langid)
  197. {
  198. HRESULT hr=S_OK;
  199. Assert(pPhrase);
  200. if ( pPhrase->pProperties == NULL )
  201. return hr;
  202. if (wcscmp(pPhrase->Rule.pszName, c_szDictTBRule) == 0)
  203. {
  204. //
  205. // Handling the toolbar commands in Dictation mode.
  206. // we have at most three commands support in Dictation mode.
  207. // Microphone, Correction and Voice Command.
  208. //
  209. // If current toolbar doesn't contain the button you spoke,
  210. // what you said would be injected to the document as a dictated text.
  211. //
  212. // such as there is no "Correction" button on the toolbar, but you said "Correction",
  213. // text "Correction" should be injected to the document.
  214. //
  215. BOOL fButtonClicked = FALSE;
  216. if (m_pLangBarSink)
  217. {
  218. if ( pPhrase->pProperties[0].pszValue )
  219. {
  220. // update the balloon
  221. _ShowCommandOnBalloon(pPhrase);
  222. fButtonClicked = m_pLangBarSink->ProcessToolbarCmd(pPhrase->pProperties[0].pszValue);
  223. }
  224. }
  225. if ( fButtonClicked )
  226. {
  227. m_pime->SaveLastUsedIPRange( );
  228. m_pime->SaveIPRange(NULL);
  229. }
  230. else
  231. {
  232. // there is no such as button on the toolbar.
  233. //
  234. // Return FAIL so that the consequent functions would inject the
  235. // the RecoResult to the document.
  236. _UpdateBalloon(IDS_DICTATING, IDS_DICTATING_TOOLTIP);
  237. TraceMsg(TF_SAPI_PERF, "There is such as button on toolbar");
  238. hr = E_FAIL;
  239. }
  240. }
  241. return hr;
  242. }
  243. //+---------------------------------------------------------------------------
  244. //
  245. // _HandleShardCmdGrammar
  246. //
  247. // Handle all commands in the shared command grammar shrdcmd.xml
  248. // shared grammar is activated in both dictatin and command modes.
  249. //----------------------------------------------------------------------------
  250. HRESULT CSpTask::_HandleShardCmdGrammar(SPPHRASE *pPhrase, LANGID langid)
  251. {
  252. HRESULT hr = S_OK;
  253. Assert(pPhrase);
  254. if ( pPhrase->pProperties == NULL )
  255. return hr;
  256. ULONG idCmd;
  257. ASSERT( VT_UI4 == pPhrase->pProperties[0].vValue.vt );
  258. idCmd = (ULONG)pPhrase->pProperties[0].vValue.ulVal;
  259. if ( idCmd == 0 )
  260. {
  261. // This is the bogus command
  262. TraceMsg(TF_GENERAL, "The Bogus command is recognized!!!");
  263. return hr;
  264. }
  265. if ( 0 == wcscmp(pPhrase->Rule.pszName, c_szSelword) )
  266. {
  267. // Handel "Selword" rule
  268. hr = _HandleSelwordCmds(pPhrase, langid, idCmd);
  269. }
  270. else if ( 0 == wcscmp(pPhrase->Rule.pszName, c_szSelThrough) )
  271. {
  272. // Handle "Select Through" rule.
  273. //
  274. hr = _HandleSelectThroughCmds(pPhrase, langid, idCmd);
  275. }
  276. else if (0 == wcscmp(pPhrase->Rule.pszName, c_szSelectSimple))
  277. {
  278. // Handle some simple selection commands.
  279. hr = _HandleSelectSimpleCmds(idCmd);
  280. }
  281. else if ( 0 == wcscmp(pPhrase->Rule.pszName, c_szEditCmds))
  282. {
  283. hr = m_pime->ProcessEditCommands(idCmd);
  284. }
  285. else if ( 0 == wcscmp(pPhrase->Rule.pszName, c_szNavigationCmds))
  286. {
  287. hr = _HandleNavigationCmds(pPhrase, langid, idCmd);
  288. }
  289. else if ( 0 == wcscmp(pPhrase->Rule.pszName, c_szCasingCmds))
  290. {
  291. hr = _HandleCasingCmds(pPhrase, langid, idCmd);
  292. }
  293. else if ( 0 == wcscmp(pPhrase->Rule.pszName, c_szKeyboardCmds))
  294. {
  295. hr = _HandleKeyboardCmds(langid, idCmd);
  296. }
  297. _ShowCommandOnBalloon(pPhrase);
  298. return hr;
  299. }
  300. HRESULT CSpTask::_HandleNavigationCmds(SPPHRASE *pPhrase, LANGID langid, ULONG idCmd)
  301. {
  302. HRESULT hr = S_OK;
  303. switch (idCmd)
  304. {
  305. case Navigate_ID_Move_Home :
  306. {
  307. WCHAR wszKeys[2];
  308. wszKeys[0] = VK_HOME;
  309. wszKeys[1] = '\0';
  310. hr = m_pime->ProcessControlKeys(wszKeys, 1,langid);
  311. break;
  312. }
  313. case Navigate_ID_Move_End :
  314. {
  315. WCHAR wszKeys[2];
  316. wszKeys[0] = VK_END;
  317. wszKeys[1] = '\0';
  318. hr = m_pime->ProcessControlKeys(wszKeys, 1,langid);
  319. break;
  320. }
  321. case Navigate_ID_Go_To_Bottom :
  322. hr = m_pime->ProcessSelectWord(NULL, 0, SELECTWORD_GOTOBOTTOM);
  323. break;
  324. case Navigate_ID_Go_To_Top :
  325. hr = m_pime->ProcessSelectWord(NULL, 0, SELECTWORD_GOTOTOP);
  326. break;
  327. case Navigate_ID_INSERTBEFORE :
  328. case Navigate_ID_INSERTAFTER :
  329. {
  330. SELECTWORD_OPERATION sw_type;
  331. CSpDynamicString dstrSelected;
  332. ULONG ulLen = 0;
  333. WORD PriLangId;
  334. ULONG ulStartSelText = 0; // Start element for the selected text
  335. ULONG ulNumSelText=0; // Number of elements for the selected text.
  336. ULONG ulStartElem, ulNumElems;
  337. ULONG ulStartDelta=0, ulNumDelta=0;
  338. if ( idCmd == Navigate_ID_INSERTBEFORE )
  339. sw_type = SELECTWORD_INSERTBEFORE;
  340. else
  341. sw_type = SELECTWORD_INSERTAFTER;
  342. PriLangId = PRIMARYLANGID(langid);
  343. if ( PriLangId == LANG_ENGLISH)
  344. {
  345. ulStartDelta = 2;
  346. ulNumDelta = 2 ;
  347. }
  348. else if ( PriLangId == LANG_JAPANESE )
  349. {
  350. ulStartDelta = 0;
  351. ulNumDelta = 2;
  352. }
  353. else if (PriLangId == LANG_CHINESE)
  354. {
  355. ulStartDelta = 1;
  356. ulNumDelta = 2;
  357. }
  358. ulStartElem = pPhrase->Rule.ulFirstElement;
  359. ulNumElems = pPhrase->Rule.ulCountOfElements;
  360. ulStartSelText = ulStartElem + ulStartDelta;
  361. if (ulNumElems > ulNumDelta)
  362. ulNumSelText = ulNumElems - ulNumDelta;
  363. else
  364. ulNumSelText = 0;
  365. //
  366. // Get the text for the selection.
  367. //
  368. for (ULONG i = ulStartSelText; i < ulStartSelText + ulNumSelText; i++ )
  369. {
  370. if ( pPhrase->pElements[i].pszDisplayText)
  371. {
  372. BYTE bAttr = pPhrase->pElements[i].bDisplayAttributes;
  373. dstrSelected.Append(pPhrase->pElements[i].pszDisplayText);
  374. if ( i < ulStartSelText + ulNumSelText-1 )
  375. {
  376. if (bAttr & SPAF_ONE_TRAILING_SPACE)
  377. dstrSelected.Append(L" ");
  378. else if (bAttr & SPAF_TWO_TRAILING_SPACES)
  379. dstrSelected.Append(L" ");
  380. }
  381. }
  382. }
  383. if ( dstrSelected )
  384. ulLen = wcslen(dstrSelected);
  385. hr = m_pime->ProcessSelectWord(dstrSelected, ulLen, sw_type);
  386. break;
  387. }
  388. default :
  389. break;
  390. }
  391. return hr;
  392. }
  393. HRESULT CSpTask::_HandleKeyboardCmds(LANGID langid, ULONG idCmd)
  394. {
  395. HRESULT hr = S_OK;
  396. WCHAR wszKeys[2];
  397. wszKeys[0] = '\0';
  398. switch ( idCmd )
  399. {
  400. case Keyboard_ID_Tab :
  401. wszKeys[0] = VK_TAB;
  402. break;
  403. case Keyboard_ID_Enter :
  404. wszKeys[0] = VK_RETURN; // { 0x0d, 0x00 }
  405. break;
  406. case Keyboard_ID_Backspace :
  407. wszKeys[0] = VK_BACK;
  408. break;
  409. case Keyboard_ID_Delete :
  410. wszKeys[0] = VK_DELETE;
  411. break;
  412. case Keyboard_ID_SpaceBar :
  413. wszKeys[0] = VK_SPACE;
  414. break;
  415. case Keyboard_ID_Move_Up :
  416. wszKeys[0] = VK_UP;
  417. break;
  418. case Keyboard_ID_Move_Down :
  419. wszKeys[0] = VK_DOWN;
  420. break;
  421. case Keyboard_ID_Move_Left :
  422. wszKeys[0] = VK_LEFT;
  423. break;
  424. case Keyboard_ID_Move_Right :
  425. wszKeys[0] = VK_RIGHT;
  426. break;
  427. case Keyboard_ID_Page_Up :
  428. wszKeys[0] = VK_PRIOR;
  429. break;
  430. case Keyboard_ID_Page_Down :
  431. wszKeys[0] = VK_NEXT;
  432. break;
  433. default :
  434. break;
  435. }
  436. if ( wszKeys[0] )
  437. {
  438. wszKeys[1] = L'\0';
  439. hr = m_pime->ProcessControlKeys(wszKeys, 1,langid);
  440. }
  441. return hr;
  442. }
  443. HRESULT CSpTask::_HandleCasingCmds(SPPHRASE *pPhrase, LANGID langid, ULONG idCmd)
  444. {
  445. HRESULT hr = S_OK;
  446. CAPCOMMAND_ID idCapCmd = CAPCOMMAND_NONE;
  447. Assert(idCmd);
  448. Assert(pPhrase);
  449. switch (idCmd)
  450. {
  451. case Case_ID_CapIt :
  452. idCapCmd = CAPCOMMAND_CapIt;
  453. break;
  454. case Case_ID_AllCaps :
  455. idCapCmd = CAPCOMMAND_AllCaps;
  456. break;
  457. case Case_ID_NoCaps :
  458. idCapCmd = CAPCOMMAND_NoCaps;
  459. break;
  460. case Case_ID_CapThat :
  461. idCapCmd = CAPCOMMAND_CapThat;
  462. break;
  463. case Case_ID_AllCapsThat :
  464. idCapCmd = CAPCOMMAND_AllCapsThat;
  465. break;
  466. case Case_ID_NoCapsThat :
  467. idCapCmd = CAPCOMMAND_NoCapsThat;
  468. break;
  469. default :
  470. Assert(0);
  471. hr = E_FAIL;
  472. TraceMsg(TF_GENERAL, "Got a wrong casing command!");
  473. return hr;
  474. }
  475. if ( idCapCmd != CAPCOMMAND_NONE )
  476. {
  477. // Capitalize command is recognized.
  478. CCapCmdHandler *pCapCmdHandler;
  479. pCapCmdHandler = m_pime->GetCapCmdHandler( );
  480. if ( pCapCmdHandler )
  481. {
  482. CSpDynamicString dstrTextToCap;
  483. ULONG ulLen = 0;
  484. if ( idCapCmd > CAPCOMMAND_MinIdWithText )
  485. {
  486. ULONG ulNumCmdElem = 2;
  487. ULONG ulStartElem, ulNumElems;
  488. ulStartElem = pPhrase->Rule.ulFirstElement;
  489. ulNumElems = pPhrase->Rule.ulCountOfElements;
  490. //
  491. // the two elements are for command itself
  492. //
  493. for (ULONG i = ulStartElem+ulNumCmdElem; i < ulStartElem + ulNumElems; i++ )
  494. {
  495. if ( pPhrase->pElements[i].pszDisplayText)
  496. {
  497. BYTE bAttr = pPhrase->pElements[i].bDisplayAttributes;
  498. dstrTextToCap.Append(pPhrase->pElements[i].pszDisplayText);
  499. if (bAttr & SPAF_ONE_TRAILING_SPACE)
  500. dstrTextToCap.Append(L" ");
  501. else if (bAttr & SPAF_TWO_TRAILING_SPACES)
  502. dstrTextToCap.Append(L" ");
  503. }
  504. }
  505. if ( dstrTextToCap )
  506. ulLen = wcslen(dstrTextToCap);
  507. }
  508. pCapCmdHandler->ProcessCapCommands(idCapCmd, dstrTextToCap, ulLen);
  509. }
  510. }
  511. return hr;
  512. }
  513. HRESULT CSpTask::_HandleSelectThroughCmds(SPPHRASE *pPhrase, LANGID langid, ULONG idCmd)
  514. {
  515. HRESULT hr = S_OK;
  516. // Select xxx through yyy.
  517. // dstrText will hold both XXX and YYY.
  518. // ulLenXXX keeps the number of characters in XXX part.
  519. // ulLen keeps the char numbers of the whole text ( XXX + YYY )
  520. CSpDynamicString dstrText;
  521. ULONG ulLenXXX = 0;
  522. ULONG ulLen = 0;
  523. ULONG ulStartElem, ulNumElems;
  524. ULONG ulXYStartElem=0, ulXYNumElems=0; // points to the elements including xxx through yyy.
  525. BOOL fPassThrough = FALSE; // indicates if the "Through" is reached and handled.
  526. SELECTWORD_OPERATION sw_type = SELECTWORD_NONE;
  527. ULONG idCommand = 0;
  528. WCHAR *pwszThrough=NULL;
  529. // This rule has three properties, the second and third properties are for "select" and "through"
  530. // the mapping relationship is different based on language.
  531. const SPPHRASEPROPERTY *pPropertyFirst = pPhrase->pProperties;
  532. const SPPHRASEPROPERTY *pPropertySecond = NULL;
  533. const SPPHRASEPROPERTY *pPropertyThird = NULL;
  534. if ( !pPropertyFirst ) return hr;
  535. pPropertySecond = pPropertyFirst->pNextSibling;
  536. if ( !pPropertySecond ) return hr;
  537. pPropertyThird = pPropertySecond->pNextSibling;
  538. if ( !pPropertyThird ) return hr;
  539. ulStartElem = pPhrase->Rule.ulFirstElement;
  540. ulNumElems = pPhrase->Rule.ulCountOfElements;
  541. switch ( PRIMARYLANGID(langid) )
  542. {
  543. case LANG_ENGLISH :
  544. ulXYStartElem = ulStartElem + 1;
  545. ulXYNumElems = ulNumElems - 1 ;
  546. // the second property is for "SelectWordCommand"
  547. // the third property is for "through"
  548. idCommand = pPropertySecond->vValue.ulVal;
  549. pwszThrough = (WCHAR *)pPropertyThird->pszValue;
  550. break;
  551. case LANG_JAPANESE :
  552. ulXYStartElem = ulStartElem;
  553. ulXYNumElems = ulNumElems - 2 ;
  554. // the second property is for "through"
  555. // the third property is for "SelectWordCommand"
  556. idCommand = pPropertyThird->vValue.ulVal;
  557. pwszThrough = (WCHAR *)pPropertySecond->pszValue;
  558. break;
  559. case LANG_CHINESE :
  560. ulXYStartElem = ulStartElem + 1;
  561. ulXYNumElems = ulNumElems - 1 ;
  562. // the second property is for "SelectWordCommand"
  563. // the third property is for "through"
  564. idCommand = pPropertySecond->vValue.ulVal;
  565. pwszThrough = (WCHAR *)pPropertyThird->pszValue;
  566. break;
  567. default :
  568. break;
  569. }
  570. switch ( idCommand )
  571. {
  572. case Select_ID_SELTHROUGH :
  573. sw_type = SELECTWORD_SELTHROUGH;
  574. break;
  575. case Select_ID_DELTHROUGH :
  576. sw_type = SELECTWORD_DELTHROUGH;
  577. break;
  578. }
  579. // if we cannot find "through" word, return here.
  580. // or there is a wrong command id.
  581. if ( !pwszThrough || (sw_type == SELECTWORD_NONE)) return hr;
  582. for (ULONG i= ulXYStartElem; i< ulXYStartElem + ulXYNumElems; i++)
  583. {
  584. const WCHAR *pElemText;
  585. pElemText = pPhrase->pElements[i].pszDisplayText;
  586. if ( !pElemText )
  587. break;
  588. if ( 0 == _wcsicmp(pElemText, pwszThrough) )
  589. {
  590. // This element is "Through"
  591. BYTE bAttrPrevElem;
  592. fPassThrough = TRUE;
  593. ulLenXXX = dstrText.Length( );
  594. // Remove the trail spaces from the previous element.
  595. if ( i>1 )
  596. {
  597. bAttrPrevElem = pPhrase->pElements[i-1].bDisplayAttributes;
  598. if ( bAttrPrevElem & SPAF_ONE_TRAILING_SPACE )
  599. ulLenXXX -- ;
  600. else if (bAttrPrevElem & SPAF_TWO_TRAILING_SPACES)
  601. ulLenXXX -= 2;
  602. dstrText.TrimToSize(ulLenXXX);
  603. }
  604. }
  605. else
  606. {
  607. // This is element for XXX (if fPassThrough is FALSE ) or YYY ( if fPassThrough is TRUE)
  608. BYTE bAttr = pPhrase->pElements[i].bDisplayAttributes;
  609. dstrText.Append(pPhrase->pElements[i].pszDisplayText);
  610. if ( i < ulNumElems-1 )
  611. {
  612. if (bAttr & SPAF_ONE_TRAILING_SPACE)
  613. dstrText.Append(L" ");
  614. else if (bAttr & SPAF_TWO_TRAILING_SPACES)
  615. dstrText.Append(L" ");
  616. }
  617. }
  618. }
  619. ulLen = dstrText.Length( );
  620. if ( dstrText && ulLenXXX > 0 && ulLen > 0 )
  621. hr = m_pime->ProcessSelectWord(dstrText, ulLen, sw_type, ulLenXXX);
  622. return hr;
  623. }
  624. HRESULT CSpTask::_HandleSelectSimpleCmds(ULONG idCmd)
  625. {
  626. HRESULT hr = S_OK;
  627. // handle "SelectSimplCmds" rule.
  628. SELECTWORD_OPERATION sw_type = SELECTWORD_NONE;
  629. switch ( idCmd )
  630. {
  631. case Select_ID_UNSELECT :
  632. sw_type = SELECTWORD_UNSELECT;
  633. break;
  634. case Select_ID_SELECTPREV :
  635. sw_type = SELECTWORD_SELECTPREV;
  636. break;
  637. case Select_ID_SELECTNEXT :
  638. sw_type = SELECTWORD_SELECTNEXT;
  639. break;
  640. case Select_ID_CORRECTPREV :
  641. sw_type = SELECTWORD_CORRECTPREV;
  642. break;
  643. case Select_ID_CORRECTNEXT :
  644. sw_type = SELECTWORD_CORRECTNEXT;
  645. break;
  646. case Select_ID_SELSENTENCE :
  647. sw_type = SELECTWORD_SELSENTENCE;
  648. break;
  649. case Select_ID_SELPARAGRAPH :
  650. sw_type = SELECTWORD_SELPARAGRAPH;
  651. break;
  652. case Select_ID_SELWORD :
  653. sw_type = SELECTWORD_SELWORD;
  654. break;
  655. case Select_ID_SelectThat :
  656. sw_type = SELECTWORD_SELTHAT;
  657. break;
  658. case Select_ID_SelectAll :
  659. hr = m_pime->ProcessEditCommands(Select_ID_SelectAll);
  660. break;
  661. case Select_ID_DeletePhrase :
  662. // call a function to remove an entire phrase
  663. hr = m_pime->EraseLastPhrase();
  664. break;
  665. case Select_ID_Convert :
  666. hr = m_pime->CorrectThat();
  667. break;
  668. case Select_ID_Finalize :
  669. hr = m_pime->FinalizeAllCompositions( );
  670. break;
  671. default :
  672. hr = E_FAIL;
  673. Assert(0);
  674. return hr;
  675. }
  676. if ( sw_type != SELECTWORD_NONE )
  677. hr = m_pime->ProcessSelectWord(NULL, 0, sw_type);
  678. return hr;
  679. }
  680. HRESULT CSpTask::_HandleSelwordCmds(SPPHRASE *pPhrase, LANGID langid, ULONG idCmd)
  681. {
  682. HRESULT hr = S_OK;
  683. Assert(idCmd);
  684. // handle "Select Word"
  685. // Get the real word/phrase which will be selected.
  686. // the phrase will contain following elements:
  687. //
  688. // <select|delete|Correct <Word0> <Word1> <word2> ...
  689. //
  690. // the first element must be gateway word.
  691. //
  692. CSpDynamicString dstrSelected;
  693. ULONG ulLen = 0;
  694. ULONG ulStartSelText = 0; // Start element for the selected text
  695. ULONG ulNumSelText=0; // Number of elements for the selected text.
  696. ULONG ulStartElem, ulNumElems;
  697. ULONG ulStartDelta=0, ulNumDelta=0;
  698. SELECTWORD_OPERATION sw_type;
  699. switch (idCmd)
  700. {
  701. case Select_ID_SELECT :
  702. sw_type = SELECTWORD_SELECT;
  703. break;
  704. case Select_ID_DELETE :
  705. sw_type = SELECTWORD_DELETE;
  706. break;
  707. case Select_ID_CORRECT :
  708. sw_type = SELECTWORD_CORRECT;
  709. break;
  710. default :
  711. Assert(0);
  712. hr = E_FAIL;
  713. return hr;
  714. }
  715. WORD prilangid;
  716. prilangid = PRIMARYLANGID(langid);
  717. if ((prilangid == LANG_ENGLISH) || (prilangid == LANG_CHINESE))
  718. {
  719. ulStartDelta = 1;
  720. ulNumDelta = 1;
  721. }
  722. else if (prilangid == LANG_JAPANESE)
  723. {
  724. ulStartDelta = 0;
  725. ulNumDelta = 2;
  726. }
  727. // Get the start element and number of elements for the text to select.
  728. ulStartElem = pPhrase->Rule.ulFirstElement;
  729. ulNumElems = pPhrase->Rule.ulCountOfElements;
  730. ulStartSelText = ulStartElem + ulStartDelta;
  731. if (ulNumElems > ulNumDelta)
  732. ulNumSelText = ulNumElems - ulNumDelta;
  733. else
  734. ulNumSelText = 0;
  735. //
  736. // Get the text for the selection.
  737. //
  738. for (ULONG i = ulStartSelText; i < ulStartSelText + ulNumSelText; i++ )
  739. {
  740. if ( pPhrase->pElements[i].pszDisplayText)
  741. {
  742. BYTE bAttr = pPhrase->pElements[i].bDisplayAttributes;
  743. dstrSelected.Append(pPhrase->pElements[i].pszDisplayText);
  744. if ( i < ulStartSelText + ulNumSelText-1 )
  745. {
  746. if (bAttr & SPAF_ONE_TRAILING_SPACE)
  747. dstrSelected.Append(L" ");
  748. else if (bAttr & SPAF_TWO_TRAILING_SPACES)
  749. dstrSelected.Append(L" ");
  750. }
  751. }
  752. }
  753. if ( dstrSelected )
  754. ulLen = wcslen(dstrSelected);
  755. if ( ulLen )
  756. {
  757. // check if this is "Select All" or "Select That".
  758. if ( sw_type == SELECTWORD_SELECT )
  759. {
  760. if ( _wcsicmp(dstrSelected, CRStr(IDS_SPCMD_SELECT_ALL) ) == 0 )
  761. {
  762. hr = m_pime->ProcessEditCommands(Select_ID_SelectAll);
  763. return hr;
  764. }
  765. if ( _wcsicmp(dstrSelected, CRStr(IDS_SPCMD_SELECT_THAT)) == 0 )
  766. sw_type = SELECTWORD_SELTHAT;
  767. }
  768. // redirect "Correct <TEXTBUF:That>" to a simple command "Correct That"
  769. if ( sw_type == SELECTWORD_CORRECT )
  770. {
  771. if ( _wcsicmp(dstrSelected, CRStr(IDS_SPCMD_SELECT_THAT)) == 0 )
  772. {
  773. hr = m_pime->CorrectThat();
  774. return hr;
  775. }
  776. }
  777. hr = m_pime->ProcessSelectWord(dstrSelected, ulLen, sw_type);
  778. }
  779. return hr;
  780. }
  781. /*
  782. HRESULT CSpTask::_HandleNumModeGrammar(SPPHRASE *pPhrase, LANGID langid)
  783. {
  784. HRESULT hr = S_OK;
  785. const WCHAR c_szNumeric[] = L"number";
  786. const WCHAR c_sz1stDigit[] = L"1st_digit";
  787. const WCHAR c_sz2ndDigit[] = L"2nd_digit";
  788. const WCHAR c_sz3rdDigit[] = L"3rd_digit";
  789. if (wcscmp(pPhrase->Rule.pszName, c_szNumeric) == 0)
  790. {
  791. // Mode bias support
  792. if (pPhrase->pProperties)
  793. {
  794. CSpDynamicString dstr;
  795. for (const SPPHRASEPROPERTY *pProp=pPhrase->pProperties; pProp != NULL; pProp = pProp->pNextSibling)
  796. {
  797. if (wcscmp(pProp->pszName, c_sz3rdDigit) == 0 ||
  798. wcscmp(pProp->pszName, c_sz2ndDigit) == 0 ||
  799. wcscmp(pProp->pszName, c_sz1stDigit) == 0)
  800. {
  801. dstr.Append(pProp->pszValue);
  802. }
  803. }
  804. hr = m_pime->InjectText(dstr, langid);
  805. }
  806. }
  807. return hr;
  808. }
  809. */
  810. //+---------------------------------------------------------------------------
  811. //
  812. // _HandleToolBarGrammar
  813. //
  814. // Handle toolbar commands when command mode.
  815. //----------------------------------------------------------------------------
  816. HRESULT CSpTask::_HandleToolBarGrammar(SPPHRASE *pPhrase, LANGID langid)
  817. {
  818. HRESULT hr = S_OK;
  819. Assert(pPhrase);
  820. if (m_pLangBarSink)
  821. {
  822. // get the toolbar cmd rule name to check match
  823. if (0 == wcscmp(pPhrase->Rule.pszName, m_pLangBarSink->GetToolbarCommandRuleName()))
  824. {
  825. // update the balloon
  826. _ShowCommandOnBalloon(pPhrase);
  827. // call the handler then
  828. const SPPHRASEPROPERTY *pProp;
  829. for (pProp=pPhrase->pProperties; pProp != NULL; pProp = pProp->pNextSibling)
  830. {
  831. m_pLangBarSink->ProcessToolbarCmd(pProp->pszName);
  832. }
  833. m_pime->SaveLastUsedIPRange( );
  834. m_pime->SaveIPRange(NULL);
  835. }
  836. }
  837. return hr;
  838. }
  839. //+------------------------------------------------------------------------
  840. //
  841. // _HandleNumITNGrammar
  842. //
  843. // Handle the number grammar.
  844. //
  845. //+-------------------------------------------------------------------------
  846. HRESULT CSpTask::_HandleNumITNGrammar(SPPHRASE *pPhrase, LANGID langid)
  847. {
  848. HRESULT hr = S_OK;
  849. Assert(pPhrase);
  850. if (S_OK == _EnsureSimpleITN())
  851. {
  852. DOUBLE dblVal;
  853. WCHAR wszVal[128];
  854. hr = m_pITNFunc->InterpretNumberSimple(pPhrase->pProperties,
  855. &dblVal, wszVal, ARRAYSIZE(wszVal));
  856. if (S_OK == hr)
  857. {
  858. int iLen = wcslen(wszVal);
  859. if ( (iLen > 0) && (iLen < 127) && (wszVal[iLen-1] != L' ') )
  860. {
  861. // Add one trailing space
  862. wszVal[iLen] = L' ';
  863. wszVal[iLen + 1] = L'\0';
  864. }
  865. hr = m_pime->InjectText(wszVal, langid);
  866. }
  867. }
  868. return hr;
  869. }
  870. //+---------------------------------------------------------------------------
  871. //
  872. // _HandleSpellGrammar
  873. //
  874. // Handing "Spell It", "Spell That", "Spelling Mode" etc. commands
  875. //----------------------------------------------------------------------------
  876. HRESULT CSpTask::_HandleSpellGrammar(SPPHRASE *pPhrase, LANGID langid)
  877. {
  878. HRESULT hr = S_OK;
  879. Assert(pPhrase);
  880. if (0 == wcscmp(pPhrase->Rule.pszName, c_szSpelling))
  881. {
  882. // Handel "Spell It"
  883. ULONG ulStartElem, ulNumElems;
  884. CSpDynamicString dstr;
  885. ulStartElem = pPhrase->Rule.ulFirstElement;
  886. ulNumElems = pPhrase->Rule.ulCountOfElements;
  887. //
  888. // the first element is for the command itself
  889. //
  890. for (ULONG i = ulStartElem+1; i < ulStartElem + ulNumElems; i++ )
  891. {
  892. if ( pPhrase->pElements[i].pszDisplayText)
  893. {
  894. dstr.Append(pPhrase->pElements[i].pszDisplayText);
  895. //
  896. // only the last element needs the attribute
  897. // handling
  898. //
  899. if (i == ulStartElem + ulNumElems - 1)
  900. {
  901. BYTE bAttr = pPhrase->pElements[i].bDisplayAttributes;
  902. if (bAttr & SPAF_ONE_TRAILING_SPACE)
  903. {
  904. dstr.Append(L" ");
  905. }
  906. else if (bAttr & SPAF_TWO_TRAILING_SPACES)
  907. {
  908. dstr.Append(L" ");
  909. }
  910. }
  911. }
  912. }
  913. hr = m_pime->ProcessSpellIt(dstr, langid);
  914. }
  915. else if (0 == wcscmp(pPhrase->Rule.pszName, c_szSpellMode))
  916. {
  917. // Handle "Spell Mode" or "Spell That"
  918. if (pPhrase->pProperties == NULL
  919. || pPhrase->pProperties[0].pszValue == NULL)
  920. {
  921. // this only happens when we hit the bogus word which
  922. // was added for weight modification
  923. }
  924. else if (0 == wcscmp(pPhrase->pProperties[0].pszValue, c_szSpellingMode))
  925. {
  926. // Handel "Spelling Mode"
  927. _SetSpellingGrammarStatus(TRUE, TRUE);
  928. m_cpRecoCtxt->Resume(0);
  929. m_pime->SaveLastUsedIPRange( );
  930. m_pime->SaveIPRange(NULL);
  931. _ShowCommandOnBalloon(pPhrase);
  932. }
  933. else if (0 == wcscmp(pPhrase->pProperties[0].pszValue, c_szSpellThat))
  934. {
  935. // Handle "Spell That"
  936. hr = m_pime->ProcessSpellThat( );
  937. _ShowCommandOnBalloon(pPhrase);
  938. }
  939. }
  940. return hr;
  941. }
  942. //
  943. // Hanlders for some commands in CSapiIMX
  944. //
  945. // Move them from sapilayr.cpp
  946. //
  947. //+---------------------------------------------------------------------------
  948. //
  949. // CSapiIMX::EraseLastPhrase
  950. //
  951. // synopsis - cleans up the feedback UI
  952. // GUID - specifies which feedback UI bar to erase
  953. //
  954. //---------------------------------------------------------------------------+
  955. HRESULT CSapiIMX::EraseLastPhrase(void)
  956. {
  957. return _RequestEditSession(ESCB_KILLLASTPHRASE, TF_ES_READWRITE);
  958. }
  959. //+---------------------------------------------------------------------------
  960. //
  961. // CSapiIMX::ProcessEditCommands(void)
  962. //
  963. // Handle command keys like "Undo That", "Cut That", "Copy That", "Paste That".
  964. //
  965. //---------------------------------------------------------------------------+
  966. HRESULT CSapiIMX::ProcessEditCommands(LONG idSharedCmd)
  967. {
  968. HRESULT hr = E_FAIL;
  969. ESDATA esData;
  970. memset(&esData, 0, sizeof(ESDATA));
  971. esData.lData1 = (LONG_PTR)idSharedCmd;
  972. hr = _RequestEditSession(ESCB_PROCESS_EDIT_COMMAND, TF_ES_READWRITE, &esData);
  973. return hr;
  974. }
  975. //+---------------------------------------------------------------------------
  976. //
  977. // _ProcessEditCommands
  978. //
  979. // Edit session functions for edit commands handling
  980. //
  981. //----------------------------------------------------------------------------
  982. HRESULT CSapiIMX::_ProcessEditCommands(TfEditCookie ec, ITfContext *pic, LONG idSharedCmd)
  983. {
  984. HRESULT hr = S_OK;
  985. if ( !pic )
  986. return E_INVALIDARG;
  987. CDocStatus ds(pic);
  988. if (ds.IsReadOnly())
  989. return S_OK;
  990. /*
  991. CComPtr<ITfRange> cpInsertionPoint;
  992. if ( cpInsertionPoint = GetSavedIP() )
  993. {
  994. // Determine if the saved IP was on this context.
  995. // if not we just ignore that
  996. CComPtr<ITfContext> cpic;
  997. hr = cpInsertionPoint->GetContext(&cpic);
  998. if (S_OK != hr || cpic != pic)
  999. {
  1000. cpInsertionPoint.Release();
  1001. }
  1002. }
  1003. if (!cpInsertionPoint)
  1004. {
  1005. hr = GetSelectionSimple(ec, pic, &cpInsertionPoint);
  1006. }
  1007. if (hr == S_OK)
  1008. {
  1009. // finalize the previous input for now
  1010. hr = _FinalizePrevComp(ec, pic, cpInsertionPoint);
  1011. }
  1012. */
  1013. if ( hr == S_OK )
  1014. {
  1015. // Handle the cmd by simulating the corresponding key events.
  1016. BYTE vkChar = 0;
  1017. switch ( idSharedCmd )
  1018. {
  1019. case Edit_ID_Undo :
  1020. vkChar = (BYTE)'Z';
  1021. break;
  1022. case Edit_ID_Cut :
  1023. case Edit_ID_Copy :
  1024. {
  1025. CComPtr<ITfRange> cpRange;
  1026. _GetCmdThatRange(ec, pic, &cpRange);
  1027. if ( cpRange )
  1028. SetSelectionSimple(ec, pic, cpRange);
  1029. if (idSharedCmd == Edit_ID_Cut)
  1030. vkChar = (BYTE)'X';
  1031. else
  1032. vkChar = (BYTE)'C';
  1033. break;
  1034. }
  1035. case Edit_ID_Paste :
  1036. vkChar = (BYTE)'V';
  1037. break;
  1038. case Select_ID_SelectAll :
  1039. vkChar = (BYTE)'A';
  1040. break;
  1041. }
  1042. if ( vkChar )
  1043. {
  1044. m_ulSimulatedKey = 2; // it will simulate two key strokes.
  1045. keybd_event((BYTE)VK_CONTROL, 0, 0, 0);
  1046. keybd_event(vkChar, 0, 0, 0);
  1047. keybd_event(vkChar, 0, KEYEVENTF_KEYUP, 0);
  1048. keybd_event((BYTE)VK_CONTROL, 0, KEYEVENTF_KEYUP, 0);
  1049. }
  1050. }
  1051. SaveLastUsedIPRange( );
  1052. SaveIPRange(NULL);
  1053. return hr;
  1054. }
  1055. //+---------------------------------------------------------------------------
  1056. //
  1057. // CSapiIMX::ProcessControlKeys(void)
  1058. //
  1059. // Handle command keys like "Tab" or "Enter".
  1060. //
  1061. //---------------------------------------------------------------------------+
  1062. HRESULT CSapiIMX::ProcessControlKeys(WCHAR *pwszKeys, ULONG ulLen, LANGID langid)
  1063. {
  1064. HRESULT hr = E_FAIL;
  1065. if ( pwszKeys == NULL || ulLen == 0 )
  1066. return E_INVALIDARG;
  1067. ESDATA esData;
  1068. memset(&esData, 0, sizeof(ESDATA));
  1069. esData.pData = (void *)pwszKeys;
  1070. esData.uByte = (ulLen+1) * sizeof(WCHAR);
  1071. esData.lData1 = (LONG_PTR)ulLen;
  1072. esData.lData2 = (LONG_PTR)langid;
  1073. hr = _RequestEditSession(ESCB_PROCESSCONTROLKEY, TF_ES_READWRITE, &esData);
  1074. return hr;
  1075. }
  1076. //+---------------------------------------------------------------------------
  1077. //
  1078. // CSapiIMX::ProcessSpellIt(WCHAR *pwszText, LANGID langid)
  1079. //
  1080. //
  1081. //---------------------------------------------------------------------------+
  1082. HRESULT CSapiIMX::ProcessSpellIt(WCHAR *pwszText, LANGID langid)
  1083. {
  1084. if ( pwszText == NULL )
  1085. return E_INVALIDARG;
  1086. ESDATA esData;
  1087. memset(&esData, 0, sizeof(ESDATA));
  1088. esData.pData = (void *)pwszText;
  1089. esData.uByte = (wcslen(pwszText)+1) * sizeof(WCHAR);
  1090. esData.lData1 = (LONG_PTR)langid;
  1091. return _RequestEditSession(ESCB_PROCESS_SPELL_IT, TF_ES_READWRITE, &esData);
  1092. }
  1093. //+---------------------------------------------------------------------------
  1094. //
  1095. // CSapiIMX::_ProcessSpellIt(WCHAR *pwszText, LANGID langid)
  1096. //
  1097. // Edit Session function for ESCB_PROCESS_SPELL_IT
  1098. //
  1099. //---------------------------------------------------------------------------+
  1100. HRESULT CSapiIMX::_ProcessSpellIt(TfEditCookie ec, ITfContext *pic, WCHAR *pwszText, LANGID langid)
  1101. {
  1102. HRESULT hr = S_OK;
  1103. hr = _ProcessSpelledText(ec, pic, pwszText, langid);
  1104. SaveLastUsedIPRange( );
  1105. SaveIPRange(NULL);
  1106. return hr;
  1107. }
  1108. //+---------------------------------------------------------------------------
  1109. //
  1110. // CSapiIMX::ProcessSpellThat(void)
  1111. //
  1112. //
  1113. //---------------------------------------------------------------------------+
  1114. HRESULT CSapiIMX::ProcessSpellThat( )
  1115. {
  1116. return _RequestEditSession(ESCB_PROCESS_SPELL_THAT, TF_ES_READWRITE);
  1117. }
  1118. //+---------------------------------------------------------------------------
  1119. //
  1120. // CSapiIMX::_ProcessSpellThat(void)
  1121. //
  1122. // Edit Session function for ESCB_PROCESS_SPELL_THAT
  1123. //
  1124. //---------------------------------------------------------------------------+
  1125. HRESULT CSapiIMX::_ProcessSpellThat(TfEditCookie ec, ITfContext *pic)
  1126. {
  1127. HRESULT hr = S_OK;
  1128. // Get the previous dictated phrase and mark it as selection.
  1129. CComPtr<ITfRange> cpRange;
  1130. hr = _GetCmdThatRange(ec, pic, &cpRange);
  1131. if ( hr == S_OK )
  1132. hr =SetSelectionSimple(ec, pic, cpRange);
  1133. // Then turn on spell mode.
  1134. if ( hr == S_OK && m_pCSpTask )
  1135. {
  1136. hr = m_pCSpTask->_SetSpellingGrammarStatus(TRUE, TRUE);
  1137. }
  1138. SaveLastUsedIPRange( );
  1139. SaveIPRange(NULL);
  1140. return hr;
  1141. }
  1142. //+---------------------------------------------------------------------------
  1143. //
  1144. // _ProcessControlKeys
  1145. //
  1146. // Real function to handle the control key commands like Tab or Enter.
  1147. //
  1148. // It will finialize the previous composing text ( actually characters in
  1149. // Feedback UI).
  1150. //
  1151. // and then simulate the related key events.
  1152. //---------------------------------------------------------------------------+
  1153. HRESULT CSapiIMX::_ProcessControlKeys(TfEditCookie ec, ITfContext *pic, WCHAR *pwszKey, ULONG ulLen, LANGID langid)
  1154. {
  1155. HRESULT hr = S_OK;
  1156. if ( !pic || !pwszKey || (ulLen == 0) )
  1157. return E_INVALIDARG;
  1158. CDocStatus ds(pic);
  1159. if (ds.IsReadOnly())
  1160. return S_OK;
  1161. /*
  1162. CComPtr<ITfRange> cpInsertionPoint;
  1163. if ( cpInsertionPoint = GetSavedIP() )
  1164. {
  1165. // Determine if the saved IP was on this context.
  1166. // if not we just ignore that
  1167. CComPtr<ITfContext> cpic;
  1168. hr = cpInsertionPoint->GetContext(&cpic);
  1169. if (S_OK != hr || cpic != pic)
  1170. {
  1171. cpInsertionPoint.Release();
  1172. }
  1173. }
  1174. if (!cpInsertionPoint)
  1175. {
  1176. hr = GetSelectionSimple(ec, pic, &cpInsertionPoint);
  1177. }
  1178. if (hr == S_OK)
  1179. {
  1180. // finalize the previous input for now
  1181. //
  1182. hr = _FinalizePrevComp(ec, pic, cpInsertionPoint);
  1183. */
  1184. if ( hr == S_OK )
  1185. {
  1186. BOOL fHandleKeySucceed = TRUE;
  1187. // simulate the keys.
  1188. for (ULONG i=0; i<ulLen; i++)
  1189. {
  1190. if ( !HandleKey( pwszKey[i] ) )
  1191. {
  1192. fHandleKeySucceed = FALSE;
  1193. break;
  1194. }
  1195. }
  1196. if ( fHandleKeySucceed == FALSE )
  1197. {
  1198. hr = InjectText(pwszKey, langid);
  1199. }
  1200. }
  1201. // }
  1202. SaveLastUsedIPRange( );
  1203. SaveIPRange(NULL);
  1204. return hr;
  1205. }
  1206. //+---------------------------------------------------------------------------
  1207. //
  1208. // _KillLastPhrase
  1209. //
  1210. //---------------------------------------------------------------------------+
  1211. HRESULT CSapiIMX::_KillLastPhrase(TfEditCookie ec, ITfContext *pic)
  1212. {
  1213. HRESULT hr = E_FAIL;
  1214. #ifdef _TRY_LATER_FOR_AIMM
  1215. TF_STATUS tss;
  1216. BOOL fCiceroNative = TRUE;
  1217. hr = pic->GetStatus(&tss);
  1218. if (S_OK == hr)
  1219. {
  1220. //
  1221. // see if the client now is AIMM
  1222. //
  1223. if (tss.dwStaticFlags & TS_SS_TRANSITORY)
  1224. {
  1225. fCiceroNative = FALSE;
  1226. }
  1227. }
  1228. #endif
  1229. CComPtr<ITfRange> cpRange;
  1230. hr = _GetCmdThatRange(ec, pic, &cpRange);
  1231. if ( hr == S_OK && cpRange )
  1232. {
  1233. // found our own input and it is not empty
  1234. _CheckStartComposition(ec, cpRange);
  1235. hr = cpRange->SetText(ec, 0, NULL, 0);
  1236. // trigger redrawing
  1237. SetSelectionSimple(ec, pic, cpRange);
  1238. }
  1239. #ifdef _TRY_LATER_FOR_AIMM
  1240. else if (fCiceroNative == FALSE)
  1241. {
  1242. CComPtr<ITfRange> pRStart;
  1243. CComPtr<ITfRange> pREnd;
  1244. BOOL fEmpty;
  1245. hr = pic->GetStart(&pRStart);
  1246. if (S_OK == hr)
  1247. hr = pic->GetEnd(&pREnd);
  1248. if (S_OK == hr)
  1249. {
  1250. hr = pRStart->IsEquealStart(ec, pREnd, TF_ANCHOR_END, &fEmpty);
  1251. }
  1252. if (S_OK == hr && fEmpty)
  1253. {
  1254. // - VK_CONTROL(down) + VK_SHIFT(down) + VK_LEFT(down), then
  1255. // - VK_LEFT(up) + VK_SHIFT(up) + VK_CONTROL(up), then
  1256. // - VK_DELETE(down) + VK_DELETE(up)
  1257. //
  1258. keybd_event((BYTE)VK_CONTROL, 0, 0, 0);
  1259. keybd_event((BYTE)VK_SHIFT, 0, 0, 0);
  1260. keybd_event((BYTE)VK_LEFT, 0, 0, 0);
  1261. keybd_event((BYTE)VK_CONTROL, 0, KEYEVENTF_KEYUP, 0);
  1262. keybd_event((BYTE)VK_SHIFT, 0, KEYEVENTF_KEYUP, 0);
  1263. keybd_event((BYTE)VK_LEFT, 0, KEYEVENTF_KEYUP, 0);
  1264. keybd_event((BYTE)VK_DELETE, 0, 0, 0);
  1265. keybd_event((BYTE)VK_DELETE, 0, KEYEVENTF_KEYUP, 0);
  1266. }
  1267. }
  1268. #endif // _TRY_LATER_FOR_AIMM
  1269. //
  1270. // these moved from HandleRecognition
  1271. //
  1272. SaveLastUsedIPRange( );
  1273. SaveIPRange(NULL);
  1274. return hr;
  1275. }
  1276. //
  1277. // CSapiIMX::_GetCmdThatRange
  1278. //
  1279. // We have many "xxx That" commands, all these commands require to get
  1280. // a right range. this method will supply a united way to get the right range.
  1281. //
  1282. // As a Rule,
  1283. //
  1284. // If there is a selection before "XXX That" is recognized, we just use
  1285. // that range.
  1286. // If there is no selection, we will try to find the previous dictated phrase
  1287. // or a word case by case.
  1288. //
  1289. //
  1290. // ppRange will hold the returned Range interface pointer, it is caller's
  1291. // responsibility to release the range object.
  1292. //
  1293. #define MAX_WORD_LENGTH 32
  1294. HRESULT CSapiIMX::_GetCmdThatRange(TfEditCookie ec, ITfContext *pic, ITfRange **ppRange)
  1295. {
  1296. HRESULT hr = S_OK;
  1297. Assert(pic);
  1298. Assert(ppRange);
  1299. CComPtr<ITfRange> cpIP;
  1300. CComPtr<ITfRange> cpRange;
  1301. BOOL fEmpty = TRUE;
  1302. BOOL fGotRange = FALSE;
  1303. TraceMsg(TF_GENERAL, "GetCmdThatRange is called");
  1304. *ppRange = NULL;
  1305. // Get the current IP.
  1306. hr = GetSelectionSimple(ec, pic, &cpIP);
  1307. // is ip empty or selection
  1308. if ( hr == S_OK )
  1309. hr = cpIP->IsEmpty(ec, &fEmpty);
  1310. if ( hr == S_OK )
  1311. {
  1312. if ( !fEmpty )
  1313. {
  1314. // current ip is a selection, just use it.
  1315. hr = cpIP->Clone(&cpRange);
  1316. if ( hr == S_OK )
  1317. fGotRange = TRUE;
  1318. }
  1319. else
  1320. {
  1321. WORD prilangid;
  1322. prilangid = PRIMARYLANGID(m_langid);
  1323. if ((prilangid == LANG_CHINESE) || (prilangid == LANG_JAPANESE) || !_GetIPChangeStatus( ))
  1324. {
  1325. // If the lang is East Asian, we always try to get the previous dictated phrase first.
  1326. // if lang is English, and there is no ip change since last dictated phrase,
  1327. // we will try to get the previous dictated phrase first.
  1328. fGotRange = _FindPrevComp(ec, pic, cpIP, &cpRange, GUID_ATTR_SAPI_INPUT);
  1329. if ( !fGotRange )
  1330. {
  1331. // With Office Auto-Correction, the static GUID_PROP_SAPI_DISPATTR property
  1332. // on the auto-corrected range could be destroyed.
  1333. // In this case, we may want to rely on our custom property GUID_PROP_SAPIRESULTOBJECT
  1334. // to find the real previous dictated phrase.
  1335. CComPtr<ITfRange> cpRangeTmp;
  1336. CComPtr<ITfProperty> cpProp;
  1337. LONG l;
  1338. hr = cpIP->Clone(&cpRangeTmp);
  1339. // shift to the previous position
  1340. if ( hr == S_OK )
  1341. hr = cpRangeTmp->ShiftStart(ec, -1, &l, NULL);
  1342. if ( hr == S_OK )
  1343. hr = pic->GetProperty(GUID_PROP_SAPIRESULTOBJECT, &cpProp);
  1344. if ( hr == S_OK)
  1345. hr = cpProp->FindRange(ec, cpRangeTmp, &cpRange, TF_ANCHOR_START);
  1346. if (hr == S_OK && cpRange)
  1347. hr = cpRange->IsEmpty(ec, &fEmpty);
  1348. fGotRange = !fEmpty;
  1349. }
  1350. }
  1351. }
  1352. }
  1353. if ( hr == S_OK && !fGotRange )
  1354. {
  1355. // IP must be empty
  1356. // There is no previous dictated phrase, or IP is moved since last dictation.
  1357. // we try to get the word around the ip.
  1358. long cch=0;
  1359. ULONG ulch =0;
  1360. CComPtr<ITfRange> cpRangeTmp;
  1361. WCHAR pwszTextBuf[MAX_WORD_LENGTH+1];
  1362. ULONG ulLeft=0, ulRight=0;
  1363. // Find the first delimiter character in left side from the current IP.
  1364. hr = cpIP->Clone(&cpRangeTmp);
  1365. if ( hr == S_OK )
  1366. hr = cpRangeTmp->ShiftStart(ec, MAX_WORD_LENGTH * (-1), &cch, NULL);
  1367. if ( hr == S_OK && cch < 0 )
  1368. hr = cpRangeTmp->GetText(ec, 0, pwszTextBuf, MAX_WORD_LENGTH, &ulch);
  1369. if ( hr == S_OK && ulch > 0 )
  1370. {
  1371. pwszTextBuf[ulch] = L'\0';
  1372. for ( long i=(long)ulch-1; i>=0; i-- )
  1373. {
  1374. WCHAR wch;
  1375. wch = pwszTextBuf[i];
  1376. if ( iswpunct(wch) || iswspace(wch) )
  1377. break;
  1378. ulLeft++;
  1379. }
  1380. }
  1381. // Find the first delimiter character in right side from the right
  1382. if ( hr == S_OK && cpRangeTmp )
  1383. {
  1384. cpRangeTmp.Release( );
  1385. hr = cpIP->Clone(&cpRangeTmp);
  1386. }
  1387. if ( hr == S_OK )
  1388. hr = cpRangeTmp->ShiftEnd(ec, MAX_WORD_LENGTH, &cch, NULL);
  1389. if ( hr == S_OK && cch > 0 )
  1390. hr = cpRangeTmp->GetText(ec, 0, pwszTextBuf, MAX_WORD_LENGTH, &ulch);
  1391. if ( hr == S_OK && ulch > 0 )
  1392. {
  1393. pwszTextBuf[ulch] = L'\0';
  1394. for ( long i=0; i<(long)ulch; i++ )
  1395. {
  1396. WCHAR wch;
  1397. wch = pwszTextBuf[i];
  1398. if ( iswpunct(wch) || iswspace(wch) )
  1399. break;
  1400. ulRight++;
  1401. }
  1402. }
  1403. if ( hr == S_OK )
  1404. hr = cpRangeTmp->Collapse(ec, TF_ANCHOR_START);
  1405. // Move end anchor right number
  1406. if (hr == S_OK && ulRight > 0 )
  1407. hr = cpRangeTmp->ShiftEnd(ec, ulRight, &cch, NULL);
  1408. // Move start anchor left number.
  1409. if ( hr == S_OK && ulLeft > 0 )
  1410. hr = cpRangeTmp->ShiftStart(ec, (long)ulLeft * (-1), &cch, NULL);
  1411. if ( hr == S_OK )
  1412. {
  1413. hr = cpRangeTmp->Clone(&cpRange);
  1414. fGotRange = TRUE;
  1415. }
  1416. }
  1417. if ( hr == S_OK && fGotRange && cpRange )
  1418. {
  1419. *ppRange = cpRange;
  1420. (*ppRange)->AddRef( );
  1421. TraceMsg(TF_GENERAL, "Got the xxx That range!");
  1422. }
  1423. return hr;
  1424. }