// // // Sapilayr TIP CCorrectionHandler implementation. // // Implement correction related dictation commands. // such as // Correct That // Recovert // Correction // // Correct // // Move the correction related functions to this separate class // // #include "private.h" #include "sapilayr.h" #include "correct.h" // ------------------------------------------------------- // // Implementation for CCorrectionHandler // // ------------------------------------------------------- CCorrectionHandler::CCorrectionHandler(CSapiIMX *psi) { m_psi = psi; fRestoreIP = FALSE; } CCorrectionHandler::~CCorrectionHandler( ) { }; // // Save the IP right before the candidate UI is opened. // // This IP would be restored if the candidate UI is cancelled. // Or after the alternate text is injected when correct command requires // to restore this ip. // // Client can call _SetRestoreIPFlag( ) to indicate if it wants to restore // the IP after a new alternate text is injected. // // Currently "Correct " command wants to restore the IP, but other // commands "Correct That, Correction, Reconvert" don't want to restore IP. // // Everytime the candidate UI is closed, this IP needs to be released to avoid // any possible memory leak. // HRESULT CCorrectionHandler::_SaveCorrectOrgIP(TfEditCookie ec, ITfContext *pic) { CComPtr cpSel; HRESULT hr = GetSelectionSimple(ec, pic, (ITfRange **)&cpSel); if (SUCCEEDED(hr)) { m_cpOrgIP.Release( ); hr = cpSel->Clone(&m_cpOrgIP); } return hr; } void CCorrectionHandler::_ReleaseCorrectOrgIP( ) { if ( m_cpOrgIP ) { // clear m_cpOrgIP so that it would not affect consequent candidate behavior // m_cpOrgIP.Release( ); } fRestoreIP = FALSE; } // // edit session callback function for RESTORE_CORRECT_ORGIP. // HRESULT CCorrectionHandler::_RestoreCorrectOrgIP(TfEditCookie ec, ITfContext *pic) { HRESULT hr = S_OK; // we just want to restore the original saved IP. if ( m_cpOrgIP ) { hr = SetSelectionSimple(ec, pic, m_cpOrgIP); _ReleaseCorrectOrgIP( ); } return hr; } // // Start an edit session to restore the original IP // // HRESULT CCorrectionHandler::RestoreCorrectOrgIP(ITfContext *pic ) { HRESULT hr = E_FAIL; if ( !m_psi ) return E_FAIL; if (pic) { hr = m_psi->_RequestEditSession(ESCB_RESTORE_CORRECT_ORGIP, TF_ES_READWRITE, NULL, pic); } return hr; } // // Handle Correct That, Reconvert commands // HRESULT CCorrectionHandler::CorrectThat() { HRESULT hr = E_FAIL; if ( !m_psi ) return E_FAIL; hr = m_psi->_RequestEditSession(ESCB_RECONV_ONIP, TF_ES_READWRITE); return hr; } // // Edit session callback function for CorrectThat. // HRESULT CCorrectionHandler::_CorrectThat(TfEditCookie ec, ITfContext *pic) { HRESULT hr = E_FAIL; ITfRange *pSel = NULL; TraceMsg(TF_GENERAL, "_CorrectThat is called"); if ( !m_psi ) return E_FAIL; if (pic) { // remove the green bar m_psi->_KillFeedbackUI(ec, pic, NULL); hr = m_psi->_GetCmdThatRange(ec, pic, &pSel); } if (SUCCEEDED(hr) && pSel) { hr = _ReconvertOnRange(pSel); } SafeRelease(pSel); // moved from _HandleRecognition as this is a command // m_psi->SaveLastUsedIPRange( ); m_psi->SaveIPRange(NULL); return hr; } HRESULT CCorrectionHandler::_SetSystemReconvFunc( ) { HRESULT hr = S_OK; if (!m_cpsysReconv) { CComPtr cpsysFuncPrv; hr = (m_psi->_tim)->GetFunctionProvider(GUID_SYSTEM_FUNCTIONPROVIDER, &cpsysFuncPrv); if (hr == S_OK) hr = cpsysFuncPrv->GetFunction(GUID_NULL, IID_ITfFnReconversion, (IUnknown **)&m_cpsysReconv); } return hr; } void CCorrectionHandler::_ReleaseSystemReconvFunc( ) { if (m_cpsysReconv) m_cpsysReconv.Release( ); } // // CCorrectionHandler::_ReconvertOnRange // // Try to get the candidate UI for the given pRange if this range contains speech alternates // data. // // pRange could be a selection or an IP. // // HRESULT CCorrectionHandler::_ReconvertOnRange(ITfRange *pRange, BOOL *pfConvertable) { HRESULT hr = E_FAIL; ITfRange *pAttrRange = NULL; BOOL fConvertable = FALSE; TraceMsg(TF_GENERAL, "_ReconvertOnRange is called"); if ( !pRange ) return E_INVALIDARG; hr = pRange->Clone(&pAttrRange); if (S_OK == hr && pAttrRange) { CComPtr cpRangeReconv; hr = _SetSystemReconvFunc( ); if ( hr == S_OK ) hr = m_cpsysReconv->QueryRange(pAttrRange, &cpRangeReconv, &fConvertable); if ( (hr == S_OK) && fConvertable && cpRangeReconv) { // The text owner could be any other tips, and other tips may want to // request a new R/W edit session to open reconvert UI. // Cicero would return E_LOCKED if other tip wants to request edit session while // speech tip is under an edit session. // // To resolve this problem, speech tip just save the cpRangeReconv post a message // to the work window and then immediatelly end this edit session. // // When the work window receives the private message, the window procedure function // will do a real reconvert work. m_cpCorrectRange.Release( ); hr = cpRangeReconv->Clone(&m_cpCorrectRange); if ( hr == S_OK ) PostMessage(m_psi->_GetWorkerWnd( ), WM_PRIV_DORECONVERT, 0, 0); } } SafeRelease(pAttrRange); if ( pfConvertable ) *pfConvertable = fConvertable; return hr; } // // CCorrectionHandler::_DoReconvertOnRange // // When WM_.... is handled, this function will be called. // ReconvertOnRange( ) post the above private message and prepare // all the necessary range data in the class object. // This function will do the real reconvertion. // HRESULT CCorrectionHandler::_DoReconvertOnRange( ) { HRESULT hr = E_FAIL; TraceMsg(TF_GENERAL, "_DoReconvertOnRange is called"); if ( !m_cpCorrectRange ) return hr; hr = _SetSystemReconvFunc( ); if ( hr == S_OK ) hr = m_cpsysReconv->Reconvert(m_cpCorrectRange); _ReleaseSystemReconvFunc( ); return hr; } // // Moved here from CSapiIMX // HRESULT CCorrectionHandler::SetReplaceSelection(ITfRange *pRange, ULONG cchReplaceStart, ULONG cchReplaceChars, ITfContext *pic) { HRESULT hr = E_FAIL; ESDATA esData; if ( !m_psi ) return E_FAIL; memset(&esData, 0, sizeof(ESDATA)); esData.lData1 = (LONG_PTR)cchReplaceStart; esData.lData2 = (LONG_PTR)cchReplaceChars; esData.pRange = pRange; hr = m_psi->_RequestEditSession(ESCB_SETREPSELECTION, TF_ES_READWRITE, &esData, pic); return hr; } // // _SetReplaceSelection // // synoposis: calculate the span of text range based on the specified length of // the selected alternate string (cchReplacexxx) // then set a selection basedon it. // HRESULT CCorrectionHandler::_SetReplaceSelection ( TfEditCookie ec, ITfContext *pic, ITfRange *pRange, ULONG cchReplaceStart, ULONG cchReplaceChars ) { // adjust pRange here CComPtr cpProp; CComPtr cpPropRange; CComPtr cpClonedPropRange; if ( !m_psi ) return E_FAIL; HRESULT hr = pic->GetProperty(GUID_PROP_SAPIRESULTOBJECT, &cpProp); if (S_OK == hr) { hr = cpProp->FindRange(ec, pRange, &cpPropRange, TF_ANCHOR_START); } if (S_OK == hr) { hr = cpPropRange->Clone(&cpClonedPropRange); } if (S_OK == hr) { hr = cpClonedPropRange->Collapse(ec, TF_ANCHOR_START); } if (S_OK == hr) { long cch; cpClonedPropRange->ShiftStart(ec, cchReplaceStart, &cch, NULL); cpClonedPropRange->ShiftEnd(ec, cchReplaceChars, &cch, NULL); } SetSelectionSimple(ec, pic, cpClonedPropRange); if ( m_psi->GetDICTATIONSTAT_DictOnOff()) m_psi->_FeedIPContextToSR(ec, pic, cpClonedPropRange); // discurd IP m_psi->SaveIPRange(NULL); return hr; } //+--------------------------------------------------------------------------- // // CCorrectionHandler::InjectAlternateText // //---------------------------------------------------------------------------- HRESULT CCorrectionHandler::InjectAlternateText ( const WCHAR *pwszResult, LANGID langid, ITfContext *pic, BOOL bHandleLeadingSpace ) { HRESULT hr = E_FAIL; Assert(pwszResult); Assert(pic); if ( !m_psi ) return E_FAIL; ESDATA esData; memset(&esData, 0, sizeof(ESDATA)); esData.pData = (void *)pwszResult; esData.uByte = (wcslen(pwszResult)+1) * sizeof(WCHAR); esData.lData1 = (LONG_PTR)langid; esData.fBool = bHandleLeadingSpace; hr = m_psi->_RequestEditSession(ESCB_PROCESS_ALTERNATE_TEXT,TF_ES_READWRITE, &esData, pic); return hr; } HRESULT CCorrectionHandler::_ProcessAlternateText(TfEditCookie ec, WCHAR *pwszText,LANGID langid, ITfContext *pic, BOOL bHandleLeadingSpace) { HRESULT hr = S_OK; if ( !m_psi ) return E_FAIL; CComPtr cpRangeText; // Save the current selection as text range which is used // later to handle leading spaces. // if ( bHandleLeadingSpace ) { CComPtr cpSelection; hr = GetSelectionSimple(ec, pic, &cpSelection); if ( hr == S_OK && cpSelection ) hr = cpSelection->Clone(&cpRangeText); } if ( hr == S_OK ) hr = m_psi->_ProcessTextInternal(ec, pwszText, GUID_ATTR_SAPI_INPUT, langid, pic, TRUE); if ( hr == S_OK && bHandleLeadingSpace && pwszText && cpRangeText) { // If the first element is updated by the alternate phrase // speech tip needs to check if this new alternate wants to // consume the leading space or if extra space is required to add // between this phrase and previous phrase. // BOOL bConsumeLeadingSpace = FALSE; WCHAR wchFirstChar = pwszText[0]; if ( iswcntrl(wchFirstChar) || iswpunct(wchFirstChar) ) bConsumeLeadingSpace = TRUE; if ( hr == S_OK) hr = m_psi->_ProcessLeadingSpaces(ec, pic, cpRangeText, bConsumeLeadingSpace, langid, FALSE); } if ( fRestoreIP ) _RestoreCorrectOrgIP(ec, pic); else _ReleaseCorrectOrgIP( ); return hr; }