#include "FanyDefines.h" #include "Globals.h" #include "Private.h" #include "MetasequoiaIME.h" #include "CandidateWindow.h" #include "CandidateListUIPresenter.h" #include "CompositionProcessorEngine.h" #include "MetasequoiaIMEBaseStructure.h" #include "define.h" #include #include #include #include #include #include "Ipc.h" #include "fmt/xchar.h" #include "FanyUtils.h" ////////////////////////////////////////////////////////////////////// // // CMetasequoiaIME candidate key handler methods // ////////////////////////////////////////////////////////////////////// const int MOVEUP_ONE = -1; const int MOVEDOWN_ONE = 1; const int MOVETO_TOP = 0; const int MOVETO_BOTTOM = -1; //+--------------------------------------------------------------------------- // // _HandleCandidateFinalize // //---------------------------------------------------------------------------- HRESULT CMetasequoiaIME::_HandleCandidateFinalize(TfEditCookie ec, _In_ ITfContext *pContext) { HRESULT hr = S_OK; CStringRange keyStrokebuffer = _pCompositionProcessorEngine->GetKeystrokeBuffer(); DWORD_PTR keystrokeBufLen = keyStrokebuffer.GetLength(); DWORD_PTR candidateLen = keystrokeBufLen; CStringRange candidateString(keyStrokebuffer); OutputDebugString(fmt::format(L"create_word, keystrokeBuffer: {}", keyStrokebuffer.ToWString()).c_str()); // _pCandidateListUIPresenter would be null in uwp/metro apps if (nullptr == _pCandidateListUIPresenter) { // goto NoPresenter; } if (candidateLen) { struct FanyImeNamedpipeDataToTsf *receivedData = TryReadDataFromServerPipeWithTimeout(); if (receivedData->msg_type == Global::DataFromServerMsgType::OutofRange) // Candidate index out of range { return hr; } else if (receivedData->msg_type == Global::DataFromServerMsgType::Normal) // 只有正常情况下才会上屏 { GlobalIme::word_for_creating_word = L""; OutputDebugString(fmt::format(L"create_word, normal???").c_str()); candidateString.Set(receivedData->candidate_string, wcslen(receivedData->candidate_string)); hr = _AddComposingAndChar(ec, pContext, &candidateString); if (FAILED(hr)) { return hr; } } /* 处理造词的逻辑 */ // 先把拼音拿到,将其和上屏的汉字串比较一下, // - 如果分词后发现是纯拼音, // - 并且没有使用辅助码, // - 并且上屏的汉字串的拼音比起实际输入的拼音要短,则认为是造词 // TSF 端需要做的事情是,将 composingString // 替换成造词的状态,即把选中的汉字串替换相应的拼音部分,同时,其他的拼音部分保持不变 // Server 端自己会判断并处理的 else if (receivedData->msg_type == Global::DataFromServerMsgType::NeedToCreateWord) { /* 更新 TSF 的 preedit 的前面的部分为已选定的拼音,并且,去除辅助码部分 */ std::wstring data = receivedData->candidate_string; if (data.find(L',') != std::wstring::npos) { /* pureFullPinyin 一直都是最初的那个完整的拼音 */ std::wstring pureFullPinyin = data.substr(0, data.find(L',')); std::wstring curWord = data.substr(data.find(L',') + 1); OutputDebugString( fmt::format(L"create_word, pureFullPinyin: {}, curWord: {}\n", pureFullPinyin, curWord).c_str()); GlobalIme::word_for_creating_word = curWord; CCompositionProcessorEngine *pCompositionProcessorEngine = nullptr; pCompositionProcessorEngine = _pCompositionProcessorEngine; // // 计算出现在的拼音部分,比如,server 端传过来了 davsai 和 大塚,那么,TSF 端的拼音部分就是 ai // std::wstring preeditStr = pureFullPinyin.substr(FanyUtils::count_utf8_chars(FanyUtils::wstring_to_string(curWord)) * 2); DWORD_PTR vKeyLen = pCompositionProcessorEngine->GetVirtualKeyLength(); for (DWORD_PTR i = 0; i < vKeyLen; i++) { DWORD_PTR curVkeyLen = pCompositionProcessorEngine->GetVirtualKeyLength(); if (curVkeyLen) { pCompositionProcessorEngine->RemoveVirtualKey(curVkeyLen - 1); } } for (DWORD_PTR i = 0; i < preeditStr.length(); i++) { pCompositionProcessorEngine->AddVirtualKey(preeditStr[i]); } if (pCompositionProcessorEngine->GetVirtualKeyLength()) { _HandleCompositionInputWorker(pCompositionProcessorEngine, ec, pContext); } else { _HandleCancel(ec, pContext); } } OutputDebugString( fmt::format(L"create_word: current word part {}\n", GlobalIme::word_for_creating_word).c_str()); return hr; } } NoPresenter: _HandleComplete(ec, pContext); return hr; } //+--------------------------------------------------------------------------- // // _HandleCandidateFinalizeForVKReturn // //---------------------------------------------------------------------------- HRESULT CMetasequoiaIME::_HandleCandidateFinalizeForVKReturn(TfEditCookie ec, _In_ ITfContext *pContext) { HRESULT hr = S_OK; CStringRange keyStrokebuffer = _pCompositionProcessorEngine->GetKeystrokeBuffer(); DWORD_PTR keystrokeBufLen = keyStrokebuffer.GetLength(); DWORD_PTR candidateLen = keystrokeBufLen; CStringRange candidateString(keyStrokebuffer); if (nullptr == _pCandidateListUIPresenter) { // goto NoPresenter; } #ifdef FANY_DEBUG std::wstring msg(candidateString.Get(), candidateLen); OutputDebugString(msg.c_str()); #endif if (candidateLen) { hr = _AddComposingAndChar(ec, pContext, &candidateString); if (FAILED(hr)) { return hr; } } NoPresenter: _HandleComplete(ec, pContext); return hr; } //+--------------------------------------------------------------------------- // // _HandleCandidateConvert // //---------------------------------------------------------------------------- HRESULT CMetasequoiaIME::_HandleCandidateConvert(TfEditCookie ec, _In_ ITfContext *pContext) { return _HandleCandidateWorker(ec, pContext); } //+--------------------------------------------------------------------------- // // _HandleCandidateWorker // //---------------------------------------------------------------------------- HRESULT CMetasequoiaIME::_HandleCandidateWorker(TfEditCookie ec, _In_ ITfContext *pContext) { HRESULT hrReturn = E_FAIL; DWORD_PTR candidateLen = 0; const WCHAR *pCandidateString = nullptr; BSTR pbstr = nullptr; CStringRange candidateString; CMetasequoiaImeArray candidatePhraseList; if (nullptr == _pCandidateListUIPresenter) { hrReturn = S_OK; goto Exit; } candidateLen = _pCandidateListUIPresenter->_GetSelectedCandidateString(&pCandidateString); if (0 == candidateLen) { hrReturn = S_FALSE; goto Exit; } candidateString.Set(pCandidateString, candidateLen); BOOL fMakePhraseFromText = _pCompositionProcessorEngine->IsMakePhraseFromText(); if (fMakePhraseFromText) // NOTICE: always no { _pCompositionProcessorEngine->GetCandidateStringInConverted(candidateString, &candidatePhraseList); LCID locale = _pCompositionProcessorEngine->GetLocale(); _pCandidateListUIPresenter->RemoveSpecificCandidateFromList(locale, candidatePhraseList, candidateString); } // We have a candidate list if candidatePhraseList.Cnt is not 0 // If we are showing reverse conversion, use CCandidateListUIPresenter CANDIDATE_MODE tempCandMode = CANDIDATE_NONE; CCandidateListUIPresenter *pTempCandListUIPresenter = nullptr; if (candidatePhraseList.Count()) // NOTICE: always 0 { tempCandMode = CANDIDATE_WITH_NEXT_COMPOSITION; pTempCandListUIPresenter = new (std::nothrow) CCandidateListUIPresenter(this, Global::AtomCandidateWindow, CATEGORY_CANDIDATE, _pCompositionProcessorEngine->GetCandidateListIndexRange(), FALSE); if (nullptr == pTempCandListUIPresenter) { hrReturn = E_OUTOFMEMORY; goto Exit; } } // call _Start*Line for CCandidateListUIPresenter or CReadingLine // we don't cache the document manager object so get it from pContext. ITfDocumentMgr *pDocumentMgr = nullptr; HRESULT hrStartCandidateList = E_FAIL; if (pContext->GetDocumentMgr(&pDocumentMgr) == S_OK) { ITfRange *pRange = nullptr; if (_pComposition->GetRange(&pRange) == S_OK) { if (pTempCandListUIPresenter) { hrStartCandidateList = pTempCandListUIPresenter->_StartCandidateList( _tfClientId, pDocumentMgr, pContext, ec, pRange, _pCompositionProcessorEngine->GetCandidateWindowWidth()); } pRange->Release(); } pDocumentMgr->Release(); } // set up candidate list if it is being shown if (SUCCEEDED(hrStartCandidateList)) // NOTICE: always false { pTempCandListUIPresenter->_SetTextColor(RGB(0, 0x80, 0), GetSysColor(COLOR_WINDOW)); // Text color is green pTempCandListUIPresenter->_SetFillColor((HBRUSH)(COLOR_WINDOW + 1)); // Background color is window pTempCandListUIPresenter->_SetText(&candidatePhraseList, FALSE); #ifdef FANY_DEBUG OutputDebugString(fmt::format(L"Fany here candidateString = {}", candidateString.Get()).c_str()); #endif OutputDebugString(fmt::format(L"Create word here?").c_str()); // Add composing character hrReturn = _AddComposingAndChar(ec, pContext, &candidateString); // close candidate list if (_pCandidateListUIPresenter) { _pCandidateListUIPresenter->_EndCandidateList(); delete _pCandidateListUIPresenter; _pCandidateListUIPresenter = nullptr; _candidateMode = CANDIDATE_NONE; _isCandidateWithWildcard = FALSE; } if (hrReturn == S_OK) { // copy temp candidate _pCandidateListUIPresenter = pTempCandListUIPresenter; _candidateMode = tempCandMode; _isCandidateWithWildcard = FALSE; } } else { // When VK_SPACE, number(for selecting candidate) comes here hrReturn = _HandleCandidateFinalize(ec, pContext); } if (pbstr) { SysFreeString(pbstr); } Exit: return hrReturn; } //+--------------------------------------------------------------------------- // // _HandleCandidateArrowKey // //---------------------------------------------------------------------------- HRESULT CMetasequoiaIME::_HandleCandidateArrowKey( // TfEditCookie ec, // _In_ ITfContext *pContext, // _In_ KEYSTROKE_FUNCTION keyFunction // ) { ec; pContext; _pCandidateListUIPresenter->AdviseUIChangedByArrowKey(keyFunction); return S_OK; } //+--------------------------------------------------------------------------- // // _HandleCandidateSelectByNumber // //---------------------------------------------------------------------------- HRESULT CMetasequoiaIME::_HandleCandidateSelectByNumber(TfEditCookie ec, _In_ ITfContext *pContext, _In_ UINT uCode) { int iSelectAsNumber = _pCompositionProcessorEngine->GetCandidateListIndexRange()->GetIndex(uCode); if (iSelectAsNumber == -1) { return S_FALSE; } if (_pCandidateListUIPresenter) { if (_pCandidateListUIPresenter->_SetSelectionInPage(iSelectAsNumber)) { return _HandleCandidateConvert(ec, pContext); } } return S_FALSE; } //+--------------------------------------------------------------------------- // // _HandlePhraseFinalize // //---------------------------------------------------------------------------- HRESULT CMetasequoiaIME::_HandlePhraseFinalize(TfEditCookie ec, _In_ ITfContext *pContext) { HRESULT hr = S_OK; DWORD phraseLen = 0; const WCHAR *pPhraseString = nullptr; phraseLen = (DWORD)_pCandidateListUIPresenter->_GetSelectedCandidateString(&pPhraseString); CStringRange phraseString; phraseString.Set(pPhraseString, phraseLen); if (phraseLen) { if ((hr = _AddCharAndFinalize(ec, pContext, &phraseString)) != S_OK) { return hr; } } _HandleComplete(ec, pContext); return S_OK; } //+--------------------------------------------------------------------------- // // _HandlePhraseArrowKey // //---------------------------------------------------------------------------- HRESULT CMetasequoiaIME::_HandlePhraseArrowKey(TfEditCookie ec, _In_ ITfContext *pContext, _In_ KEYSTROKE_FUNCTION keyFunction) { ec; pContext; _pCandidateListUIPresenter->AdviseUIChangedByArrowKey(keyFunction); return S_OK; } //+--------------------------------------------------------------------------- // // _HandlePhraseSelectByNumber // //---------------------------------------------------------------------------- HRESULT CMetasequoiaIME::_HandlePhraseSelectByNumber(TfEditCookie ec, _In_ ITfContext *pContext, _In_ UINT uCode) { // isSelectAsNumber starts from 0 int iSelectAsNumber = _pCompositionProcessorEngine->GetCandidateListIndexRange()->GetIndex(uCode); if (iSelectAsNumber == -1) { return S_FALSE; } if (_pCandidateListUIPresenter) { if (_pCandidateListUIPresenter->_SetSelectionInPage(iSelectAsNumber)) { return _HandlePhraseFinalize(ec, pContext); } } return S_FALSE; } ////////////////////////////////////////////////////////////////////// // // CCandidateListUIPresenter class // ////////////////////////////////////////////////////////////////////// //+--------------------------------------------------------------------------- // // ctor // //---------------------------------------------------------------------------- CCandidateListUIPresenter::CCandidateListUIPresenter(_In_ CMetasequoiaIME *pTextService, ATOM atom, KEYSTROKE_CATEGORY Category, _In_ CCandidateRange *pIndexRange, BOOL hideWindow) : CTfTextLayoutSink(pTextService) { _atom = atom; _pIndexRange = pIndexRange; _parentWndHandle = nullptr; _pCandidateWnd = nullptr; _Category = Category; _updatedFlags = 0; _uiElementId = (DWORD)-1; _isShowMode = TRUE; // store return value from BeginUIElement _hideWindow = hideWindow; // Hide window flag from [Configuration] CandidateList.Phrase.HideWindow _pTextService = pTextService; _pTextService->AddRef(); _refCount = 1; } //+--------------------------------------------------------------------------- // // dtor // //---------------------------------------------------------------------------- CCandidateListUIPresenter::~CCandidateListUIPresenter() { _EndCandidateList(); _pTextService->Release(); } //+--------------------------------------------------------------------------- // // ITfCandidateListUIElement::IUnknown::QueryInterface // //---------------------------------------------------------------------------- STDAPI CCandidateListUIPresenter::QueryInterface(REFIID riid, _Outptr_ void **ppvObj) { if (CTfTextLayoutSink::QueryInterface(riid, ppvObj) == S_OK) { return S_OK; } if (ppvObj == nullptr) { return E_INVALIDARG; } *ppvObj = nullptr; if (IsEqualIID(riid, IID_ITfUIElement) || IsEqualIID(riid, IID_ITfCandidateListUIElement)) { *ppvObj = (ITfCandidateListUIElement *)this; } else if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_ITfCandidateListUIElementBehavior)) { *ppvObj = (ITfCandidateListUIElementBehavior *)this; } else if (IsEqualIID(riid, __uuidof(ITfIntegratableCandidateListUIElement))) { *ppvObj = (ITfIntegratableCandidateListUIElement *)this; } if (*ppvObj) { AddRef(); return S_OK; } return E_NOINTERFACE; } //+--------------------------------------------------------------------------- // // ITfCandidateListUIElement::IUnknown::AddRef // //---------------------------------------------------------------------------- STDAPI_(ULONG) CCandidateListUIPresenter::AddRef() { CTfTextLayoutSink::AddRef(); return ++_refCount; } //+--------------------------------------------------------------------------- // // ITfCandidateListUIElement::IUnknown::Release // //---------------------------------------------------------------------------- STDAPI_(ULONG) CCandidateListUIPresenter::Release() { CTfTextLayoutSink::Release(); LONG cr = --_refCount; assert(_refCount >= 0); if (_refCount == 0) { delete this; } return cr; } //+--------------------------------------------------------------------------- // // ITfCandidateListUIElement::ITfUIElement::GetDescription // //---------------------------------------------------------------------------- STDAPI CCandidateListUIPresenter::GetDescription(BSTR *pbstr) { if (pbstr) { *pbstr = SysAllocString(L"Cand"); } return S_OK; } //+--------------------------------------------------------------------------- // // ITfCandidateListUIElement::ITfUIElement::GetGUID // //---------------------------------------------------------------------------- STDAPI CCandidateListUIPresenter::GetGUID(GUID *pguid) { *pguid = Global::MetasequoiaIMEGuidCandUIElement; return S_OK; } //+--------------------------------------------------------------------------- // // ITfCandidateListUIElement::ITfUIElement::Show // //---------------------------------------------------------------------------- STDAPI CCandidateListUIPresenter::Show(BOOL showCandidateWindow) { if (showCandidateWindow) { ToShowCandidateWindow(); } else { ToHideCandidateWindow(); } return S_OK; } HRESULT CCandidateListUIPresenter::ToShowCandidateWindow() { if (_hideWindow) { _pCandidateWnd->_Show(FALSE); } else { _MoveWindowToTextExt(); _pCandidateWnd->_Show(TRUE); } return S_OK; } HRESULT CCandidateListUIPresenter::ToHideCandidateWindow() { if (_pCandidateWnd) { _pCandidateWnd->_Show(FALSE); } _updatedFlags = TF_CLUIE_SELECTION | TF_CLUIE_CURRENTPAGE; _UpdateUIElement(); return S_OK; } //+--------------------------------------------------------------------------- // // ITfCandidateListUIElement::ITfUIElement::IsShown // //---------------------------------------------------------------------------- STDAPI CCandidateListUIPresenter::IsShown(BOOL *pIsShow) { *pIsShow = _pCandidateWnd->_IsWindowVisible(); return S_OK; } //+--------------------------------------------------------------------------- // // ITfCandidateListUIElement::GetUpdatedFlags // //---------------------------------------------------------------------------- STDAPI CCandidateListUIPresenter::GetUpdatedFlags(DWORD *pdwFlags) { *pdwFlags = _updatedFlags; return S_OK; } //+--------------------------------------------------------------------------- // // ITfCandidateListUIElement::GetDocumentMgr // //---------------------------------------------------------------------------- STDAPI CCandidateListUIPresenter::GetDocumentMgr(ITfDocumentMgr **ppdim) { *ppdim = nullptr; return E_NOTIMPL; } //+--------------------------------------------------------------------------- // // ITfCandidateListUIElement::GetCount // //---------------------------------------------------------------------------- STDAPI CCandidateListUIPresenter::GetCount(UINT *pCandidateCount) { if (_pCandidateWnd) { *pCandidateCount = _pCandidateWnd->_GetCount(); } else { *pCandidateCount = 0; } return S_OK; } //+--------------------------------------------------------------------------- // // ITfCandidateListUIElement::GetSelection // //---------------------------------------------------------------------------- STDAPI CCandidateListUIPresenter::GetSelection(UINT *pSelectedCandidateIndex) { if (_pCandidateWnd) { *pSelectedCandidateIndex = _pCandidateWnd->_GetSelection(); } else { *pSelectedCandidateIndex = 0; } return S_OK; } //+--------------------------------------------------------------------------- // // ITfCandidateListUIElement::GetString // //---------------------------------------------------------------------------- STDAPI CCandidateListUIPresenter::GetString(UINT uIndex, BSTR *pbstr) { if (!_pCandidateWnd || (uIndex > _pCandidateWnd->_GetCount())) { return E_FAIL; } DWORD candidateLen = 0; const WCHAR *pCandidateString = nullptr; candidateLen = _pCandidateWnd->_GetCandidateString(uIndex, &pCandidateString); *pbstr = (candidateLen == 0) ? nullptr : SysAllocStringLen(pCandidateString, candidateLen); return S_OK; } //+--------------------------------------------------------------------------- // // ITfCandidateListUIElement::GetPageIndex // //---------------------------------------------------------------------------- STDAPI CCandidateListUIPresenter::GetPageIndex(UINT *pIndex, UINT uSize, UINT *puPageCnt) { if (!_pCandidateWnd) { if (pIndex) { *pIndex = 0; } *puPageCnt = 0; return S_OK; } return _pCandidateWnd->_GetPageIndex(pIndex, uSize, puPageCnt); } //+--------------------------------------------------------------------------- // // ITfCandidateListUIElement::SetPageIndex // //---------------------------------------------------------------------------- STDAPI CCandidateListUIPresenter::SetPageIndex(UINT *pIndex, UINT uPageCnt) { if (!_pCandidateWnd) { return E_FAIL; } return _pCandidateWnd->_SetPageIndex(pIndex, uPageCnt); } //+--------------------------------------------------------------------------- // // ITfCandidateListUIElement::GetCurrentPage // //---------------------------------------------------------------------------- STDAPI CCandidateListUIPresenter::GetCurrentPage(UINT *puPage) { if (!_pCandidateWnd) { *puPage = 0; return S_OK; } return _pCandidateWnd->_GetCurrentPage(puPage); } //+--------------------------------------------------------------------------- // // ITfCandidateListUIElementBehavior::SetSelection // It is related of the mouse clicking behavior upon the suggestion window //---------------------------------------------------------------------------- STDAPI CCandidateListUIPresenter::SetSelection(UINT nIndex) { if (_pCandidateWnd) { _pCandidateWnd->_SetSelection(nIndex); } return S_OK; } //+--------------------------------------------------------------------------- // // ITfCandidateListUIElementBehavior::Finalize // It is related of the mouse clicking behavior upon the suggestion window //---------------------------------------------------------------------------- STDAPI CCandidateListUIPresenter::Finalize(void) { _CandidateChangeNotification(CAND_ITEM_SELECT); return S_OK; } //+--------------------------------------------------------------------------- // // ITfCandidateListUIElementBehavior::Abort // //---------------------------------------------------------------------------- STDAPI CCandidateListUIPresenter::Abort(void) { return E_NOTIMPL; } //+--------------------------------------------------------------------------- // // ITfIntegratableCandidateListUIElement::SetIntegrationStyle // To show candidateNumbers on the suggestion window //---------------------------------------------------------------------------- STDAPI CCandidateListUIPresenter::SetIntegrationStyle(GUID guidIntegrationStyle) { return (guidIntegrationStyle == GUID_INTEGRATIONSTYLE_SEARCHBOX) ? S_OK : E_NOTIMPL; } //+--------------------------------------------------------------------------- // // ITfIntegratableCandidateListUIElement::GetSelectionStyle // //---------------------------------------------------------------------------- STDAPI CCandidateListUIPresenter::GetSelectionStyle(_Out_ TfIntegratableCandidateListSelectionStyle *ptfSelectionStyle) { *ptfSelectionStyle = STYLE_ACTIVE_SELECTION; return S_OK; } //+--------------------------------------------------------------------------- // // ITfIntegratableCandidateListUIElement::OnKeyDown // //---------------------------------------------------------------------------- STDAPI CCandidateListUIPresenter::OnKeyDown(_In_ WPARAM wParam, _In_ LPARAM lParam, _Out_ BOOL *pIsEaten) { wParam; lParam; *pIsEaten = TRUE; return S_OK; } //+--------------------------------------------------------------------------- // // ITfIntegratableCandidateListUIElement::ShowCandidateNumbers // //---------------------------------------------------------------------------- STDAPI CCandidateListUIPresenter::ShowCandidateNumbers(_Out_ BOOL *pIsShow) { *pIsShow = TRUE; return S_OK; } //+--------------------------------------------------------------------------- // // ITfIntegratableCandidateListUIElement::FinalizeExactCompositionString // //---------------------------------------------------------------------------- STDAPI CCandidateListUIPresenter::FinalizeExactCompositionString() { return E_NOTIMPL; } //+--------------------------------------------------------------------------- // // _StartCandidateList // //---------------------------------------------------------------------------- HRESULT CCandidateListUIPresenter::_StartCandidateList(TfClientId tfClientId, _In_ ITfDocumentMgr *pDocumentMgr, _In_ ITfContext *pContextDocument, TfEditCookie ec, _In_ ITfRange *pRangeComposition, UINT wndWidth) { pDocumentMgr; tfClientId; HRESULT hr = E_FAIL; if (FAILED(_StartLayout(pContextDocument, ec, pRangeComposition))) { goto Exit; } BeginUIElement(); hr = MakeCandidateWindow(pContextDocument, wndWidth); if (FAILED(hr)) { OutputDebugString(L"MakeCandidateWindow failed\n"); // goto Exit; } // Show(_isShowMode); RECT rcTextExt; if (SUCCEEDED(_GetTextExt(&rcTextExt))) { Global::Point[0] = rcTextExt.left; Global::Point[1] = rcTextExt.bottom; _LayoutChangeNotification(&rcTextExt); } Exit: if (FAILED(hr)) { _EndCandidateList(); } return hr; } //+--------------------------------------------------------------------------- // // _EndCandidateList // //---------------------------------------------------------------------------- void CCandidateListUIPresenter::_EndCandidateList() { EndUIElement(); /* 告诉 Server 端隐藏候选框窗口 */ DisposeCandidateWindow(); _EndLayout(); } void CCandidateListUIPresenter::_NotifyUI() { CStringRange keyStringBuffer = _pTextService->GetCompositionProcessorEngine()->GetKeystrokeBuffer(); std::wstring pinyinString(keyStringBuffer.Get(), keyStringBuffer.GetLength()); Global::PinyinLength = pinyinString.length(); WriteDataToSharedMemory( // Global::Keycode, // Global::wch, // Global::ModifiersDown, // Global::Point, // Global::PinyinLength, // Global::PinyinString, // 0b111111 // ); SendShowCandidateWndEventToUIProcess(); } //+--------------------------------------------------------------------------- // // _SetText // //---------------------------------------------------------------------------- void CCandidateListUIPresenter::_SetText(_In_ CMetasequoiaImeArray *pCandidateList, BOOL isAddFindKeyCode) { AddCandidateToCandidateListUI(pCandidateList, isAddFindKeyCode); SetPageIndexWithScrollInfo(pCandidateList); if (_isShowMode) { // _pCandidateWnd->_InvalidateRect(); _NotifyUI(); } else { _updatedFlags = TF_CLUIE_COUNT | TF_CLUIE_SELECTION | TF_CLUIE_STRING | TF_CLUIE_PAGEINDEX | TF_CLUIE_CURRENTPAGE; _UpdateUIElement(); } } void CCandidateListUIPresenter::AddCandidateToCandidateListUI( // _In_ CMetasequoiaImeArray *pCandidateList, // BOOL isAddFindKeyCode // ) { for (UINT index = 0; index < pCandidateList->Count(); index++) { _pCandidateWnd->_AddString(pCandidateList->GetAt(index), isAddFindKeyCode); } } void CCandidateListUIPresenter::SetPageIndexWithScrollInfo( // _In_ CMetasequoiaImeArray *pCandidateList // ) { UINT candCntInPage = _pIndexRange->Count(); UINT bufferSize = pCandidateList->Count() / candCntInPage + 1; UINT *puPageIndex = new (std::nothrow) UINT[bufferSize]; if (puPageIndex != nullptr) { for (UINT i = 0; i < bufferSize; i++) { puPageIndex[i] = i * candCntInPage; } _pCandidateWnd->_SetPageIndex(puPageIndex, bufferSize); delete[] puPageIndex; } _pCandidateWnd->_SetScrollInfo(pCandidateList->Count(), candCntInPage); // nMax:range of max, nPage:number of items in page } //+--------------------------------------------------------------------------- // // _ClearList // //---------------------------------------------------------------------------- void CCandidateListUIPresenter::_ClearList() { _pCandidateWnd->_ClearList(); } //+--------------------------------------------------------------------------- // // _SetTextColor // _SetFillColor // //---------------------------------------------------------------------------- void CCandidateListUIPresenter::_SetTextColor(COLORREF crColor, COLORREF crBkColor) { _pCandidateWnd->_SetTextColor(crColor, crBkColor); } void CCandidateListUIPresenter::_SetFillColor(HBRUSH hBrush) { _pCandidateWnd->_SetFillColor(hBrush); } //+--------------------------------------------------------------------------- // // _GetSelectedCandidateString // //---------------------------------------------------------------------------- DWORD_PTR CCandidateListUIPresenter::_GetSelectedCandidateString( _Outptr_result_maybenull_ const WCHAR **ppwchCandidateString) { return _pCandidateWnd->_GetSelectedCandidateString(ppwchCandidateString); } //+--------------------------------------------------------------------------- // // _MoveSelection // //---------------------------------------------------------------------------- BOOL CCandidateListUIPresenter::_MoveSelection(_In_ int offSet) { BOOL ret = _pCandidateWnd->_MoveSelection(offSet, TRUE); if (ret) { if (_isShowMode) { // _pCandidateWnd->_InvalidateRect(); _NotifyUI(); } else { _updatedFlags = TF_CLUIE_SELECTION; _UpdateUIElement(); } } return ret; } //+--------------------------------------------------------------------------- // // _SetSelection // //---------------------------------------------------------------------------- BOOL CCandidateListUIPresenter::_SetSelection(_In_ int selectedIndex) { BOOL ret = _pCandidateWnd->_SetSelection(selectedIndex, TRUE); if (ret) { if (_isShowMode) { } else { _updatedFlags = TF_CLUIE_SELECTION | TF_CLUIE_CURRENTPAGE; _UpdateUIElement(); } } return ret; } //+--------------------------------------------------------------------------- // // _MovePage // //---------------------------------------------------------------------------- BOOL CCandidateListUIPresenter::_MovePage(_In_ int offSet) { BOOL ret = _pCandidateWnd->_MovePage(offSet, TRUE); if (ret) { if (_isShowMode) { // _pCandidateWnd->_InvalidateRect(); _NotifyUI(); } else { _updatedFlags = TF_CLUIE_SELECTION | TF_CLUIE_CURRENTPAGE; _UpdateUIElement(); } } return ret; } //+--------------------------------------------------------------------------- // // _MoveWindowToTextExt // //---------------------------------------------------------------------------- void CCandidateListUIPresenter::_MoveWindowToTextExt() { RECT rc; if (FAILED(_GetTextExt(&rc))) { return; } _pCandidateWnd->_Move(rc.left, rc.bottom); } //+--------------------------------------------------------------------------- // // _LayoutChangeNotification // //---------------------------------------------------------------------------- VOID CCandidateListUIPresenter::_LayoutChangeNotification(_In_ RECT *lpRect) { // OutputDebugString(L"LayoutChangeNotification\n"); #ifdef FANY_DEBUG // TODO: Log _LayoutChangeNotification firefox cnt: Global::firefox_like_cnt #endif WriteDataToSharedMemory( // Global::Keycode, // Global::wch, // 0, // Global::Point, // Global::PinyinLength, // Global::PinyinString, // 0b001000 // ); SendMoveCandidateWndEventToUIProcess(); } //+--------------------------------------------------------------------------- // // _LayoutDestroyNotification // //---------------------------------------------------------------------------- VOID CCandidateListUIPresenter::_LayoutDestroyNotification() { _EndCandidateList(); } //+--------------------------------------------------------------------------- // // _CandidateChangeNotifiction // //---------------------------------------------------------------------------- HRESULT CCandidateListUIPresenter::_CandidateChangeNotification(_In_ enum CANDWND_ACTION action) { HRESULT hr = E_FAIL; TfClientId tfClientId = _pTextService->_GetClientId(); ITfThreadMgr *pThreadMgr = nullptr; ITfDocumentMgr *pDocumentMgr = nullptr; ITfContext *pContext = nullptr; _KEYSTROKE_STATE KeyState; KeyState.Category = _Category; KeyState.Function = FUNCTION_FINALIZE_CANDIDATELIST; if (CAND_ITEM_SELECT != action) { goto Exit; } pThreadMgr = _pTextService->_GetThreadMgr(); if (nullptr == pThreadMgr) { goto Exit; } hr = pThreadMgr->GetFocus(&pDocumentMgr); if (FAILED(hr)) { goto Exit; } hr = pDocumentMgr->GetTop(&pContext); if (FAILED(hr)) { pDocumentMgr->Release(); goto Exit; } CKeyHandlerEditSession *pEditSession = new (std::nothrow) CKeyHandlerEditSession(_pTextService, pContext, 0, 0, KeyState); if (nullptr != pEditSession) { HRESULT hrSession = S_OK; hr = pContext->RequestEditSession(tfClientId, pEditSession, TF_ES_SYNC | TF_ES_READWRITE, &hrSession); if (hrSession == TF_E_SYNCHRONOUS || hrSession == TS_E_READONLY) { hr = pContext->RequestEditSession(tfClientId, pEditSession, TF_ES_ASYNC | TF_ES_READWRITE, &hrSession); } pEditSession->Release(); } pContext->Release(); pDocumentMgr->Release(); Exit: return hr; } //+--------------------------------------------------------------------------- // // _CandWndCallback // //---------------------------------------------------------------------------- // static HRESULT CCandidateListUIPresenter::_CandWndCallback(_In_ void *pv, _In_ enum CANDWND_ACTION action) { CCandidateListUIPresenter *fakeThis = (CCandidateListUIPresenter *)pv; return fakeThis->_CandidateChangeNotification(action); } //+--------------------------------------------------------------------------- // // _UpdateUIElement // //---------------------------------------------------------------------------- HRESULT CCandidateListUIPresenter::_UpdateUIElement() { HRESULT hr = S_OK; ITfThreadMgr *pThreadMgr = _pTextService->_GetThreadMgr(); if (nullptr == pThreadMgr) { return S_OK; } ITfUIElementMgr *pUIElementMgr = nullptr; hr = pThreadMgr->QueryInterface(IID_ITfUIElementMgr, (void **)&pUIElementMgr); if (hr == S_OK) { pUIElementMgr->UpdateUIElement(_uiElementId); pUIElementMgr->Release(); } return S_OK; } //+--------------------------------------------------------------------------- // // OnSetThreadFocus // //---------------------------------------------------------------------------- HRESULT CCandidateListUIPresenter::OnSetThreadFocus() { if (_isShowMode) { Show(TRUE); } return S_OK; } //+--------------------------------------------------------------------------- // // OnKillThreadFocus // //---------------------------------------------------------------------------- HRESULT CCandidateListUIPresenter::OnKillThreadFocus() { if (_isShowMode) { Show(FALSE); } return S_OK; } void CCandidateListUIPresenter::RemoveSpecificCandidateFromList( // _In_ LCID Locale, // _Inout_ CMetasequoiaImeArray &candidateList, // _In_ CStringRange &candidateString // ) { for (UINT index = 0; index < candidateList.Count();) { CCandidateListItem *pLI = candidateList.GetAt(index); if (CStringRange::Compare(Locale, &candidateString, &pLI->_ItemString) == CSTR_EQUAL) { candidateList.RemoveAt(index); continue; } index++; } } void CCandidateListUIPresenter::AdviseUIChangedByArrowKey(_In_ KEYSTROKE_FUNCTION arrowKey) { switch (arrowKey) { case FUNCTION_MOVE_UP: { _MoveSelection(MOVEUP_ONE); break; } case FUNCTION_MOVE_DOWN: { _MoveSelection(MOVEDOWN_ONE); break; } case FUNCTION_MOVE_PAGE_UP: { // Page prev _MovePage(MOVEUP_ONE); break; } case FUNCTION_MOVE_PAGE_DOWN: { // Page next _MovePage(MOVEDOWN_ONE); break; } case FUNCTION_MOVE_PAGE_TOP: { _SetSelection(MOVETO_TOP); break; } case FUNCTION_MOVE_PAGE_BOTTOM: { _SetSelection(MOVETO_BOTTOM); break; } default: break; } } HRESULT CCandidateListUIPresenter::BeginUIElement() { HRESULT hr = S_OK; ITfThreadMgr *pThreadMgr = _pTextService->_GetThreadMgr(); if (nullptr == pThreadMgr) { hr = E_FAIL; goto Exit; } ITfUIElementMgr *pUIElementMgr = nullptr; hr = pThreadMgr->QueryInterface(IID_ITfUIElementMgr, (void **)&pUIElementMgr); if (hr == S_OK) { pUIElementMgr->BeginUIElement(this, &_isShowMode, &_uiElementId); pUIElementMgr->Release(); } Exit: return hr; } HRESULT CCandidateListUIPresenter::EndUIElement() { HRESULT hr = S_OK; ITfThreadMgr *pThreadMgr = _pTextService->_GetThreadMgr(); if ((nullptr == pThreadMgr) || (-1 == _uiElementId)) { hr = E_FAIL; goto Exit; } ITfUIElementMgr *pUIElementMgr = nullptr; hr = pThreadMgr->QueryInterface(IID_ITfUIElementMgr, (void **)&pUIElementMgr); if (hr == S_OK) { pUIElementMgr->EndUIElement(_uiElementId); pUIElementMgr->Release(); } Exit: return hr; } //+--------------------------------------------------------------------------- // MakeCandidateWindow // // Create the candidate window //---------------------------------------------------------------------------- HRESULT CCandidateListUIPresenter::MakeCandidateWindow(_In_ ITfContext *pContextDocument, _In_ UINT wndWidth) { HRESULT hr = S_OK; if (nullptr != _pCandidateWnd) { return hr; } _pCandidateWnd = new (std::nothrow) CCandidateWindow(_CandWndCallback, this, _pIndexRange, _pTextService->_IsStoreAppMode(), _pTextService); if (_pCandidateWnd == nullptr) { hr = E_OUTOFMEMORY; goto Exit; } HWND parentWndHandle = nullptr; ITfContextView *pView = nullptr; if (SUCCEEDED(pContextDocument->GetActiveView(&pView))) { // pView->GetWnd(&parentWndHandle); if (FAILED(pView->GetWnd(&parentWndHandle)) || (parentWndHandle == nullptr)) { parentWndHandle = GetFocus(); } } if (!_pCandidateWnd->_Create(_atom, wndWidth, parentWndHandle)) { hr = E_OUTOFMEMORY; goto Exit; } Exit: return hr; } void CCandidateListUIPresenter::DisposeCandidateWindow() { if (nullptr == _pCandidateWnd) { return; } // // Hide the global candidate window // // ShowWindow(Global::MainWindowHandle, SW_HIDE); // HWND UIHwnd = FindWindow(L"global_candidate_window", NULL); // UINT WM_HIDE_MAIN_WINDOW = RegisterWindowMessage(L"WM_HIDE_MAIN_WINDOW"); SendHideCandidateWndEventToUIProcess(); _pCandidateWnd->_Destroy(); delete _pCandidateWnd; _pCandidateWnd = nullptr; }