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
1791 lines
50 KiB
//
|
|
// 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:
|
|
//
|
|
// <select|delete|Correct <Word0> <Word1> <word2> ...
|
|
//
|
|
// 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 <TEXTBUF:That>" 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<ITfRange> cpInsertionPoint;
|
|
|
|
if ( cpInsertionPoint = GetSavedIP() )
|
|
{
|
|
// Determine if the saved IP was on this context.
|
|
// if not we just ignore that
|
|
|
|
CComPtr<ITfContext> 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<ITfRange> 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<ITfRange> 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<ITfRange> cpInsertionPoint;
|
|
|
|
if ( cpInsertionPoint = GetSavedIP() )
|
|
{
|
|
// Determine if the saved IP was on this context.
|
|
// if not we just ignore that
|
|
|
|
CComPtr<ITfContext> 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; i<ulLen; i++)
|
|
{
|
|
if ( !HandleKey( pwszKey[i] ) )
|
|
{
|
|
fHandleKeySucceed = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( fHandleKeySucceed == FALSE )
|
|
{
|
|
hr = InjectText(pwszKey, langid);
|
|
}
|
|
}
|
|
|
|
// }
|
|
|
|
SaveLastUsedIPRange( );
|
|
SaveIPRange(NULL);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// _KillLastPhrase
|
|
//
|
|
//---------------------------------------------------------------------------+
|
|
HRESULT CSapiIMX::_KillLastPhrase(TfEditCookie ec, ITfContext *pic)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
|
|
#ifdef _TRY_LATER_FOR_AIMM
|
|
TF_STATUS tss;
|
|
BOOL fCiceroNative = TRUE;
|
|
|
|
|
|
hr = pic->GetStatus(&tss);
|
|
if (S_OK == hr)
|
|
{
|
|
//
|
|
// see if the client now is AIMM
|
|
//
|
|
if (tss.dwStaticFlags & TS_SS_TRANSITORY)
|
|
{
|
|
fCiceroNative = FALSE;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
CComPtr<ITfRange> 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<ITfRange> pRStart;
|
|
CComPtr<ITfRange> 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<ITfRange> cpIP;
|
|
CComPtr<ITfRange> 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<ITfRange> cpRangeTmp;
|
|
CComPtr<ITfProperty> 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<ITfRange> 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;
|
|
}
|
|
|
|
|