diff --git a/src/Composition/CompositionProcessorEngine.cpp b/src/Composition/CompositionProcessorEngine.cpp index 6dd1fd0a..2d50806f 100644 --- a/src/Composition/CompositionProcessorEngine.cpp +++ b/src/Composition/CompositionProcessorEngine.cpp @@ -452,12 +452,31 @@ BOOL CCompositionProcessorEngine::IsPunctuation(WCHAR wch) namespace { -bool IsCommitWithFirstCandidatePunctuationInCandidateMode(WCHAR wch, CANDIDATE_MODE candidateMode) +bool IsCommitWithFirstCandidatePunctuationInCandidateMode(UINT uCode, WCHAR wch, CANDIDATE_MODE candidateMode) { if (candidateMode == CANDIDATE_NONE) { return false; } + + // Candidate paging keys must keep their navigation semantics even if the + // corresponding character is also listed in CommitWithFirstCandPunc. + switch (uCode) + { + case VK_PRIOR: + case VK_NEXT: + case VK_OEM_MINUS: + case VK_OEM_PLUS: + case VK_SUBTRACT: + case VK_ADD: + case VK_HOME: + case VK_END: + case VK_TAB: + return false; + default: + break; + } + return wch != 0 && Global::CommitWithFirstCandPunc.count(wch) > 0; } @@ -1671,18 +1690,18 @@ void CCompositionProcessorEngine::SetDefaultCandidateTextFont() // If engine need this virtual key code, returns true. Otherwise returns false. //---------------------------------------------------------------------------- -BOOL CCompositionProcessorEngine::IsVirtualKeyNeed( // - UINT uCode, // +BOOL CCompositionProcessorEngine::IsVirtualKeyNeed( // + UINT uCode, // _In_reads_(1) WCHAR *pwch, // BOOL fComposing, // CANDIDATE_MODE candidateMode, // BOOL hasCandidateWithWildcard, // _Out_opt_ _KEYSTROKE_STATE *pKeyState // -) -{ - if (pKeyState) - { - pKeyState->Category = CATEGORY_NONE; +) +{ + if (pKeyState) + { + pKeyState->Category = CATEGORY_NONE; pKeyState->Function = FUNCTION_NONE; } @@ -1701,7 +1720,7 @@ BOOL CCompositionProcessorEngine::IsVirtualKeyNeed( // return TRUE; } - if (IsCommitWithFirstCandidatePunctuationInCandidateMode(pwch ? *pwch : 0, candidateMode)) + if (IsCommitWithFirstCandidatePunctuationInCandidateMode(uCode, pwch ? *pwch : 0, candidateMode)) { if (pKeyState) { @@ -1986,13 +2005,13 @@ BOOL CCompositionProcessorEngine::IsVirtualKeyNeed( // pKeyState->Function = FUNCTION_MOVE_PAGE_UP; } return TRUE; - case VK_OEM_MINUS: - if (pKeyState) - { - pKeyState->Category = CATEGORY_CANDIDATE; - pKeyState->Function = FUNCTION_MOVE_PAGE_UP; - } - return TRUE; + case VK_OEM_MINUS: + if (pKeyState) + { + pKeyState->Category = CATEGORY_CANDIDATE; + pKeyState->Function = FUNCTION_MOVE_PAGE_UP; + } + return TRUE; case VK_NEXT: if (pKeyState) { @@ -2000,13 +2019,13 @@ BOOL CCompositionProcessorEngine::IsVirtualKeyNeed( // pKeyState->Function = FUNCTION_MOVE_PAGE_DOWN; } return TRUE; - case VK_OEM_PLUS: - if (pKeyState) - { - pKeyState->Category = CATEGORY_CANDIDATE; - pKeyState->Function = FUNCTION_MOVE_PAGE_DOWN; - } - return TRUE; + case VK_OEM_PLUS: + if (pKeyState) + { + pKeyState->Category = CATEGORY_CANDIDATE; + pKeyState->Function = FUNCTION_MOVE_PAGE_DOWN; + } + return TRUE; case VK_TAB: if (pKeyState) { @@ -2086,13 +2105,13 @@ BOOL CCompositionProcessorEngine::IsVirtualKeyNeed( // pKeyState->Function = FUNCTION_MOVE_PAGE_UP; } return TRUE; - case VK_OEM_MINUS: - if (pKeyState) - { - pKeyState->Category = CATEGORY_CANDIDATE; - pKeyState->Function = FUNCTION_MOVE_PAGE_UP; - } - return TRUE; + case VK_OEM_MINUS: + if (pKeyState) + { + pKeyState->Category = CATEGORY_CANDIDATE; + pKeyState->Function = FUNCTION_MOVE_PAGE_UP; + } + return TRUE; case VK_NEXT: if (pKeyState) { @@ -2100,13 +2119,13 @@ BOOL CCompositionProcessorEngine::IsVirtualKeyNeed( // pKeyState->Function = FUNCTION_MOVE_PAGE_DOWN; } return TRUE; - case VK_OEM_PLUS: - if (pKeyState) - { - pKeyState->Category = CATEGORY_CANDIDATE; - pKeyState->Function = FUNCTION_MOVE_PAGE_DOWN; - } - return TRUE; + case VK_OEM_PLUS: + if (pKeyState) + { + pKeyState->Category = CATEGORY_CANDIDATE; + pKeyState->Function = FUNCTION_MOVE_PAGE_DOWN; + } + return TRUE; case VK_TAB: if (pKeyState) { @@ -2180,15 +2199,15 @@ BOOL CCompositionProcessorEngine::IsVirtualKeyNeed( // return FALSE; } - if (*pwch && !IsVirtualKeyKeystrokeComposition(uCode, pKeyState, FUNCTION_NONE)) - { - if (pKeyState) - { - pKeyState->Category = CATEGORY_INVOKE_COMPOSITION_EDIT_SESSION; - pKeyState->Function = FUNCTION_FINALIZE_TEXTSTORE; - } - return FALSE; - } + if (*pwch && !IsVirtualKeyKeystrokeComposition(uCode, pKeyState, FUNCTION_NONE)) + { + if (pKeyState) + { + pKeyState->Category = CATEGORY_INVOKE_COMPOSITION_EDIT_SESSION; + pKeyState->Function = FUNCTION_FINALIZE_TEXTSTORE; + } + return FALSE; + } #ifdef FANY_DEBUG OutputDebugString(L"[msime]: Unknown keystroke."); diff --git a/src/Key/KeyHandler.cpp b/src/Key/KeyHandler.cpp index b1fea64e..689ff77b 100644 --- a/src/Key/KeyHandler.cpp +++ b/src/Key/KeyHandler.cpp @@ -13,10 +13,10 @@ #include "Ipc.h" #include "FanyDefines.h" -namespace -{ -std::wstring g_toggleImeFallbackBuffer; -} +namespace +{ +std::wstring g_toggleImeFallbackBuffer; +} ////////////////////////////////////////////////////////////////////// // @@ -592,12 +592,21 @@ Exit: // //---------------------------------------------------------------------------- -HRESULT CMetasequoiaIME::_HandleCompositionArrowKey(TfEditCookie ec, _In_ ITfContext *pContext, - KEYSTROKE_FUNCTION keyFunction) -{ - ITfRange *pRangeComposition = nullptr; - TF_SELECTION tfSelection; - ULONG fetched = 0; +HRESULT CMetasequoiaIME::_HandleCompositionArrowKey(TfEditCookie ec, _In_ ITfContext *pContext, + KEYSTROKE_FUNCTION keyFunction) +{ + if ((keyFunction == FUNCTION_MOVE_PAGE_UP) || (keyFunction == FUNCTION_MOVE_PAGE_DOWN) || + (keyFunction == FUNCTION_MOVE_PAGE_TOP) || (keyFunction == FUNCTION_MOVE_PAGE_BOTTOM)) + { + if ((_pCandidateListUIPresenter == nullptr) || (_pCandidateListUIPresenter->_GetCount() <= 1)) + { + return S_OK; + } + } + + ITfRange *pRangeComposition = nullptr; + TF_SELECTION tfSelection; + ULONG fetched = 0; // get the selection if (FAILED(pContext->GetSelection(ec, TF_DEFAULT_SELECTION, 1, &tfSelection, &fetched)) || fetched != 1) @@ -606,11 +615,11 @@ HRESULT CMetasequoiaIME::_HandleCompositionArrowKey(TfEditCookie ec, _In_ ITfCon return S_OK; } - // get the composition range - if (FAILED(_pComposition->GetRange(&pRangeComposition))) - { - goto Exit; - } + // get the composition range + if ((_pComposition == nullptr) || FAILED(_pComposition->GetRange(&pRangeComposition))) + { + goto Exit; + } // For incremental candidate list if (_pCandidateListUIPresenter) diff --git a/src/Key/KeyHandlerEditSession.cpp b/src/Key/KeyHandlerEditSession.cpp index 47d1e17c..78e2a94e 100644 --- a/src/Key/KeyHandlerEditSession.cpp +++ b/src/Key/KeyHandlerEditSession.cpp @@ -1,11 +1,10 @@ #include "Private.h" #include "KeyHandlerEditSession.h" #include "EditSession.h" -#include "MetasequoiaIME.h" -#include "CompositionProcessorEngine.h" -#include "KeyStateCategory.h" - -////////////////////////////////////////////////////////////////////// +#include "MetasequoiaIME.h" +#include "CompositionProcessorEngine.h" +#include "KeyStateCategory.h" +////////////////////////////////////////////////////////////////////// // // ITfEditSession // CEditSessionBase @@ -19,21 +18,21 @@ // //---------------------------------------------------------------------------- -STDAPI CKeyHandlerEditSession::DoEditSession(TfEditCookie ec) -{ - HRESULT hResult = S_OK; - - CKeyStateCategoryFactory *pKeyStateCategoryFactory = CKeyStateCategoryFactory::Instance(); +STDAPI CKeyHandlerEditSession::DoEditSession(TfEditCookie ec) +{ + HRESULT hResult = S_OK; + + CKeyStateCategoryFactory *pKeyStateCategoryFactory = CKeyStateCategoryFactory::Instance(); CKeyStateCategory *pKeyStateCategory = pKeyStateCategoryFactory->MakeKeyStateCategory(_KeyState.Category, _pTextService); - if (pKeyStateCategory) - { - KeyHandlerEditSessionDTO keyHandlerEditSessioDTO(ec, _pContext, _uCode, _wch, _KeyState.Function); - hResult = pKeyStateCategory->KeyStateHandler(_KeyState.Function, keyHandlerEditSessioDTO); - - pKeyStateCategory->Release(); - pKeyStateCategoryFactory->Release(); + if (pKeyStateCategory) + { + KeyHandlerEditSessionDTO keyHandlerEditSessioDTO(ec, _pContext, _uCode, _wch, _KeyState.Function); + hResult = pKeyStateCategory->KeyStateHandler(_KeyState.Function, keyHandlerEditSessioDTO); + + pKeyStateCategory->Release(); + pKeyStateCategoryFactory->Release(); } return hResult; diff --git a/src/UI/CandidateListUIPresenter.cpp b/src/UI/CandidateListUIPresenter.cpp index bb165b83..fd115785 100644 --- a/src/UI/CandidateListUIPresenter.cpp +++ b/src/UI/CandidateListUIPresenter.cpp @@ -236,6 +236,20 @@ HRESULT CMetasequoiaIME::_HandleCandidateArrowKey( // ec; pContext; + if (_pCandidateListUIPresenter == nullptr) + { + return S_OK; + } + + if ((keyFunction == FUNCTION_MOVE_PAGE_UP) || (keyFunction == FUNCTION_MOVE_PAGE_DOWN) || + (keyFunction == FUNCTION_MOVE_PAGE_TOP) || (keyFunction == FUNCTION_MOVE_PAGE_BOTTOM)) + { + if (_pCandidateListUIPresenter->_GetCount() <= 1) + { + return S_OK; + } + } + _pCandidateListUIPresenter->AdviseUIChangedByArrowKey(keyFunction); return S_OK; @@ -770,8 +784,19 @@ void CCandidateListUIPresenter::SetPageIndexWithScrollInfo( // _In_ CMetasequoiaImeArray *pCandidateList // ) { - UINT candCntInPage = _pIndexRange->Count(); - UINT bufferSize = pCandidateList->Count() / candCntInPage + 1; + if ((pCandidateList == nullptr) || (_pIndexRange == nullptr)) + { + return; + } + + const UINT candCntInPage = _pIndexRange->Count(); + if (candCntInPage == 0) + { + return; + } + + const UINT candidateCount = pCandidateList->Count(); + const UINT bufferSize = (candidateCount == 0) ? 0 : ((candidateCount - 1) / candCntInPage + 1); UINT *puPageIndex = new (std::nothrow) UINT[bufferSize]; if (puPageIndex != nullptr) { @@ -783,7 +808,12 @@ void CCandidateListUIPresenter::SetPageIndexWithScrollInfo( // _candidateState.SetPageIndex(puPageIndex, bufferSize); delete[] puPageIndex; } - _candidateState.SetScrollInfo(pCandidateList->Count(), + else if (bufferSize == 0) + { + _candidateState.SetPageIndex(nullptr, 0); + } + + _candidateState.SetScrollInfo(candidateCount, candCntInPage); // nMax:range of max, nPage:number of items in page } //+--------------------------------------------------------------------------- diff --git a/src/UI/CandidateListUIPresenter.h b/src/UI/CandidateListUIPresenter.h index 15f5c87b..4f484fae 100644 --- a/src/UI/CandidateListUIPresenter.h +++ b/src/UI/CandidateListUIPresenter.h @@ -75,6 +75,10 @@ class CCandidateListUIPresenter : public CTfTextLayoutSink, void _NotifyUI(); void _SetText(_In_ CMetasequoiaImeArray *pCandidateList, BOOL isAddFindKeyCode); void _ClearList(); + UINT _GetCount() const + { + return _candidateState.GetCount(); + } DWORD_PTR _GetSelectedCandidateString(_Outptr_result_maybenull_ const WCHAR **ppwchCandidateString); BOOL _SetSelectionInPage(int nPos) diff --git a/src/UI/CandidateSessionState.cpp b/src/UI/CandidateSessionState.cpp index 7d937109..cdfe78ff 100644 --- a/src/UI/CandidateSessionState.cpp +++ b/src/UI/CandidateSessionState.cpp @@ -1,6 +1,8 @@ #include "Private.h" #include "CandidateSessionState.h" #include +#include +#include "fmt/xchar.h" CCandidateSessionState::CCandidateSessionState(_In_ CCandidateRange *pIndexRange) { @@ -132,13 +134,13 @@ DWORD CCandidateSessionState::GetSelectedCandidateString( BOOL CCandidateSessionState::SetSelectionInPage(_In_ int nPos) { - if (nPos < 0) + if ((nPos < 0) || (_candidateList.Count() == 0) || (_pageIndex.Count() == 0) || (_pIndexRange == nullptr)) { return FALSE; } - UINT pos = static_cast(nPos); - if (pos >= _candidateList.Count()) + const UINT pageSize = _pIndexRange->Count(); + if (pageSize == 0) { return FALSE; } @@ -149,12 +151,24 @@ BOOL CCandidateSessionState::SetSelectionInPage(_In_ int nPos) return FALSE; } - _currentSelection = *_pageIndex.GetAt(currentPage) + nPos; + const UINT pageStart = *_pageIndex.GetAt(currentPage); + const UINT selection = pageStart + static_cast(nPos); + if ((selection >= _candidateList.Count()) || (selection >= pageStart + pageSize)) + { + return FALSE; + } + + _currentSelection = selection; return TRUE; } BOOL CCandidateSessionState::MoveSelection(_In_ int offSet) { + if (_candidateList.Count() == 0) + { + return FALSE; + } + if (_currentSelection + offSet >= _candidateList.Count()) { return FALSE; @@ -199,6 +213,17 @@ BOOL CCandidateSessionState::MovePage(_In_ int offSet) return TRUE; } + if ((_candidateList.Count() == 0) || (_pageIndex.Count() == 0) || (_pIndexRange == nullptr)) + { + return FALSE; + } + + const UINT pageSize = _pIndexRange->Count(); + if (pageSize == 0) + { + return FALSE; + } + int currentPage = 0; if (FAILED(GetCurrentPage(¤tPage))) { @@ -211,14 +236,21 @@ BOOL CCandidateSessionState::MovePage(_In_ int offSet) return FALSE; } - if (_currentSelection % _pIndexRange->Count() == 0 && _currentSelection == *_pageIndex.GetAt(currentPage)) + if ((_currentSelection % pageSize) == 0 && _currentSelection == *_pageIndex.GetAt(currentPage)) { _dontAdjustOnEmptyItemPage = TRUE; } - int selectionOffset = _currentSelection - *_pageIndex.GetAt(currentPage); - _currentSelection = *_pageIndex.GetAt(newPage) + selectionOffset; - _currentSelection = _candidateList.Count() > _currentSelection ? _currentSelection : _candidateList.Count() - 1; + const UINT currentPageStart = *_pageIndex.GetAt(currentPage); + const UINT newPageStart = *_pageIndex.GetAt(newPage); + if (newPageStart >= _candidateList.Count()) + { + return FALSE; + } + + const UINT selectionOffset = _currentSelection - currentPageStart; + const UINT maxSelectionInPage = min(pageSize - 1, _candidateList.Count() - 1 - newPageStart); + _currentSelection = newPageStart + min(selectionOffset, maxSelectionInPage); return TRUE; }