// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A // PARTICULAR PURPOSE. // // Copyright (c) Microsoft Corporation. All rights reserved #include "Private.h" #include "SampleIME.h" #include "CompositionProcessorEngine.h" #include "TableDictionaryEngine.h" #include "DictionarySearch.h" #include "TfInputProcessorProfile.h" #include "Globals.h" #include "Compartment.h" #include "LanguageBar.h" #include "RegKey.h" ////////////////////////////////////////////////////////////////////// // // CSampleIME implementation. // ////////////////////////////////////////////////////////////////////// //+--------------------------------------------------------------------------- // // _AddTextProcessorEngine // //---------------------------------------------------------------------------- BOOL CSampleIME::_AddTextProcessorEngine() { LANGID langid = 0; CLSID clsid = GUID_NULL; GUID guidProfile = GUID_NULL; // Get default profile. CTfInputProcessorProfile profile; if (FAILED(profile.CreateInstance())) { return FALSE; } if (FAILED(profile.GetCurrentLanguage(&langid))) { return FALSE; } if (FAILED(profile.GetDefaultLanguageProfile(langid, GUID_TFCAT_TIP_KEYBOARD, &clsid, &guidProfile))) { return FALSE; } // Is this already added? if (_pCompositionProcessorEngine != nullptr) { LANGID langidProfile = 0; GUID guidLanguageProfile = GUID_NULL; guidLanguageProfile = _pCompositionProcessorEngine->GetLanguageProfile(&langidProfile); if ((langid == langidProfile) && IsEqualGUID(guidProfile, guidLanguageProfile)) { return TRUE; } } // Create composition processor engine if (_pCompositionProcessorEngine == nullptr) { _pCompositionProcessorEngine = new (std::nothrow) CCompositionProcessorEngine(); } if (!_pCompositionProcessorEngine) { return FALSE; } // setup composition processor engine if (FALSE == _pCompositionProcessorEngine->SetupLanguageProfile(langid, guidProfile, _GetThreadMgr(), _GetClientId(), _IsSecureMode(), _IsComLess())) { return FALSE; } return TRUE; } ////////////////////////////////////////////////////////////////////// // // CompositionProcessorEngine implementation. // ////////////////////////////////////////////////////////////////////// //+--------------------------------------------------------------------------- // // ctor // //---------------------------------------------------------------------------- CCompositionProcessorEngine::CCompositionProcessorEngine() { _pTableDictionaryEngine = nullptr; _pDictionaryFile = nullptr; _langid = 0xffff; _guidProfile = GUID_NULL; _tfClientId = TF_CLIENTID_NULL; _pLanguageBar_IMEMode = nullptr; _pLanguageBar_DoubleSingleByte = nullptr; _pLanguageBar_Punctuation = nullptr; _pCompartmentConversion = nullptr; _pCompartmentKeyboardOpenEventSink = nullptr; _pCompartmentConversionEventSink = nullptr; _pCompartmentDoubleSingleByteEventSink = nullptr; _pCompartmentPunctuationEventSink = nullptr; _hasWildcardIncludedInKeystrokeBuffer = FALSE; _isWildcard = FALSE; _isDisableWildcardAtFirst = FALSE; _hasMakePhraseFromText = FALSE; _isKeystrokeSort = FALSE; _candidateListPhraseModifier = 0; _candidateWndWidth = CAND_WIDTH; InitKeyStrokeTable(); } //+--------------------------------------------------------------------------- // // dtor // //---------------------------------------------------------------------------- CCompositionProcessorEngine::~CCompositionProcessorEngine() { if (_pTableDictionaryEngine) { delete _pTableDictionaryEngine; _pTableDictionaryEngine = nullptr; } if (_pLanguageBar_IMEMode) { _pLanguageBar_IMEMode->CleanUp(); _pLanguageBar_IMEMode->Release(); _pLanguageBar_IMEMode = nullptr; } if (_pLanguageBar_DoubleSingleByte) { _pLanguageBar_DoubleSingleByte->CleanUp(); _pLanguageBar_DoubleSingleByte->Release(); _pLanguageBar_DoubleSingleByte = nullptr; } if (_pLanguageBar_Punctuation) { _pLanguageBar_Punctuation->CleanUp(); _pLanguageBar_Punctuation->Release(); _pLanguageBar_Punctuation = nullptr; } if (_pCompartmentConversion) { delete _pCompartmentConversion; _pCompartmentConversion = nullptr; } if (_pCompartmentKeyboardOpenEventSink) { _pCompartmentKeyboardOpenEventSink->_Unadvise(); delete _pCompartmentKeyboardOpenEventSink; _pCompartmentKeyboardOpenEventSink = nullptr; } if (_pCompartmentConversionEventSink) { _pCompartmentConversionEventSink->_Unadvise(); delete _pCompartmentConversionEventSink; _pCompartmentConversionEventSink = nullptr; } if (_pCompartmentDoubleSingleByteEventSink) { _pCompartmentDoubleSingleByteEventSink->_Unadvise(); delete _pCompartmentDoubleSingleByteEventSink; _pCompartmentDoubleSingleByteEventSink = nullptr; } if (_pCompartmentPunctuationEventSink) { _pCompartmentPunctuationEventSink->_Unadvise(); delete _pCompartmentPunctuationEventSink; _pCompartmentPunctuationEventSink = nullptr; } if (_pDictionaryFile) { delete _pDictionaryFile; _pDictionaryFile = nullptr; } } //+--------------------------------------------------------------------------- // // SetupLanguageProfile // // Setup language profile for Composition Processor Engine. // param // [in] LANGID langid = Specify language ID // [in] GUID guidLanguageProfile - Specify GUID language profile which GUID is as same as Text Service Framework language profile. // [in] ITfThreadMgr - pointer ITfThreadMgr. // [in] tfClientId - TfClientId value. // [in] isSecureMode - secure mode // returns // If setup succeeded, returns true. Otherwise returns false. // N.B. For reverse conversion, ITfThreadMgr is NULL, TfClientId is 0 and isSecureMode is ignored. //+--------------------------------------------------------------------------- BOOL CCompositionProcessorEngine::SetupLanguageProfile(LANGID langid, REFGUID guidLanguageProfile, _In_ ITfThreadMgr *pThreadMgr, TfClientId tfClientId, BOOL isSecureMode, BOOL isComLessMode) { BOOL ret = TRUE; if ((tfClientId == 0) && (pThreadMgr == nullptr)) { ret = FALSE; goto Exit; } _isComLessMode = isComLessMode; _langid = langid; _guidProfile = guidLanguageProfile; _tfClientId = tfClientId; SetupPreserved(pThreadMgr, tfClientId); InitializeSampleIMECompartment(pThreadMgr, tfClientId); SetupPunctuationPair(); SetupLanguageBar(pThreadMgr, tfClientId, isSecureMode); SetupKeystroke(); SetupConfiguration(); SetupDictionaryFile(); Exit: return ret; } //+--------------------------------------------------------------------------- // // AddVirtualKey // Add virtual key code to Composition Processor Engine for used to parse keystroke data. // param // [in] uCode - Specify virtual key code. // returns // State of Text Processor Engine. //---------------------------------------------------------------------------- BOOL CCompositionProcessorEngine::AddVirtualKey(WCHAR wch) { if (!wch) { return FALSE; } // // append one keystroke in buffer. // DWORD_PTR srgKeystrokeBufLen = _keystrokeBuffer.GetLength(); PWCHAR pwch = new (std::nothrow) WCHAR[ srgKeystrokeBufLen + 1 ]; if (!pwch) { return FALSE; } memcpy(pwch, _keystrokeBuffer.Get(), srgKeystrokeBufLen * sizeof(WCHAR)); pwch[ srgKeystrokeBufLen ] = wch; if (_keystrokeBuffer.Get()) { delete [] _keystrokeBuffer.Get(); } _keystrokeBuffer.Set(pwch, srgKeystrokeBufLen + 1); return TRUE; } //+--------------------------------------------------------------------------- // // RemoveVirtualKey // Remove stored virtual key code. // param // [in] dwIndex - Specified index. // returns // none. //---------------------------------------------------------------------------- void CCompositionProcessorEngine::RemoveVirtualKey(DWORD_PTR dwIndex) { DWORD_PTR srgKeystrokeBufLen = _keystrokeBuffer.GetLength(); if (dwIndex + 1 < srgKeystrokeBufLen) { // shift following eles left memmove((BYTE*)_keystrokeBuffer.Get() + (dwIndex * sizeof(WCHAR)), (BYTE*)_keystrokeBuffer.Get() + ((dwIndex + 1) * sizeof(WCHAR)), (srgKeystrokeBufLen - dwIndex - 1) * sizeof(WCHAR)); } _keystrokeBuffer.Set(_keystrokeBuffer.Get(), srgKeystrokeBufLen - 1); } //+--------------------------------------------------------------------------- // // PurgeVirtualKey // Purge stored virtual key code. // param // none. // returns // none. //---------------------------------------------------------------------------- void CCompositionProcessorEngine::PurgeVirtualKey() { if (_keystrokeBuffer.Get()) { delete [] _keystrokeBuffer.Get(); _keystrokeBuffer.Set(NULL, 0); } } WCHAR CCompositionProcessorEngine::GetVirtualKey(DWORD_PTR dwIndex) { if (dwIndex < _keystrokeBuffer.GetLength()) { return *(_keystrokeBuffer.Get() + dwIndex); } return 0; } //+--------------------------------------------------------------------------- // // GetReadingStrings // Retrieves string from Composition Processor Engine. // param // [out] pReadingStrings - Specified returns pointer of CUnicodeString. // returns // none // //---------------------------------------------------------------------------- void CCompositionProcessorEngine::GetReadingStrings(_Inout_ CSampleImeArray *pReadingStrings, _Out_ BOOL *pIsWildcardIncluded) { CStringRange oneKeystroke; _hasWildcardIncludedInKeystrokeBuffer = FALSE; if (pReadingStrings->Count() == 0 && _keystrokeBuffer.GetLength()) { CStringRange* pNewString = nullptr; pNewString = pReadingStrings->Append(); if (pNewString) { *pNewString = _keystrokeBuffer; } for (DWORD index = 0; index < _keystrokeBuffer.GetLength(); index++) { oneKeystroke.Set(_keystrokeBuffer.Get() + index, 1); if (IsWildcard() && IsWildcardChar(*oneKeystroke.Get())) { _hasWildcardIncludedInKeystrokeBuffer = TRUE; } } } *pIsWildcardIncluded = _hasWildcardIncludedInKeystrokeBuffer; } //+--------------------------------------------------------------------------- // // GetCandidateList // //---------------------------------------------------------------------------- void CCompositionProcessorEngine::GetCandidateList(_Inout_ CSampleImeArray *pCandidateList, BOOL isIncrementalWordSearch, BOOL isWildcardSearch) { if (!IsDictionaryAvailable()) { return; } if (isIncrementalWordSearch) { CStringRange wildcardSearch; DWORD_PTR keystrokeBufLen = _keystrokeBuffer.GetLength() + 2; PWCHAR pwch = new (std::nothrow) WCHAR[ keystrokeBufLen ]; if (!pwch) { return; } // check keystroke buffer already has wildcard char which end user want wildcard serach DWORD wildcardIndex = 0; BOOL isFindWildcard = FALSE; if (IsWildcard()) { for (wildcardIndex = 0; wildcardIndex < _keystrokeBuffer.GetLength(); wildcardIndex++) { if (IsWildcardChar(*(_keystrokeBuffer.Get() + wildcardIndex))) { isFindWildcard = TRUE; break; } } } StringCchCopyN(pwch, keystrokeBufLen, _keystrokeBuffer.Get(), _keystrokeBuffer.GetLength()); if (!isFindWildcard) { // add wildcard char for incremental search StringCchCat(pwch, keystrokeBufLen, L"*"); } size_t len = 0; if (StringCchLength(pwch, STRSAFE_MAX_CCH, &len) == S_OK) { wildcardSearch.Set(pwch, len); } else { return; } _pTableDictionaryEngine->CollectWordForWildcard(&wildcardSearch, pCandidateList); if (0 >= pCandidateList->Count()) { return; } if (IsKeystrokeSort()) { _pTableDictionaryEngine->SortListItemByFindKeyCode(pCandidateList); } // Incremental search would show keystroke data from all candidate list items // but wont show identical keystroke data for user inputted. for (UINT index = 0; index < pCandidateList->Count(); index++) { CCandidateListItem *pLI = pCandidateList->GetAt(index); DWORD_PTR keystrokeBufferLen = 0; if (IsWildcard()) { keystrokeBufferLen = wildcardIndex; } else { keystrokeBufferLen = _keystrokeBuffer.GetLength(); } CStringRange newFindKeyCode; newFindKeyCode.Set(pLI->_FindKeyCode.Get() + keystrokeBufferLen, pLI->_FindKeyCode.GetLength() - keystrokeBufferLen); pLI->_FindKeyCode.Set(newFindKeyCode); } delete [] pwch; } else if (isWildcardSearch) { _pTableDictionaryEngine->CollectWordForWildcard(&_keystrokeBuffer, pCandidateList); } else { _pTableDictionaryEngine->CollectWord(&_keystrokeBuffer, pCandidateList); } for (UINT index = 0; index < pCandidateList->Count();) { CCandidateListItem *pLI = pCandidateList->GetAt(index); CStringRange startItemString; CStringRange endItemString; startItemString.Set(pLI->_ItemString.Get(), 1); endItemString.Set(pLI->_ItemString.Get() + pLI->_ItemString.GetLength() - 1, 1); index++; } } //+--------------------------------------------------------------------------- // // GetCandidateStringInConverted // //---------------------------------------------------------------------------- void CCompositionProcessorEngine::GetCandidateStringInConverted(CStringRange &searchString, _In_ CSampleImeArray *pCandidateList) { if (!IsDictionaryAvailable()) { return; } // Search phrase from SECTION_TEXT's converted string list CStringRange wildcardSearch; DWORD_PTR srgKeystrokeBufLen = searchString.GetLength() + 2; PWCHAR pwch = new (std::nothrow) WCHAR[ srgKeystrokeBufLen ]; if (!pwch) { return; } StringCchCopyN(pwch, srgKeystrokeBufLen, searchString.Get(), searchString.GetLength()); StringCchCat(pwch, srgKeystrokeBufLen, L"*"); // add wildcard char size_t len = 0; if (StringCchLength(pwch, STRSAFE_MAX_CCH, &len) != S_OK) { return; } wildcardSearch.Set(pwch, len); _pTableDictionaryEngine->CollectWordFromConvertedStringForWildcard(&wildcardSearch, pCandidateList); if (IsKeystrokeSort()) { _pTableDictionaryEngine->SortListItemByFindKeyCode(pCandidateList); } wildcardSearch.Clear(); delete [] pwch; } //+--------------------------------------------------------------------------- // // IsPunctuation // //---------------------------------------------------------------------------- BOOL CCompositionProcessorEngine::IsPunctuation(WCHAR wch) { for (int i = 0; i < ARRAYSIZE(Global::PunctuationTable); i++) { if (Global::PunctuationTable[i]._Code == wch) { return TRUE; } } for (UINT j = 0; j < _PunctuationPair.Count(); j++) { CPunctuationPair* pPuncPair = _PunctuationPair.GetAt(j); if (pPuncPair->_punctuation._Code == wch) { return TRUE; } } for (UINT k = 0; k < _PunctuationNestPair.Count(); k++) { CPunctuationNestPair* pPuncNestPair = _PunctuationNestPair.GetAt(k); if (pPuncNestPair->_punctuation_begin._Code == wch) { return TRUE; } if (pPuncNestPair->_punctuation_end._Code == wch) { return TRUE; } } return FALSE; } //+--------------------------------------------------------------------------- // // GetPunctuationPair // //---------------------------------------------------------------------------- WCHAR CCompositionProcessorEngine::GetPunctuation(WCHAR wch) { for (int i = 0; i < ARRAYSIZE(Global::PunctuationTable); i++) { if (Global::PunctuationTable[i]._Code == wch) { return Global::PunctuationTable[i]._Punctuation; } } for (UINT j = 0; j < _PunctuationPair.Count(); j++) { CPunctuationPair* pPuncPair = _PunctuationPair.GetAt(j); if (pPuncPair->_punctuation._Code == wch) { if (! pPuncPair->_isPairToggle) { pPuncPair->_isPairToggle = TRUE; return pPuncPair->_punctuation._Punctuation; } else { pPuncPair->_isPairToggle = FALSE; return pPuncPair->_pairPunctuation; } } } for (UINT k = 0; k < _PunctuationNestPair.Count(); k++) { CPunctuationNestPair* pPuncNestPair = _PunctuationNestPair.GetAt(k); if (pPuncNestPair->_punctuation_begin._Code == wch) { if (pPuncNestPair->_nestCount++ == 0) { return pPuncNestPair->_punctuation_begin._Punctuation; } else { return pPuncNestPair->_pairPunctuation_begin; } } if (pPuncNestPair->_punctuation_end._Code == wch) { if (--pPuncNestPair->_nestCount == 0) { return pPuncNestPair->_punctuation_end._Punctuation; } else { return pPuncNestPair->_pairPunctuation_end; } } } return 0; } //+--------------------------------------------------------------------------- // // IsDoubleSingleByte // //---------------------------------------------------------------------------- BOOL CCompositionProcessorEngine::IsDoubleSingleByte(WCHAR wch) { if (L' ' <= wch && wch <= L'~') { return TRUE; } return FALSE; } //+--------------------------------------------------------------------------- // // SetupKeystroke // //---------------------------------------------------------------------------- void CCompositionProcessorEngine::SetupKeystroke() { SetKeystrokeTable(&_KeystrokeComposition); return; } //+--------------------------------------------------------------------------- // // SetKeystrokeTable // //---------------------------------------------------------------------------- void CCompositionProcessorEngine::SetKeystrokeTable(_Inout_ CSampleImeArray<_KEYSTROKE> *pKeystroke) { for (int i = 0; i < 26; i++) { _KEYSTROKE* pKS = nullptr; pKS = pKeystroke->Append(); if (!pKS) { break; } *pKS = _keystrokeTable[i]; } } //+--------------------------------------------------------------------------- // // SetupPreserved // //---------------------------------------------------------------------------- void CCompositionProcessorEngine::SetupPreserved(_In_ ITfThreadMgr *pThreadMgr, TfClientId tfClientId) { TF_PRESERVEDKEY preservedKeyImeMode; preservedKeyImeMode.uVKey = VK_SHIFT; preservedKeyImeMode.uModifiers = _TF_MOD_ON_KEYUP_SHIFT_ONLY; SetPreservedKey(Global::SampleIMEGuidImeModePreserveKey, preservedKeyImeMode, Global::ImeModeDescription, &_PreservedKey_IMEMode); TF_PRESERVEDKEY preservedKeyDoubleSingleByte; preservedKeyDoubleSingleByte.uVKey = VK_SPACE; preservedKeyDoubleSingleByte.uModifiers = TF_MOD_SHIFT; SetPreservedKey(Global::SampleIMEGuidDoubleSingleBytePreserveKey, preservedKeyDoubleSingleByte, Global::DoubleSingleByteDescription, &_PreservedKey_DoubleSingleByte); TF_PRESERVEDKEY preservedKeyPunctuation; preservedKeyPunctuation.uVKey = VK_OEM_PERIOD; preservedKeyPunctuation.uModifiers = TF_MOD_CONTROL; SetPreservedKey(Global::SampleIMEGuidPunctuationPreserveKey, preservedKeyPunctuation, Global::PunctuationDescription, &_PreservedKey_Punctuation); InitPreservedKey(&_PreservedKey_IMEMode, pThreadMgr, tfClientId); InitPreservedKey(&_PreservedKey_DoubleSingleByte, pThreadMgr, tfClientId); InitPreservedKey(&_PreservedKey_Punctuation, pThreadMgr, tfClientId); return; } //+--------------------------------------------------------------------------- // // SetKeystrokeTable // //---------------------------------------------------------------------------- void CCompositionProcessorEngine::SetPreservedKey(const CLSID clsid, TF_PRESERVEDKEY & tfPreservedKey, _In_z_ LPCWSTR pwszDescription, _Out_ XPreservedKey *pXPreservedKey) { pXPreservedKey->Guid = clsid; TF_PRESERVEDKEY *ptfPsvKey1 = pXPreservedKey->TSFPreservedKeyTable.Append(); if (!ptfPsvKey1) { return; } *ptfPsvKey1 = tfPreservedKey; size_t srgKeystrokeBufLen = 0; if (StringCchLength(pwszDescription, STRSAFE_MAX_CCH, &srgKeystrokeBufLen) != S_OK) { return; } pXPreservedKey->Description = new (std::nothrow) WCHAR[srgKeystrokeBufLen + 1]; if (!pXPreservedKey->Description) { return; } StringCchCopy((LPWSTR)pXPreservedKey->Description, srgKeystrokeBufLen, pwszDescription); return; } //+--------------------------------------------------------------------------- // // InitPreservedKey // // Register a hot key. // //---------------------------------------------------------------------------- BOOL CCompositionProcessorEngine::InitPreservedKey(_In_ XPreservedKey *pXPreservedKey, _In_ ITfThreadMgr *pThreadMgr, TfClientId tfClientId) { ITfKeystrokeMgr *pKeystrokeMgr = nullptr; if (IsEqualGUID(pXPreservedKey->Guid, GUID_NULL)) { return FALSE; } if (pThreadMgr->QueryInterface(IID_ITfKeystrokeMgr, (void **)&pKeystrokeMgr) != S_OK) { return FALSE; } for (UINT i = 0; i < pXPreservedKey->TSFPreservedKeyTable.Count(); i++) { TF_PRESERVEDKEY preservedKey = *pXPreservedKey->TSFPreservedKeyTable.GetAt(i); preservedKey.uModifiers &= 0xffff; size_t lenOfDesc = 0; if (StringCchLength(pXPreservedKey->Description, STRSAFE_MAX_CCH, &lenOfDesc) != S_OK) { return FALSE; } pKeystrokeMgr->PreserveKey(tfClientId, pXPreservedKey->Guid, &preservedKey, pXPreservedKey->Description, static_cast(lenOfDesc)); } pKeystrokeMgr->Release(); return TRUE; } //+--------------------------------------------------------------------------- // // CheckShiftKeyOnly // //---------------------------------------------------------------------------- BOOL CCompositionProcessorEngine::CheckShiftKeyOnly(_In_ CSampleImeArray *pTSFPreservedKeyTable) { for (UINT i = 0; i < pTSFPreservedKeyTable->Count(); i++) { TF_PRESERVEDKEY *ptfPsvKey = pTSFPreservedKeyTable->GetAt(i); if (((ptfPsvKey->uModifiers & (_TF_MOD_ON_KEYUP_SHIFT_ONLY & 0xffff0000)) && !Global::IsShiftKeyDownOnly) || ((ptfPsvKey->uModifiers & (_TF_MOD_ON_KEYUP_CONTROL_ONLY & 0xffff0000)) && !Global::IsControlKeyDownOnly) || ((ptfPsvKey->uModifiers & (_TF_MOD_ON_KEYUP_ALT_ONLY & 0xffff0000)) && !Global::IsAltKeyDownOnly) ) { return FALSE; } } return TRUE; } //+--------------------------------------------------------------------------- // // OnPreservedKey // //---------------------------------------------------------------------------- void CCompositionProcessorEngine::OnPreservedKey(REFGUID rguid, _Out_ BOOL *pIsEaten, _In_ ITfThreadMgr *pThreadMgr, TfClientId tfClientId) { if (IsEqualGUID(rguid, _PreservedKey_IMEMode.Guid)) { if (!CheckShiftKeyOnly(&_PreservedKey_IMEMode.TSFPreservedKeyTable)) { *pIsEaten = FALSE; return; } BOOL isOpen = FALSE; CCompartment CompartmentKeyboardOpen(pThreadMgr, tfClientId, GUID_COMPARTMENT_KEYBOARD_OPENCLOSE); CompartmentKeyboardOpen._GetCompartmentBOOL(isOpen); CompartmentKeyboardOpen._SetCompartmentBOOL(isOpen ? FALSE : TRUE); *pIsEaten = TRUE; } else if (IsEqualGUID(rguid, _PreservedKey_DoubleSingleByte.Guid)) { if (!CheckShiftKeyOnly(&_PreservedKey_DoubleSingleByte.TSFPreservedKeyTable)) { *pIsEaten = FALSE; return; } BOOL isDouble = FALSE; CCompartment CompartmentDoubleSingleByte(pThreadMgr, tfClientId, Global::SampleIMEGuidCompartmentDoubleSingleByte); CompartmentDoubleSingleByte._GetCompartmentBOOL(isDouble); CompartmentDoubleSingleByte._SetCompartmentBOOL(isDouble ? FALSE : TRUE); *pIsEaten = TRUE; } else if (IsEqualGUID(rguid, _PreservedKey_Punctuation.Guid)) { if (!CheckShiftKeyOnly(&_PreservedKey_Punctuation.TSFPreservedKeyTable)) { *pIsEaten = FALSE; return; } BOOL isPunctuation = FALSE; CCompartment CompartmentPunctuation(pThreadMgr, tfClientId, Global::SampleIMEGuidCompartmentPunctuation); CompartmentPunctuation._GetCompartmentBOOL(isPunctuation); CompartmentPunctuation._SetCompartmentBOOL(isPunctuation ? FALSE : TRUE); *pIsEaten = TRUE; } else { *pIsEaten = FALSE; } *pIsEaten = TRUE; } //+--------------------------------------------------------------------------- // // SetupConfiguration // //---------------------------------------------------------------------------- void CCompositionProcessorEngine::SetupConfiguration() { _isWildcard = TRUE; _isDisableWildcardAtFirst = TRUE; _hasMakePhraseFromText = TRUE; _isKeystrokeSort = TRUE; _candidateWndWidth = CAND_WIDTH; SetInitialCandidateListRange(); SetDefaultCandidateTextFont(); return; } //+--------------------------------------------------------------------------- // // SetupLanguageBar // //---------------------------------------------------------------------------- void CCompositionProcessorEngine::SetupLanguageBar(_In_ ITfThreadMgr *pThreadMgr, TfClientId tfClientId, BOOL isSecureMode) { DWORD dwEnable = 1; CreateLanguageBarButton(dwEnable, GUID_LBI_INPUTMODE, Global::LangbarImeModeDescription, Global::ImeModeDescription, Global::ImeModeOnIcoIndex, Global::ImeModeOffIcoIndex, &_pLanguageBar_IMEMode, isSecureMode); CreateLanguageBarButton(dwEnable, Global::SampleIMEGuidLangBarDoubleSingleByte, Global::LangbarDoubleSingleByteDescription, Global::DoubleSingleByteDescription, Global::DoubleSingleByteOnIcoIndex, Global::DoubleSingleByteOffIcoIndex, &_pLanguageBar_DoubleSingleByte, isSecureMode); CreateLanguageBarButton(dwEnable, Global::SampleIMEGuidLangBarPunctuation, Global::LangbarPunctuationDescription, Global::PunctuationDescription, Global::PunctuationOnIcoIndex, Global::PunctuationOffIcoIndex, &_pLanguageBar_Punctuation, isSecureMode); InitLanguageBar(_pLanguageBar_IMEMode, pThreadMgr, tfClientId, GUID_COMPARTMENT_KEYBOARD_OPENCLOSE); InitLanguageBar(_pLanguageBar_DoubleSingleByte, pThreadMgr, tfClientId, Global::SampleIMEGuidCompartmentDoubleSingleByte); InitLanguageBar(_pLanguageBar_Punctuation, pThreadMgr, tfClientId, Global::SampleIMEGuidCompartmentPunctuation); _pCompartmentConversion = new (std::nothrow) CCompartment(pThreadMgr, tfClientId, GUID_COMPARTMENT_KEYBOARD_INPUTMODE_CONVERSION); _pCompartmentKeyboardOpenEventSink = new (std::nothrow) CCompartmentEventSink(CompartmentCallback, this); _pCompartmentConversionEventSink = new (std::nothrow) CCompartmentEventSink(CompartmentCallback, this); _pCompartmentDoubleSingleByteEventSink = new (std::nothrow) CCompartmentEventSink(CompartmentCallback, this); _pCompartmentPunctuationEventSink = new (std::nothrow) CCompartmentEventSink(CompartmentCallback, this); if (_pCompartmentKeyboardOpenEventSink) { _pCompartmentKeyboardOpenEventSink->_Advise(pThreadMgr, GUID_COMPARTMENT_KEYBOARD_OPENCLOSE); } if (_pCompartmentConversionEventSink) { _pCompartmentConversionEventSink->_Advise(pThreadMgr, GUID_COMPARTMENT_KEYBOARD_INPUTMODE_CONVERSION); } if (_pCompartmentDoubleSingleByteEventSink) { _pCompartmentDoubleSingleByteEventSink->_Advise(pThreadMgr, Global::SampleIMEGuidCompartmentDoubleSingleByte); } if (_pCompartmentPunctuationEventSink) { _pCompartmentPunctuationEventSink->_Advise(pThreadMgr, Global::SampleIMEGuidCompartmentPunctuation); } return; } //+--------------------------------------------------------------------------- // // CreateLanguageBarButton // //---------------------------------------------------------------------------- void CCompositionProcessorEngine::CreateLanguageBarButton(DWORD dwEnable, GUID guidLangBar, _In_z_ LPCWSTR pwszDescriptionValue, _In_z_ LPCWSTR pwszTooltipValue, DWORD dwOnIconIndex, DWORD dwOffIconIndex, _Outptr_result_maybenull_ CLangBarItemButton **ppLangBarItemButton, BOOL isSecureMode) { dwEnable; if (ppLangBarItemButton) { *ppLangBarItemButton = new (std::nothrow) CLangBarItemButton(guidLangBar, pwszDescriptionValue, pwszTooltipValue, dwOnIconIndex, dwOffIconIndex, isSecureMode); } return; } //+--------------------------------------------------------------------------- // // InitLanguageBar // //---------------------------------------------------------------------------- BOOL CCompositionProcessorEngine::InitLanguageBar(_In_ CLangBarItemButton *pLangBarItemButton, _In_ ITfThreadMgr *pThreadMgr, TfClientId tfClientId, REFGUID guidCompartment) { if (pLangBarItemButton) { if (pLangBarItemButton->_AddItem(pThreadMgr) == S_OK) { if (pLangBarItemButton->_RegisterCompartment(pThreadMgr, tfClientId, guidCompartment)) { return TRUE; } } } return FALSE; } //+--------------------------------------------------------------------------- // // SetupDictionaryFile // //---------------------------------------------------------------------------- BOOL CCompositionProcessorEngine::SetupDictionaryFile() { // Not yet registered // Register CFileMapping WCHAR wszFileName[MAX_PATH] = {'\0'}; DWORD cchA = GetModuleFileName(Global::dllInstanceHandle, wszFileName, ARRAYSIZE(wszFileName)); size_t iDicFileNameLen = cchA + wcslen(TEXTSERVICE_DIC); WCHAR *pwszFileName = new (std::nothrow) WCHAR[iDicFileNameLen + 1]; if (!pwszFileName) { goto ErrorExit; } *pwszFileName = L'\0'; // find the last '/' while (cchA--) { WCHAR wszChar = wszFileName[cchA]; if (wszChar == '\\' || wszChar == '/') { StringCchCopyN(pwszFileName, iDicFileNameLen + 1, wszFileName, cchA + 1); StringCchCatN(pwszFileName, iDicFileNameLen + 1, TEXTSERVICE_DIC, wcslen(TEXTSERVICE_DIC)); break; } } // create CFileMapping object if (_pDictionaryFile == nullptr) { _pDictionaryFile = new (std::nothrow) CFileMapping(); if (!_pDictionaryFile) { goto ErrorExit; } } if (!(_pDictionaryFile)->CreateFile(pwszFileName, GENERIC_READ, OPEN_EXISTING, FILE_SHARE_READ)) { goto ErrorExit; } _pTableDictionaryEngine = new (std::nothrow) CTableDictionaryEngine(GetLocale(), _pDictionaryFile); if (!_pTableDictionaryEngine) { goto ErrorExit; } delete []pwszFileName; return TRUE; ErrorExit: if (pwszFileName) { delete []pwszFileName; } return FALSE; } //+--------------------------------------------------------------------------- // // GetDictionaryFile // //---------------------------------------------------------------------------- CFile* CCompositionProcessorEngine::GetDictionaryFile() { return _pDictionaryFile; } //+--------------------------------------------------------------------------- // // SetupPunctuationPair // //---------------------------------------------------------------------------- void CCompositionProcessorEngine::SetupPunctuationPair() { // Punctuation pair const int pair_count = 2; CPunctuationPair punc_quotation_mark(L'"', 0x201C, 0x201D); CPunctuationPair punc_apostrophe(L'\'', 0x2018, 0x2019); CPunctuationPair puncPairs[pair_count] = { punc_quotation_mark, punc_apostrophe, }; for (int i = 0; i < pair_count; ++i) { CPunctuationPair *pPuncPair = _PunctuationPair.Append(); *pPuncPair = puncPairs[i]; } // Punctuation nest pair CPunctuationNestPair punc_angle_bracket(L'<', 0x300A, 0x3008, L'>', 0x300B, 0x3009); CPunctuationNestPair* pPuncNestPair = _PunctuationNestPair.Append(); *pPuncNestPair = punc_angle_bracket; } void CCompositionProcessorEngine::InitializeSampleIMECompartment(_In_ ITfThreadMgr *pThreadMgr, TfClientId tfClientId) { // set initial mode CCompartment CompartmentKeyboardOpen(pThreadMgr, tfClientId, GUID_COMPARTMENT_KEYBOARD_OPENCLOSE); CompartmentKeyboardOpen._SetCompartmentBOOL(TRUE); CCompartment CompartmentDoubleSingleByte(pThreadMgr, tfClientId, Global::SampleIMEGuidCompartmentDoubleSingleByte); CompartmentDoubleSingleByte._SetCompartmentBOOL(FALSE); CCompartment CompartmentPunctuation(pThreadMgr, tfClientId, Global::SampleIMEGuidCompartmentPunctuation); CompartmentPunctuation._SetCompartmentBOOL(TRUE); PrivateCompartmentsUpdated(pThreadMgr); } //+--------------------------------------------------------------------------- // // CompartmentCallback // //---------------------------------------------------------------------------- // static HRESULT CCompositionProcessorEngine::CompartmentCallback(_In_ void *pv, REFGUID guidCompartment) { CCompositionProcessorEngine* fakeThis = (CCompositionProcessorEngine*)pv; if (nullptr == fakeThis) { return E_INVALIDARG; } ITfThreadMgr* pThreadMgr = nullptr; HRESULT hr = CoCreateInstance(CLSID_TF_ThreadMgr, nullptr, CLSCTX_INPROC_SERVER, IID_ITfThreadMgr, (void**)&pThreadMgr); if (FAILED(hr)) { return E_FAIL; } if (IsEqualGUID(guidCompartment, Global::SampleIMEGuidCompartmentDoubleSingleByte) || IsEqualGUID(guidCompartment, Global::SampleIMEGuidCompartmentPunctuation)) { fakeThis->PrivateCompartmentsUpdated(pThreadMgr); } else if (IsEqualGUID(guidCompartment, GUID_COMPARTMENT_KEYBOARD_INPUTMODE_CONVERSION) || IsEqualGUID(guidCompartment, GUID_COMPARTMENT_KEYBOARD_INPUTMODE_SENTENCE)) { fakeThis->ConversionModeCompartmentUpdated(pThreadMgr); } else if (IsEqualGUID(guidCompartment, GUID_COMPARTMENT_KEYBOARD_OPENCLOSE)) { fakeThis->KeyboardOpenCompartmentUpdated(pThreadMgr); } pThreadMgr->Release(); pThreadMgr = nullptr; return S_OK; } //+--------------------------------------------------------------------------- // // UpdatePrivateCompartments // //---------------------------------------------------------------------------- void CCompositionProcessorEngine::ConversionModeCompartmentUpdated(_In_ ITfThreadMgr *pThreadMgr) { if (!_pCompartmentConversion) { return; } DWORD conversionMode = 0; if (FAILED(_pCompartmentConversion->_GetCompartmentDWORD(conversionMode))) { return; } BOOL isDouble = FALSE; CCompartment CompartmentDoubleSingleByte(pThreadMgr, _tfClientId, Global::SampleIMEGuidCompartmentDoubleSingleByte); if (SUCCEEDED(CompartmentDoubleSingleByte._GetCompartmentBOOL(isDouble))) { if (!isDouble && (conversionMode & TF_CONVERSIONMODE_FULLSHAPE)) { CompartmentDoubleSingleByte._SetCompartmentBOOL(TRUE); } else if (isDouble && !(conversionMode & TF_CONVERSIONMODE_FULLSHAPE)) { CompartmentDoubleSingleByte._SetCompartmentBOOL(FALSE); } } BOOL isPunctuation = FALSE; CCompartment CompartmentPunctuation(pThreadMgr, _tfClientId, Global::SampleIMEGuidCompartmentPunctuation); if (SUCCEEDED(CompartmentPunctuation._GetCompartmentBOOL(isPunctuation))) { if (!isPunctuation && (conversionMode & TF_CONVERSIONMODE_SYMBOL)) { CompartmentPunctuation._SetCompartmentBOOL(TRUE); } else if (isPunctuation && !(conversionMode & TF_CONVERSIONMODE_SYMBOL)) { CompartmentPunctuation._SetCompartmentBOOL(FALSE); } } BOOL fOpen = FALSE; CCompartment CompartmentKeyboardOpen(pThreadMgr, _tfClientId, GUID_COMPARTMENT_KEYBOARD_OPENCLOSE); if (SUCCEEDED(CompartmentKeyboardOpen._GetCompartmentBOOL(fOpen))) { if (fOpen && !(conversionMode & TF_CONVERSIONMODE_NATIVE)) { CompartmentKeyboardOpen._SetCompartmentBOOL(FALSE); } else if (!fOpen && (conversionMode & TF_CONVERSIONMODE_NATIVE)) { CompartmentKeyboardOpen._SetCompartmentBOOL(TRUE); } } } //+--------------------------------------------------------------------------- // // PrivateCompartmentsUpdated() // //---------------------------------------------------------------------------- void CCompositionProcessorEngine::PrivateCompartmentsUpdated(_In_ ITfThreadMgr *pThreadMgr) { if (!_pCompartmentConversion) { return; } DWORD conversionMode = 0; DWORD conversionModePrev = 0; if (FAILED(_pCompartmentConversion->_GetCompartmentDWORD(conversionMode))) { return; } conversionModePrev = conversionMode; BOOL isDouble = FALSE; CCompartment CompartmentDoubleSingleByte(pThreadMgr, _tfClientId, Global::SampleIMEGuidCompartmentDoubleSingleByte); if (SUCCEEDED(CompartmentDoubleSingleByte._GetCompartmentBOOL(isDouble))) { if (!isDouble && (conversionMode & TF_CONVERSIONMODE_FULLSHAPE)) { conversionMode &= ~TF_CONVERSIONMODE_FULLSHAPE; } else if (isDouble && !(conversionMode & TF_CONVERSIONMODE_FULLSHAPE)) { conversionMode |= TF_CONVERSIONMODE_FULLSHAPE; } } BOOL isPunctuation = FALSE; CCompartment CompartmentPunctuation(pThreadMgr, _tfClientId, Global::SampleIMEGuidCompartmentPunctuation); if (SUCCEEDED(CompartmentPunctuation._GetCompartmentBOOL(isPunctuation))) { if (!isPunctuation && (conversionMode & TF_CONVERSIONMODE_SYMBOL)) { conversionMode &= ~TF_CONVERSIONMODE_SYMBOL; } else if (isPunctuation && !(conversionMode & TF_CONVERSIONMODE_SYMBOL)) { conversionMode |= TF_CONVERSIONMODE_SYMBOL; } } if (conversionMode != conversionModePrev) { _pCompartmentConversion->_SetCompartmentDWORD(conversionMode); } } //+--------------------------------------------------------------------------- // // KeyboardOpenCompartmentUpdated // //---------------------------------------------------------------------------- void CCompositionProcessorEngine::KeyboardOpenCompartmentUpdated(_In_ ITfThreadMgr *pThreadMgr) { if (!_pCompartmentConversion) { return; } DWORD conversionMode = 0; DWORD conversionModePrev = 0; if (FAILED(_pCompartmentConversion->_GetCompartmentDWORD(conversionMode))) { return; } conversionModePrev = conversionMode; BOOL isOpen = FALSE; CCompartment CompartmentKeyboardOpen(pThreadMgr, _tfClientId, GUID_COMPARTMENT_KEYBOARD_OPENCLOSE); if (SUCCEEDED(CompartmentKeyboardOpen._GetCompartmentBOOL(isOpen))) { if (isOpen && !(conversionMode & TF_CONVERSIONMODE_NATIVE)) { conversionMode |= TF_CONVERSIONMODE_NATIVE; } else if (!isOpen && (conversionMode & TF_CONVERSIONMODE_NATIVE)) { conversionMode &= ~TF_CONVERSIONMODE_NATIVE; } } if (conversionMode != conversionModePrev) { _pCompartmentConversion->_SetCompartmentDWORD(conversionMode); } } ////////////////////////////////////////////////////////////////////// // // XPreservedKey implementation. // ////////////////////////////////////////////////////////////////////// //+--------------------------------------------------------------------------- // // UninitPreservedKey // //---------------------------------------------------------------------------- BOOL CCompositionProcessorEngine::XPreservedKey::UninitPreservedKey(_In_ ITfThreadMgr *pThreadMgr) { ITfKeystrokeMgr* pKeystrokeMgr = nullptr; if (IsEqualGUID(Guid, GUID_NULL)) { return FALSE; } if (FAILED(pThreadMgr->QueryInterface(IID_ITfKeystrokeMgr, (void **)&pKeystrokeMgr))) { return FALSE; } for (UINT i = 0; i < TSFPreservedKeyTable.Count(); i++) { TF_PRESERVEDKEY pPreservedKey = *TSFPreservedKeyTable.GetAt(i); pPreservedKey.uModifiers &= 0xffff; pKeystrokeMgr->UnpreserveKey(Guid, &pPreservedKey); } pKeystrokeMgr->Release(); return TRUE; } CCompositionProcessorEngine::XPreservedKey::XPreservedKey() { Guid = GUID_NULL; Description = nullptr; } CCompositionProcessorEngine::XPreservedKey::~XPreservedKey() { ITfThreadMgr* pThreadMgr = nullptr; HRESULT hr = CoCreateInstance(CLSID_TF_ThreadMgr, NULL, CLSCTX_INPROC_SERVER, IID_ITfThreadMgr, (void**)&pThreadMgr); if (SUCCEEDED(hr)) { UninitPreservedKey(pThreadMgr); pThreadMgr->Release(); pThreadMgr = nullptr; } if (Description) { delete [] Description; } } //+--------------------------------------------------------------------------- // // CSampleIME::CreateInstance // //---------------------------------------------------------------------------- HRESULT CSampleIME::CreateInstance(REFCLSID rclsid, REFIID riid, _Outptr_result_maybenull_ LPVOID* ppv, _Out_opt_ HINSTANCE* phInst, BOOL isComLessMode) { HRESULT hr = S_OK; if (phInst == nullptr) { return E_INVALIDARG; } *phInst = nullptr; if (!isComLessMode) { hr = ::CoCreateInstance(rclsid, NULL, CLSCTX_INPROC_SERVER, riid, ppv); } else { hr = CSampleIME::ComLessCreateInstance(rclsid, riid, ppv, phInst); } return hr; } //+--------------------------------------------------------------------------- // // CSampleIME::ComLessCreateInstance // //---------------------------------------------------------------------------- HRESULT CSampleIME::ComLessCreateInstance(REFGUID rclsid, REFIID riid, _Outptr_result_maybenull_ void **ppv, _Out_opt_ HINSTANCE *phInst) { HRESULT hr = S_OK; HINSTANCE sampleIMEDllHandle = nullptr; WCHAR wchPath[MAX_PATH] = {'\0'}; WCHAR szExpandedPath[MAX_PATH] = {'\0'}; DWORD dwCnt = 0; *ppv = nullptr; hr = phInst ? S_OK : E_FAIL; if (SUCCEEDED(hr)) { *phInst = nullptr; hr = CSampleIME::GetComModuleName(rclsid, wchPath, ARRAYSIZE(wchPath)); if (SUCCEEDED(hr)) { dwCnt = ExpandEnvironmentStringsW(wchPath, szExpandedPath, ARRAYSIZE(szExpandedPath)); hr = (0 < dwCnt && dwCnt <= ARRAYSIZE(szExpandedPath)) ? S_OK : E_FAIL; if (SUCCEEDED(hr)) { sampleIMEDllHandle = LoadLibraryEx(szExpandedPath, NULL, 0); hr = sampleIMEDllHandle ? S_OK : E_FAIL; if (SUCCEEDED(hr)) { *phInst = sampleIMEDllHandle; FARPROC pfn = GetProcAddress(sampleIMEDllHandle, "DllGetClassObject"); hr = pfn ? S_OK : E_FAIL; if (SUCCEEDED(hr)) { IClassFactory *pClassFactory = nullptr; hr = ((HRESULT (STDAPICALLTYPE *)(REFCLSID rclsid, REFIID riid, LPVOID *ppv))(pfn))(rclsid, IID_IClassFactory, (void **)&pClassFactory); if (SUCCEEDED(hr) && pClassFactory) { hr = pClassFactory->CreateInstance(NULL, riid, ppv); pClassFactory->Release(); } } } } } } if (!SUCCEEDED(hr) && phInst && *phInst) { FreeLibrary(*phInst); *phInst = 0; } return hr; } //+--------------------------------------------------------------------------- // // CSampleIME::GetComModuleName // //---------------------------------------------------------------------------- HRESULT CSampleIME::GetComModuleName(REFGUID rclsid, _Out_writes_(cchPath)WCHAR* wchPath, DWORD cchPath) { HRESULT hr = S_OK; CRegKey key; WCHAR wchClsid[CLSID_STRLEN + 1]; hr = CLSIDToString(rclsid, wchClsid) ? S_OK : E_FAIL; if (SUCCEEDED(hr)) { WCHAR wchKey[MAX_PATH]; hr = StringCchPrintfW(wchKey, ARRAYSIZE(wchKey), L"CLSID\\%s\\InProcServer32", wchClsid); if (SUCCEEDED(hr)) { hr = (key.Open(HKEY_CLASSES_ROOT, wchKey, KEY_READ) == ERROR_SUCCESS) ? S_OK : E_FAIL; if (SUCCEEDED(hr)) { WCHAR wszModel[MAX_PATH]; ULONG cch = ARRAYSIZE(wszModel); hr = (key.QueryStringValue(L"ThreadingModel", wszModel, &cch) == ERROR_SUCCESS) ? S_OK : E_FAIL; if (SUCCEEDED(hr)) { if (CompareStringOrdinal(wszModel, -1, L"Apartment", -1, TRUE) == CSTR_EQUAL) { hr = (key.QueryStringValue(NULL, wchPath, &cchPath) == ERROR_SUCCESS) ? S_OK : E_FAIL; } else { hr = E_FAIL; } } } } } return hr; } void CCompositionProcessorEngine::InitKeyStrokeTable() { for (int i = 0; i < 26; i++) { _keystrokeTable[i].VirtualKey = 'A' + i; _keystrokeTable[i].Modifiers = 0; _keystrokeTable[i].Function = FUNCTION_INPUT; } } void CCompositionProcessorEngine::ShowAllLanguageBarIcons() { SetLanguageBarStatus(TF_LBI_STATUS_HIDDEN, FALSE); } void CCompositionProcessorEngine::HideAllLanguageBarIcons() { SetLanguageBarStatus(TF_LBI_STATUS_HIDDEN, TRUE); } void CCompositionProcessorEngine::SetInitialCandidateListRange() { for (DWORD i = 1; i <= 10; i++) { DWORD* pNewIndexRange = nullptr; pNewIndexRange = _candidateListIndexRange.Append(); if (pNewIndexRange != nullptr) { if (i != 10) { *pNewIndexRange = i; } else { *pNewIndexRange = 0; } } } } void CCompositionProcessorEngine::SetDefaultCandidateTextFont() { // Candidate Text Font if (Global::defaultlFontHandle == nullptr) { WCHAR fontName[50] = {'\0'}; LoadString(Global::dllInstanceHandle, IDS_DEFAULT_FONT, fontName, 50); Global::defaultlFontHandle = CreateFont(-MulDiv(10, GetDeviceCaps(GetDC(NULL), LOGPIXELSY), 72), 0, 0, 0, FW_MEDIUM, 0, 0, 0, 0, 0, 0, 0, 0, fontName); if (!Global::defaultlFontHandle) { LOGFONT lf; SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(LOGFONT), &lf, 0); // Fall back to the default GUI font on failure. Global::defaultlFontHandle = CreateFont(-MulDiv(10, GetDeviceCaps(GetDC(NULL), LOGPIXELSY), 72), 0, 0, 0, FW_MEDIUM, 0, 0, 0, 0, 0, 0, 0, 0, lf.lfFaceName); } } } ////////////////////////////////////////////////////////////////////// // // CCompositionProcessorEngine // ////////////////////////////////////////////////////////////////////// //+--------------------------------------------------------------------------- // // CCompositionProcessorEngine::IsVirtualKeyNeed // // Test virtual key code need to the Composition Processor Engine. // param // [in] uCode - Specify virtual key code. // [in/out] pwch - char code // [in] fComposing - Specified composing. // [in] fCandidateMode - Specified candidate mode. // [out] pKeyState - Returns function regarding virtual key. // returns // If engine need this virtual key code, returns true. Otherwise returns false. //---------------------------------------------------------------------------- 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; pKeyState->Function = FUNCTION_NONE; } if (candidateMode == CANDIDATE_ORIGINAL || candidateMode == CANDIDATE_PHRASE || candidateMode == CANDIDATE_WITH_NEXT_COMPOSITION) { fComposing = FALSE; } if (fComposing || candidateMode == CANDIDATE_INCREMENTAL || candidateMode == CANDIDATE_NONE) { if (IsVirtualKeyKeystrokeComposition(uCode, pKeyState, FUNCTION_NONE)) { return TRUE; } else if ((IsWildcard() && IsWildcardChar(*pwch) && !IsDisableWildcardAtFirst()) || (IsWildcard() && IsWildcardChar(*pwch) && IsDisableWildcardAtFirst() && _keystrokeBuffer.GetLength())) { if (pKeyState) { pKeyState->Category = CATEGORY_COMPOSING; pKeyState->Function = FUNCTION_INPUT; } return TRUE; } else if (_hasWildcardIncludedInKeystrokeBuffer && uCode == VK_SPACE) { if (pKeyState) { pKeyState->Category = CATEGORY_COMPOSING; pKeyState->Function = FUNCTION_CONVERT_WILDCARD; } return TRUE; } } if (candidateMode == CANDIDATE_ORIGINAL || candidateMode == CANDIDATE_PHRASE || candidateMode == CANDIDATE_WITH_NEXT_COMPOSITION) { BOOL isRetCode = TRUE; if (IsVirtualKeyKeystrokeCandidate(uCode, pKeyState, candidateMode, &isRetCode, &_KeystrokeCandidate)) { return isRetCode; } if (hasCandidateWithWildcard) { if (IsVirtualKeyKeystrokeCandidate(uCode, pKeyState, candidateMode, &isRetCode, &_KeystrokeCandidateWildcard)) { return isRetCode; } } // Candidate list could not handle key. We can try to restart the composition. if (IsVirtualKeyKeystrokeComposition(uCode, pKeyState, FUNCTION_INPUT)) { if (candidateMode != CANDIDATE_ORIGINAL) { return TRUE; } else { if (pKeyState) { pKeyState->Category = CATEGORY_CANDIDATE; pKeyState->Function = FUNCTION_FINALIZE_CANDIDATELIST_AND_INPUT; } return TRUE; } } } // CANDIDATE_INCREMENTAL should process Keystroke.Candidate virtual keys. else if (candidateMode == CANDIDATE_INCREMENTAL) { BOOL isRetCode = TRUE; if (IsVirtualKeyKeystrokeCandidate(uCode, pKeyState, candidateMode, &isRetCode, &_KeystrokeCandidate)) { return isRetCode; } } if (!fComposing && candidateMode != CANDIDATE_ORIGINAL && candidateMode != CANDIDATE_PHRASE && candidateMode != CANDIDATE_WITH_NEXT_COMPOSITION) { if (IsVirtualKeyKeystrokeComposition(uCode, pKeyState, FUNCTION_INPUT)) { return TRUE; } } // System pre-defined keystroke if (fComposing) { if ((candidateMode != CANDIDATE_INCREMENTAL)) { switch (uCode) { case VK_LEFT: if (pKeyState) { pKeyState->Category = CATEGORY_COMPOSING; pKeyState->Function = FUNCTION_MOVE_LEFT; } return TRUE; case VK_RIGHT: if (pKeyState) { pKeyState->Category = CATEGORY_COMPOSING; pKeyState->Function = FUNCTION_MOVE_RIGHT; } return TRUE; case VK_RETURN: if (pKeyState) { pKeyState->Category = CATEGORY_COMPOSING; pKeyState->Function = FUNCTION_FINALIZE_CANDIDATELIST; } return TRUE; case VK_ESCAPE: if (pKeyState) { pKeyState->Category = CATEGORY_COMPOSING; pKeyState->Function = FUNCTION_CANCEL; } return TRUE; case VK_BACK: if (pKeyState) { pKeyState->Category = CATEGORY_COMPOSING; pKeyState->Function = FUNCTION_BACKSPACE; } return TRUE; case VK_UP: if (pKeyState) { pKeyState->Category = CATEGORY_COMPOSING; pKeyState->Function = FUNCTION_MOVE_UP; } return TRUE; case VK_DOWN: if (pKeyState) { pKeyState->Category = CATEGORY_COMPOSING; pKeyState->Function = FUNCTION_MOVE_DOWN; } return TRUE; case VK_PRIOR: if (pKeyState) { pKeyState->Category = CATEGORY_COMPOSING; pKeyState->Function = FUNCTION_MOVE_PAGE_UP; } return TRUE; case VK_NEXT: if (pKeyState) { pKeyState->Category = CATEGORY_COMPOSING; pKeyState->Function = FUNCTION_MOVE_PAGE_DOWN; } return TRUE; case VK_HOME: if (pKeyState) { pKeyState->Category = CATEGORY_COMPOSING; pKeyState->Function = FUNCTION_MOVE_PAGE_TOP; } return TRUE; case VK_END: if (pKeyState) { pKeyState->Category = CATEGORY_COMPOSING; pKeyState->Function = FUNCTION_MOVE_PAGE_BOTTOM; } return TRUE; case VK_SPACE: if (pKeyState) { pKeyState->Category = CATEGORY_COMPOSING; pKeyState->Function = FUNCTION_CONVERT; } return TRUE; } } else if ((candidateMode == CANDIDATE_INCREMENTAL)) { switch (uCode) { // VK_LEFT, VK_RIGHT - set *pIsEaten = FALSE for application could move caret left or right. // and for CUAS, invoke _HandleCompositionCancel() edit session due to ignore CUAS default key handler for send out terminate composition case VK_LEFT: case VK_RIGHT: { if (pKeyState) { pKeyState->Category = CATEGORY_INVOKE_COMPOSITION_EDIT_SESSION; pKeyState->Function = FUNCTION_CANCEL; } } return FALSE; case VK_RETURN: if (pKeyState) { pKeyState->Category = CATEGORY_CANDIDATE; pKeyState->Function = FUNCTION_FINALIZE_CANDIDATELIST; } return TRUE; case VK_ESCAPE: if (pKeyState) { pKeyState->Category = CATEGORY_CANDIDATE; pKeyState->Function = FUNCTION_CANCEL; } return TRUE; // VK_BACK - remove one char from reading string. case VK_BACK: if (pKeyState) { pKeyState->Category = CATEGORY_COMPOSING; pKeyState->Function = FUNCTION_BACKSPACE; } return TRUE; case VK_UP: if (pKeyState) { pKeyState->Category = CATEGORY_CANDIDATE; pKeyState->Function = FUNCTION_MOVE_UP; } return TRUE; case VK_DOWN: if (pKeyState) { pKeyState->Category = CATEGORY_CANDIDATE; pKeyState->Function = FUNCTION_MOVE_DOWN; } return TRUE; case VK_PRIOR: if (pKeyState) { pKeyState->Category = CATEGORY_CANDIDATE; pKeyState->Function = FUNCTION_MOVE_PAGE_UP; } return TRUE; case VK_NEXT: if (pKeyState) { pKeyState->Category = CATEGORY_CANDIDATE; pKeyState->Function = FUNCTION_MOVE_PAGE_DOWN; } return TRUE; case VK_HOME: if (pKeyState) { pKeyState->Category = CATEGORY_CANDIDATE; pKeyState->Function = FUNCTION_MOVE_PAGE_TOP; } return TRUE; case VK_END: if (pKeyState) { pKeyState->Category = CATEGORY_CANDIDATE; pKeyState->Function = FUNCTION_MOVE_PAGE_BOTTOM; } return TRUE; case VK_SPACE: { if (candidateMode == CANDIDATE_INCREMENTAL) { if (pKeyState) { pKeyState->Category = CATEGORY_CANDIDATE; pKeyState->Function = FUNCTION_CONVERT; } return TRUE; } else { if (pKeyState) { pKeyState->Category = CATEGORY_COMPOSING; pKeyState->Function = FUNCTION_CONVERT; } return TRUE; } } } } } if ((candidateMode == CANDIDATE_ORIGINAL) || (candidateMode == CANDIDATE_WITH_NEXT_COMPOSITION)) { switch (uCode) { case VK_UP: if (pKeyState) { pKeyState->Category = CATEGORY_CANDIDATE; pKeyState->Function = FUNCTION_MOVE_UP; } return TRUE; case VK_DOWN: if (pKeyState) { pKeyState->Category = CATEGORY_CANDIDATE; pKeyState->Function = FUNCTION_MOVE_DOWN; } return TRUE; case VK_PRIOR: if (pKeyState) { pKeyState->Category = CATEGORY_CANDIDATE; pKeyState->Function = FUNCTION_MOVE_PAGE_UP; } return TRUE; case VK_NEXT: if (pKeyState) { pKeyState->Category = CATEGORY_CANDIDATE; pKeyState->Function = FUNCTION_MOVE_PAGE_DOWN; } return TRUE; case VK_HOME: if (pKeyState) { pKeyState->Category = CATEGORY_CANDIDATE; pKeyState->Function = FUNCTION_MOVE_PAGE_TOP; } return TRUE; case VK_END: if (pKeyState) { pKeyState->Category = CATEGORY_CANDIDATE; pKeyState->Function = FUNCTION_MOVE_PAGE_BOTTOM; } return TRUE; case VK_RETURN: if (pKeyState) { pKeyState->Category = CATEGORY_CANDIDATE; pKeyState->Function = FUNCTION_FINALIZE_CANDIDATELIST; } return TRUE; case VK_SPACE: if (pKeyState) { pKeyState->Category = CATEGORY_CANDIDATE; pKeyState->Function = FUNCTION_CONVERT; } return TRUE; case VK_BACK: if (pKeyState) { pKeyState->Category = CATEGORY_CANDIDATE; pKeyState->Function = FUNCTION_CANCEL; } return TRUE; case VK_ESCAPE: { if (candidateMode == CANDIDATE_WITH_NEXT_COMPOSITION) { if (pKeyState) { pKeyState->Category = CATEGORY_INVOKE_COMPOSITION_EDIT_SESSION; pKeyState->Function = FUNCTION_FINALIZE_TEXTSTORE; } return TRUE; } else { if (pKeyState) { pKeyState->Category = CATEGORY_CANDIDATE; pKeyState->Function = FUNCTION_CANCEL; } return TRUE; } } } if (candidateMode == CANDIDATE_WITH_NEXT_COMPOSITION) { if (IsVirtualKeyKeystrokeComposition(uCode, NULL, FUNCTION_NONE)) { if (pKeyState) { pKeyState->Category = CATEGORY_COMPOSING; pKeyState->Function = FUNCTION_FINALIZE_TEXTSTORE_AND_INPUT; } return TRUE; } } } if (candidateMode == CANDIDATE_PHRASE) { switch (uCode) { case VK_UP: if (pKeyState) { pKeyState->Category = CATEGORY_PHRASE; pKeyState->Function = FUNCTION_MOVE_UP; } return TRUE; case VK_DOWN: if (pKeyState) { pKeyState->Category = CATEGORY_PHRASE; pKeyState->Function = FUNCTION_MOVE_DOWN; } return TRUE; case VK_PRIOR: if (pKeyState) { pKeyState->Category = CATEGORY_PHRASE; pKeyState->Function = FUNCTION_MOVE_PAGE_UP; } return TRUE; case VK_NEXT: if (pKeyState) { pKeyState->Category = CATEGORY_PHRASE; pKeyState->Function = FUNCTION_MOVE_PAGE_DOWN; } return TRUE; case VK_HOME: if (pKeyState) { pKeyState->Category = CATEGORY_PHRASE; pKeyState->Function = FUNCTION_MOVE_PAGE_TOP; } return TRUE; case VK_END: if (pKeyState) { pKeyState->Category = CATEGORY_PHRASE; pKeyState->Function = FUNCTION_MOVE_PAGE_BOTTOM; } return TRUE; case VK_RETURN: if (pKeyState) { pKeyState->Category = CATEGORY_PHRASE; pKeyState->Function = FUNCTION_FINALIZE_CANDIDATELIST; } return TRUE; case VK_SPACE: if (pKeyState) { pKeyState->Category = CATEGORY_PHRASE; pKeyState->Function = FUNCTION_CONVERT; } return TRUE; case VK_ESCAPE: if (pKeyState) { pKeyState->Category = CATEGORY_PHRASE; pKeyState->Function = FUNCTION_CANCEL; } return TRUE; case VK_BACK: if (pKeyState) { pKeyState->Category = CATEGORY_CANDIDATE; pKeyState->Function = FUNCTION_CANCEL; } return TRUE; } } if (IsKeystrokeRange(uCode, pKeyState, candidateMode)) { return TRUE; } else if (pKeyState && pKeyState->Category != CATEGORY_NONE) { 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; } return FALSE; } //+--------------------------------------------------------------------------- // // CCompositionProcessorEngine::IsVirtualKeyKeystrokeComposition // //---------------------------------------------------------------------------- BOOL CCompositionProcessorEngine::IsVirtualKeyKeystrokeComposition(UINT uCode, _Out_opt_ _KEYSTROKE_STATE *pKeyState, KEYSTROKE_FUNCTION function) { if (pKeyState == nullptr) { return FALSE; } pKeyState->Category = CATEGORY_NONE; pKeyState->Function = FUNCTION_NONE; for (UINT i = 0; i < _KeystrokeComposition.Count(); i++) { _KEYSTROKE *pKeystroke = nullptr; pKeystroke = _KeystrokeComposition.GetAt(i); if ((pKeystroke->VirtualKey == uCode) && Global::CheckModifiers(Global::ModifiersValue, pKeystroke->Modifiers)) { if (function == FUNCTION_NONE) { pKeyState->Category = CATEGORY_COMPOSING; pKeyState->Function = pKeystroke->Function; return TRUE; } else if (function == pKeystroke->Function) { pKeyState->Category = CATEGORY_COMPOSING; pKeyState->Function = pKeystroke->Function; return TRUE; } } } return FALSE; } //+--------------------------------------------------------------------------- // // CCompositionProcessorEngine::IsVirtualKeyKeystrokeCandidate // //---------------------------------------------------------------------------- BOOL CCompositionProcessorEngine::IsVirtualKeyKeystrokeCandidate(UINT uCode, _In_ _KEYSTROKE_STATE *pKeyState, CANDIDATE_MODE candidateMode, _Out_ BOOL *pfRetCode, _In_ CSampleImeArray<_KEYSTROKE> *pKeystrokeMetric) { if (pfRetCode == nullptr) { return FALSE; } *pfRetCode = FALSE; for (UINT i = 0; i < pKeystrokeMetric->Count(); i++) { _KEYSTROKE *pKeystroke = nullptr; pKeystroke = pKeystrokeMetric->GetAt(i); if ((pKeystroke->VirtualKey == uCode) && Global::CheckModifiers(Global::ModifiersValue, pKeystroke->Modifiers)) { *pfRetCode = TRUE; if (pKeyState) { pKeyState->Category = (candidateMode == CANDIDATE_ORIGINAL ? CATEGORY_CANDIDATE : candidateMode == CANDIDATE_PHRASE ? CATEGORY_PHRASE : CATEGORY_CANDIDATE); pKeyState->Function = pKeystroke->Function; } return TRUE; } } return FALSE; } //+--------------------------------------------------------------------------- // // CCompositionProcessorEngine::IsKeyKeystrokeRange // //---------------------------------------------------------------------------- BOOL CCompositionProcessorEngine::IsKeystrokeRange(UINT uCode, _Out_ _KEYSTROKE_STATE *pKeyState, CANDIDATE_MODE candidateMode) { if (pKeyState == nullptr) { return FALSE; } pKeyState->Category = CATEGORY_NONE; pKeyState->Function = FUNCTION_NONE; if (_candidateListIndexRange.IsRange(uCode)) { if (candidateMode == CANDIDATE_PHRASE) { // Candidate phrase could specify modifier if ((GetCandidateListPhraseModifier() == 0 && Global::ModifiersValue == 0) || (GetCandidateListPhraseModifier() != 0 && Global::CheckModifiers(Global::ModifiersValue, GetCandidateListPhraseModifier()))) { pKeyState->Category = CATEGORY_PHRASE; pKeyState->Function = FUNCTION_SELECT_BY_NUMBER; return TRUE; } else { pKeyState->Category = CATEGORY_INVOKE_COMPOSITION_EDIT_SESSION; pKeyState->Function = FUNCTION_FINALIZE_TEXTSTORE_AND_INPUT; return FALSE; } } else if (candidateMode == CANDIDATE_WITH_NEXT_COMPOSITION) { // Candidate phrase could specify modifier if ((GetCandidateListPhraseModifier() == 0 && Global::ModifiersValue == 0) || (GetCandidateListPhraseModifier() != 0 && Global::CheckModifiers(Global::ModifiersValue, GetCandidateListPhraseModifier()))) { pKeyState->Category = CATEGORY_CANDIDATE; pKeyState->Function = FUNCTION_SELECT_BY_NUMBER; return TRUE; } // else next composition } else if (candidateMode != CANDIDATE_NONE) { pKeyState->Category = CATEGORY_CANDIDATE; pKeyState->Function = FUNCTION_SELECT_BY_NUMBER; return TRUE; } } return FALSE; }