// // command.cpp // This file contains methods related to C&C grammars' commands handling. // // #include "private.h" #include "globals.h" #include "sapilayr.h" #include "nui.h" #include "cresstr.h" #define LB_ID_Correction 200 #define LB_ID_Microphone 201 #define LB_ID_VoiceCmd 202 #define Select_ID_SELECT 1 #define Select_ID_DELETE 2 #define Select_ID_CORRECT 3 #define Select_ID_SELTHROUGH 4 #define Select_ID_DELTHROUGH 5 #define Select_ID_UNSELECT 6 #define Select_ID_SELECTPREV 7 #define Select_ID_SELECTNEXT 8 #define Select_ID_CORRECTPREV 9 #define Select_ID_CORRECTNEXT 10 #define Select_ID_SELSENTENCE 11 #define Select_ID_SELPARAGRAPH 12 #define Select_ID_SELWORD 13 #define Select_ID_SelectAll 14 #define Select_ID_DeletePhrase 15 // Scratch That #define Select_ID_Convert 16 #define Select_ID_SelectThat 17 #define Select_ID_Finalize 18 #define Navigate_ID_INSERTBEFORE 20 #define Navigate_ID_INSERTAFTER 21 #define Navigate_ID_Go_To_Bottom 22 #define Navigate_ID_Go_To_Top 23 #define Navigate_ID_Move_Home 24 #define Navigate_ID_Move_End 25 #define Edit_ID_Undo 30 #define Edit_ID_Cut 31 #define Edit_ID_Copy 32 #define Edit_ID_Paste 33 #define Keyboard_ID_Move_Up 40 #define Keyboard_ID_Move_Down 41 #define Keyboard_ID_Move_Left 42 #define Keyboard_ID_Move_Right 43 #define Keyboard_ID_Page_Up 44 #define Keyboard_ID_Page_Down 45 #define Keyboard_ID_Tab 46 #define Keyboard_ID_Enter 47 #define Keyboard_ID_Backspace 48 #define Keyboard_ID_Delete 49 #define Keyboard_ID_SpaceBar 50 #define Case_ID_CapIt 70 #define Case_ID_AllCaps 71 #define Case_ID_NoCaps 72 #define Case_ID_CapThat 73 #define Case_ID_AllCapsThat 74 #define Case_ID_NoCapsThat 75 // // CSpTask::_DoCommand // // review: the rulename may need to be localizable? // HRESULT CSpTask::_DoCommand(ULONGLONG ullGramId, SPPHRASE *pPhrase, LANGID langid) { HRESULT hr = S_OK; TraceMsg(TF_GENERAL, "_DoCommand is called"); if ( pPhrase->Rule.pszName ) { switch (ullGramId) { case GRAM_ID_URLSPELL: case GRAM_ID_CCDICT: TraceMsg(TF_GENERAL, "Grammar is GRAM_ID_CCDICT"); if (wcscmp(pPhrase->Rule.pszName, c_szDictTBRule) == 0) { hr = _HandleDictCmdGrammar(pPhrase, langid); } else hr = _HandleModeBiasCmd(pPhrase, langid); break; case GRAM_ID_CMDSHARED: TraceMsg(TF_SAPI_PERF, "Grammar is GRAM_ID_CMDSHARED"); hr = _HandleShardCmdGrammar(pPhrase, langid); break; /* case GRAM_ID_NUMMODE: TraceMsg(TF_GENERAL, "Grammar is GRAM_ID_NUMMODE"); hr = _HandleNumModeGrammar(pPhrase, langid); break; */ case GRAM_ID_TBCMD: TraceMsg(TF_GENERAL, "Grammar is GRAM_ID_TBCMD"); hr = _HandleToolBarGrammar(pPhrase, langid); break; case GRID_INTEGER_STANDALONE: TraceMsg(TF_GENERAL, "Grammar is GRID_INTEGER_STANDALONE"); hr = _HandleNumITNGrammar(pPhrase, langid); break; case GRAM_ID_SPELLING: TraceMsg(TF_GENERAL, "Grammar is GRAM_ID_SPELLING"); hr = _HandleSpellGrammar(pPhrase, langid); break; default: break; } if (SUCCEEDED(hr) && m_pime && m_pime->IsFocusFullAware(m_pime->_tim)) { // If this is a Cicero full aware application, // we need to finalize the composition after the text has // been handled ( changed ) successfully for this command. hr = m_pime->_FinalizeComposition(); } // Feeding context to the dictation grammar if it is in diction mode. if ( SUCCEEDED(hr) && m_pime && m_pime->GetDICTATIONSTAT_DictOnOff() ) m_pime->_SetCurrentIPtoSR(); } return hr; } //+--------------------------------------------------------------------------- // // _ShowCommandOnBalloon // // Show the command text from currnet Phrase to the Balloon //---------------------------------------------------------------------------- void CSpTask::_ShowCommandOnBalloon(SPPHRASE *pPhrase) { Assert(pPhrase); if (m_pime->GetSpeechUIServer()) { ULONG ulStartElem, ulNumElems; CSpDynamicString dstr; ulStartElem = pPhrase->Rule.ulFirstElement; ulNumElems = pPhrase->Rule.ulCountOfElements; for (ULONG i = ulStartElem; i < ulStartElem + ulNumElems; i++ ) { if (pPhrase->pElements[i].pszDisplayText) { BYTE bAttr = 0; bAttr = pPhrase->pElements[i].bDisplayAttributes; dstr.Append(pPhrase->pElements[i].pszDisplayText); if (bAttr & SPAF_ONE_TRAILING_SPACE) { dstr.Append(L" "); } else if (bAttr & SPAF_TWO_TRAILING_SPACES) { dstr.Append(L" "); } } } m_pime->GetSpeechUIServer()->UpdateBalloon(TF_LB_BALLOON_RECO, (WCHAR *)dstr, -1); } } HRESULT CSpTask::_HandleModeBiasCmd(SPPHRASE *pPhrase, LANGID langid) { HRESULT hr = S_OK; if (wcscmp(pPhrase->Rule.pszName, c_szDynUrlHist) == 0 || wcscmp(pPhrase->Rule.pszName, c_szStaticUrlHist) == 0 || wcscmp(pPhrase->Rule.pszName, c_szStaticUrlSpell) == 0 ) { // at this moment it's pretty simple, we just handle the first element // for recognition // if ( pPhrase->pProperties && pPhrase->pProperties[0].pszValue) { if (wcscmp( pPhrase->pProperties[0].pszValue, L"dict") != 0) { hr = m_pime->InjectModebiasText(pPhrase->pProperties[0].pszValue, langid); } else { ULONG ulStartElem, ulNumElems; CSpDynamicString dstr; ulStartElem = pPhrase->Rule.ulFirstElement; ulNumElems = pPhrase->Rule.ulCountOfElements; for (ULONG i = ulStartElem; i < ulStartElem + ulNumElems; i++ ) { if (pPhrase->pElements[i].pszDisplayText) { dstr.Append(pPhrase->pElements[i].pszDisplayText); } } hr = m_pime->InjectModebiasText(dstr, langid); } } } return hr; } //+--------------------------------------------------------------------------- // // _HandleDictCmdGrammar // // Handle all the commands in grammar dictcmd.xml // //---------------------------------------------------------------------------- HRESULT CSpTask::_HandleDictCmdGrammar(SPPHRASE *pPhrase, LANGID langid) { HRESULT hr=S_OK; Assert(pPhrase); if ( pPhrase->pProperties == NULL ) return hr; if (wcscmp(pPhrase->Rule.pszName, c_szDictTBRule) == 0) { // // Handling the toolbar commands in Dictation mode. // we have at most three commands support in Dictation mode. // Microphone, Correction and Voice Command. // // If current toolbar doesn't contain the button you spoke, // what you said would be injected to the document as a dictated text. // // such as there is no "Correction" button on the toolbar, but you said "Correction", // text "Correction" should be injected to the document. // BOOL fButtonClicked = FALSE; if (m_pLangBarSink) { if ( pPhrase->pProperties[0].pszValue ) { // update the balloon _ShowCommandOnBalloon(pPhrase); fButtonClicked = m_pLangBarSink->ProcessToolbarCmd(pPhrase->pProperties[0].pszValue); } } if ( fButtonClicked ) { m_pime->SaveLastUsedIPRange( ); m_pime->SaveIPRange(NULL); } else { // there is no such as button on the toolbar. // // Return FAIL so that the consequent functions would inject the // the RecoResult to the document. _UpdateBalloon(IDS_DICTATING, IDS_DICTATING_TOOLTIP); TraceMsg(TF_SAPI_PERF, "There is such as button on toolbar"); hr = E_FAIL; } } return hr; } //+--------------------------------------------------------------------------- // // _HandleShardCmdGrammar // // Handle all commands in the shared command grammar shrdcmd.xml // shared grammar is activated in both dictatin and command modes. //---------------------------------------------------------------------------- HRESULT CSpTask::_HandleShardCmdGrammar(SPPHRASE *pPhrase, LANGID langid) { HRESULT hr = S_OK; Assert(pPhrase); if ( pPhrase->pProperties == NULL ) return hr; ULONG idCmd; ASSERT( VT_UI4 == pPhrase->pProperties[0].vValue.vt ); idCmd = (ULONG)pPhrase->pProperties[0].vValue.ulVal; if ( idCmd == 0 ) { // This is the bogus command TraceMsg(TF_GENERAL, "The Bogus command is recognized!!!"); return hr; } if ( 0 == wcscmp(pPhrase->Rule.pszName, c_szSelword) ) { // Handel "Selword" rule hr = _HandleSelwordCmds(pPhrase, langid, idCmd); } else if ( 0 == wcscmp(pPhrase->Rule.pszName, c_szSelThrough) ) { // Handle "Select Through" rule. // hr = _HandleSelectThroughCmds(pPhrase, langid, idCmd); } else if (0 == wcscmp(pPhrase->Rule.pszName, c_szSelectSimple)) { // Handle some simple selection commands. hr = _HandleSelectSimpleCmds(idCmd); } else if ( 0 == wcscmp(pPhrase->Rule.pszName, c_szEditCmds)) { hr = m_pime->ProcessEditCommands(idCmd); } else if ( 0 == wcscmp(pPhrase->Rule.pszName, c_szNavigationCmds)) { hr = _HandleNavigationCmds(pPhrase, langid, idCmd); } else if ( 0 == wcscmp(pPhrase->Rule.pszName, c_szCasingCmds)) { hr = _HandleCasingCmds(pPhrase, langid, idCmd); } else if ( 0 == wcscmp(pPhrase->Rule.pszName, c_szKeyboardCmds)) { hr = _HandleKeyboardCmds(langid, idCmd); } _ShowCommandOnBalloon(pPhrase); return hr; } HRESULT CSpTask::_HandleNavigationCmds(SPPHRASE *pPhrase, LANGID langid, ULONG idCmd) { HRESULT hr = S_OK; switch (idCmd) { case Navigate_ID_Move_Home : { WCHAR wszKeys[2]; wszKeys[0] = VK_HOME; wszKeys[1] = '\0'; hr = m_pime->ProcessControlKeys(wszKeys, 1,langid); break; } case Navigate_ID_Move_End : { WCHAR wszKeys[2]; wszKeys[0] = VK_END; wszKeys[1] = '\0'; hr = m_pime->ProcessControlKeys(wszKeys, 1,langid); break; } case Navigate_ID_Go_To_Bottom : hr = m_pime->ProcessSelectWord(NULL, 0, SELECTWORD_GOTOBOTTOM); break; case Navigate_ID_Go_To_Top : hr = m_pime->ProcessSelectWord(NULL, 0, SELECTWORD_GOTOTOP); break; case Navigate_ID_INSERTBEFORE : case Navigate_ID_INSERTAFTER : { SELECTWORD_OPERATION sw_type; CSpDynamicString dstrSelected; ULONG ulLen = 0; WORD PriLangId; ULONG ulStartSelText = 0; // Start element for the selected text ULONG ulNumSelText=0; // Number of elements for the selected text. ULONG ulStartElem, ulNumElems; ULONG ulStartDelta=0, ulNumDelta=0; if ( idCmd == Navigate_ID_INSERTBEFORE ) sw_type = SELECTWORD_INSERTBEFORE; else sw_type = SELECTWORD_INSERTAFTER; PriLangId = PRIMARYLANGID(langid); if ( PriLangId == LANG_ENGLISH) { ulStartDelta = 2; ulNumDelta = 2 ; } else if ( PriLangId == LANG_JAPANESE ) { ulStartDelta = 0; ulNumDelta = 2; } else if (PriLangId == LANG_CHINESE) { ulStartDelta = 1; ulNumDelta = 2; } ulStartElem = pPhrase->Rule.ulFirstElement; ulNumElems = pPhrase->Rule.ulCountOfElements; ulStartSelText = ulStartElem + ulStartDelta; if (ulNumElems > ulNumDelta) ulNumSelText = ulNumElems - ulNumDelta; else ulNumSelText = 0; // // Get the text for the selection. // for (ULONG i = ulStartSelText; i < ulStartSelText + ulNumSelText; i++ ) { if ( pPhrase->pElements[i].pszDisplayText) { BYTE bAttr = pPhrase->pElements[i].bDisplayAttributes; dstrSelected.Append(pPhrase->pElements[i].pszDisplayText); if ( i < ulStartSelText + ulNumSelText-1 ) { if (bAttr & SPAF_ONE_TRAILING_SPACE) dstrSelected.Append(L" "); else if (bAttr & SPAF_TWO_TRAILING_SPACES) dstrSelected.Append(L" "); } } } if ( dstrSelected ) ulLen = wcslen(dstrSelected); hr = m_pime->ProcessSelectWord(dstrSelected, ulLen, sw_type); break; } default : break; } return hr; } HRESULT CSpTask::_HandleKeyboardCmds(LANGID langid, ULONG idCmd) { HRESULT hr = S_OK; WCHAR wszKeys[2]; wszKeys[0] = '\0'; switch ( idCmd ) { case Keyboard_ID_Tab : wszKeys[0] = VK_TAB; break; case Keyboard_ID_Enter : wszKeys[0] = VK_RETURN; // { 0x0d, 0x00 } break; case Keyboard_ID_Backspace : wszKeys[0] = VK_BACK; break; case Keyboard_ID_Delete : wszKeys[0] = VK_DELETE; break; case Keyboard_ID_SpaceBar : wszKeys[0] = VK_SPACE; break; case Keyboard_ID_Move_Up : wszKeys[0] = VK_UP; break; case Keyboard_ID_Move_Down : wszKeys[0] = VK_DOWN; break; case Keyboard_ID_Move_Left : wszKeys[0] = VK_LEFT; break; case Keyboard_ID_Move_Right : wszKeys[0] = VK_RIGHT; break; case Keyboard_ID_Page_Up : wszKeys[0] = VK_PRIOR; break; case Keyboard_ID_Page_Down : wszKeys[0] = VK_NEXT; break; default : break; } if ( wszKeys[0] ) { wszKeys[1] = L'\0'; hr = m_pime->ProcessControlKeys(wszKeys, 1,langid); } return hr; } HRESULT CSpTask::_HandleCasingCmds(SPPHRASE *pPhrase, LANGID langid, ULONG idCmd) { HRESULT hr = S_OK; CAPCOMMAND_ID idCapCmd = CAPCOMMAND_NONE; Assert(idCmd); Assert(pPhrase); switch (idCmd) { case Case_ID_CapIt : idCapCmd = CAPCOMMAND_CapIt; break; case Case_ID_AllCaps : idCapCmd = CAPCOMMAND_AllCaps; break; case Case_ID_NoCaps : idCapCmd = CAPCOMMAND_NoCaps; break; case Case_ID_CapThat : idCapCmd = CAPCOMMAND_CapThat; break; case Case_ID_AllCapsThat : idCapCmd = CAPCOMMAND_AllCapsThat; break; case Case_ID_NoCapsThat : idCapCmd = CAPCOMMAND_NoCapsThat; break; default : Assert(0); hr = E_FAIL; TraceMsg(TF_GENERAL, "Got a wrong casing command!"); return hr; } if ( idCapCmd != CAPCOMMAND_NONE ) { // Capitalize command is recognized. CCapCmdHandler *pCapCmdHandler; pCapCmdHandler = m_pime->GetCapCmdHandler( ); if ( pCapCmdHandler ) { CSpDynamicString dstrTextToCap; ULONG ulLen = 0; if ( idCapCmd > CAPCOMMAND_MinIdWithText ) { ULONG ulNumCmdElem = 2; ULONG ulStartElem, ulNumElems; ulStartElem = pPhrase->Rule.ulFirstElement; ulNumElems = pPhrase->Rule.ulCountOfElements; // // the two elements are for command itself // for (ULONG i = ulStartElem+ulNumCmdElem; i < ulStartElem + ulNumElems; i++ ) { if ( pPhrase->pElements[i].pszDisplayText) { BYTE bAttr = pPhrase->pElements[i].bDisplayAttributes; dstrTextToCap.Append(pPhrase->pElements[i].pszDisplayText); if (bAttr & SPAF_ONE_TRAILING_SPACE) dstrTextToCap.Append(L" "); else if (bAttr & SPAF_TWO_TRAILING_SPACES) dstrTextToCap.Append(L" "); } } if ( dstrTextToCap ) ulLen = wcslen(dstrTextToCap); } pCapCmdHandler->ProcessCapCommands(idCapCmd, dstrTextToCap, ulLen); } } return hr; } HRESULT CSpTask::_HandleSelectThroughCmds(SPPHRASE *pPhrase, LANGID langid, ULONG idCmd) { HRESULT hr = S_OK; // Select xxx through yyy. // dstrText will hold both XXX and YYY. // ulLenXXX keeps the number of characters in XXX part. // ulLen keeps the char numbers of the whole text ( XXX + YYY ) CSpDynamicString dstrText; ULONG ulLenXXX = 0; ULONG ulLen = 0; ULONG ulStartElem, ulNumElems; ULONG ulXYStartElem=0, ulXYNumElems=0; // points to the elements including xxx through yyy. BOOL fPassThrough = FALSE; // indicates if the "Through" is reached and handled. SELECTWORD_OPERATION sw_type = SELECTWORD_NONE; ULONG idCommand = 0; WCHAR *pwszThrough=NULL; // This rule has three properties, the second and third properties are for "select" and "through" // the mapping relationship is different based on language. const SPPHRASEPROPERTY *pPropertyFirst = pPhrase->pProperties; const SPPHRASEPROPERTY *pPropertySecond = NULL; const SPPHRASEPROPERTY *pPropertyThird = NULL; if ( !pPropertyFirst ) return hr; pPropertySecond = pPropertyFirst->pNextSibling; if ( !pPropertySecond ) return hr; pPropertyThird = pPropertySecond->pNextSibling; if ( !pPropertyThird ) return hr; ulStartElem = pPhrase->Rule.ulFirstElement; ulNumElems = pPhrase->Rule.ulCountOfElements; switch ( PRIMARYLANGID(langid) ) { case LANG_ENGLISH : ulXYStartElem = ulStartElem + 1; ulXYNumElems = ulNumElems - 1 ; // the second property is for "SelectWordCommand" // the third property is for "through" idCommand = pPropertySecond->vValue.ulVal; pwszThrough = (WCHAR *)pPropertyThird->pszValue; break; case LANG_JAPANESE : ulXYStartElem = ulStartElem; ulXYNumElems = ulNumElems - 2 ; // the second property is for "through" // the third property is for "SelectWordCommand" idCommand = pPropertyThird->vValue.ulVal; pwszThrough = (WCHAR *)pPropertySecond->pszValue; break; case LANG_CHINESE : ulXYStartElem = ulStartElem + 1; ulXYNumElems = ulNumElems - 1 ; // the second property is for "SelectWordCommand" // the third property is for "through" idCommand = pPropertySecond->vValue.ulVal; pwszThrough = (WCHAR *)pPropertyThird->pszValue; break; default : break; } switch ( idCommand ) { case Select_ID_SELTHROUGH : sw_type = SELECTWORD_SELTHROUGH; break; case Select_ID_DELTHROUGH : sw_type = SELECTWORD_DELTHROUGH; break; } // if we cannot find "through" word, return here. // or there is a wrong command id. if ( !pwszThrough || (sw_type == SELECTWORD_NONE)) return hr; for (ULONG i= ulXYStartElem; i< ulXYStartElem + ulXYNumElems; i++) { const WCHAR *pElemText; pElemText = pPhrase->pElements[i].pszDisplayText; if ( !pElemText ) break; if ( 0 == _wcsicmp(pElemText, pwszThrough) ) { // This element is "Through" BYTE bAttrPrevElem; fPassThrough = TRUE; ulLenXXX = dstrText.Length( ); // Remove the trail spaces from the previous element. if ( i>1 ) { bAttrPrevElem = pPhrase->pElements[i-1].bDisplayAttributes; if ( bAttrPrevElem & SPAF_ONE_TRAILING_SPACE ) ulLenXXX -- ; else if (bAttrPrevElem & SPAF_TWO_TRAILING_SPACES) ulLenXXX -= 2; dstrText.TrimToSize(ulLenXXX); } } else { // This is element for XXX (if fPassThrough is FALSE ) or YYY ( if fPassThrough is TRUE) BYTE bAttr = pPhrase->pElements[i].bDisplayAttributes; dstrText.Append(pPhrase->pElements[i].pszDisplayText); if ( i < ulNumElems-1 ) { if (bAttr & SPAF_ONE_TRAILING_SPACE) dstrText.Append(L" "); else if (bAttr & SPAF_TWO_TRAILING_SPACES) dstrText.Append(L" "); } } } ulLen = dstrText.Length( ); if ( dstrText && ulLenXXX > 0 && ulLen > 0 ) hr = m_pime->ProcessSelectWord(dstrText, ulLen, sw_type, ulLenXXX); return hr; } HRESULT CSpTask::_HandleSelectSimpleCmds(ULONG idCmd) { HRESULT hr = S_OK; // handle "SelectSimplCmds" rule. SELECTWORD_OPERATION sw_type = SELECTWORD_NONE; switch ( idCmd ) { case Select_ID_UNSELECT : sw_type = SELECTWORD_UNSELECT; break; case Select_ID_SELECTPREV : sw_type = SELECTWORD_SELECTPREV; break; case Select_ID_SELECTNEXT : sw_type = SELECTWORD_SELECTNEXT; break; case Select_ID_CORRECTPREV : sw_type = SELECTWORD_CORRECTPREV; break; case Select_ID_CORRECTNEXT : sw_type = SELECTWORD_CORRECTNEXT; break; case Select_ID_SELSENTENCE : sw_type = SELECTWORD_SELSENTENCE; break; case Select_ID_SELPARAGRAPH : sw_type = SELECTWORD_SELPARAGRAPH; break; case Select_ID_SELWORD : sw_type = SELECTWORD_SELWORD; break; case Select_ID_SelectThat : sw_type = SELECTWORD_SELTHAT; break; case Select_ID_SelectAll : hr = m_pime->ProcessEditCommands(Select_ID_SelectAll); break; case Select_ID_DeletePhrase : // call a function to remove an entire phrase hr = m_pime->EraseLastPhrase(); break; case Select_ID_Convert : hr = m_pime->CorrectThat(); break; case Select_ID_Finalize : hr = m_pime->FinalizeAllCompositions( ); break; default : hr = E_FAIL; Assert(0); return hr; } if ( sw_type != SELECTWORD_NONE ) hr = m_pime->ProcessSelectWord(NULL, 0, sw_type); return hr; } HRESULT CSpTask::_HandleSelwordCmds(SPPHRASE *pPhrase, LANGID langid, ULONG idCmd) { HRESULT hr = S_OK; Assert(idCmd); // handle "Select Word" // Get the real word/phrase which will be selected. // the phrase will contain following elements: // // ... // // the first element must be gateway word. // CSpDynamicString dstrSelected; ULONG ulLen = 0; ULONG ulStartSelText = 0; // Start element for the selected text ULONG ulNumSelText=0; // Number of elements for the selected text. ULONG ulStartElem, ulNumElems; ULONG ulStartDelta=0, ulNumDelta=0; SELECTWORD_OPERATION sw_type; switch (idCmd) { case Select_ID_SELECT : sw_type = SELECTWORD_SELECT; break; case Select_ID_DELETE : sw_type = SELECTWORD_DELETE; break; case Select_ID_CORRECT : sw_type = SELECTWORD_CORRECT; break; default : Assert(0); hr = E_FAIL; return hr; } WORD prilangid; prilangid = PRIMARYLANGID(langid); if ((prilangid == LANG_ENGLISH) || (prilangid == LANG_CHINESE)) { ulStartDelta = 1; ulNumDelta = 1; } else if (prilangid == LANG_JAPANESE) { ulStartDelta = 0; ulNumDelta = 2; } // Get the start element and number of elements for the text to select. ulStartElem = pPhrase->Rule.ulFirstElement; ulNumElems = pPhrase->Rule.ulCountOfElements; ulStartSelText = ulStartElem + ulStartDelta; if (ulNumElems > ulNumDelta) ulNumSelText = ulNumElems - ulNumDelta; else ulNumSelText = 0; // // Get the text for the selection. // for (ULONG i = ulStartSelText; i < ulStartSelText + ulNumSelText; i++ ) { if ( pPhrase->pElements[i].pszDisplayText) { BYTE bAttr = pPhrase->pElements[i].bDisplayAttributes; dstrSelected.Append(pPhrase->pElements[i].pszDisplayText); if ( i < ulStartSelText + ulNumSelText-1 ) { if (bAttr & SPAF_ONE_TRAILING_SPACE) dstrSelected.Append(L" "); else if (bAttr & SPAF_TWO_TRAILING_SPACES) dstrSelected.Append(L" "); } } } if ( dstrSelected ) ulLen = wcslen(dstrSelected); if ( ulLen ) { // check if this is "Select All" or "Select That". if ( sw_type == SELECTWORD_SELECT ) { if ( _wcsicmp(dstrSelected, CRStr(IDS_SPCMD_SELECT_ALL) ) == 0 ) { hr = m_pime->ProcessEditCommands(Select_ID_SelectAll); return hr; } if ( _wcsicmp(dstrSelected, CRStr(IDS_SPCMD_SELECT_THAT)) == 0 ) sw_type = SELECTWORD_SELTHAT; } // redirect "Correct " to a simple command "Correct That" if ( sw_type == SELECTWORD_CORRECT ) { if ( _wcsicmp(dstrSelected, CRStr(IDS_SPCMD_SELECT_THAT)) == 0 ) { hr = m_pime->CorrectThat(); return hr; } } hr = m_pime->ProcessSelectWord(dstrSelected, ulLen, sw_type); } return hr; } /* HRESULT CSpTask::_HandleNumModeGrammar(SPPHRASE *pPhrase, LANGID langid) { HRESULT hr = S_OK; const WCHAR c_szNumeric[] = L"number"; const WCHAR c_sz1stDigit[] = L"1st_digit"; const WCHAR c_sz2ndDigit[] = L"2nd_digit"; const WCHAR c_sz3rdDigit[] = L"3rd_digit"; if (wcscmp(pPhrase->Rule.pszName, c_szNumeric) == 0) { // Mode bias support if (pPhrase->pProperties) { CSpDynamicString dstr; for (const SPPHRASEPROPERTY *pProp=pPhrase->pProperties; pProp != NULL; pProp = pProp->pNextSibling) { if (wcscmp(pProp->pszName, c_sz3rdDigit) == 0 || wcscmp(pProp->pszName, c_sz2ndDigit) == 0 || wcscmp(pProp->pszName, c_sz1stDigit) == 0) { dstr.Append(pProp->pszValue); } } hr = m_pime->InjectText(dstr, langid); } } return hr; } */ //+--------------------------------------------------------------------------- // // _HandleToolBarGrammar // // Handle toolbar commands when command mode. //---------------------------------------------------------------------------- HRESULT CSpTask::_HandleToolBarGrammar(SPPHRASE *pPhrase, LANGID langid) { HRESULT hr = S_OK; Assert(pPhrase); if (m_pLangBarSink) { // get the toolbar cmd rule name to check match if (0 == wcscmp(pPhrase->Rule.pszName, m_pLangBarSink->GetToolbarCommandRuleName())) { // update the balloon _ShowCommandOnBalloon(pPhrase); // call the handler then const SPPHRASEPROPERTY *pProp; for (pProp=pPhrase->pProperties; pProp != NULL; pProp = pProp->pNextSibling) { m_pLangBarSink->ProcessToolbarCmd(pProp->pszName); } m_pime->SaveLastUsedIPRange( ); m_pime->SaveIPRange(NULL); } } return hr; } //+------------------------------------------------------------------------ // // _HandleNumITNGrammar // // Handle the number grammar. // //+------------------------------------------------------------------------- HRESULT CSpTask::_HandleNumITNGrammar(SPPHRASE *pPhrase, LANGID langid) { HRESULT hr = S_OK; Assert(pPhrase); if (S_OK == _EnsureSimpleITN()) { DOUBLE dblVal; WCHAR wszVal[128]; hr = m_pITNFunc->InterpretNumberSimple(pPhrase->pProperties, &dblVal, wszVal, ARRAYSIZE(wszVal)); if (S_OK == hr) { int iLen = wcslen(wszVal); if ( (iLen > 0) && (iLen < 127) && (wszVal[iLen-1] != L' ') ) { // Add one trailing space wszVal[iLen] = L' '; wszVal[iLen + 1] = L'\0'; } hr = m_pime->InjectText(wszVal, langid); } } return hr; } //+--------------------------------------------------------------------------- // // _HandleSpellGrammar // // Handing "Spell It", "Spell That", "Spelling Mode" etc. commands //---------------------------------------------------------------------------- HRESULT CSpTask::_HandleSpellGrammar(SPPHRASE *pPhrase, LANGID langid) { HRESULT hr = S_OK; Assert(pPhrase); if (0 == wcscmp(pPhrase->Rule.pszName, c_szSpelling)) { // Handel "Spell It" ULONG ulStartElem, ulNumElems; CSpDynamicString dstr; ulStartElem = pPhrase->Rule.ulFirstElement; ulNumElems = pPhrase->Rule.ulCountOfElements; // // the first element is for the command itself // for (ULONG i = ulStartElem+1; i < ulStartElem + ulNumElems; i++ ) { if ( pPhrase->pElements[i].pszDisplayText) { dstr.Append(pPhrase->pElements[i].pszDisplayText); // // only the last element needs the attribute // handling // if (i == ulStartElem + ulNumElems - 1) { BYTE bAttr = pPhrase->pElements[i].bDisplayAttributes; if (bAttr & SPAF_ONE_TRAILING_SPACE) { dstr.Append(L" "); } else if (bAttr & SPAF_TWO_TRAILING_SPACES) { dstr.Append(L" "); } } } } hr = m_pime->ProcessSpellIt(dstr, langid); } else if (0 == wcscmp(pPhrase->Rule.pszName, c_szSpellMode)) { // Handle "Spell Mode" or "Spell That" if (pPhrase->pProperties == NULL || pPhrase->pProperties[0].pszValue == NULL) { // this only happens when we hit the bogus word which // was added for weight modification } else if (0 == wcscmp(pPhrase->pProperties[0].pszValue, c_szSpellingMode)) { // Handel "Spelling Mode" _SetSpellingGrammarStatus(TRUE, TRUE); m_cpRecoCtxt->Resume(0); m_pime->SaveLastUsedIPRange( ); m_pime->SaveIPRange(NULL); _ShowCommandOnBalloon(pPhrase); } else if (0 == wcscmp(pPhrase->pProperties[0].pszValue, c_szSpellThat)) { // Handle "Spell That" hr = m_pime->ProcessSpellThat( ); _ShowCommandOnBalloon(pPhrase); } } return hr; } // // Hanlders for some commands in CSapiIMX // // Move them from sapilayr.cpp // //+--------------------------------------------------------------------------- // // CSapiIMX::EraseLastPhrase // // synopsis - cleans up the feedback UI // GUID - specifies which feedback UI bar to erase // //---------------------------------------------------------------------------+ HRESULT CSapiIMX::EraseLastPhrase(void) { return _RequestEditSession(ESCB_KILLLASTPHRASE, TF_ES_READWRITE); } //+--------------------------------------------------------------------------- // // CSapiIMX::ProcessEditCommands(void) // // Handle command keys like "Undo That", "Cut That", "Copy That", "Paste That". // //---------------------------------------------------------------------------+ HRESULT CSapiIMX::ProcessEditCommands(LONG idSharedCmd) { HRESULT hr = E_FAIL; ESDATA esData; memset(&esData, 0, sizeof(ESDATA)); esData.lData1 = (LONG_PTR)idSharedCmd; hr = _RequestEditSession(ESCB_PROCESS_EDIT_COMMAND, TF_ES_READWRITE, &esData); return hr; } //+--------------------------------------------------------------------------- // // _ProcessEditCommands // // Edit session functions for edit commands handling // //---------------------------------------------------------------------------- HRESULT CSapiIMX::_ProcessEditCommands(TfEditCookie ec, ITfContext *pic, LONG idSharedCmd) { HRESULT hr = S_OK; if ( !pic ) return E_INVALIDARG; CDocStatus ds(pic); if (ds.IsReadOnly()) return S_OK; /* CComPtr cpInsertionPoint; if ( cpInsertionPoint = GetSavedIP() ) { // Determine if the saved IP was on this context. // if not we just ignore that CComPtr cpic; hr = cpInsertionPoint->GetContext(&cpic); if (S_OK != hr || cpic != pic) { cpInsertionPoint.Release(); } } if (!cpInsertionPoint) { hr = GetSelectionSimple(ec, pic, &cpInsertionPoint); } if (hr == S_OK) { // finalize the previous input for now hr = _FinalizePrevComp(ec, pic, cpInsertionPoint); } */ if ( hr == S_OK ) { // Handle the cmd by simulating the corresponding key events. BYTE vkChar = 0; switch ( idSharedCmd ) { case Edit_ID_Undo : vkChar = (BYTE)'Z'; break; case Edit_ID_Cut : case Edit_ID_Copy : { CComPtr cpRange; _GetCmdThatRange(ec, pic, &cpRange); if ( cpRange ) SetSelectionSimple(ec, pic, cpRange); if (idSharedCmd == Edit_ID_Cut) vkChar = (BYTE)'X'; else vkChar = (BYTE)'C'; break; } case Edit_ID_Paste : vkChar = (BYTE)'V'; break; case Select_ID_SelectAll : vkChar = (BYTE)'A'; break; } if ( vkChar ) { m_ulSimulatedKey = 2; // it will simulate two key strokes. keybd_event((BYTE)VK_CONTROL, 0, 0, 0); keybd_event(vkChar, 0, 0, 0); keybd_event(vkChar, 0, KEYEVENTF_KEYUP, 0); keybd_event((BYTE)VK_CONTROL, 0, KEYEVENTF_KEYUP, 0); } } SaveLastUsedIPRange( ); SaveIPRange(NULL); return hr; } //+--------------------------------------------------------------------------- // // CSapiIMX::ProcessControlKeys(void) // // Handle command keys like "Tab" or "Enter". // //---------------------------------------------------------------------------+ HRESULT CSapiIMX::ProcessControlKeys(WCHAR *pwszKeys, ULONG ulLen, LANGID langid) { HRESULT hr = E_FAIL; if ( pwszKeys == NULL || ulLen == 0 ) return E_INVALIDARG; ESDATA esData; memset(&esData, 0, sizeof(ESDATA)); esData.pData = (void *)pwszKeys; esData.uByte = (ulLen+1) * sizeof(WCHAR); esData.lData1 = (LONG_PTR)ulLen; esData.lData2 = (LONG_PTR)langid; hr = _RequestEditSession(ESCB_PROCESSCONTROLKEY, TF_ES_READWRITE, &esData); return hr; } //+--------------------------------------------------------------------------- // // CSapiIMX::ProcessSpellIt(WCHAR *pwszText, LANGID langid) // // //---------------------------------------------------------------------------+ HRESULT CSapiIMX::ProcessSpellIt(WCHAR *pwszText, LANGID langid) { if ( pwszText == NULL ) return E_INVALIDARG; ESDATA esData; memset(&esData, 0, sizeof(ESDATA)); esData.pData = (void *)pwszText; esData.uByte = (wcslen(pwszText)+1) * sizeof(WCHAR); esData.lData1 = (LONG_PTR)langid; return _RequestEditSession(ESCB_PROCESS_SPELL_IT, TF_ES_READWRITE, &esData); } //+--------------------------------------------------------------------------- // // CSapiIMX::_ProcessSpellIt(WCHAR *pwszText, LANGID langid) // // Edit Session function for ESCB_PROCESS_SPELL_IT // //---------------------------------------------------------------------------+ HRESULT CSapiIMX::_ProcessSpellIt(TfEditCookie ec, ITfContext *pic, WCHAR *pwszText, LANGID langid) { HRESULT hr = S_OK; hr = _ProcessSpelledText(ec, pic, pwszText, langid); SaveLastUsedIPRange( ); SaveIPRange(NULL); return hr; } //+--------------------------------------------------------------------------- // // CSapiIMX::ProcessSpellThat(void) // // //---------------------------------------------------------------------------+ HRESULT CSapiIMX::ProcessSpellThat( ) { return _RequestEditSession(ESCB_PROCESS_SPELL_THAT, TF_ES_READWRITE); } //+--------------------------------------------------------------------------- // // CSapiIMX::_ProcessSpellThat(void) // // Edit Session function for ESCB_PROCESS_SPELL_THAT // //---------------------------------------------------------------------------+ HRESULT CSapiIMX::_ProcessSpellThat(TfEditCookie ec, ITfContext *pic) { HRESULT hr = S_OK; // Get the previous dictated phrase and mark it as selection. CComPtr cpRange; hr = _GetCmdThatRange(ec, pic, &cpRange); if ( hr == S_OK ) hr =SetSelectionSimple(ec, pic, cpRange); // Then turn on spell mode. if ( hr == S_OK && m_pCSpTask ) { hr = m_pCSpTask->_SetSpellingGrammarStatus(TRUE, TRUE); } SaveLastUsedIPRange( ); SaveIPRange(NULL); return hr; } //+--------------------------------------------------------------------------- // // _ProcessControlKeys // // Real function to handle the control key commands like Tab or Enter. // // It will finialize the previous composing text ( actually characters in // Feedback UI). // // and then simulate the related key events. //---------------------------------------------------------------------------+ HRESULT CSapiIMX::_ProcessControlKeys(TfEditCookie ec, ITfContext *pic, WCHAR *pwszKey, ULONG ulLen, LANGID langid) { HRESULT hr = S_OK; if ( !pic || !pwszKey || (ulLen == 0) ) return E_INVALIDARG; CDocStatus ds(pic); if (ds.IsReadOnly()) return S_OK; /* CComPtr cpInsertionPoint; if ( cpInsertionPoint = GetSavedIP() ) { // Determine if the saved IP was on this context. // if not we just ignore that CComPtr cpic; hr = cpInsertionPoint->GetContext(&cpic); if (S_OK != hr || cpic != pic) { cpInsertionPoint.Release(); } } if (!cpInsertionPoint) { hr = GetSelectionSimple(ec, pic, &cpInsertionPoint); } if (hr == S_OK) { // finalize the previous input for now // hr = _FinalizePrevComp(ec, pic, cpInsertionPoint); */ if ( hr == S_OK ) { BOOL fHandleKeySucceed = TRUE; // simulate the keys. for (ULONG i=0; iGetStatus(&tss); if (S_OK == hr) { // // see if the client now is AIMM // if (tss.dwStaticFlags & TS_SS_TRANSITORY) { fCiceroNative = FALSE; } } #endif CComPtr cpRange; hr = _GetCmdThatRange(ec, pic, &cpRange); if ( hr == S_OK && cpRange ) { // found our own input and it is not empty _CheckStartComposition(ec, cpRange); hr = cpRange->SetText(ec, 0, NULL, 0); // trigger redrawing SetSelectionSimple(ec, pic, cpRange); } #ifdef _TRY_LATER_FOR_AIMM else if (fCiceroNative == FALSE) { CComPtr pRStart; CComPtr pREnd; BOOL fEmpty; hr = pic->GetStart(&pRStart); if (S_OK == hr) hr = pic->GetEnd(&pREnd); if (S_OK == hr) { hr = pRStart->IsEquealStart(ec, pREnd, TF_ANCHOR_END, &fEmpty); } if (S_OK == hr && fEmpty) { // - VK_CONTROL(down) + VK_SHIFT(down) + VK_LEFT(down), then // - VK_LEFT(up) + VK_SHIFT(up) + VK_CONTROL(up), then // - VK_DELETE(down) + VK_DELETE(up) // keybd_event((BYTE)VK_CONTROL, 0, 0, 0); keybd_event((BYTE)VK_SHIFT, 0, 0, 0); keybd_event((BYTE)VK_LEFT, 0, 0, 0); keybd_event((BYTE)VK_CONTROL, 0, KEYEVENTF_KEYUP, 0); keybd_event((BYTE)VK_SHIFT, 0, KEYEVENTF_KEYUP, 0); keybd_event((BYTE)VK_LEFT, 0, KEYEVENTF_KEYUP, 0); keybd_event((BYTE)VK_DELETE, 0, 0, 0); keybd_event((BYTE)VK_DELETE, 0, KEYEVENTF_KEYUP, 0); } } #endif // _TRY_LATER_FOR_AIMM // // these moved from HandleRecognition // SaveLastUsedIPRange( ); SaveIPRange(NULL); return hr; } // // CSapiIMX::_GetCmdThatRange // // We have many "xxx That" commands, all these commands require to get // a right range. this method will supply a united way to get the right range. // // As a Rule, // // If there is a selection before "XXX That" is recognized, we just use // that range. // If there is no selection, we will try to find the previous dictated phrase // or a word case by case. // // // ppRange will hold the returned Range interface pointer, it is caller's // responsibility to release the range object. // #define MAX_WORD_LENGTH 32 HRESULT CSapiIMX::_GetCmdThatRange(TfEditCookie ec, ITfContext *pic, ITfRange **ppRange) { HRESULT hr = S_OK; Assert(pic); Assert(ppRange); CComPtr cpIP; CComPtr cpRange; BOOL fEmpty = TRUE; BOOL fGotRange = FALSE; TraceMsg(TF_GENERAL, "GetCmdThatRange is called"); *ppRange = NULL; // Get the current IP. hr = GetSelectionSimple(ec, pic, &cpIP); // is ip empty or selection if ( hr == S_OK ) hr = cpIP->IsEmpty(ec, &fEmpty); if ( hr == S_OK ) { if ( !fEmpty ) { // current ip is a selection, just use it. hr = cpIP->Clone(&cpRange); if ( hr == S_OK ) fGotRange = TRUE; } else { WORD prilangid; prilangid = PRIMARYLANGID(m_langid); if ((prilangid == LANG_CHINESE) || (prilangid == LANG_JAPANESE) || !_GetIPChangeStatus( )) { // If the lang is East Asian, we always try to get the previous dictated phrase first. // if lang is English, and there is no ip change since last dictated phrase, // we will try to get the previous dictated phrase first. fGotRange = _FindPrevComp(ec, pic, cpIP, &cpRange, GUID_ATTR_SAPI_INPUT); if ( !fGotRange ) { // With Office Auto-Correction, the static GUID_PROP_SAPI_DISPATTR property // on the auto-corrected range could be destroyed. // In this case, we may want to rely on our custom property GUID_PROP_SAPIRESULTOBJECT // to find the real previous dictated phrase. CComPtr cpRangeTmp; CComPtr cpProp; LONG l; hr = cpIP->Clone(&cpRangeTmp); // shift to the previous position if ( hr == S_OK ) hr = cpRangeTmp->ShiftStart(ec, -1, &l, NULL); if ( hr == S_OK ) hr = pic->GetProperty(GUID_PROP_SAPIRESULTOBJECT, &cpProp); if ( hr == S_OK) hr = cpProp->FindRange(ec, cpRangeTmp, &cpRange, TF_ANCHOR_START); if (hr == S_OK && cpRange) hr = cpRange->IsEmpty(ec, &fEmpty); fGotRange = !fEmpty; } } } } if ( hr == S_OK && !fGotRange ) { // IP must be empty // There is no previous dictated phrase, or IP is moved since last dictation. // we try to get the word around the ip. long cch=0; ULONG ulch =0; CComPtr cpRangeTmp; WCHAR pwszTextBuf[MAX_WORD_LENGTH+1]; ULONG ulLeft=0, ulRight=0; // Find the first delimiter character in left side from the current IP. hr = cpIP->Clone(&cpRangeTmp); if ( hr == S_OK ) hr = cpRangeTmp->ShiftStart(ec, MAX_WORD_LENGTH * (-1), &cch, NULL); if ( hr == S_OK && cch < 0 ) hr = cpRangeTmp->GetText(ec, 0, pwszTextBuf, MAX_WORD_LENGTH, &ulch); if ( hr == S_OK && ulch > 0 ) { pwszTextBuf[ulch] = L'\0'; for ( long i=(long)ulch-1; i>=0; i-- ) { WCHAR wch; wch = pwszTextBuf[i]; if ( iswpunct(wch) || iswspace(wch) ) break; ulLeft++; } } // Find the first delimiter character in right side from the right if ( hr == S_OK && cpRangeTmp ) { cpRangeTmp.Release( ); hr = cpIP->Clone(&cpRangeTmp); } if ( hr == S_OK ) hr = cpRangeTmp->ShiftEnd(ec, MAX_WORD_LENGTH, &cch, NULL); if ( hr == S_OK && cch > 0 ) hr = cpRangeTmp->GetText(ec, 0, pwszTextBuf, MAX_WORD_LENGTH, &ulch); if ( hr == S_OK && ulch > 0 ) { pwszTextBuf[ulch] = L'\0'; for ( long i=0; i<(long)ulch; i++ ) { WCHAR wch; wch = pwszTextBuf[i]; if ( iswpunct(wch) || iswspace(wch) ) break; ulRight++; } } if ( hr == S_OK ) hr = cpRangeTmp->Collapse(ec, TF_ANCHOR_START); // Move end anchor right number if (hr == S_OK && ulRight > 0 ) hr = cpRangeTmp->ShiftEnd(ec, ulRight, &cch, NULL); // Move start anchor left number. if ( hr == S_OK && ulLeft > 0 ) hr = cpRangeTmp->ShiftStart(ec, (long)ulLeft * (-1), &cch, NULL); if ( hr == S_OK ) { hr = cpRangeTmp->Clone(&cpRange); fGotRange = TRUE; } } if ( hr == S_OK && fGotRange && cpRange ) { *ppRange = cpRange; (*ppRange)->AddRef( ); TraceMsg(TF_GENERAL, "Got the xxx That range!"); } return hr; }