/*++ Copyright (c) 1985 - 1999, Microsoft Corporation Module Name: txtevcb.cpp Abstract: This file implements the CTextEventSinkCallBack Class. Author: Revision History: Notes: --*/ #include "private.h" #include "txtevcb.h" #include "ime.h" #include "editses.h" #include "context.h" #include "profile.h" // from ctf\sapilayr\globals.cpp const GUID GUID_ATTR_SAPI_GREENBAR = { 0xc3a9e2e8, 0x738c, 0x48e0, {0xac, 0xc8, 0x43, 0xee, 0xfa, 0xbf, 0x83, 0xc8} }; //+--------------------------------------------------------------------------- // // CTextEventSinkCallBack::_IsSapiFeedbackUIPresent // //---------------------------------------------------------------------------- BOOL CTextEventSinkCallBack::_IsSapiFeedbackUIPresent( Interface_Attach& ic, TESENDEDIT *ee ) { EnumPropertyArgs args; args.comp_guid = GUID_ATTR_SAPI_GREENBAR; if (FAILED(ic->GetProperty(GUID_PROP_ATTRIBUTE, args.Property))) return FALSE; Interface EnumReadOnlyProperty; if (FAILED(args.Property->EnumRanges(ee->ecReadOnly, EnumReadOnlyProperty, NULL))) return FALSE; args.ec = ee->ecReadOnly; args.pLibTLS = m_pLibTLS; CEnumrateInterface Enumrate(EnumReadOnlyProperty, EnumPropertyCallback, &args); // Argument of callback func. ENUM_RET ret_prop_attribute = Enumrate.DoEnumrate(); if (ret_prop_attribute == ENUM_FIND) return TRUE; return FALSE; } //+--------------------------------------------------------------------------- // // CTextEventSinkCallBack::_IsComposingPresent // //---------------------------------------------------------------------------- BOOL CTextEventSinkCallBack::_IsComposingPresent( Interface_Attach& ic, TESENDEDIT *ee ) { /* * This is automatic detection code of the Hangul + alphanumeric input * If detected a Hangul + alphanumeric, then we finalized all text. */ EnumTrackPropertyArgs args; const GUID *guids[] = {&GUID_PROP_COMPOSING, &GUID_PROP_MSIMTF_PREPARE_RECONVERT}; const int guid_size = sizeof(guids) / sizeof(GUID*); args.guids = (GUID**)guids; args.num_guids = guid_size; if (FAILED(ic->TrackProperties(guids, args.num_guids, // system property NULL, 0, // application property args.Property))) return FALSE; // // get the text range that does not include read only area for // reconversion. // Interface rangeAllText; LONG cch; if (FAILED(ImmIfEditSessionCallBack::GetAllTextRange(ee->ecReadOnly, ic, &rangeAllText, &cch))) return FALSE; Interface EnumReadOnlyProperty; if (FAILED(args.Property->EnumRanges(ee->ecReadOnly, EnumReadOnlyProperty, rangeAllText))) return FALSE; args.ec = ee->ecReadOnly; args.pLibTLS = m_pLibTLS; CEnumrateInterface Enumrate(EnumReadOnlyProperty, EnumTrackPropertyCallback, &args); // Argument of callback func. ENUM_RET ret_prop_composing = Enumrate.DoEnumrate(); if (ret_prop_composing == ENUM_FIND) return TRUE; return FALSE; } //+--------------------------------------------------------------------------- // // CTextEventSinkCallBack::_IsInterim // //---------------------------------------------------------------------------- BOOL CTextEventSinkCallBack::_IsInterim( Interface_Attach& ic, TESENDEDIT *ee ) { Interface_TFSELECTION sel; ULONG cFetched; if (ic->GetSelection(ee->ecReadOnly, TF_DEFAULT_SELECTION, 1, sel, &cFetched) != S_OK) return FALSE; if (sel->style.fInterimChar) { return TRUE; } return FALSE; } //+--------------------------------------------------------------------------- // // CTextEventSinkCallBack::_IsCompositionChanged // //---------------------------------------------------------------------------- BOOL CTextEventSinkCallBack::_IsCompositionChanged( Interface_Attach& ic, TESENDEDIT *ee ) { ENUM_RET enumret; BOOL fChanged; if (SUCCEEDED(ee->pEditRecord->GetSelectionStatus(&fChanged))) { if (fChanged) return TRUE; } // // Find GUID_PROP_MSIMTF_TRACKCOMPOSITION property. // EnumFindFirstTrackCompRangeArgs argsFindFirstTrackCompRange; Interface PropertyTrackComposition; if (FAILED(ic->GetProperty(GUID_PROP_MSIMTF_TRACKCOMPOSITION, argsFindFirstTrackCompRange.Property))) return FALSE; Interface EnumFindFirstTrackCompRange; if (FAILED(argsFindFirstTrackCompRange.Property->EnumRanges(ee->ecReadOnly, EnumFindFirstTrackCompRange, NULL))) return FALSE; argsFindFirstTrackCompRange.ec = ee->ecReadOnly; CEnumrateInterface EnumrateFindFirstTrackCompRange( EnumFindFirstTrackCompRange, EnumFindFirstTrackCompRangeCallback, &argsFindFirstTrackCompRange); enumret = EnumrateFindFirstTrackCompRange.DoEnumrate(); // // if there is no track composition property, // the composition has been changed since we put it. // if (enumret != ENUM_FIND) return TRUE; Interface rangeTrackComposition; if (FAILED(argsFindFirstTrackCompRange.Range->Clone(rangeTrackComposition))) return FALSE; // // get the text range that does not include read only area for // reconversion. // Interface rangeAllText; LONG cch; if (FAILED(ImmIfEditSessionCallBack::GetAllTextRange(ee->ecReadOnly, ic, &rangeAllText, &cch))) return FALSE; LONG lResult; if (FAILED(rangeTrackComposition->CompareStart(ee->ecReadOnly, rangeAllText, TF_ANCHOR_START, &lResult))) return FALSE; // // if the start position of the track composition range is not // the beggining of IC, // the composition has been changed since we put it. // if (lResult != 0) return TRUE; if (FAILED(rangeTrackComposition->CompareEnd(ee->ecReadOnly, rangeAllText, TF_ANCHOR_END, &lResult))) return FALSE; // // if the start position of the track composition range is not // the beggining of IC, // the composition has been changed since we put it. // if (lResult != 0) return TRUE; // // If we find the changes in these property, we need to update hIMC. // const GUID *guids[] = {&GUID_PROP_COMPOSING, &GUID_PROP_ATTRIBUTE, &GUID_PROP_READING, &GUID_PROP_MSIMTF_PREPARE_RECONVERT}; const int guid_size = sizeof(guids) / sizeof(GUID*); Interface EnumPropertyChanged; if (FAILED(ee->pEditRecord->GetTextAndPropertyUpdates(TF_GTP_INCL_TEXT, guids, guid_size, EnumPropertyChanged))) return FALSE; EnumPropertyChangedCallbackArgs args; args.ec = ee->ecReadOnly; CEnumrateInterface Enumrate(EnumPropertyChanged, EnumPropertyChangedCallback, &args); enumret = Enumrate.DoEnumrate(); if (enumret == ENUM_FIND) return TRUE; return FALSE; } //+--------------------------------------------------------------------------- // // CTextEventSinkCallBack::TextEventSinkCallback // //---------------------------------------------------------------------------- // static HRESULT CTextEventSinkCallBack::TextEventSinkCallback( UINT uCode, void *pv, void *pvData ) { DebugMsg(TF_FUNC, TEXT("TextEventSinkCallback")); ASSERT(uCode == ICF_TEXTDELTA); // the pvData cast only works in this case if (uCode != ICF_TEXTDELTA) return S_OK; CTextEventSinkCallBack* _this = (CTextEventSinkCallBack*)pv; ASSERT(_this); TESENDEDIT* ee = (TESENDEDIT*)pvData; ASSERT(ee); HRESULT hr; IMCLock imc(_this->m_hIMC); if (FAILED(hr = imc.GetResult())) return hr; IMCCLock imc_ctfime(imc->hCtfImeContext); if (FAILED(hr=imc_ctfime.GetResult())) return hr; CicInputContext* _pCicContext = imc_ctfime->m_pCicContext; if (_pCicContext == NULL) return E_FAIL; ASSERT(_pCicContext != NULL); #ifdef UNSELECTCHECK if (!_pAImeContext->m_fSelected) return S_OK; #endif UNSELECTCHECK Interface_Attach ic(_pCicContext->GetInputContext()); /* * We have composition text. */ TLS* ptls = TLS::GetTLS(); if (ptls == NULL) { return E_OUTOFMEMORY; } BOOL fInReconvertEditSession; fInReconvertEditSession = _pCicContext->m_fInReconvertEditSession.IsSetFlag(); if (!_this->_IsCompositionChanged(ic, ee)) return S_OK; BOOL fComp; BOOL fSapiFeedback; // need to check speech feedback UI. fSapiFeedback = _this->_IsSapiFeedbackUIPresent(ic, ee); fComp = _this->_IsComposingPresent(ic, ee); LANGID langid; CicProfile* _pProfile = ptls->GetCicProfile(); if (_pProfile == NULL) { DebugMsg(TF_ERROR, TEXT("CTextEventSinkCallBack::TextEventSinkCallback. _pProfile==NULL.")); return E_OUTOFMEMORY; } _pProfile->GetLangId(&langid); switch (PRIMARYLANGID(langid)) { case LANG_KOREAN: // // We will finalize Korean composition string immediately if it // doesn't set the interim definition. For example, Korean // Handwriting TIP case. // // Bug#482983 - Notepad doesn't support the level 2 composition // window for Korean. // // And we should not do this during HanjaReConversion. // if (fComp && _pCicContext->m_fHanjaReConversion.IsResetFlag()) { if (!(_this->_IsInterim(ic, ee))) { // // Reset fComp to false to complete the current composition // string. // fComp = FALSE; } } break; case LANG_JAPANESE: case LANG_CHINESE: break; default: // // #500698 // // Reset fComp to false to complete the current composition // string. // if (!ptls->NonEACompositionEnabled()) { fComp = FALSE; } break; } // // Update composition and generate WM_IME_COMPOSITION // // if there is SAPI green bar. // - there is only hIMC composition if there is Speech Green bar. // // if Reconversion just started. // - because some tip may not change the text yet. // then there is no composition range yet. // // if now clearing DocFeed buffer. // - because the change happens in read-only text // nothing in hIMC changes. // if (fComp || fSapiFeedback || fInReconvertEditSession || _pCicContext->m_fInClearDocFeedEditSession.IsSetFlag()) { // // Retreive text delta from GUID_PROP_COMPOSING // const GUID *guids[] = {&GUID_PROP_COMPOSING}; const int guid_size = sizeof(guids) / sizeof(GUID*); Interface EnumPropertyUpdate; hr = ee->pEditRecord->GetTextAndPropertyUpdates(0, // dwFlags guids, guid_size, EnumPropertyUpdate); if (SUCCEEDED(hr)) { EnumPropertyUpdateArgs args(ic.GetPtr(), _this->m_tid, imc, _this->m_pLibTLS); if (FAILED(hr=ic->GetProperty(GUID_PROP_COMPOSING, args.Property))) return hr; args.ec = ee->ecReadOnly; args.dwDeltaStart = 0; CEnumrateInterface Enumrate(EnumPropertyUpdate, EnumPropertyUpdateCallback, &args); // Argument of callback func. ENUM_RET ret_prop_update = Enumrate.DoEnumrate(); if (ret_prop_update == ENUM_FIND) { // // Remove GUID_PROP_MSIMTF_PREPARE_RECONVERT property. // _this->EscbRemoveProperty(imc, &GUID_PROP_MSIMTF_PREPARE_RECONVERT); // // Update composition string with delta start position // return _this->EscbUpdateCompositionString(imc, args.dwDeltaStart); } } // // Update composition string // return _this->EscbUpdateCompositionString(imc); } else { // // Clear DocFeed range's text store. // Find GUID_PROP_MSIMTF_READONLY property and SetText(NULL). // // ImmIfIME::ClearDocFeedBuffer() essential function for all ESCB_RECONVERTSTRING's edit // session except only ImmIfIME::SetupDocFeedString() since this is provided for keyboard // TIP's DocFeeding. // _this->EscbClearDocFeedBuffer(imc, *_pCicContext, FALSE); // No TF_ES_SYNC // // Composition complete. // return _this->EscbCompComplete(imc, FALSE); // No TF_ES_SYNC } } //+--------------------------------------------------------------------------- // // CTextEventSinkCallBack::EnumPropertyCallback // //---------------------------------------------------------------------------- // static ENUM_RET CTextEventSinkCallBack::EnumPropertyCallback( ITfRange* pRange, EnumPropertyArgs *pargs ) { ENUM_RET ret = ENUM_CONTINUE; VARIANT var; QuickVariantInit(&var); HRESULT hr = pargs->Property->GetValue(pargs->ec, pRange, &var); if (SUCCEEDED(hr)) { if (IsEqualIID(pargs->comp_guid, GUID_NULL)) { if ((V_VT(&var) == VT_I4 && V_I4(&var) != 0)) ret = ENUM_FIND; } else if (V_VT(&var) == VT_I4) { TfGuidAtom guid = V_I4(&var); if (IsEqualTFGUIDATOM(pargs->pLibTLS, guid, pargs->comp_guid)) ret = ENUM_FIND; } } VariantClear(&var); return ret; } //+--------------------------------------------------------------------------- // // CTextEventSinkCallBack::EnumTrackPropertyCallback // //---------------------------------------------------------------------------- // static ENUM_RET CTextEventSinkCallBack::EnumTrackPropertyCallback( ITfRange* pRange, EnumTrackPropertyArgs *pargs ) { ENUM_RET ret = ENUM_CONTINUE; VARIANT var; QuickVariantInit(&var); HRESULT hr = pargs->Property->GetValue(pargs->ec, pRange, &var); if (SUCCEEDED(hr)) { Interface EnumPropVal; hr = var.punkVal->QueryInterface(IID_IEnumTfPropertyValue, EnumPropVal); if (SUCCEEDED(hr)) { TF_PROPERTYVAL tfPropertyVal; while (EnumPropVal->Next(1, &tfPropertyVal, NULL) == S_OK) { for (int i=0; i < pargs->num_guids; i++) { if (IsEqualGUID(tfPropertyVal.guidId, *pargs->guids[i])) { if ((V_VT(&tfPropertyVal.varValue) == VT_I4 && V_I4(&tfPropertyVal.varValue) != 0)) { ret = ENUM_FIND; break; } } } VariantClear(&tfPropertyVal.varValue); if (ret == ENUM_FIND) break; } } } VariantClear(&var); return ret; } //+--------------------------------------------------------------------------- // // CTextEventSinkCallBack::EnumPropertyUpdateCallback // //---------------------------------------------------------------------------- // static ENUM_RET CTextEventSinkCallBack::EnumPropertyUpdateCallback( ITfRange* update_range, EnumPropertyUpdateArgs *pargs ) { ENUM_RET ret = ENUM_CONTINUE; VARIANT var; QuickVariantInit(&var); HRESULT hr = pargs->Property->GetValue(pargs->ec, update_range, &var); if (SUCCEEDED(hr)) { if ((V_VT(&var) == VT_I4 && V_I4(&var) != 0)) { Interface_Creator _pEditSession( new ImmIfEditSession(ESCB_GET_ALL_TEXT_RANGE, pargs->imc, pargs->tid, pargs->ic.GetPtr(), pargs->pLibTLS) ); if (_pEditSession.Valid()) { Interface full_range; if (SUCCEEDED(_pEditSession->RequestEditSession(TF_ES_READ | TF_ES_SYNC, &full_range))) { if (SUCCEEDED(full_range->ShiftEndToRange(pargs->ec, update_range, TF_ANCHOR_START))) { Interface unupdate_range; if (SUCCEEDED(full_range->QueryInterface(IID_ITfRangeACP, unupdate_range))) { LONG acpStart; LONG cch; if (SUCCEEDED(unupdate_range->GetExtent(&acpStart, &cch))) { pargs->dwDeltaStart = cch; ret = ENUM_FIND; } } } } } } } VariantClear(&var); return ret; } //+--------------------------------------------------------------------------- // // CTextEventSinkCallBack::EnumPropertyChangedCallback // //---------------------------------------------------------------------------- // static ENUM_RET CTextEventSinkCallBack::EnumPropertyChangedCallback( ITfRange* update_range, EnumPropertyChangedCallbackArgs *pargs ) { BOOL empty; if (update_range->IsEmpty(pargs->ec, &empty) == S_OK && empty) return ENUM_CONTINUE; return ENUM_FIND; } //+--------------------------------------------------------------------------- // // CTextEventSinkCallBack::EnumPropertyChangedCallback // //---------------------------------------------------------------------------- // static ENUM_RET CTextEventSinkCallBack::EnumFindFirstTrackCompRangeCallback( ITfRange* update_range, EnumFindFirstTrackCompRangeArgs *pargs ) { ENUM_RET ret = ENUM_CONTINUE; VARIANT var; QuickVariantInit(&var); HRESULT hr = pargs->Property->GetValue(pargs->ec, update_range, &var); if (SUCCEEDED(hr)) { if ((V_VT(&var) == VT_I4 && V_I4(&var) != 0)) { update_range->Clone(pargs->Range); ret = ENUM_FIND; } } VariantClear(&var); return ret; }