#include "private.h" #include "korimx.h" #include "timsink.h" #include "immxutil.h" #include "fnrecon.h" #include "helpers.h" #include "skbdmode.h" #include "osver.h" // REVIEW /*--------------------------------------------------------------------------- CKorIMX::_EditSessionCallback ---------------------------------------------------------------------------*/ HRESULT CKorIMX::_EditSessionCallback2(TfEditCookie ec, CEditSession2 *pes) { ITfContext* pic = pes->GetContext(); CKorIMX* pKorTip = pes->GetTIP(); ESSTRUCT* pess = pes->GetStruct(); ITfRange* pRange; CHangulAutomata* pAutomata; LPWSTR pszCand = NULL; Assert(pic != NULL); Assert(pKorTip != NULL); if ((pKorTip == NULL) || (pic == NULL)) return E_FAIL; switch (pess->id) { case ESCB_FINALIZECONVERSION: { CCandidateListEx *pCandList; CCandidateStringEx *pCandItem; pCandList = pess->pCandList; pCandItem = pess->pCandStr; pRange = pess->pRange; pszCand = pCandItem->m_psz; if (pszCand) { size_t cchCand = 0; StringCchLengthW(pszCand, CIC_KOR_CANDSTR_MAX, &cchCand); // Set Reading text SetTextAndReading(pKorTip->_GetLibTLS(), ec, pic, pRange, pszCand, cchCand, pCandItem->m_langid, pCandItem->m_pszRead); } pCandList->Release(); // First complete current comp string if (pAutomata = pKorTip->GetAutomata(pic)) pAutomata->MakeComplete(); pKorTip->MakeResultString(ec, pic, pRange); pKorTip->CloseCandidateUIProc(); break; } case ESCB_COMPLETE: { BOOL fReleaseRange = fFalse; // If No composition exist, nothing to complete if (pKorTip->GetIPComposition(pic) == NULL) break; pRange = pess->pRange; pAutomata = pKorTip->GetAutomata(pic); // Close cand UI if exist. pKorTip->CloseCandidateUIProc(); if (pRange == NULL) { GetSelectionSimple(ec, pic, &pRange); fReleaseRange = fTrue; } if (pRange) { if (pAutomata) pAutomata->MakeComplete(); pKorTip->MakeResultString(ec, pic, pRange); } if (fReleaseRange) { SafeRelease(pRange); } //return pKorTip->_MultiRangeConversion(ec, pes->_state.u, pic, pRange); break; } case ESCB_INSERT_PAD_STRING: { WCHAR szText[2]; GetSelectionSimple(ec, pic, &pRange); szText[0] = (WCHAR)pess->wParam; szText[1] = L'\0'; if (FAILED(pKorTip->SetInputString(ec, pic, pRange, szText, CKorIMX::GetLangID()))) break; pKorTip->MakeResultString(ec, pic, pRange); SafeRelease(pRange); break; } case ESCB_KEYSTROKE: { WPARAM wParam = pess->wParam; LPARAM lParam = pess->lParam; return pKorTip->_Keystroke(ec, pic, wParam, lParam, (const BYTE *)pess->pv1); break; } // Complete and Selection range changed case ESCB_TEXTEVENT: if (pKorTip->IsKeyFocus() && (GetSelectionSimple(ec, pic, &pRange) == S_OK)) { ITfComposition *pComposition; ITfRange *pRangeOldComp; //IEnumTfRanges *pEnumText = pess->pEnumRange; BOOL fChanged = fFalse; BOOL fEmpty; // Check modebias here if (pess->fBool) fChanged = pKorTip->CheckModeBias(ec, pic, pRange); ////////////////////////////////////////////////////////////////// // To complete on mouse click we using Range change notification. // In future version, we could remove this code and use custom property // or reading string. Cutom property can hold Hangul Automata object. // // Office apps explicitly call complete but this for unknown Cicero apps. ////////////////////////////////////////////////////////////////// pComposition = pKorTip->GetIPComposition(pic); if (pComposition == NULL) goto ExitTextEvent; // Office apps are not going through here. pComposition->GetRange(&pRangeOldComp); if (pRangeOldComp == NULL) goto ExitTextEvent; pRange->IsEmpty(ec, &fEmpty); if (fEmpty && (CR_EQUAL != CompareRanges(ec, pRange, pRangeOldComp))) { ITfProperty *pPropAttr; TfGuidAtom attr; // Clear attrib if (SUCCEEDED(pic->GetProperty(GUID_PROP_ATTRIBUTE, &pPropAttr))) { if (SUCCEEDED(GetAttrPropertyData(ec, pPropAttr, pRangeOldComp, &attr))) { if (pKorTip->IsKorIMX_GUID_ATOM(attr)) { pPropAttr->Clear(ec, pRangeOldComp); } } pPropAttr->Release(); } pAutomata = pKorTip->GetAutomata(pic); if (pAutomata) pAutomata->MakeComplete(); pKorTip->EndIPComposition(ec, pic); // pKorTip->MakeResultString(ec, pic, pRangeOldComp); fChanged = fTrue; } SafeRelease(pRangeOldComp); ExitTextEvent: pRange->Release(); // Close cand UI if exist. if (fChanged) pKorTip->CloseCandidateUIProc(); } break; // case ESCB_RANGEBROKEN: // pKorTip->FlushIPRange(ec, pic); // break; case ESCB_CANDUI_CLOSECANDUI: // u : ESCB_CANDUI_CLOSECANDUI // pv : this // hwnd : - (not used) // wParam : - (not used) // lParam : - (not used) // pv1 : - (not used) // pv2 : - (not used) // pic : - (not used) // pRange : - (not used) // fBool : - (not used) pKorTip->CloseCandidateUIProc(); break; // Hanja conv button up case ESCB_HANJA_CONV: // u : ESCB_HANJA_CONV // pv : this // hwnd : - (not used) // wParam : - (not used) // lParam : - (not used) // pv1 : - (not used) // pv2 : - (not used) // pic : - pic // pRange : - (not used) // fBool : - (not used) // O10 #220177: Simulate VK_HANJA key to invoke HHC if (GetAIMM(pic) && (IsOnNT5() || PRIMARYLANGID(LANGIDFROMLCID(GetSystemDefaultLCID())) != LANG_JAPANESE)) { keybd_event(VK_HANJA, 0, 0, 0); keybd_event(VK_HANJA, 0, KEYEVENTF_KEYUP, 0); } else if (GetSelectionSimple(ec, pic, &pRange) == S_OK) { if (pKorTip->GetIPComposition(pic)) pKorTip->DoHanjaConversion(ec, pic, pRange); else pKorTip->Reconvert(pRange); SafeRelease(pRange); } // Update Hanja button if (pKorTip->m_pToolBar != NULL) pKorTip->m_pToolBar->Update(UPDTTB_HJMODE); break; /////////////////////////////////////////////////////////////////////////// // Reconversion Callbacks case ESCB_FINALIZERECONVERSION: { CCandidateListEx *pCandList = pess->pCandList; CCandidateStringEx *pCandItem = pess->pCandStr; pRange = pess->pRange; pszCand = pCandItem->m_psz; Assert(pRange != NULL); pKorTip->CloseCandidateUI(pic); if (GetAIMM(pic) == fFalse) { if (pszCand) { size_t cchCand = 0; StringCchLengthW(pszCand, CIC_KOR_CANDSTR_MAX, &cchCand); //ITfRange *pRangeTmp; SetTextAndReading(pKorTip->_GetLibTLS(), ec, pic, pRange, pszCand, cchCand, pCandItem->m_langid, pCandItem->m_pszRead); } // To clear current selection and composition pKorTip->MakeResultString(ec, pic, pRange); } else { if (pszCand) { pRange->SetText(ec, 0, pszCand, 1/* wcslen(pszCand)*/); SetSelectionSimple(ec, pic, pRange); } pKorTip->EndIPComposition(ec, pic); } // if hit reconversion on composition string, we need to clear automata. pAutomata = pKorTip->GetAutomata(pic); if (pRange && pAutomata && pAutomata->GetCompositionChar()) pAutomata->MakeComplete(); SafeRelease(pRange); break; } case ESCB_ONSELECTRECONVERSION: break; case ESCB_ONCANCELRECONVERSION: pRange = pess->pRange; pKorTip->CancelCandidate(ec, pic); if (GetAIMM(pic) == fFalse) { // To clear current selection and composition pKorTip->MakeResultString(ec, pic, pRange); } else pKorTip->EndIPComposition(ec, pic); // if hit reconversion on composition string, we need to clear automata. pAutomata = pKorTip->GetAutomata(pic); if (pRange && pAutomata && pAutomata->GetCompositionChar()) pAutomata->MakeComplete(); SafeRelease(pRange); break; case ESCB_RECONV_QUERYRECONV: { CFnReconversion *pReconv = (CFnReconversion *)pess->pv1; if (pKorTip->IsCandUIOpen()) return E_FAIL; return pReconv->_QueryRange(ec, pic, pess->pRange, (ITfRange **)pess->pv2); } case ESCB_RECONV_GETRECONV: { CFnReconversion *pReconv = (CFnReconversion *)pess->pv1; if (pKorTip->IsCandUIOpen()) return E_FAIL; return pReconv->_GetReconversion(ec, pic, pess->pRange, (CCandidateListEx **)pess->pv2, pess->fBool); } case ESCB_RECONV_SHOWCAND: { ITfComposition* pComposition; GUID attr; ITfProperty* pProp = NULL; pRange = pess->pRange; pComposition = pKorTip->GetIPComposition(pic); if (/*GetAIMM(pic) == fFalse && */ pComposition == NULL) { pKorTip->CreateIPComposition(ec, pic, pRange); // Set input attr and composing state. if (SUCCEEDED(pic->GetProperty(GUID_PROP_ATTRIBUTE, &pProp))) { attr = GUID_ATTR_KORIMX_INPUT; SetAttrPropertyData(pKorTip->_GetLibTLS(), ec, pProp, pRange, attr); pProp->Release(); } } pKorTip->OpenCandidateUI(ec, pic, pess->pRange, pess->pCandList); break; } case ESCB_INIT_MODEBIAS: // Check mode bias // // id : ESCB_INIT_MODEBIAS // ptip : this // pic : pic pKorTip->InitializeModeBias(ec, pic); break; } return S_OK; } /*--------------------------------------------------------------------------- CKorIMX::_DIMCallback ---------------------------------------------------------------------------*/ /* static */ HRESULT CKorIMX::_DIMCallback(UINT uCode, ITfDocumentMgr *pdimNew, ITfDocumentMgr *pdimPrev, void *pv) { ITfContext *pic = NULL; CKorIMX *pKorImx = (CKorIMX *)pv; Assert(pKorImx != NULL); switch (uCode) { case TIM_CODE_SETFOCUS: if (pdimPrev) { TraceMsg(DM_TRACE, TEXT("TIM_CODE_SETFOCUS: pdimPrev")); pdimPrev->GetTop(&pic); pKorImx->OnFocusChange(pic, fFalse); SafeRelease(pic); SafeReleaseClear(pKorImx->m_pCurrentDim); } if (pdimNew) { TraceMsg(DM_TRACE, TEXT("TIM_CODE_SETFOCUS: pdimNew")); SafeReleaseClear(pKorImx->m_pCurrentDim); // Set New dim pKorImx->m_pCurrentDim = pdimNew; pKorImx->m_pCurrentDim->AddRef(); pdimNew->GetTop(&pic); pKorImx->OnFocusChange(pic, fTrue); if (pic) pic->Release(); } break; } return S_OK; } /*--------------------------------------------------------------------------- CKorIMX::_ICCallback Document Input Manager callback. ITfThreadMgrEventSink ---------------------------------------------------------------------------*/ /* static */ HRESULT CKorIMX::_ICCallback(UINT uCode, ITfContext *pic, void *pv) { CKorIMX *_this = (CKorIMX *)pv; switch (uCode) { case TIM_CODE_INITIC: if (!_this->IsPendingCleanup()) // ignore new ic's if we're being shutdown. { _this->_InitICPriv(pic); } break; case TIM_CODE_UNINITIC: _this->_DeleteICPriv(pic); break; } return S_OK; } /*--------------------------------------------------------------------------- CKorIMX::_CompEventSinkCallback ---------------------------------------------------------------------------*/ HRESULT CKorIMX::_CompEventSinkCallback(void *pv, REFGUID rguid) { CICPriv* picp = (CICPriv*)pv; ITfContext* pic; CKorIMX *_this; if (picp == NULL) return S_OK; // error pic = picp->GetIC(); if (pic == NULL) return S_OK; // error _this = picp->GetIMX(); if (_this == NULL || _this->m_pToolBar == NULL) return S_OK; // do nothinig // if Open/Close compartment if (IsEqualGUID(rguid, GUID_COMPARTMENT_KEYBOARD_OPENCLOSE)) { _this->m_pToolBar->Update(UPDTTB_CMODE|UPDTTB_FHMODE); } else // if conversion mode compartment if (IsEqualGUID(rguid, GUID_COMPARTMENT_KORIMX_CONVMODE)) { DWORD dwConvMode = _this->GetConvMode(pic); BOOL fIsOn = _this->IsOn(pic); // We just open for Hangul mode do not close for Alphanumeric mode for Cicero full aware apps. // This will prevent redundant Open/Close compartment call. if (dwConvMode == TIP_ALPHANUMERIC_MODE && fIsOn) _this->SetOnOff(pic, fFalse); else if (dwConvMode != TIP_ALPHANUMERIC_MODE && fIsOn == fFalse) _this->SetOnOff(pic, fTrue); _this->m_pToolBar->Update(UPDTTB_CMODE|UPDTTB_FHMODE); } else // if SoftKeyboard compartmemnt if (IsEqualGUID(rguid, GUID_COMPARTMENT_KOR_SOFTKBD_OPENCLOSE)) { BOOL fSkbdOn = _this->GetSoftKBDOnOff(); _this->ShowSoftKBDWindow(fSkbdOn); if (_this->m_pToolBar && _this->m_pToolBar->GetSkbdMode()) _this->m_pToolBar->GetSkbdMode()->UpdateToggle(); } else // if SoftKeyboard compartmemnt if (IsEqualGUID(rguid, GUID_COMPARTMENT_SOFTKBD_KBDLAYOUT)) { DWORD dwSoftLayout, dwCurLabel; HRESULT hr; if (_this->m_pSoftKbd == NULL) return E_FAIL; dwSoftLayout = _this->GetSoftKBDLayout(); dwCurLabel = _this->GetHangulSKbd()->dwCurLabel; hr = _this->m_pSoftKbd->SelectSoftKeyboard(dwSoftLayout); if (FAILED(hr)) return hr; if (dwSoftLayout == _this->m_KbdStandard.dwSoftKbdLayout) hr = _this->m_pSoftKbd->SetKeyboardLabelText(GetKeyboardLayout(0)); else hr = _this->m_pSoftKbd->SetKeyboardLabelTextCombination(dwCurLabel); if (FAILED(hr)) return hr; if (_this->GetSoftKBDOnOff()) { hr = _this->m_pSoftKbd->ShowSoftKeyboard(fTrue); return hr; } } return S_OK; } /*--------------------------------------------------------------------------- CKorIMX::_PreKeyCallback ---------------------------------------------------------------------------*/ HRESULT CKorIMX::_PreKeyCallback(ITfContext *pic, REFGUID rguid, BOOL *pfEaten, void *pv) { CKorIMX *_this = (CKorIMX *)pv; if (_this == NULL) return S_OK; if (IsEqualGUID(rguid, GUID_KOREAN_HANGULSIMULATE)) { DWORD dwConvMode; // Toggle Hangul mode dwConvMode = _this->GetConvMode(pic); dwConvMode ^= TIP_HANGUL_MODE; _this->SetConvMode(pic, dwConvMode); *pfEaten = fTrue; } else if (IsEqualGUID(rguid, GUID_KOREAN_HANJASIMULATE)) { // O10 #317983 if (PRIMARYLANGID(LANGIDFROMLCID(GetSystemDefaultLCID())) != LANG_JAPANESE) { keybd_event(VK_HANJA, 0, 0, 0); keybd_event(VK_HANJA, 0, KEYEVENTF_KEYUP, 0); *pfEaten = fTrue; } else *pfEaten = fFalse; } return S_OK; } /* O N E N D E D I T */ /*------------------------------------------------------------------------------ ------------------------------------------------------------------------------*/ HRESULT CKorIMX::OnEndEdit(ITfContext *pic, TfEditCookie ecReadOnly, ITfEditRecord *pEditRecord) { UNREFERENCED_PARAMETER(ecReadOnly); static const GUID *rgModeBiasProperties[] = { &GUID_PROP_MODEBIAS }; static const GUID *rgAttrProperties[] = { &GUID_PROP_ATTRIBUTE, }; CEditSession2 *pes; ESSTRUCT ess; HRESULT hr; BOOL fInWriteSession; CICPriv *picp; IEnumTfRanges *pEnumText = NULL; ITfRange *pRange = NULL; ULONG ulFetched = 0; BOOL fCallES = fFalse; BOOL fSelChanged = fFalse; Assert(pic != NULL); if (pic == NULL) return S_OK; // error pic->InWriteSession(GetTID(), &fInWriteSession); if (fInWriteSession) return S_OK; // own change. picp = GetInputContextPriv(pic); if (picp == NULL) return S_OK; // error if (picp->GetfTransaction()) return S_OK; // skip in transaction. ////////////////////////////////////////////////////////////////////////// // Init to call ESCB_TEXTEVENT ESStructInit(&ess, ESCB_TEXTEVENT); // Call ESCB_TEXTEVENT callback only if GUID_PROP_MODEBIAS changed. hr = pEditRecord->GetTextAndPropertyUpdates(0/*TF_GTP_INCL_TEXT*/, rgModeBiasProperties, ARRAYSIZE(rgModeBiasProperties), &pEnumText); if (FAILED(hr) || pEnumText == NULL) return S_OK; if (pEnumText->Next(1, &pRange, &ulFetched) == S_OK) { SafeRelease(pRange); // ModeBias changed. ess.fBool = fTrue; } pEnumText->Release(); // Selection changed? pEditRecord->GetSelectionStatus(&fSelChanged); // If Attribute changed, set selection change true. if (fSelChanged == fFalse) { hr = pEditRecord->GetTextAndPropertyUpdates(0/*TF_GTP_INCL_TEXT*/, rgAttrProperties, ARRAYSIZE(rgAttrProperties), &pEnumText); if (FAILED(hr) || pEnumText == NULL) return S_OK; if (pEnumText->Next(1, &pRange, &ulFetched) == S_OK) { SafeRelease(pRange); fSelChanged = fTrue; } pEnumText->Release(); } // Perf: Call ES only if (ModeBias change) or (Selection changed and comp object exist) // I guess calling ES is pretty much costing since sel change occurs for ever cursor move. if (fSelChanged) fSelChanged = (GetIPComposition(pic) != NULL) ? fTrue : fFalse; // If ModeBias changed or Selection changed, then call ESCB_TEXTEVENT sink if (ess.fBool || fSelChanged) { if ((pes = new CEditSession2( pic, this, &ess, _EditSessionCallback2 )) != NULL) { pes->Invoke(ES2_READWRITE | ES2_ASYNC, &hr); pes->Release(); } } return S_OK; } /* O N S T A R T E D I T T R A N S A C T I O N */ /*------------------------------------------------------------------------------ ------------------------------------------------------------------------------*/ HRESULT CKorIMX::OnStartEditTransaction(ITfContext *pic) { CICPriv *picp; if (pic == NULL) return S_OK; // error picp = GetInputContextPriv(pic); if (picp) picp->SetfTransaction(fTrue); return S_OK; } /* O N E N D E D I T T R A N S A C T I O N */ /*------------------------------------------------------------------------------ ------------------------------------------------------------------------------*/ HRESULT CKorIMX::OnEndEditTransaction(ITfContext *pic) { BOOL ftran; CICPriv *picp; if (pic == NULL) return S_OK; // error picp = GetInputContextPriv(pic); if (picp) { ftran = picp->GetfTransaction(); if (ftran) { CEditSession2 *pes; ESSTRUCT ess; HRESULT hr; picp->SetfTransaction(fFalse); ESStructInit(&ess, ESCB_TEXTEVENT); ess.pEnumRange = NULL; if ((pes = new CEditSession2( pic, this, &ess, _EditSessionCallback2 )) != NULL) { pes->Invoke(ES2_READWRITE | ES2_ASYNC, &hr); pes->Release(); } } } return S_OK; }