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.
487 lines
13 KiB
487 lines
13 KiB
//
|
|
//
|
|
// Sapilayr TIP CCapCmdHandler implementation.
|
|
//
|
|
//
|
|
#include "private.h"
|
|
#include "sapilayr.h"
|
|
#include "capital.h"
|
|
|
|
|
|
// ----------------------------------------------------------------------------------------------------------
|
|
//
|
|
// Implementation for CCapCmdHandler
|
|
//
|
|
// -----------------------------------------------------------------------------------------------------------
|
|
|
|
CCapCmdHandler::CCapCmdHandler(CSapiIMX *psi)
|
|
{
|
|
m_psi = psi;
|
|
}
|
|
|
|
CCapCmdHandler::~CCapCmdHandler( )
|
|
{
|
|
|
|
};
|
|
|
|
/* --------------------------------------------------------
|
|
// Function Name: ProcessCapCommands
|
|
//
|
|
// Description: public functions used by command handler
|
|
// to handle any Capital related dictation
|
|
// commands.
|
|
//
|
|
// ----------------------------------------------------------*/
|
|
HRESULT CCapCmdHandler::ProcessCapCommands(CAPCOMMAND_ID idCapCmd, WCHAR *pwszTextToCap, ULONG ulLen )
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
|
|
if ( !m_psi )
|
|
return E_FAIL;
|
|
|
|
if ( (idCapCmd > CAPCOMMAND_MinIdWithText ) && (!pwszTextToCap || !ulLen))
|
|
return E_INVALIDARG;
|
|
|
|
WCHAR *pwszText=NULL;
|
|
ESDATA esData;
|
|
|
|
memset(&esData, 0, sizeof(ESDATA));
|
|
|
|
if ( pwszTextToCap )
|
|
{
|
|
esData.pData = pwszTextToCap;
|
|
esData.uByte = (ulLen + 1) * sizeof(WCHAR);
|
|
}
|
|
|
|
esData.lData1 = (LONG_PTR)idCapCmd;
|
|
esData.lData2 = (LONG_PTR)ulLen;
|
|
|
|
hr = m_psi->_RequestEditSession(ESCB_PROCESS_CAP_COMMANDS, TF_ES_READWRITE, &esData);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
/* --------------------------------------------------------
|
|
// Function Name: _ProcessCapCommands
|
|
//
|
|
// Description: Edit session call back funtion for
|
|
// ProcessSelectionWord.
|
|
//
|
|
// it does real work for selection handling
|
|
// ----------------------------------------------------------*/
|
|
HRESULT CCapCmdHandler::_ProcessCapCommands(TfEditCookie ec,ITfContext *pic, CAPCOMMAND_ID idCapCmd, WCHAR *pwszTextToCap, ULONG ulLen)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
// Get the Dictation Grammar
|
|
TraceMsg(TF_GENERAL, "_ProcessCapCommands() is called");
|
|
|
|
if ( m_psi == NULL)
|
|
return E_FAIL;
|
|
|
|
CComPtr<ITfRange> cpIP;
|
|
|
|
cpIP = m_psi->GetSavedIP();
|
|
|
|
if ( cpIP == NULL )
|
|
{
|
|
// Get the current IP.
|
|
hr = GetSelectionSimple(ec, pic, &cpIP);
|
|
}
|
|
|
|
// Start to a new command.
|
|
// Clear all the information saved for the previous command handling.
|
|
|
|
m_dstrTextToCap.Clear( );
|
|
m_cpCapRange = cpIP;
|
|
m_idCapCmd = idCapCmd;
|
|
|
|
switch ( idCapCmd )
|
|
{
|
|
case CAPCOMMAND_CapThat :
|
|
case CAPCOMMAND_AllCapsThat :
|
|
case CAPCOMMAND_NoCapsThat :
|
|
hr = _HandleCapsThat(ec, pic);
|
|
break;
|
|
|
|
case CAPCOMMAND_CapsOn :
|
|
hr = _CapsOnOff(ec, pic, TRUE);
|
|
break;
|
|
case CAPCOMMAND_CapsOff :
|
|
hr = _CapsOnOff(ec, pic, FALSE);
|
|
break;
|
|
|
|
// Below commands require pwszTextToCap contains real text to be capitalized
|
|
// injected to the document.
|
|
|
|
case CAPCOMMAND_CapIt :
|
|
case CAPCOMMAND_AllCaps :
|
|
case CAPCOMMAND_NoCaps :
|
|
m_dstrTextToCap.Append(pwszTextToCap);
|
|
m_ulLen = ulLen;
|
|
hr = _HandleCapsIt(ec, pic);
|
|
break;
|
|
|
|
case CAPCOMMAND_CapLetter :
|
|
hr = _HandleCapsThat(ec, pic, towlower(pwszTextToCap[0]));
|
|
break;
|
|
|
|
default :
|
|
break;
|
|
}
|
|
|
|
// update the saved ip so that next time the hypothesis will
|
|
// start from this new selection.
|
|
m_psi->SaveLastUsedIPRange( );
|
|
m_psi->SaveIPRange(NULL);
|
|
|
|
return hr;
|
|
}
|
|
|
|
/* --------------------------------------------------------------
|
|
// Function Name: _SetNewText
|
|
//
|
|
// Description: Inject the new text to m_cpCapRange in
|
|
// the document and update necessary property
|
|
// data.
|
|
//
|
|
// --------------------------------------------------------------*/
|
|
|
|
HRESULT CCapCmdHandler::_SetNewText(TfEditCookie ec,ITfContext *pic, WCHAR *pwszNewText, BOOL fSapiText)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
BOOL fInsertOk;
|
|
CComPtr<ITfRange> cpRange;
|
|
|
|
if (!pwszNewText)
|
|
return E_INVALIDARG;
|
|
|
|
m_cpCapRange->Clone(&cpRange);
|
|
|
|
hr = cpRange->AdjustForInsert(ec, wcslen(pwszNewText), &fInsertOk);
|
|
if (S_OK == hr && fInsertOk)
|
|
{
|
|
// start a composition here if we haven't already
|
|
m_psi->_CheckStartComposition(ec, cpRange);
|
|
|
|
// set the text
|
|
hr = cpRange->SetText(ec, 0, pwszNewText, -1);
|
|
|
|
|
|
if ( fSapiText )
|
|
{
|
|
//
|
|
// set attribute range
|
|
//
|
|
CComPtr<ITfRange> cpAttrRange = NULL;
|
|
CComPtr<ITfProperty> cpProp = NULL;
|
|
|
|
if (hr == S_OK)
|
|
{
|
|
hr = pic->GetProperty(GUID_PROP_SAPI_DISPATTR, &cpProp);
|
|
}
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
hr = cpRange->Clone(&cpAttrRange);
|
|
}
|
|
|
|
if (S_OK == hr && cpAttrRange)
|
|
{
|
|
SetGUIDPropertyData(m_psi->_GetLibTLS( ), ec, cpProp, cpAttrRange, GUID_ATTR_SAPI_INPUT);
|
|
}
|
|
|
|
//
|
|
// setup langid property
|
|
//
|
|
//_SetLangID(ec, pic, cpRange, langid);
|
|
}
|
|
|
|
if ( hr == S_OK )
|
|
{
|
|
cpRange->Collapse(ec, TF_ANCHOR_END);
|
|
SetSelectionSimple(ec, pic, cpRange);
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------
|
|
// Function Name: _CapsText
|
|
//
|
|
// Description: Generate capitalized text based on current
|
|
// Capital command id.
|
|
//
|
|
// Inside this function, it will allocate memory
|
|
// for new generated capitaized text.
|
|
// Caller is responsible for release the allocated
|
|
// memory
|
|
// -------------------------------------------------------------------*/
|
|
HRESULT CCapCmdHandler::_CapsText(WCHAR **pwszNewText, WCHAR wchLetter)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
WCHAR *pwszNew, *pwszTextToCap;
|
|
ULONG i;
|
|
|
|
// Generate new text based on the requirement
|
|
|
|
if ( !pwszNewText )
|
|
return E_INVALIDARG;
|
|
|
|
*pwszNewText = NULL;
|
|
pwszTextToCap = (WCHAR *)m_dstrTextToCap;
|
|
|
|
pwszNew = (WCHAR *)cicMemAlloc((m_ulLen+1)*sizeof(WCHAR));
|
|
if ( pwszNew )
|
|
{
|
|
WCHAR wch;
|
|
|
|
switch (m_idCapCmd)
|
|
{
|
|
case CAPCOMMAND_CapThat :
|
|
case CAPCOMMAND_CapIt :
|
|
case CAPCOMMAND_CapLetter :
|
|
{
|
|
BOOL fFoundFirstAlpha=FALSE;
|
|
|
|
for (i=0; i<m_ulLen; i++)
|
|
{
|
|
wch = pwszTextToCap[i];
|
|
|
|
if ( iswalpha(wch) && !fFoundFirstAlpha )
|
|
{
|
|
if ( (wchLetter==0) && (m_idCapCmd != CAPCOMMAND_CapLetter) )
|
|
pwszNew[i] = towupper(wch);
|
|
else
|
|
{
|
|
if (wch == wchLetter)
|
|
pwszNew[i] = towupper(wch);
|
|
else
|
|
pwszNew[i] = wch;
|
|
}
|
|
|
|
fFoundFirstAlpha = TRUE;
|
|
}
|
|
else
|
|
{
|
|
pwszNew[i] = wch;
|
|
//
|
|
// We treat apostrophe as a normal character when handling capitalization
|
|
//
|
|
if ( (towupper(wch) == towlower(wch)) && ( wch != L'\'') && ( wch != 0x2019) )
|
|
{
|
|
// reach to a non-alpha character.
|
|
// now start to find first alphar for next word.
|
|
fFoundFirstAlpha = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
pwszNew[m_ulLen] = L'\0';
|
|
}
|
|
|
|
break;
|
|
|
|
case CAPCOMMAND_AllCapsThat :
|
|
case CAPCOMMAND_AllCaps :
|
|
|
|
for ( i=0; i<m_ulLen; i++)
|
|
{
|
|
wch = pwszTextToCap[i];
|
|
if ( iswalpha(wch) )
|
|
pwszNew[i] = towupper(wch);
|
|
else
|
|
pwszNew[i] = wch;
|
|
}
|
|
|
|
pwszNew[m_ulLen] = L'\0';
|
|
|
|
break;
|
|
|
|
case CAPCOMMAND_NoCapsThat :
|
|
case CAPCOMMAND_NoCaps :
|
|
|
|
for ( i=0; i<m_ulLen; i++)
|
|
{
|
|
wch = pwszTextToCap[i];
|
|
|
|
if ( iswalpha(wch) )
|
|
pwszNew[i] = towlower(wch);
|
|
else
|
|
pwszNew[i] = wch;
|
|
}
|
|
|
|
pwszNew[m_ulLen] = L'\0';
|
|
break;
|
|
}
|
|
|
|
*pwszNewText = pwszNew;
|
|
}
|
|
|
|
if ( *pwszNewText != NULL )
|
|
hr = S_OK;
|
|
else
|
|
{
|
|
if ( pwszNew )
|
|
cicMemFree(pwszNew);
|
|
|
|
hr = E_FAIL;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
/* ------------------------------------------------------------------
|
|
// Function Name: _GetCapPhrase
|
|
//
|
|
// Description: Generate the range to capitalize.
|
|
// it could be previous dictated phrase,
|
|
// or current selection,
|
|
// or current word around IP or before IP
|
|
// depends on the current text situation.
|
|
// -------------------------------------------------------------------*/
|
|
HRESULT CCapCmdHandler::_GetCapPhrase(TfEditCookie ec,ITfContext *pic, BOOL *fSapiText)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
CComPtr<ITfRange> cpCapRange;
|
|
BOOL bSapiText = FALSE;
|
|
|
|
if ( !m_psi ) return E_FAIL;
|
|
|
|
if ( !fSapiText ) return E_INVALIDARG;
|
|
|
|
hr = m_psi->_GetCmdThatRange(ec, pic, &cpCapRange);
|
|
|
|
if ( hr == S_OK && cpCapRange )
|
|
{
|
|
m_cpCapRange = cpCapRange;
|
|
|
|
// Set bSapiText here.
|
|
// If the range is inside a dictated phrase, set bSapiText = TRUE;
|
|
|
|
CComPtr<ITfProperty> cpProp;
|
|
CComPtr<ITfRange> cpSapiPropRange;
|
|
long l1=0, l2=0;
|
|
|
|
hr = pic->GetProperty(GUID_PROP_SAPI_DISPATTR, &cpProp);
|
|
|
|
if ( hr == S_OK )
|
|
hr = cpProp->FindRange(ec, cpCapRange, &cpSapiPropRange, TF_ANCHOR_START);
|
|
|
|
// Is cpRange inside cpSapiPropRange ?
|
|
|
|
if ( hr == S_OK )
|
|
hr = cpCapRange->CompareStart(ec, cpSapiPropRange, TF_ANCHOR_START, &l1);
|
|
|
|
if ( hr == S_OK )
|
|
hr = cpCapRange->CompareEnd(ec, cpSapiPropRange, TF_ANCHOR_END, &l2);
|
|
|
|
if ( hr == S_OK && (l1>=0 && l2<=0) )
|
|
{
|
|
// the Range is inside SAPI input range.
|
|
bSapiText = TRUE;
|
|
}
|
|
|
|
// hr could be S_FALSE, if the range is not dictated.
|
|
// We still treat S_FALSE as S_OK in the return hr.
|
|
if ( SUCCEEDED(hr) )
|
|
hr = S_OK;
|
|
}
|
|
|
|
*fSapiText = bSapiText;
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CCapCmdHandler::_HandleCapsThat(TfEditCookie ec,ITfContext *pic, WCHAR wchLetter)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
BOOL fSapiText;
|
|
|
|
// Get the range to capitalize
|
|
|
|
hr = _GetCapPhrase(ec, pic, &fSapiText);
|
|
|
|
if ( hr == S_OK )
|
|
{
|
|
CComPtr<ITfRange> cpRangeCloned;
|
|
BOOL fEmpty = TRUE;
|
|
|
|
hr = m_cpCapRange->IsEmpty(ec, &fEmpty);
|
|
|
|
if ( hr == S_OK && !fEmpty )
|
|
{
|
|
hr = m_cpCapRange->Clone(&cpRangeCloned);
|
|
|
|
// Get the text in the CapRange.
|
|
if ( hr == S_OK )
|
|
{
|
|
ULONG ucch;
|
|
|
|
while(S_OK == hr && (S_OK == cpRangeCloned->IsEmpty(ec, &fEmpty)) && !fEmpty)
|
|
{
|
|
WCHAR sz[128];
|
|
|
|
hr = cpRangeCloned->GetText(ec, TF_TF_MOVESTART, sz, ARRAYSIZE(sz)-1, &ucch);
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
sz[ucch] = L'\0';
|
|
m_dstrTextToCap.Append(sz);
|
|
}
|
|
}
|
|
|
|
m_ulLen = m_dstrTextToCap.Length( );
|
|
}
|
|
|
|
if ( hr==S_OK && m_dstrTextToCap)
|
|
{
|
|
// Generate new text based on the requirement
|
|
WCHAR *pwszNewText;
|
|
|
|
hr = _CapsText(&pwszNewText, wchLetter);
|
|
|
|
if ( hr == S_OK )
|
|
{
|
|
hr = _SetNewText(ec, pic, (WCHAR *)pwszNewText, fSapiText);
|
|
cicMemFree(pwszNewText);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CCapCmdHandler::_CapsOnOff(TfEditCookie ec,ITfContext *pic, BOOL fOn)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CCapCmdHandler::_HandleCapsIt(TfEditCookie ec,ITfContext *pic)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if ( m_dstrTextToCap)
|
|
{
|
|
// Generate new text based on the requirement
|
|
WCHAR *pwszNewText;
|
|
|
|
hr = _CapsText(&pwszNewText);
|
|
|
|
if ( hr == S_OK )
|
|
{
|
|
hr = _SetNewText(ec, pic, (WCHAR *)pwszNewText, TRUE);
|
|
cicMemFree(pwszNewText);
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|