├── doc ├── RegKey.md ├── EnumTfCandidates.md ├── TipCandidateList.md ├── TipCandidateString.md ├── EnumDisplayAttributeInfo.md ├── SearchCandidateProvider.md ├── uiless │ ├── debug.png │ ├── meow-uiless.exe │ ├── uiless.md │ ├── 按键处理.md │ └── 启动过程.md ├── composition │ ├── 完成汉字输入.png │ ├── 输出汉字的第一种方式.png │ ├── 输出汉字的第二种方式.png │ ├── EndComposition.md │ ├── StartComposition.md │ └── Composition.md ├── ScrollBarWindow.md ├── ButtonWindow.md ├── BaseDictionaryEngine.md ├── DictionaryParser.md ├── KeyHandlerEditSession.md ├── EditSession.md ├── GetTextExtentEditSession.md ├── Server.md ├── DisplayAttributeInfo.md ├── ThreadFocusSink.md ├── ActiveLanguageProfileNotifySink.md ├── KeyStateCategory.md ├── ShadowWindow.md ├── DllMain.md ├── KeyEventSink.md ├── File.md ├── Register.md ├── TfInputProcessorProfile.md ├── FileMapping.md ├── DisplayAttributeProvider.md ├── Globals.md ├── FunctionProviderSink.md ├── appendix │ ├── 完成汉字输入.md │ ├── 按键处理.md │ ├── 选择候选字词.md │ └── 销毁候选窗口.md ├── BaseWindow.md ├── TableDictionaryEngine.md ├── ThreadMgrEventSink.md ├── Compartment.md ├── SampleIME.md ├── TfTextLayoutSink.md ├── SampleIMEBaseStructure.md ├── LanguageBar.md ├── TextEditSink.md ├── DisplayAttribute.md ├── DictionarySearch.md ├── CandidateWindow.md ├── CandidateListUIPresenter.md ├── CompositionProcessorEngine.md └── KeyHandler.md ├── SampleIME ├── File.cpp ├── Globals.h ├── BaseWindow.h ├── Globals.cpp ├── RegKey.cpp ├── Register.cpp ├── SampleIME.h ├── Server.cpp ├── BaseWindow.cpp ├── KeyHandler.cpp ├── SampleIME.cpp ├── ButtonWindow.cpp ├── CandidateWindow.h ├── Compartment.cpp ├── Composition.cpp ├── EditSession.cpp ├── FileMapping.cpp ├── KeyEventSink.cpp ├── LanguageBar.cpp ├── ShadowWindow.cpp ├── TextEditSink.cpp ├── CandidateWindow.cpp ├── EndComposition.cpp ├── ScrollBarWindow.cpp ├── ThreadFocusSink.cpp ├── image │ ├── ImeModeOn.ico │ ├── SampleIme.ico │ ├── ImeModeOff.ico │ ├── PunctuationOff.ico │ ├── PunctuationOn.ico │ ├── DoubleSingleByteOff.ico │ └── DoubleSingleByteOn.ico ├── DictionaryParser.cpp ├── DictionarySearch.cpp ├── DisplayAttribute.cpp ├── EnumTfCandidates.cpp ├── KeyStateCategory.cpp ├── StartComposition.cpp ├── TfTextLayoutSink.cpp ├── ThreadMgrEventSink.cpp ├── TipCandidateList.cpp ├── BaseDictionaryEngine.cpp ├── DisplayAttributeInfo.cpp ├── FunctionProviderSink.cpp ├── SampleIMEBaseStructure.h ├── KeyHandlerEditSession.cpp ├── SampleIMEBaseStructure.cpp ├── SearchCandidateProvider.cpp ├── TableDictionaryEngine.cpp ├── TfInputProcessorProfile.cpp ├── CandidateListUIPresenter.cpp ├── CompositionProcessorEngine.h ├── DisplayAttributeProvider.cpp ├── EnumDisplayAttributeInfo.cpp ├── GetTextExtentEditSession.cpp ├── ActiveLanguageProfileNotifySink.cpp ├── Dictionary │ ├── SampleIMESimplifiedQuanPin.txt │ └── .gitattributes ├── SampleIME.def ├── stdafx.h ├── Private.h ├── TfInputProcessorProfile.h ├── ButtonWindow.h ├── EditSession.h ├── KeyHandlerEditSession.h ├── resource.h ├── TipCandidateString.h ├── DllMain.cpp ├── EnumDisplayAttributeInfo.h ├── ShadowWindow.h ├── TableDictionaryEngine.h ├── BaseDictionaryEngine.h ├── GetTextExtentEditSession.h ├── FileMapping.h ├── DisplayString.h ├── TipCandidateList.h ├── TfTextLayoutSink.h ├── EnumTfCandidates.h ├── DictionaryParser.h ├── RegKey.h ├── Compartment.h ├── SearchCandidateProvider.h ├── File.h ├── SampleIMEStructureArray.h ├── Define.h ├── DictionarySearch.h ├── LanguageBar.h ├── DisplayAttributeInfo.h ├── TipCandidateString.cpp ├── SampleIME.rc ├── CandidateListUIPresenter.h ├── ScrollBarWindow.h └── KeyStateCategory.h ├── uninstall.bat ├── install.bat ├── SampleIME.sln └── README.md /doc/RegKey.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /doc/EnumTfCandidates.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /doc/TipCandidateList.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /doc/TipCandidateString.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /doc/EnumDisplayAttributeInfo.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /doc/SearchCandidateProvider.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /SampleIME/File.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChineseInputMethod/SampleIME/HEAD/SampleIME/File.cpp -------------------------------------------------------------------------------- /SampleIME/Globals.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChineseInputMethod/SampleIME/HEAD/SampleIME/Globals.h -------------------------------------------------------------------------------- /SampleIME/BaseWindow.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChineseInputMethod/SampleIME/HEAD/SampleIME/BaseWindow.h -------------------------------------------------------------------------------- /SampleIME/Globals.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChineseInputMethod/SampleIME/HEAD/SampleIME/Globals.cpp -------------------------------------------------------------------------------- /SampleIME/RegKey.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChineseInputMethod/SampleIME/HEAD/SampleIME/RegKey.cpp -------------------------------------------------------------------------------- /SampleIME/Register.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChineseInputMethod/SampleIME/HEAD/SampleIME/Register.cpp -------------------------------------------------------------------------------- /SampleIME/SampleIME.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChineseInputMethod/SampleIME/HEAD/SampleIME/SampleIME.h -------------------------------------------------------------------------------- /SampleIME/Server.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChineseInputMethod/SampleIME/HEAD/SampleIME/Server.cpp -------------------------------------------------------------------------------- /doc/uiless/debug.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChineseInputMethod/SampleIME/HEAD/doc/uiless/debug.png -------------------------------------------------------------------------------- /SampleIME/BaseWindow.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChineseInputMethod/SampleIME/HEAD/SampleIME/BaseWindow.cpp -------------------------------------------------------------------------------- /SampleIME/KeyHandler.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChineseInputMethod/SampleIME/HEAD/SampleIME/KeyHandler.cpp -------------------------------------------------------------------------------- /SampleIME/SampleIME.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChineseInputMethod/SampleIME/HEAD/SampleIME/SampleIME.cpp -------------------------------------------------------------------------------- /SampleIME/ButtonWindow.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChineseInputMethod/SampleIME/HEAD/SampleIME/ButtonWindow.cpp -------------------------------------------------------------------------------- /SampleIME/CandidateWindow.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChineseInputMethod/SampleIME/HEAD/SampleIME/CandidateWindow.h -------------------------------------------------------------------------------- /SampleIME/Compartment.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChineseInputMethod/SampleIME/HEAD/SampleIME/Compartment.cpp -------------------------------------------------------------------------------- /SampleIME/Composition.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChineseInputMethod/SampleIME/HEAD/SampleIME/Composition.cpp -------------------------------------------------------------------------------- /SampleIME/EditSession.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChineseInputMethod/SampleIME/HEAD/SampleIME/EditSession.cpp -------------------------------------------------------------------------------- /SampleIME/FileMapping.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChineseInputMethod/SampleIME/HEAD/SampleIME/FileMapping.cpp -------------------------------------------------------------------------------- /SampleIME/KeyEventSink.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChineseInputMethod/SampleIME/HEAD/SampleIME/KeyEventSink.cpp -------------------------------------------------------------------------------- /SampleIME/LanguageBar.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChineseInputMethod/SampleIME/HEAD/SampleIME/LanguageBar.cpp -------------------------------------------------------------------------------- /SampleIME/ShadowWindow.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChineseInputMethod/SampleIME/HEAD/SampleIME/ShadowWindow.cpp -------------------------------------------------------------------------------- /SampleIME/TextEditSink.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChineseInputMethod/SampleIME/HEAD/SampleIME/TextEditSink.cpp -------------------------------------------------------------------------------- /doc/composition/完成汉字输入.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChineseInputMethod/SampleIME/HEAD/doc/composition/完成汉字输入.png -------------------------------------------------------------------------------- /doc/uiless/meow-uiless.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChineseInputMethod/SampleIME/HEAD/doc/uiless/meow-uiless.exe -------------------------------------------------------------------------------- /SampleIME/CandidateWindow.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChineseInputMethod/SampleIME/HEAD/SampleIME/CandidateWindow.cpp -------------------------------------------------------------------------------- /SampleIME/EndComposition.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChineseInputMethod/SampleIME/HEAD/SampleIME/EndComposition.cpp -------------------------------------------------------------------------------- /SampleIME/ScrollBarWindow.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChineseInputMethod/SampleIME/HEAD/SampleIME/ScrollBarWindow.cpp -------------------------------------------------------------------------------- /SampleIME/ThreadFocusSink.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChineseInputMethod/SampleIME/HEAD/SampleIME/ThreadFocusSink.cpp -------------------------------------------------------------------------------- /SampleIME/image/ImeModeOn.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChineseInputMethod/SampleIME/HEAD/SampleIME/image/ImeModeOn.ico -------------------------------------------------------------------------------- /SampleIME/image/SampleIme.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChineseInputMethod/SampleIME/HEAD/SampleIME/image/SampleIme.ico -------------------------------------------------------------------------------- /SampleIME/DictionaryParser.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChineseInputMethod/SampleIME/HEAD/SampleIME/DictionaryParser.cpp -------------------------------------------------------------------------------- /SampleIME/DictionarySearch.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChineseInputMethod/SampleIME/HEAD/SampleIME/DictionarySearch.cpp -------------------------------------------------------------------------------- /SampleIME/DisplayAttribute.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChineseInputMethod/SampleIME/HEAD/SampleIME/DisplayAttribute.cpp -------------------------------------------------------------------------------- /SampleIME/EnumTfCandidates.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChineseInputMethod/SampleIME/HEAD/SampleIME/EnumTfCandidates.cpp -------------------------------------------------------------------------------- /SampleIME/KeyStateCategory.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChineseInputMethod/SampleIME/HEAD/SampleIME/KeyStateCategory.cpp -------------------------------------------------------------------------------- /SampleIME/StartComposition.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChineseInputMethod/SampleIME/HEAD/SampleIME/StartComposition.cpp -------------------------------------------------------------------------------- /SampleIME/TfTextLayoutSink.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChineseInputMethod/SampleIME/HEAD/SampleIME/TfTextLayoutSink.cpp -------------------------------------------------------------------------------- /SampleIME/ThreadMgrEventSink.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChineseInputMethod/SampleIME/HEAD/SampleIME/ThreadMgrEventSink.cpp -------------------------------------------------------------------------------- /SampleIME/TipCandidateList.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChineseInputMethod/SampleIME/HEAD/SampleIME/TipCandidateList.cpp -------------------------------------------------------------------------------- /SampleIME/image/ImeModeOff.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChineseInputMethod/SampleIME/HEAD/SampleIME/image/ImeModeOff.ico -------------------------------------------------------------------------------- /doc/composition/输出汉字的第一种方式.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChineseInputMethod/SampleIME/HEAD/doc/composition/输出汉字的第一种方式.png -------------------------------------------------------------------------------- /doc/composition/输出汉字的第二种方式.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChineseInputMethod/SampleIME/HEAD/doc/composition/输出汉字的第二种方式.png -------------------------------------------------------------------------------- /SampleIME/BaseDictionaryEngine.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChineseInputMethod/SampleIME/HEAD/SampleIME/BaseDictionaryEngine.cpp -------------------------------------------------------------------------------- /SampleIME/DisplayAttributeInfo.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChineseInputMethod/SampleIME/HEAD/SampleIME/DisplayAttributeInfo.cpp -------------------------------------------------------------------------------- /SampleIME/FunctionProviderSink.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChineseInputMethod/SampleIME/HEAD/SampleIME/FunctionProviderSink.cpp -------------------------------------------------------------------------------- /SampleIME/SampleIMEBaseStructure.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChineseInputMethod/SampleIME/HEAD/SampleIME/SampleIMEBaseStructure.h -------------------------------------------------------------------------------- /SampleIME/image/PunctuationOff.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChineseInputMethod/SampleIME/HEAD/SampleIME/image/PunctuationOff.ico -------------------------------------------------------------------------------- /SampleIME/image/PunctuationOn.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChineseInputMethod/SampleIME/HEAD/SampleIME/image/PunctuationOn.ico -------------------------------------------------------------------------------- /doc/ScrollBarWindow.md: -------------------------------------------------------------------------------- 1 | ## 3.33 滚动条窗口 2 | 3 | 滚动条窗口,未讲解此文件。 4 | 5 | 在Windows 10的开始菜单搜索框中,打开本工程输入法,在候选框右侧会出现滚动条。 6 | 7 | 本文不讲解此UI模式。 -------------------------------------------------------------------------------- /SampleIME/KeyHandlerEditSession.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChineseInputMethod/SampleIME/HEAD/SampleIME/KeyHandlerEditSession.cpp -------------------------------------------------------------------------------- /SampleIME/SampleIMEBaseStructure.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChineseInputMethod/SampleIME/HEAD/SampleIME/SampleIMEBaseStructure.cpp -------------------------------------------------------------------------------- /SampleIME/SearchCandidateProvider.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChineseInputMethod/SampleIME/HEAD/SampleIME/SearchCandidateProvider.cpp -------------------------------------------------------------------------------- /SampleIME/TableDictionaryEngine.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChineseInputMethod/SampleIME/HEAD/SampleIME/TableDictionaryEngine.cpp -------------------------------------------------------------------------------- /SampleIME/TfInputProcessorProfile.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChineseInputMethod/SampleIME/HEAD/SampleIME/TfInputProcessorProfile.cpp -------------------------------------------------------------------------------- /doc/ButtonWindow.md: -------------------------------------------------------------------------------- 1 | ## 3.34 按钮窗口 2 | 3 | 按钮窗口,未讲解此文件。 4 | 5 | 在Windows 10的开始菜单搜索框中,打开本工程输入法,在候选框右侧出现的滚动条两端,有两个滚动按钮。 6 | 7 | 本文不讲解此UI模式。 -------------------------------------------------------------------------------- /SampleIME/CandidateListUIPresenter.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChineseInputMethod/SampleIME/HEAD/SampleIME/CandidateListUIPresenter.cpp -------------------------------------------------------------------------------- /SampleIME/CompositionProcessorEngine.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChineseInputMethod/SampleIME/HEAD/SampleIME/CompositionProcessorEngine.h -------------------------------------------------------------------------------- /SampleIME/DisplayAttributeProvider.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChineseInputMethod/SampleIME/HEAD/SampleIME/DisplayAttributeProvider.cpp -------------------------------------------------------------------------------- /SampleIME/EnumDisplayAttributeInfo.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChineseInputMethod/SampleIME/HEAD/SampleIME/EnumDisplayAttributeInfo.cpp -------------------------------------------------------------------------------- /SampleIME/GetTextExtentEditSession.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChineseInputMethod/SampleIME/HEAD/SampleIME/GetTextExtentEditSession.cpp -------------------------------------------------------------------------------- /SampleIME/image/DoubleSingleByteOff.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChineseInputMethod/SampleIME/HEAD/SampleIME/image/DoubleSingleByteOff.ico -------------------------------------------------------------------------------- /SampleIME/image/DoubleSingleByteOn.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChineseInputMethod/SampleIME/HEAD/SampleIME/image/DoubleSingleByteOn.ico -------------------------------------------------------------------------------- /SampleIME/ActiveLanguageProfileNotifySink.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChineseInputMethod/SampleIME/HEAD/SampleIME/ActiveLanguageProfileNotifySink.cpp -------------------------------------------------------------------------------- /SampleIME/Dictionary/SampleIMESimplifiedQuanPin.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChineseInputMethod/SampleIME/HEAD/SampleIME/Dictionary/SampleIMESimplifiedQuanPin.txt -------------------------------------------------------------------------------- /SampleIME/Dictionary/.gitattributes: -------------------------------------------------------------------------------- 1 | # The *.txt files in this directory are UTF-16-encoded and should 2 | # therefore be treated as pure binary data. 3 | *.txt binary 4 | -------------------------------------------------------------------------------- /doc/BaseDictionaryEngine.md: -------------------------------------------------------------------------------- 1 | ## 3.26 词典引擎基类 2 | 3 | 词典引擎基类,为词典引擎提供辅助函数。 4 | 5 | ## 3.26.1 归并排序 6 | 7 | Function |Description 8 | -|- 9 | MergeSortByFindKeyCode() |归并排序。 -------------------------------------------------------------------------------- /SampleIME/SampleIME.def: -------------------------------------------------------------------------------- 1 | LIBRARY SampleIME.dll 2 | 3 | EXPORTS 4 | DllGetClassObject PRIVATE 5 | DllCanUnloadNow PRIVATE 6 | DllRegisterServer PRIVATE 7 | DllUnregisterServer PRIVATE 8 | -------------------------------------------------------------------------------- /uninstall.bat: -------------------------------------------------------------------------------- 1 | %1 mshta vbscript:CreateObject("Shell.Application").ShellExecute("cmd.exe","/c %~s0 ::","","runas",1)(window.close)&&exit 2 | 3 | %windir%\system32\regsvr32.exe /u %~dp0x64\Debug\SampleIME.dll 4 | del %~dp0x64\Debug\SampleIMESimplifiedQuanPin.txt -------------------------------------------------------------------------------- /install.bat: -------------------------------------------------------------------------------- 1 | %1 mshta vbscript:CreateObject("Shell.Application").ShellExecute("cmd.exe","/c %~s0 ::","","runas",1)(window.close)&&exit 2 | 3 | %windir%\system32\regsvr32.exe %~dp0x64\Debug\SampleIME.dll 4 | copy %~dp0SampleIME\Dictionary\SampleIMESimplifiedQuanPin.txt %~dp0x64\Debug -------------------------------------------------------------------------------- /doc/DictionaryParser.md: -------------------------------------------------------------------------------- 1 | ## 3.25 词典搜索基类 2 | 3 | 词典搜索基类,为词典搜索提供辅助函数。 4 | 5 | ## 3.25.1 辅助函数 6 | 7 | 码表以如下格式在文件中存储: 8 | "A"="啊"\r\n 9 | ... 10 | 11 | Function |Description 12 | -|- 13 | GetOneLine() |以\r做为分隔符,读取一行数据。 14 | ParseLine() |以=做为分隔符,解析一行中的编码和汉字。 15 | GetToken() |获取=位置。 -------------------------------------------------------------------------------- /doc/KeyHandlerEditSession.md: -------------------------------------------------------------------------------- 1 | ## 3.20 按键处理编辑会话 2 | 3 | 按键处理编辑会话,在会话中按照按键分类调用相应处理函数。 4 | 5 | ## 3.20.1 文档锁 6 | 7 | Function |Description 8 | -|- 9 | DoEditSession() |由TSF管理器提供的TfEditCookie唯一标识符,用于访问上下文。 10 | 11 | 当输入法申请ITfEditSession编辑会话后,TSF管理器锁定上下文,调用ITfEditSession::DoEditSession()函数,读取和/或修改上下文的文本和属性。 -------------------------------------------------------------------------------- /SampleIME/stdafx.h: -------------------------------------------------------------------------------- 1 | // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF 2 | // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO 3 | // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 4 | // PARTICULAR PURPOSE. 5 | // 6 | // Copyright (c) Microsoft Corporation. All rights reserved 7 | 8 | #pragma once 9 | #include 10 | -------------------------------------------------------------------------------- /doc/EditSession.md: -------------------------------------------------------------------------------- 1 | ## 3.19 编辑会话 2 | 3 | 编辑会话基接口,本章工程所有编辑会话均继承此接口。 4 | 5 | ## 3.19.1 编辑会话简介 6 | 7 | Interface |Description 8 | -|- 9 | [ITfEditSession][1] |编辑会话,由TSF管理器调用,用来修改上下文的文本和属性。 10 | 11 | [1]: https://github.com/ChineseInputMethod/Interface/blob/master/TextService/ITfEditSession.md 12 | 13 | 当文本服务必须在上下文中获取、修改或设置文本时,必须发起编辑会话。 14 | 发起编辑会话时,TSF管理器会与目标上下文的所有者协商请求类型的文档锁。 15 | 授予文档锁后,TSF管理器允许文本服务读取和/或写入上下文中的文本。 -------------------------------------------------------------------------------- /doc/GetTextExtentEditSession.md: -------------------------------------------------------------------------------- 1 | ## 3.29 文本布局会话 2 | 3 | 文本布局会话,处理文本布局消息。 4 | 5 | ## 3.29.1 处理文本布局消息 6 | 7 | 在文本布局会话中,输入法获取合成位置,然后在合成下方显示候选窗口。 8 | 9 | ```C++ 10 | STDAPI CGetTextExtentEditSession::DoEditSession(TfEditCookie ec) 11 | { 12 | RECT rc = {0, 0, 0, 0}; 13 | BOOL isClipped = TRUE; 14 | 15 | if (SUCCEEDED(_pContextView->GetTextExt(ec, _pRangeComposition, &rc, &isClipped))) 16 | { 17 | _pTfTextLayoutSink->_LayoutChangeNotification(&rc); 18 | } 19 | 20 | return S_OK; 21 | } 22 | ``` -------------------------------------------------------------------------------- /SampleIME/Private.h: -------------------------------------------------------------------------------- 1 | // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF 2 | // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO 3 | // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 4 | // PARTICULAR PURPOSE. 5 | // 6 | // Copyright (c) Microsoft Corporation. All rights reserved 7 | 8 | #pragma once 9 | 10 | #include "stdafx.h" 11 | #include "sal.h" 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | #include 19 | 20 | #include "initguid.h" 21 | #include "msctf.h" 22 | #include "ctffunc.h" 23 | -------------------------------------------------------------------------------- /doc/Server.md: -------------------------------------------------------------------------------- 1 | ## 3.3 COM导出函数 2 | 3 | Server.cpp提供了TSF输入法作为COM组件的标准实现。 4 | 5 | ## 3.3.1 注册输入法 6 | 7 | 注册COM组件时,DllRegisterServer()函数被调用。在此函数中实现了注册输入法。 8 | 9 | ```C++ 10 | STDAPI DllRegisterServer(void) 11 | { 12 | if ((!RegisterServer()) || (!RegisterProfiles()) || (!RegisterCategories())) 13 | { 14 | DllUnregisterServer(); 15 | return E_FAIL; 16 | } 17 | return S_OK; 18 | } 19 | ``` 20 | 21 | ## 3.3.2 创建输入法实例 22 | 23 | 创建COM对象时,DllGetClassObject()函数被调用。通过调用CSampleIME::CreateInstance(),创建输入法实例。 24 | 25 | ```C++ 26 | void BuildGlobalObjects(void) 27 | { 28 | classFactoryObjects[0] = new (std::nothrow) CClassFactory(Global::SampleIMECLSID, CSampleIME::CreateInstance); 29 | } 30 | ``` -------------------------------------------------------------------------------- /doc/DisplayAttributeInfo.md: -------------------------------------------------------------------------------- 1 | ## 3.40 显示属性信息 2 | 3 | 显示属性信息对象,为应用程序提供显示属性信息。 4 | 5 | 输入法需要实现ITfDisplayAttributeInfo接口,提供显示属性信息对象。 6 | 7 | ## 3.40.1 显示属性信息对象 8 | 9 | Interface |Description 10 | -|- 11 | [ITfDisplayAttributeInfo][1] |显示属性信息对象,为应用程序提供显示属性信息。 12 | 13 | [1]: https://github.com/ChineseInputMethod/Interface/blob/master/TextService/ITfDisplayAttributeInfo.md 14 | 15 | ```C++ 16 | STDAPI CDisplayAttributeInfo::GetAttributeInfo(_Out_ TF_DISPLAYATTRIBUTE *ptfDisplayAttr) 17 | { 18 | if (ptfDisplayAttr == nullptr) 19 | { 20 | return E_INVALIDARG; 21 | } 22 | 23 | // return the default display attribute. 24 | *ptfDisplayAttr = *_pDisplayAttribute; 25 | 26 | return S_OK; 27 | } 28 | ``` -------------------------------------------------------------------------------- /doc/ThreadFocusSink.md: -------------------------------------------------------------------------------- 1 | ## 3.10 线程输入焦点消息接收器 2 | 3 | 线程输入焦点消息接收器,在线程接收或失去UI焦点时接收通知。 4 | 5 | ## 3.10.1 安装线程输入焦点消息接收器 6 | 7 | ```C++ 8 | if (FAILED(_pThreadMgr->QueryInterface(IID_ITfSource, (void **)&pSource))) 9 | { 10 | return FALSE; 11 | } 12 | 13 | if (FAILED(pSource->AdviseSink(IID_ITfThreadFocusSink, (ITfThreadFocusSink *)this, &_dwThreadFocusSinkCookie))) 14 | { 15 | pSource->Release(); 16 | return FALSE; 17 | } 18 | ``` 19 | 20 | ## 3.10.2 响应线程输入焦点消息 21 | 22 | 当线程获得输入焦点时,重绘候选窗口。 23 | 24 | Interface |Description 25 | -|- 26 | [ITfThreadFocusSink][1] |线程输入焦点消息接收器,在线程接收或失去UI焦点时接收通知。 27 | 28 | [1]: https://github.com/ChineseInputMethod/Interface/blob/master/TextService/ITfThreadFocusSink.md -------------------------------------------------------------------------------- /SampleIME/TfInputProcessorProfile.h: -------------------------------------------------------------------------------- 1 | // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF 2 | // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO 3 | // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 4 | // PARTICULAR PURPOSE. 5 | // 6 | // Copyright (c) Microsoft Corporation. All rights reserved 7 | 8 | #pragma once 9 | 10 | class CTfInputProcessorProfile 11 | { 12 | public: 13 | CTfInputProcessorProfile(); 14 | ~CTfInputProcessorProfile(); 15 | 16 | HRESULT CreateInstance(); 17 | HRESULT GetCurrentLanguage(_Out_ LANGID *plangid); 18 | HRESULT GetDefaultLanguageProfile(LANGID langid, REFGUID catid, _Out_ CLSID *pclsid, _Out_ GUID *pguidProfile); 19 | 20 | private: 21 | ITfInputProcessorProfiles* _pInputProcessorProfile; 22 | }; 23 | -------------------------------------------------------------------------------- /doc/ActiveLanguageProfileNotifySink.md: -------------------------------------------------------------------------------- 1 | ## 3.9 语言配置激活消息接收器 2 | 3 | 语言配置激活消息接收器,当更改激活语言配置文件时,框架调用接收器。 4 | 5 | ## 3.9.1 安装语言配置激活消息接收器 6 | 7 | ```C++ 8 | if (_pThreadMgr->QueryInterface(IID_ITfSource, (void **)&pSource) != S_OK) 9 | { 10 | return ret; 11 | } 12 | 13 | if (pSource->AdviseSink(IID_ITfActiveLanguageProfileNotifySink, (ITfActiveLanguageProfileNotifySink *)this, &_activeLanguageProfileNotifySinkCookie) != S_OK) 14 | { 15 | _activeLanguageProfileNotifySinkCookie = TF_INVALID_COOKIE; 16 | goto Exit; 17 | } 18 | ``` 19 | 20 | ## 3.9.2 响应输入法激活事件 21 | 22 | ITfActiveLanguageProfileNotifySink接口,用于响应输入法激活、停用消息。 23 | 24 | Interface |Description 25 | -|- 26 | [ITfActiveLanguageProfileNotifySink][1] |语言配置激活消息接收器,当更改激活语言配置文件时,框架调用接收器。 27 | 28 | [1]: https://github.com/ChineseInputMethod/Interface/blob/master/Application/ITfActiveLanguageProfileNotifySink.md -------------------------------------------------------------------------------- /SampleIME/ButtonWindow.h: -------------------------------------------------------------------------------- 1 | // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF 2 | // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO 3 | // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 4 | // PARTICULAR PURPOSE. 5 | // 6 | // Copyright (c) Microsoft Corporation. All rights reserved 7 | 8 | 9 | #pragma once 10 | 11 | #include "BaseWindow.h" 12 | 13 | class CButtonWindow : public CBaseWindow 14 | { 15 | public: 16 | CButtonWindow(); 17 | virtual ~CButtonWindow(); 18 | 19 | LRESULT CALLBACK _WindowProcCallback(_In_ HWND wndHandle, UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam); 20 | 21 | virtual void _OnPaint(_In_ HDC dcHandle, _In_ PAINTSTRUCT *pps); 22 | virtual void _OnLButtonDown(POINT pt); 23 | virtual void _OnLButtonUp(POINT pt); 24 | 25 | protected: 26 | UINT typeOfControl; // DFCS_PUSHED, DFCS_INACTIVE or others for DrawFrameControl 27 | }; 28 | -------------------------------------------------------------------------------- /SampleIME/EditSession.h: -------------------------------------------------------------------------------- 1 | // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF 2 | // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO 3 | // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 4 | // PARTICULAR PURPOSE. 5 | // 6 | // Copyright (c) Microsoft Corporation. All rights reserved 7 | 8 | 9 | #pragma once 10 | 11 | class CSampleIME; 12 | 13 | class CEditSessionBase : public ITfEditSession 14 | { 15 | public: 16 | CEditSessionBase(_In_ CSampleIME *pTextService, _In_ ITfContext *pContext); 17 | virtual ~CEditSessionBase(); 18 | 19 | // IUnknown 20 | STDMETHODIMP QueryInterface(REFIID riid, _Outptr_ void **ppvObj); 21 | STDMETHODIMP_(ULONG) AddRef(void); 22 | STDMETHODIMP_(ULONG) Release(void); 23 | 24 | // ITfEditSession 25 | virtual STDMETHODIMP DoEditSession(TfEditCookie ec) = 0; 26 | 27 | protected: 28 | ITfContext *_pContext; 29 | CSampleIME *_pTextService; 30 | 31 | private: 32 | LONG _refCount; // COM ref count 33 | }; 34 | -------------------------------------------------------------------------------- /doc/KeyStateCategory.md: -------------------------------------------------------------------------------- 1 | ## 3.21 按键分类 2 | 3 | 按键分类,按照按键分类调用相应处理函数。 4 | 5 | ## 3.21.1 当前工作状态 6 | 7 | 按照当前工作状态,创建相应的扩展类。 8 | 9 | ```C++ 10 | CKeyStateCategory* CKeyStateCategoryFactory::MakeKeyStateCategory(KEYSTROKE_CATEGORY keyCategory, _In_ CSampleIME *pTextService) 11 | { 12 | CKeyStateCategory* pKeyState = nullptr; 13 | 14 | switch (keyCategory) 15 | { 16 | case CATEGORY_NONE: 17 | pKeyState = new (std::nothrow) CKeyStateNull(pTextService); 18 | break; 19 | 20 | case CATEGORY_COMPOSING: 21 | pKeyState = new (std::nothrow) CKeyStateComposing(pTextService); 22 | break; 23 | 24 | case CATEGORY_CANDIDATE: 25 | pKeyState = new (std::nothrow) CKeyStateCandidate(pTextService); 26 | break; 27 | 28 | case CATEGORY_PHRASE: 29 | pKeyState = new (std::nothrow) CKeyStatePhrase(pTextService); 30 | break; 31 | 32 | default: 33 | pKeyState = new (std::nothrow) CKeyStateNull(pTextService); 34 | break; 35 | } 36 | return pKeyState; 37 | } 38 | ``` -------------------------------------------------------------------------------- /doc/ShadowWindow.md: -------------------------------------------------------------------------------- 1 | ## 3.32 阴影窗口 2 | 3 | 阴影窗口,为窗口添加阴影。
4 | 5 | ## 3.32.1 创建窗口 6 | 7 | 候选窗口类将this做为参数调用_pShadowWnd->_Create()函数。 8 | 9 | ```C++ 10 | BOOL CCandidateWindow::_CreateBackGroundShadowWindow() 11 | { 12 | _pShadowWnd = new (std::nothrow) CShadowWindow(this); 13 | if (_pShadowWnd == nullptr) 14 | { 15 | return FALSE; 16 | } 17 | 18 | if (!_pShadowWnd->_Create(Global::AtomShadowWindow, 19 | WS_EX_TOPMOST | WS_EX_TOOLWINDOW | WS_EX_LAYERED, 20 | WS_DISABLED | WS_POPUP, this)) 21 | { 22 | _DeleteShadowWnd(); 23 | return FALSE; 24 | } 25 | 26 | return TRUE; 27 | } 28 | ``` 29 | 30 | 阴影窗口类调用窗口基类。 31 | 32 | ```C++ 33 | BOOL CShadowWindow::_Create(ATOM atom, DWORD dwExStyle, DWORD dwStyle, _In_opt_ CBaseWindow *pParent, int wndWidth, int wndHeight) 34 | { 35 | if (!CBaseWindow::_Create(atom, dwExStyle, dwStyle, pParent, wndWidth, wndHeight)) 36 | { 37 | return FALSE; 38 | } 39 | 40 | return _Initialize(); 41 | } 42 | ``` 43 | 44 | 在窗口基类中调用API函数创建窗口。 -------------------------------------------------------------------------------- /SampleIME/KeyHandlerEditSession.h: -------------------------------------------------------------------------------- 1 | // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF 2 | // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO 3 | // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 4 | // PARTICULAR PURPOSE. 5 | // 6 | // Copyright (c) Microsoft Corporation. All rights reserved 7 | 8 | #pragma once 9 | 10 | #include "EditSession.h" 11 | #include "Globals.h" 12 | 13 | class CKeyHandlerEditSession : public CEditSessionBase 14 | { 15 | public: 16 | CKeyHandlerEditSession(CSampleIME *pTextService, ITfContext *pContext, UINT uCode, WCHAR wch, _KEYSTROKE_STATE keyState) : CEditSessionBase(pTextService, pContext) 17 | { 18 | _uCode = uCode; 19 | _wch = wch; 20 | _KeyState = keyState; 21 | } 22 | 23 | // ITfEditSession 24 | STDMETHODIMP DoEditSession(TfEditCookie ec); 25 | 26 | private: 27 | UINT _uCode; // virtual key code 28 | WCHAR _wch; // character code 29 | _KEYSTROKE_STATE _KeyState; // key function regarding virtual key 30 | }; 31 | -------------------------------------------------------------------------------- /SampleIME/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by SampleIME.rc 4 | // 5 | #define SUBLANG_ENGLISH_US 0x01 6 | #define LANG_ENGLISH 0x09 7 | #define IDIS_SAMPLEIME 12 8 | #define IDS_DEFAULT_FONT 13 9 | #define IDS_IME_MODE 20 10 | #define IDI_IME_MODE_ON 21 11 | #define IDI_IME_MODE_OFF 22 12 | #define IDS_DOUBLE_SINGLE_BYTE 23 13 | #define IDI_DOUBLE_SINGLE_BYTE_ON 24 14 | #define IDI_DOUBLE_SINGLE_BYTE_OFF 25 15 | #define IDS_PUNCTUATION 26 16 | #define IDI_PUNCTUATION_ON 27 17 | #define IDI_PUNCTUATION_OFF 28 18 | #define IDR_VERSION2 107 19 | 20 | // Next default values for new objects 21 | // 22 | #ifdef APSTUDIO_INVOKED 23 | #ifndef APSTUDIO_READONLY_SYMBOLS 24 | #define _APS_NEXT_RESOURCE_VALUE 108 25 | #define _APS_NEXT_COMMAND_VALUE 40001 26 | #define _APS_NEXT_CONTROL_VALUE 1000 27 | #define _APS_NEXT_SYMED_VALUE 101 28 | #endif 29 | #endif 30 | -------------------------------------------------------------------------------- /doc/DllMain.md: -------------------------------------------------------------------------------- 1 | ## 3.1 DLL入口点 2 | 3 | 尝试了一下将跟踪信息重定向到文件中,似乎没有解决方案,只能继续沿用第二章介绍的调试方法。 4 | 好在我已经学会调试DLL了,当初制作ime输入法的时候,我什么都不会,源码是一点儿也看不懂。 5 | 6 | 原先CSDN人还多的时候,有人说《C++ Primer》比较难懂,问有什么学习方法。 7 | 我根本没看过这本书,不懂装懂的说,学习好比盲人摸象的那种看局部猜图游戏。 8 | 9 | 当你不懂不会的时候,也就是说你没见过谜底图片中的物品时,这些知识点是杂乱无章毫无关联的。 10 | 当你很熟悉谜底图片中的物品时,这些线索就是有关联的,你看到局部就能猜出图片。 11 | 12 | 现在明白了当时不可能学会,我根本没接触过Windows编程,就是在控制台写Hello Word的水平。 13 | 我只不过自认为很专业而已,这一点同民科没有本质的区别。 14 | 15 | 现在终于明白了自己为何如此浅薄,差一点沦为了时代的玩物。 16 | 这是因为人的精力是有限的,这个精力可以是大脑的算力或是天赋技能点什么的。 17 | 18 | 假设奋斗是与人或社会的对弈,你要计算每一步的广度和深度。 19 | 20 | 又试了试调试输入法,看来只能自己编写调试函数,才能完美解决输入法的调试问题。 21 | 22 | 调试程序,曾经对于我来讲是个陌生的领域。现在对于我而言,也是复杂的问题。 23 | 然而,我已经不像当初那么焦虑了。 24 | 25 | 对于创造性的领域,并没有具体的指导书,所有的技术难题都是平铺在你面前。 26 | 你不知道究竟应该学习哪些知识,这些知识要学习到何种程度。 27 | 28 | 要把精力以广度为底取对数。 29 | 30 | 用成功学的话说,那叫选择比努力重要。 31 | 可是成功学不会告诉你,选择要远比努力困难。 32 | 33 | 以输入法为例,我刚刚明白为什么所有同行都选择了相同的道路。 34 | 我原先的认知是,英语也不是用音标打字。现在刚刚知道,原来日韩的文字就是读音,读音就是文字。 35 | 36 | ## 3.1.1 主要函数 37 | 38 | DLL入口点函数只初始化了候选窗口类,只响应了进程开始和结束消息。 39 | 40 | ```C++ 41 | BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID pvReserved) 42 | { 43 | Globals.cpp#Global::RegisterWindowClass() 44 | } 45 | ``` -------------------------------------------------------------------------------- /doc/KeyEventSink.md: -------------------------------------------------------------------------------- 1 | ## 3.8 键盘事件接收器 2 | 3 | 键盘事件接收器,用于接收按键和虚拟按键事件。 4 | 5 | ## 3.8.1 安装键盘事件接收器 6 | 7 | Interface |Description 8 | -|- 9 | [ITfKeystrokeMgr][1] |按键管理器,主要用来安装键盘事件接收器和注册保留键。 10 | 11 | [1]: https://github.com/ChineseInputMethod/Interface/blob/master/TSFmanager/ITfKeystrokeMgr.md 12 | 13 | ```C++ 14 | if (FAILED(_pThreadMgr->QueryInterface(IID_ITfKeystrokeMgr, (void **)&pKeystrokeMgr))) 15 | { 16 | return FALSE; 17 | } 18 | 19 | hr = pKeystrokeMgr->AdviseKeyEventSink(_tfClientId, (ITfKeyEventSink *)this, TRUE); 20 | ``` 21 | 22 | ## 3.8.2 预处理按键 23 | 24 | 当按下和释放按键时,OnTestKeyDown()和OnTestKeyUp()方法将被调用。 25 | OnTestKeyDown()和OnTestKeyUp()方法调用_IsKeyEaten()函数,判断是否需要继续处理按键事件。 26 | 当_IsKeyEaten()函数返回TRUE时,TSF管理器将继续调用相应的OnKeyDown()和OnKeyUp()函数。 27 | 28 | >如果用wordpad.exe调试输入法,不会调用OnTestKeyDown()和OnTestKeyUp()函数。 29 | 30 | ## 3.8.3 处理按键事件 31 | 32 | Interface |Description 33 | -|- 34 | [ITfKeyEventSink][2] |键盘事件接收器,用于接收按键和保留键事件。 35 | 36 | [2]: https://github.com/ChineseInputMethod/Interface/blob/master/TextService/ITfKeyEventSink.md 37 | 38 | 当按下编码键,TSF管理器会调用输入法的OnKeyDown()方法。 39 | 在OnKeyDown()方法中,当_IsKeyEaten()函数返回TRUE时,调用_InvokeKeyHandler()方法处理按键事件。 -------------------------------------------------------------------------------- /doc/File.md: -------------------------------------------------------------------------------- 1 | ## 3.16 文件 2 | 3 | 文件,打开码表文件。 4 | 5 | ## 3.16.1 打开码表文件 6 | 7 | 输入法的权限继承首次激活输入法的应用程序的权限。当第一个激活输入法的应用程序不具备读写输入法码表文件的能力时,输入法就无法正常工作。 8 | 所以,一般输入法都有一个自启动服务,在后台为输入法提供码表文件。或者,将码表文件放入一个无权限限制的文件夹中。 9 | 10 | 当前工程,以只读方式打开码表文件,所以在大多数环境中都能正常工作。 11 | 12 | ```C++ 13 | BOOL CFile::CreateFile(_In_ PCWSTR pFileName, DWORD desiredAccess, 14 | DWORD creationDisposition, 15 | DWORD sharedMode, _Inout_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD flagsAndAttributes, _Inout_opt_ HANDLE templateFileHandle) 16 | { 17 | size_t fullPathLen = wcslen(pFileName); 18 | if (!_pFileName) 19 | { 20 | _pFileName = new (std::nothrow) WCHAR[ fullPathLen + 1 ]; 21 | } 22 | if (!_pFileName) 23 | { 24 | return FALSE; 25 | } 26 | 27 | StringCchCopyN(_pFileName, fullPathLen + 1, pFileName, fullPathLen); 28 | 29 | _fileHandle = ::CreateFile(pFileName, desiredAccess, sharedMode, 30 | lpSecurityAttributes, creationDisposition, flagsAndAttributes, templateFileHandle); 31 | 32 | if (_fileHandle == INVALID_HANDLE_VALUE) 33 | { 34 | return FALSE; 35 | } 36 | 37 | _fileSize = ::GetFileSize(_fileHandle, NULL); 38 | 39 | return TRUE; 40 | } 41 | ``` -------------------------------------------------------------------------------- /doc/Register.md: -------------------------------------------------------------------------------- 1 | ## 3.4 注册输入法 2 | 3 | 注册TSF输入法分为三步。 4 | RegisterServer()函数在注册表中注册COM组件。 5 | RegisterProfiles()函数注册文字服务及配置。 6 | RegisterCategories()函数注册输入法类别。 7 | 8 | ## 3.4.1 注册COM组件 9 | 10 | 执行RegisterServer()函数后,会在注册表创建以下键及键值: 11 | 12 | ```C++ 13 | HKEY_CLASSES_ROOT\CLSID\{D2291A80-84D8-4641-9AB2-BDD1472C846B}\InProcServer32 14 | ``` 15 | 16 | ## 3.4.2 注册文本服务和配置文件 17 | 18 | Interface |Description 19 | -|- 20 | [ITfInputProcessorProfileMgr][1] |文本服务语言配置管理器,管理键盘布局和文本服务,建议使用此接口注册输入法。 21 | 22 | [1]: https://github.com/ChineseInputMethod/Interface/blob/master/TSFmanager/ITfInputProcessorProfileMgr.md 23 | 24 | 执行RegisterProfiles()函数后,会在注册表创建以下键及键值: 25 | 26 | ```C++ 27 | HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\CTF\TIP\{D2291A80-84D8-4641-9AB2-BDD1472C846B}\LanguageProfile\0x00000804\{83955C0E-2C09-47A5-BCF3-F2B98E11EE8B} 28 | ``` 29 | 30 | ## 3.4.3 注册输入法类别 31 | 32 | Interface |Description 33 | -|- 34 | [ITfCategoryMgr][2] |类别管理器,为输入法注册类别。 35 | 36 | [2]: https://github.com/ChineseInputMethod/Interface/blob/master/TSFmanager/ITfCategoryMgr.md 37 | 38 | 执行RegisterCategories()函数后,会在注册表创建以下键及键值: 39 | 40 | ```C++ 41 | HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\CTF\TIP\{D2291A80-84D8-4641-9AB2-BDD1472C846B}\Category 42 | ``` -------------------------------------------------------------------------------- /SampleIME.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 11.00 3 | # Visual Studio 2010 4 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SampleIME", "SampleIME\SampleIME.vcxproj", "{0C8FE010-5146-44D8-AF99-82702C15FEA5}" 5 | EndProject 6 | Global 7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 8 | Debug|Win32 = Debug|Win32 9 | Debug|x64 = Debug|x64 10 | Release|Win32 = Release|Win32 11 | Release|x64 = Release|x64 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {0C8FE010-5146-44D8-AF99-82702C15FEA5}.Debug|Win32.ActiveCfg = Debug|Win32 15 | {0C8FE010-5146-44D8-AF99-82702C15FEA5}.Debug|Win32.Build.0 = Debug|Win32 16 | {0C8FE010-5146-44D8-AF99-82702C15FEA5}.Debug|x64.ActiveCfg = Debug|x64 17 | {0C8FE010-5146-44D8-AF99-82702C15FEA5}.Debug|x64.Build.0 = Debug|x64 18 | {0C8FE010-5146-44D8-AF99-82702C15FEA5}.Release|Win32.ActiveCfg = Release|Win32 19 | {0C8FE010-5146-44D8-AF99-82702C15FEA5}.Release|Win32.Build.0 = Release|Win32 20 | {0C8FE010-5146-44D8-AF99-82702C15FEA5}.Release|x64.ActiveCfg = Release|x64 21 | {0C8FE010-5146-44D8-AF99-82702C15FEA5}.Release|x64.Build.0 = Release|x64 22 | EndGlobalSection 23 | GlobalSection(SolutionProperties) = preSolution 24 | HideSolutionNode = FALSE 25 | EndGlobalSection 26 | EndGlobal 27 | -------------------------------------------------------------------------------- /SampleIME/TipCandidateString.h: -------------------------------------------------------------------------------- 1 | // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF 2 | // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO 3 | // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 4 | // PARTICULAR PURPOSE. 5 | // 6 | // Copyright (c) Microsoft Corporation. All rights reserved 7 | 8 | #pragma once 9 | 10 | #include "private.h" 11 | #include 12 | 13 | class CTipCandidateString : public ITfCandidateString 14 | { 15 | protected: 16 | CTipCandidateString(); 17 | virtual ~CTipCandidateString(); 18 | 19 | public: 20 | static HRESULT CreateInstance(_Outptr_ CTipCandidateString **ppobj); 21 | static HRESULT CreateInstance(REFIID riid, _Outptr_ void **ppvObj); 22 | 23 | // IUnknown methods 24 | virtual STDMETHODIMP QueryInterface(REFIID riid, _Outptr_ void **ppvObj); 25 | virtual STDMETHODIMP_(ULONG) AddRef(); 26 | virtual STDMETHODIMP_(ULONG) Release(); 27 | 28 | // ITfCandidateString methods 29 | virtual STDMETHODIMP GetString(BSTR *pbstr); 30 | virtual STDMETHODIMP GetIndex(_Out_ ULONG *pnIndex); 31 | 32 | virtual STDMETHODIMP SetIndex(ULONG uIndex); 33 | virtual STDMETHODIMP SetString(_In_ const WCHAR *pch, DWORD_PTR length); 34 | 35 | protected: 36 | long _refCount; 37 | int _index; 38 | std::wstring _candidateStr; 39 | }; -------------------------------------------------------------------------------- /SampleIME/DllMain.cpp: -------------------------------------------------------------------------------- 1 | // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF 2 | // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO 3 | // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 4 | // PARTICULAR PURPOSE. 5 | // 6 | // Copyright (c) Microsoft Corporation. All rights reserved 7 | 8 | #include "Private.h" 9 | #include "Globals.h" 10 | 11 | //+--------------------------------------------------------------------------- 12 | // 13 | // DllMain 14 | // 15 | //---------------------------------------------------------------------------- 16 | 17 | BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID pvReserved) 18 | { 19 | pvReserved; 20 | 21 | switch (dwReason) 22 | { 23 | case DLL_PROCESS_ATTACH: 24 | 25 | Global::dllInstanceHandle = hInstance; 26 | 27 | if (!InitializeCriticalSectionAndSpinCount(&Global::CS, 0)) 28 | { 29 | return FALSE; 30 | } 31 | 32 | if (!Global::RegisterWindowClass()) { 33 | return FALSE; 34 | } 35 | 36 | break; 37 | 38 | case DLL_PROCESS_DETACH: 39 | 40 | DeleteCriticalSection(&Global::CS); 41 | 42 | break; 43 | 44 | case DLL_THREAD_ATTACH: 45 | 46 | break; 47 | 48 | case DLL_THREAD_DETACH: 49 | 50 | break; 51 | } 52 | 53 | return TRUE; 54 | } 55 | -------------------------------------------------------------------------------- /SampleIME/EnumDisplayAttributeInfo.h: -------------------------------------------------------------------------------- 1 | // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF 2 | // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO 3 | // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 4 | // PARTICULAR PURPOSE. 5 | // 6 | // Copyright (c) Microsoft Corporation. All rights reserved 7 | 8 | #pragma once 9 | 10 | //+--------------------------------------------------------------------------- 11 | // 12 | // CEnumDisplayAttributeInfo 13 | // 14 | //---------------------------------------------------------------------------- 15 | 16 | class CEnumDisplayAttributeInfo : public IEnumTfDisplayAttributeInfo 17 | { 18 | public: 19 | CEnumDisplayAttributeInfo(); 20 | ~CEnumDisplayAttributeInfo(); 21 | 22 | // IUnknown 23 | STDMETHODIMP QueryInterface(REFIID riid, _Outptr_ void **ppvObj); 24 | STDMETHODIMP_(ULONG) AddRef(void); 25 | STDMETHODIMP_(ULONG) Release(void); 26 | 27 | // IEnumTfDisplayAttributeInfo 28 | STDMETHODIMP Clone(_Out_ IEnumTfDisplayAttributeInfo **ppEnum); 29 | STDMETHODIMP Next(ULONG ulCount, __RPC__out_ecount_part(ulCount, *pcFetched) ITfDisplayAttributeInfo **rgInfo, __RPC__out ULONG *pcFetched); 30 | STDMETHODIMP Reset(); 31 | STDMETHODIMP Skip(ULONG ulCount); 32 | 33 | private: 34 | LONG _index; // next display attribute to enum 35 | LONG _refCount; // COM ref count 36 | }; 37 | -------------------------------------------------------------------------------- /doc/TfInputProcessorProfile.md: -------------------------------------------------------------------------------- 1 | ## 3.13 文本服务语言配置 2 | 3 | 文本服务语言配置,注册表中输入法的属性设置。 4 | 5 | Interface |Description 6 | -|- 7 | [ITfInputProcessorProfiles][1] |文本服务语言配置,用来操作一个或多个文本服务的语言配置文件。 8 | 9 | [1]: https://github.com/ChineseInputMethod/Interface/blob/master/TSFmanager/ITfInputProcessorProfiles.md 10 | 11 | ## 3.13.1 获取语言配置接口指针 12 | 13 | ```C++ 14 | HRESULT CTfInputProcessorProfile::CreateInstance() 15 | { 16 | HRESULT hr = CoCreateInstance(CLSID_TF_InputProcessorProfiles, nullptr, CLSCTX_INPROC_SERVER, 17 | IID_ITfInputProcessorProfiles, (void**)&_pInputProcessorProfile); 18 | 19 | return hr; 20 | } 21 | ``` 22 | 23 | ## 3.13.2 获取当前活动语言的标识符 24 | 25 | ```C++ 26 | HRESULT CTfInputProcessorProfile::GetCurrentLanguage(_Out_ LANGID *plangid) 27 | { 28 | if (_pInputProcessorProfile) 29 | { 30 | return _pInputProcessorProfile->GetCurrentLanguage(plangid); 31 | } 32 | else 33 | { 34 | return E_FAIL; 35 | } 36 | } 37 | ``` 38 | 39 | ## 3.13.3 获取当前活动语言的默认配置文件 40 | 41 | ```C++ 42 | HRESULT CTfInputProcessorProfile::GetDefaultLanguageProfile(LANGID langid, REFGUID catid, _Out_ CLSID *pclsid, _Out_ GUID *pguidProfile) 43 | { 44 | if (_pInputProcessorProfile) 45 | { 46 | return _pInputProcessorProfile->GetDefaultLanguageProfile(langid, catid, pclsid, pguidProfile); 47 | } 48 | else 49 | { 50 | return E_FAIL; 51 | } 52 | } 53 | ``` -------------------------------------------------------------------------------- /SampleIME/ShadowWindow.h: -------------------------------------------------------------------------------- 1 | // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF 2 | // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO 3 | // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 4 | // PARTICULAR PURPOSE. 5 | // 6 | // Copyright (c) Microsoft Corporation. All rights reserved 7 | 8 | #pragma once 9 | 10 | #include "BaseWindow.h" 11 | 12 | class CShadowWindow : public CBaseWindow 13 | { 14 | public: 15 | CShadowWindow(_In_ CBaseWindow *pWndOwner) 16 | { 17 | _pWndOwner = pWndOwner; 18 | _color = RGB(0, 0, 0); 19 | _sizeShift.cx = 0; 20 | _sizeShift.cy = 0; 21 | _isGradient = FALSE; 22 | } 23 | virtual ~CShadowWindow() 24 | { 25 | } 26 | 27 | BOOL _Create(ATOM atom, DWORD dwExStyle, DWORD dwStyle, _In_opt_ CBaseWindow *pParent = nullptr, int wndWidth = 0, int wndHeight = 0); 28 | 29 | void _Show(BOOL isShowWnd); 30 | 31 | LRESULT CALLBACK _WindowProcCallback(_In_ HWND wndHandle, UINT uMsg, WPARAM wParam, LPARAM lParam); 32 | 33 | void _OnSettingChange(); 34 | void _OnOwnerWndMoved(BOOL isResized); 35 | 36 | private: 37 | BOOL _Initialize(); 38 | void _InitSettings(); 39 | void _AdjustWindowPos(); 40 | void _InitShadow(); 41 | 42 | private: 43 | CBaseWindow* _pWndOwner; 44 | COLORREF _color; 45 | SIZE _sizeShift; 46 | BOOL _isGradient; 47 | }; 48 | -------------------------------------------------------------------------------- /SampleIME/TableDictionaryEngine.h: -------------------------------------------------------------------------------- 1 | // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF 2 | // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO 3 | // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 4 | // PARTICULAR PURPOSE. 5 | // 6 | // Copyright (c) Microsoft Corporation. All rights reserved 7 | 8 | #pragma once 9 | 10 | #include "BaseDictionaryEngine.h" 11 | 12 | class CTableDictionaryEngine : public CBaseDictionaryEngine 13 | { 14 | public: 15 | CTableDictionaryEngine(LCID locale, _In_ CFile *pDictionaryFile) : CBaseDictionaryEngine(locale, pDictionaryFile) { } 16 | virtual ~CTableDictionaryEngine() { } 17 | 18 | // Collect word from phrase string. 19 | // param 20 | // [in] psrgKeyCode - Specified key code pointer 21 | // [out] pasrgWordString - Specified returns pointer of word as CStringRange. 22 | // returns 23 | // none. 24 | VOID CollectWord(_In_ CStringRange *pKeyCode, _Inout_ CSampleImeArray *pWordStrings); 25 | VOID CollectWord(_In_ CStringRange *pKeyCode, _Inout_ CSampleImeArray *pItemList); 26 | 27 | VOID CollectWordForWildcard(_In_ CStringRange *psrgKeyCode, _Inout_ CSampleImeArray *pItemList); 28 | 29 | VOID CollectWordFromConvertedStringForWildcard(_In_ CStringRange *pString, _Inout_ CSampleImeArray *pItemList); 30 | }; 31 | -------------------------------------------------------------------------------- /SampleIME/BaseDictionaryEngine.h: -------------------------------------------------------------------------------- 1 | // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF 2 | // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO 3 | // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 4 | // PARTICULAR PURPOSE. 5 | // 6 | // Copyright (c) Microsoft Corporation. All rights reserved 7 | 8 | 9 | #pragma once 10 | 11 | #include "File.h" 12 | #include "SampleIMEBaseStructure.h" 13 | 14 | class CBaseDictionaryEngine 15 | { 16 | public: 17 | CBaseDictionaryEngine(LCID locale, _In_ CFile *pDictionaryFile); 18 | virtual ~CBaseDictionaryEngine(); 19 | 20 | virtual VOID CollectWord(_In_ CStringRange *psrgKeyCode, _Out_ CSampleImeArray *pasrgWordString) 21 | { 22 | psrgKeyCode; 23 | pasrgWordString = nullptr; 24 | } 25 | 26 | virtual VOID CollectWord(_In_ CStringRange *psrgKeyCode, _Out_ CSampleImeArray *pItemList) 27 | { 28 | psrgKeyCode; 29 | pItemList = nullptr; 30 | } 31 | 32 | virtual VOID SortListItemByFindKeyCode(_Inout_ CSampleImeArray *pItemList); 33 | 34 | protected: 35 | CFile* _pDictionaryFile; 36 | LCID _locale; 37 | 38 | private: 39 | VOID MergeSortByFindKeyCode(_Inout_ CSampleImeArray *pItemList, int leftRange, int rightRange); 40 | int CalculateCandidateCount(int leftRange, int rightRange); 41 | }; 42 | -------------------------------------------------------------------------------- /doc/composition/EndComposition.md: -------------------------------------------------------------------------------- 1 | ## 3.37 结束合成 2 | 3 | 结束合成,完成汉字输入。 4 | 5 | 选择候选字词完成汉字输入的函数调用过程,请参考:[附录B](../appendix/选择候选字词.md) 6 | 7 | ## 3.37.1 完成汉字输入 8 | 9 | Interface |Description 10 | -|- 11 | [ITfComposition][1] |终止ITfComposition合成。 12 | 13 | [1]: https://github.com/ChineseInputMethod/Interface/blob/master/TSFmanager/ITfComposition.md 14 | 15 | 当用户按下空格键,选择候选字词,最终CSampleIME::_TerminateComposition()函数会被调用。 16 | 在_TerminateComposition()函数中调用ITfComposition::EndComposition()方法,结束合成。 17 | 18 | ```C++ 19 | void CSampleIME::_TerminateComposition(TfEditCookie ec, _In_ ITfContext *pContext, BOOL isCalledFromDeactivate) 20 | { 21 | isCalledFromDeactivate; 22 | 23 | if (_pComposition != nullptr) 24 | { 25 | // remove the display attribute from the composition range. 26 | _ClearCompositionDisplayAttributes(ec, pContext); 27 | 28 | if (FAILED(_pComposition->EndComposition(ec)))//将该行注释掉,汉字不会输出到文档中 29 | { 30 | // if we fail to EndComposition, then we need to close the reverse reading window. 31 | _DeleteCandidateList(TRUE, pContext); 32 | } 33 | 34 | _pComposition->Release(); 35 | _pComposition = nullptr; 36 | 37 | if (_pContext) 38 | { 39 | _pContext->Release(); 40 | _pContext = nullptr; 41 | } 42 | } 43 | } 44 | ``` 45 | 46 | 将EndComposition.cpp文件的第64行注释掉,汉字就不会被输出到文档中。 47 | 48 | ![完成汉字输入](完成汉字输入.png) -------------------------------------------------------------------------------- /SampleIME/GetTextExtentEditSession.h: -------------------------------------------------------------------------------- 1 | // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF 2 | // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO 3 | // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 4 | // PARTICULAR PURPOSE. 5 | // 6 | // Copyright (c) Microsoft Corporation. All rights reserved 7 | 8 | #pragma once 9 | 10 | #include "EditSession.h" 11 | 12 | class CSampleIME; 13 | class CTfTextLayoutSink; 14 | 15 | ////////////////////////////////////////////////////////////////////// 16 | // 17 | // ITfEditSession 18 | // CEditSessionBase 19 | // CGetTextExtentEditSession class 20 | // 21 | ////////////////////////////////////////////////////////////////////// 22 | 23 | //+--------------------------------------------------------------------------- 24 | // 25 | // CGetTextExtentEditSession 26 | // 27 | //---------------------------------------------------------------------------- 28 | 29 | class CGetTextExtentEditSession : public CEditSessionBase 30 | { 31 | public: 32 | CGetTextExtentEditSession(_In_ CSampleIME *pTextService, _In_ ITfContext *pContext, _In_ ITfContextView *pContextView, _In_ ITfRange *pRangeComposition, _In_ CTfTextLayoutSink *pTextLayoutSink); 33 | 34 | // ITfEditSession 35 | STDMETHODIMP DoEditSession(TfEditCookie ec); 36 | 37 | private: 38 | ITfContextView* _pContextView; 39 | ITfRange* _pRangeComposition; 40 | CTfTextLayoutSink* _pTfTextLayoutSink; 41 | }; 42 | -------------------------------------------------------------------------------- /doc/FileMapping.md: -------------------------------------------------------------------------------- 1 | ## 3.17 文件映射 2 | 3 | 文件映射,将码表文件创建为文件映射对象。 4 | 5 | ## 3.17.1 创建文件映射对象 6 | 7 | 当前工程的码表文件是UTF-16 LE格式。 8 | 所以Unicode编码的字符,在码表文件中存储为低端在前。 9 | 例如编码为4E00的汉字“一”,在码表文件中存储为004E。 10 | 11 | ```C++ 12 | BOOL CFileMapping::SetupReadBuffer() 13 | { 14 | if (_fileSize > sizeof(WCHAR)) 15 | { 16 | // 17 | // Read file in file mapping 18 | //文件映射对象 19 | _fileMappingHandle = CreateFileMapping(_fileHandle, NULL, PAGE_READONLY, 0, 0, NULL); 20 | if (_fileMappingHandle) 21 | { 22 | _pMapBuffer = (const WCHAR *)MapViewOfFile(_fileMappingHandle, FILE_MAP_READ, 0, 0, 0); 23 | if (_pMapBuffer) 24 | { 25 | if (IsTextUnicode(_pMapBuffer, (int)_fileSize, NULL)) 26 | { 27 | _pReadBuffer = (WCHAR*)_pMapBuffer; 28 | 29 | // skip Unicode byte order mark 30 | if (*((WCHAR*)_pMapBuffer) == Global::UnicodeByteOrderMark) 31 | { 32 | _pReadBuffer++; 33 | _fileSize--; 34 | } 35 | return TRUE; 36 | } 37 | 38 | UnmapViewOfFile(_pReadBuffer); 39 | _pReadBuffer = nullptr; 40 | } 41 | 42 | CloseHandle(_fileMappingHandle); 43 | _fileMappingHandle = nullptr; 44 | } 45 | } 46 | 47 | return FALSE; 48 | } 49 | ``` -------------------------------------------------------------------------------- /SampleIME/FileMapping.h: -------------------------------------------------------------------------------- 1 | // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF 2 | // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO 3 | // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 4 | // PARTICULAR PURPOSE. 5 | // 6 | // Copyright (c) Microsoft Corporation. All rights reserved 7 | 8 | #pragma once 9 | 10 | #include "File.h" 11 | 12 | class CFileMapping : public CFile 13 | { 14 | public: 15 | CFileMapping(); 16 | virtual ~CFileMapping(); 17 | 18 | BOOL CreateFile(_In_ PCWSTR pFileName, DWORD desiredAccess, DWORD creationDisposition, 19 | DWORD sharedMode = 0, _Inout_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes = nullptr, DWORD flagsAndAttributes = 0, _Inout_opt_ HANDLE templateFileHandle = nullptr) 20 | 21 | { 22 | return CFile::CreateFile(pFileName, desiredAccess, creationDisposition, 23 | sharedMode, lpSecurityAttributes, flagsAndAttributes, templateFileHandle); 24 | } 25 | 26 | BOOL IsEndOfFile() 27 | { 28 | return CFile::IsEndOfFile(); 29 | } 30 | VOID NextLine() 31 | { 32 | CFile::NextLine(); 33 | } 34 | 35 | const WCHAR *GetReadBufferPointer() { return CFile::GetReadBufferPointer(); } 36 | DWORD_PTR GetFileSize() { return CFile::GetFileSize(); } 37 | 38 | LPCWSTR GetFileName() { return CFile::GetFileName(); } 39 | 40 | protected: 41 | BOOL SetupReadBuffer(); 42 | 43 | private: 44 | HANDLE _fileMappingHandle; // file handle for CreateFileMapping 45 | const VOID *_pMapBuffer; // read buffer memory. 46 | }; 47 | -------------------------------------------------------------------------------- /SampleIME/DisplayString.h: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////// 2 | // 3 | // Copyright (C) Microsoft Corporation. All rights reserved. 4 | // 5 | // DisplayString.h 6 | // 7 | // CDisplayString declaration. 8 | // 9 | ////////////////////////////////////////////////////////////////////// 10 | 11 | #pragma once 12 | 13 | #include "StringRange.h" 14 | #include "PointerArray.h" 15 | 16 | class CDisplayString 17 | { 18 | public: 19 | CDisplayString() { } 20 | ~CDisplayString() { } 21 | 22 | int Count() { } 23 | // VOID SetDisplayString(int iIndex, WCHAR* pchText, USHORT cchMax, TF_DISPLAYATTRIBUTE tfDisplayAttribute) { } 24 | // VOID GetDisplayString(int iIndex, WCHAR* pchText, USHORT cchMax, USHORT* pch, TF_DISPLAYATTRIBUTE tfDisplayAttribute) { } 25 | VOID SetLogicalFont(LOGFONTW LogFont) { } 26 | VOID GetLogicalFont(LOGFONTW* pLogFont) { } 27 | 28 | private: 29 | //typedef struct _DISPLAY_STRING { 30 | // CStringRange _StringRange; // Unicode string. 31 | // // Length and MaximumLength is in character count. 32 | // // Buffer doesn't add zero terminate. 33 | // TF_DISPLAYATTRIBUTE _tfDisplayAttribute; // Display attribute for each array. 34 | //} DISPLAY_STRING; 35 | 36 | // 37 | // Array of DISPLAY_STRING 38 | // 39 | //CPointerArray _pDisplayString; 40 | 41 | // Logical font 42 | LOGFONTW _logfont; 43 | }; 44 | -------------------------------------------------------------------------------- /SampleIME/TipCandidateList.h: -------------------------------------------------------------------------------- 1 | // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF 2 | // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO 3 | // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 4 | // PARTICULAR PURPOSE. 5 | // 6 | // Copyright (c) Microsoft Corporation. All rights reserved 7 | 8 | #pragma once 9 | 10 | #include "SampleIMEBaseStructure.h" 11 | 12 | // 13 | // CTipCandidateList 14 | // 15 | 16 | class CTipCandidateList : public ITfCandidateList 17 | { 18 | protected: 19 | CTipCandidateList(size_t candStrReserveSize); 20 | virtual ~CTipCandidateList(); 21 | 22 | public: 23 | static HRESULT CreateInstance(_Outptr_ ITfCandidateList **ppobj, size_t candStrReserveSize = 0); 24 | static HRESULT CreateInstance(REFIID riid, _Outptr_ void **ppvObj, size_t candStrReserveSize = 0); 25 | 26 | // IUnknown methods 27 | virtual STDMETHODIMP QueryInterface(REFIID riid, _Outptr_ void **ppvObj); 28 | virtual STDMETHODIMP_(ULONG) AddRef(); 29 | virtual STDMETHODIMP_(ULONG) Release(); 30 | 31 | // ITfCandidateList methods 32 | virtual STDMETHODIMP EnumCandidates(_Outptr_ IEnumTfCandidates **ppEnum); 33 | virtual STDMETHODIMP GetCandidate(ULONG nIndex, _Outptr_result_maybenull_ ITfCandidateString **ppCandStr); 34 | virtual STDMETHODIMP GetCandidateNum(_Out_ ULONG *pnCnt); 35 | virtual STDMETHODIMP SetResult(ULONG nIndex, TfCandidateResult imcr); 36 | 37 | virtual STDMETHODIMP SetCandidate(_In_ ITfCandidateString **ppCandStr); 38 | 39 | protected: 40 | long _refCount; 41 | CSampleImeArray _tfCandStrList; 42 | }; 43 | 44 | -------------------------------------------------------------------------------- /SampleIME/TfTextLayoutSink.h: -------------------------------------------------------------------------------- 1 | // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF 2 | // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO 3 | // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 4 | // PARTICULAR PURPOSE. 5 | // 6 | // Copyright (c) Microsoft Corporation. All rights reserved 7 | 8 | #pragma once 9 | 10 | class CSampleIME; 11 | 12 | class CTfTextLayoutSink : public ITfTextLayoutSink 13 | { 14 | public: 15 | CTfTextLayoutSink(_In_ CSampleIME *pTextService); 16 | virtual ~CTfTextLayoutSink(); 17 | 18 | // IUnknown methods 19 | STDMETHODIMP QueryInterface(REFIID riid, _Outptr_ void **ppvObj); 20 | STDMETHODIMP_(ULONG) AddRef(void); 21 | STDMETHODIMP_(ULONG) Release(void); 22 | 23 | // ITfTextLayoutSink 24 | STDMETHODIMP OnLayoutChange(_In_ ITfContext *pContext, TfLayoutCode lcode, _In_ ITfContextView *pContextView); 25 | 26 | HRESULT _StartLayout(_In_ ITfContext *pContextDocument, TfEditCookie ec, _In_ ITfRange *pRangeComposition); 27 | VOID _EndLayout(); 28 | 29 | HRESULT _GetTextExt(_Out_ RECT *lpRect); 30 | ITfContext* _GetContextDocument() { return _pContextDocument; }; 31 | 32 | virtual VOID _LayoutChangeNotification(_In_ RECT *lpRect) = 0; 33 | virtual VOID _LayoutDestroyNotification() = 0; 34 | 35 | private: 36 | HRESULT _AdviseTextLayoutSink(); 37 | HRESULT _UnadviseTextLayoutSink(); 38 | 39 | private: 40 | ITfRange* _pRangeComposition; 41 | ITfContext* _pContextDocument; 42 | TfEditCookie _tfEditCookie; 43 | CSampleIME* _pTextService; 44 | DWORD _dwCookieTextLayoutSink; 45 | LONG _refCount; 46 | }; 47 | -------------------------------------------------------------------------------- /doc/DisplayAttributeProvider.md: -------------------------------------------------------------------------------- 1 | ## 3.39 显示属性提供者 2 | 3 | 显示属性提供者,由TSF管理器用来枚举和获取单个显示属性信息对象。 4 | 5 | 如果输入法调用了ITfProperty::SetValue()方法,那么TSF管理器会向输入法查询ITfDisplayAttributeProvider接口。 6 | 7 | ## 3.39.1 提供显示属性 8 | 9 | Interface |Description 10 | -|- 11 | [ITfDisplayAttributeProvider][1] |显示属性提供者,由TSF管理器用来枚举和获取单个显示属性信息对象。 12 | 13 | [1]: https://github.com/ChineseInputMethod/Interface/blob/master/TextService/ITfDisplayAttributeProvider.md 14 | 15 | 如果输入法调用了ITfProperty::SetValue()方法,那么TSF管理器会调用ITfDisplayAttributeProvider::GetDisplayAttributeInfo()方法。 16 | 17 | 在ITfDisplayAttributeProvider::GetDisplayAttributeInfo()方法中,输入法向TSF管理器提供ITfDisplayAttributeInfo显示属性信息组件对象。 18 | 19 | ```C++ 20 | STDAPI CSampleIME::GetDisplayAttributeInfo(__RPC__in REFGUID guidInfo, __RPC__deref_out_opt ITfDisplayAttributeInfo **ppInfo) 21 | { 22 | if (ppInfo == nullptr) 23 | { 24 | return E_INVALIDARG; 25 | } 26 | 27 | *ppInfo = nullptr; 28 | 29 | // Which display attribute GUID? 30 | if (IsEqualGUID(guidInfo, Global::SampleIMEGuidDisplayAttributeInput)) 31 | { 32 | *ppInfo = new (std::nothrow) CDisplayAttributeInfoInput(); 33 | if ((*ppInfo) == nullptr) 34 | { 35 | return E_OUTOFMEMORY; 36 | } 37 | } 38 | else if (IsEqualGUID(guidInfo, Global::SampleIMEGuidDisplayAttributeConverted)) 39 | { 40 | *ppInfo = new (std::nothrow) CDisplayAttributeInfoConverted(); 41 | if ((*ppInfo) == nullptr) 42 | { 43 | return E_OUTOFMEMORY; 44 | } 45 | } 46 | else 47 | { 48 | return E_INVALIDARG; 49 | } 50 | 51 | 52 | return S_OK; 53 | } 54 | ``` -------------------------------------------------------------------------------- /SampleIME/EnumTfCandidates.h: -------------------------------------------------------------------------------- 1 | // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF 2 | // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO 3 | // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 4 | // PARTICULAR PURPOSE. 5 | // 6 | // Copyright (c) Microsoft Corporation. All rights reserved 7 | 8 | #pragma once 9 | 10 | #include "Globals.h" 11 | 12 | class CEnumTfCandidates : public IEnumTfCandidates 13 | { 14 | protected: 15 | // constructor/destructor 16 | CEnumTfCandidates(_In_ const CSampleImeArray &rgelm, UINT currentNum); 17 | 18 | virtual ~CEnumTfCandidates(void); 19 | 20 | public: 21 | // create instance 22 | static HRESULT CreateInstance(_Out_ CEnumTfCandidates **ppobj, _In_ const CSampleImeArray &rgelm, UINT currentNum = 0); 23 | static HRESULT CreateInstance(REFIID riid, _Out_ void **ppvObj, _In_ const CSampleImeArray &rgelm, UINT currentNum = 0); 24 | 25 | // IUnknown methods 26 | virtual STDMETHODIMP QueryInterface(REFIID riid, _Outptr_ void **ppvObj); 27 | virtual STDMETHODIMP_(ULONG) AddRef(void); 28 | virtual STDMETHODIMP_(ULONG) Release(void); 29 | 30 | // IEnumTfCandidates methods 31 | virtual STDMETHODIMP Next(ULONG ulCount, _Out_ ITfCandidateString **ppObj, _Out_ ULONG *pcFetched); 32 | virtual STDMETHODIMP Skip(ULONG ulCount); 33 | virtual STDMETHODIMP Reset(); 34 | virtual STDMETHODIMP Clone(_Out_ IEnumTfCandidates **ppEnum); 35 | 36 | protected: 37 | LONG _refCount; 38 | CSampleImeArray _rgelm; 39 | UINT _currentCandidateStrIndex; 40 | }; 41 | -------------------------------------------------------------------------------- /doc/Globals.md: -------------------------------------------------------------------------------- 1 | ## 3.2 全局设置 2 | 3 | 想学习牛爵爷躲在乡下发明个万有引力,发现这招真管用。有一天我可以骄傲的说,我不过是把排队等着让人捅嗓子眼儿的时间,用在了学习上而已。 4 | 也想学习求伯君吃泡面开发wps,发现这个绝对是假的。这几年我最大的问题就是始终解决不了吃饭的问题。 5 | 6 | 今天,胃口又难受了,只好换了家餐馆。我可以自己做饭,但是如果自己做饭的话,那一天的时间,都花在做饭吃饭上了,根本没有时间开发。 7 | 8 | 院子里长满了草,再过几天房东又要来收房租了,看见满院子杂草,又该不高兴了。必须在房东来之前,把草拔干净。 9 | 问题在于,草是不断生长的,拔干净了,过几天又要长出来。 10 | 11 | 我想,穷人最大的障碍是,根本就没有提升自己的机会。光是活着就用尽全力了,根本没有富于的时间,去尝试改变。 12 | 13 | 我有一个理论,聪明的和愚蠢的都是少数的,80%的人都是中间的普通人。无论如何提高教育水平,这种分布比例是不会改变的。 14 | 随着社会进步,人类知识水平进化,人变得更聪明,但相对的,依然有10%的人是相对愚蠢的。 15 | 16 | 每个人的天赋特长也是如此,只有少数人是真的天赋高,大多数人的天赋点数都是差不多的。 17 | 区别在于,可能有的人某方面天赋更高一些。 18 | 19 | 假设: 20 | A数学100分,语文60分,英语50分。 21 | B数学60分,语文100分,英语50分。 22 | C数学50分,语文60分,英语100分。 23 | 三个人合作,就会组建成一个数学、语文、英语都是100分的团队。 24 | 25 | 如果三个人分别去高考,那么就会被淘汰。 26 | 27 | D数学70分,语文70分,英语70分。 28 | E数学80分,语文80分,英语80分。 29 | 同样D会被B所淘汰。 30 | 31 | 最终,大家会在每门学科的90分到100分之间内卷。 32 | 而且,这10分比拼的并不是天赋点,而是回字有多少种写法。 33 | 34 | 结果就是某科天赋100+的被卷出局。 35 | 更可悲的是,还有第四门功课,殿试。 36 | 37 | 大概是去年买了一套《魔鬼经济学》,第一册都没读完。昨天看了李永乐介绍的《贫穷的本质》,感觉就是我想要表达的。 38 | 以金钱举例最好理解。
39 | 富人赚的钱=穷人花的钱
40 | 在每一笔交易中,富人都要从穷人身上赚取利润。
41 | 不断循环的结果是,财富集中到富人手中,而穷人失去所有。 42 | 43 | 过去的解决方案是战争,杀掉一些人,重新洗牌。
44 | 现在的解决方案是温和的通货膨胀,给穷人发钱。 45 | 46 | 但是,这种解决方案,只不过是又一次重启交易而已。
47 | 慢慢的,穷人手里的财富还是不断的流向富人。
48 | 直到,下一次重启交易。 49 | 50 | 把这里的金钱,可以替换为生命,时间。
51 | 实际上富人赚取的不是金钱,而是穷人的劳动->时间->生命。 52 | 53 | 我在相当长的时间里,是认为自己不够努力,太贪玩了,没有百分百的付出努力。 54 | 55 | 写给未来的自己: 56 | 57 | 你尽力了,因为你试过了每一种可能。 58 | 59 | ## 3.2.1 主要函数 60 | 61 | ```C++ 62 | BOOL RegisterWindowClass() 63 | { 64 | BaseWindow.cpp#CBaseWindow::_InitWindowClass(CandidateClassName, &AtomCandidateWindow) 65 | BaseWindow.cpp#CBaseWindow::_InitWindowClass(ShadowClassName, &AtomShadowWindow) 66 | BaseWindow.cpp#CBaseWindow::_InitWindowClass(ScrollBarClassName, &AtomScrollBarWindow) 67 | } 68 | ``` -------------------------------------------------------------------------------- /SampleIME/DictionaryParser.h: -------------------------------------------------------------------------------- 1 | // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF 2 | // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO 3 | // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 4 | // PARTICULAR PURPOSE. 5 | // 6 | // Copyright (c) Microsoft Corporation. All rights reserved 7 | 8 | 9 | #pragma once 10 | 11 | #include "Globals.h" 12 | 13 | ////////////////////////////////////////////////////////////////////// 14 | // 15 | 16 | class CParserStringRange : public CStringRange 17 | { 18 | public: 19 | CParserStringRange() : CStringRange() 20 | { 21 | _fEscapeIncluded = FALSE; 22 | } 23 | 24 | BOOL _fEscapeIncluded; // flag. This string range included escape sequence. 25 | }; 26 | 27 | ////////////////////////////////////////////////////////////////////// 28 | // 29 | 30 | class CDictionaryParser 31 | { 32 | public: 33 | CDictionaryParser(LCID locale); 34 | virtual ~CDictionaryParser(); 35 | 36 | BOOL ParseLine(_In_reads_(dwBufLen) LPCWSTR pwszBuffer, DWORD_PTR dwBufLen, _Out_ CParserStringRange *psrgKeyword, _Inout_opt_ CSampleImeArray *pValue = nullptr); 37 | 38 | // dwBufLen - in character count 39 | _Ret_maybenull_ 40 | LPCWSTR GetToken(_In_reads_(dwBufLen) LPCWSTR pwszBuffer, DWORD_PTR dwBufLen, _In_ const WCHAR chDelimiter, _Out_ CParserStringRange *srgKeyWord); 41 | 42 | protected: 43 | BOOL RemoveWhiteSpaceFromBegin(_Inout_opt_ CStringRange *pString); 44 | BOOL RemoveWhiteSpaceFromEnd(_Inout_opt_ CStringRange *pString); 45 | BOOL RemoveStringDelimiter(_Inout_opt_ CStringRange *pString); 46 | 47 | DWORD_PTR GetOneLine(_In_z_ LPCWSTR pwszBuffer, DWORD_PTR dwBufLen); 48 | 49 | LCID _locale; // used for CompareString 50 | }; 51 | -------------------------------------------------------------------------------- /SampleIME/RegKey.h: -------------------------------------------------------------------------------- 1 | // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF 2 | // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO 3 | // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 4 | // PARTICULAR PURPOSE. 5 | // 6 | // Copyright (c) Microsoft Corporation. All rights reserved 7 | 8 | #pragma once 9 | 10 | class CRegKey 11 | { 12 | public: 13 | CRegKey(); 14 | ~CRegKey(); 15 | 16 | HKEY GetHKEY(); 17 | 18 | LONG Create(_In_ HKEY hKeyPresent, _In_ LPCWSTR pwszKeyName, 19 | _In_reads_opt_(255) LPWSTR pwszClass = REG_NONE, 20 | DWORD dwOptions = REG_OPTION_NON_VOLATILE, 21 | REGSAM samDesired = KEY_READ | KEY_WRITE, 22 | _Inout_ LPSECURITY_ATTRIBUTES lpSecAttr = nullptr, 23 | _Out_opt_ LPDWORD lpdwDisposition = nullptr); 24 | 25 | LONG Open(_In_ HKEY hKeyParent, _In_ LPCWSTR pwszKeyName, 26 | REGSAM samDesired = KEY_READ | KEY_WRITE); 27 | 28 | LONG Close(); 29 | 30 | LONG DeleteSubKey(_In_ LPCWSTR pwszSubKey); 31 | LONG RecurseDeleteKey(_In_ LPCWSTR pwszSubKey); 32 | LONG DeleteValue(_In_ LPCWSTR pwszValue); 33 | 34 | LONG QueryStringValue(_In_opt_ LPCWSTR pwszValueName, _Out_writes_opt_(*pnChars) LPWSTR pwszValue, _Inout_ ULONG *pnChars); 35 | LONG SetStringValue(_In_opt_ LPCWSTR pwszValueName, _In_ LPCWSTR pwszValue, DWORD dwType = REG_SZ); 36 | 37 | LONG QueryDWORDValue(_In_opt_ LPCWSTR pwszValueName, _Out_ DWORD &dwValue); 38 | LONG SetDWORDValue(_In_opt_ LPCWSTR pwszValueName, DWORD dwValue); 39 | 40 | LONG QueryBinaryValue(_In_opt_ LPCWSTR pwszValueName, _Out_writes_opt_(cbData) BYTE* lpData, DWORD cbData); 41 | LONG SetBinaryValue(_In_opt_ LPCWSTR pwszValueName, _In_reads_(cbData) BYTE* lpData, DWORD cbData); 42 | 43 | private: 44 | HKEY _keyHandle; 45 | }; 46 | -------------------------------------------------------------------------------- /SampleIME/Compartment.h: -------------------------------------------------------------------------------- 1 | // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF 2 | // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO 3 | // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 4 | // PARTICULAR PURPOSE. 5 | // 6 | // Copyright (c) Microsoft Corporation. All rights reserved 7 | 8 | #pragma once 9 | 10 | class CCompartment 11 | { 12 | public: 13 | CCompartment(_In_ IUnknown *punk, TfClientId tfClientId, _In_ REFGUID guidCompartment); 14 | ~CCompartment(); 15 | 16 | HRESULT _GetCompartment(_Outptr_ ITfCompartment **ppCompartment); 17 | HRESULT _GetCompartmentBOOL(_Out_ BOOL &flag); 18 | HRESULT _SetCompartmentBOOL(_In_ BOOL flag); 19 | HRESULT _GetCompartmentDWORD(_Out_ DWORD &dw); 20 | HRESULT _SetCompartmentDWORD(_In_ DWORD dw); 21 | HRESULT _ClearCompartment(); 22 | 23 | VOID _GetGUID(GUID *pguid) 24 | { 25 | *pguid = _guidCompartment; 26 | } 27 | 28 | private: 29 | GUID _guidCompartment; 30 | IUnknown* _punk; 31 | TfClientId _tfClientId; 32 | }; 33 | 34 | typedef HRESULT (*CESCALLBACK)(void *pv, REFGUID guidCompartment); 35 | 36 | class CCompartmentEventSink : public ITfCompartmentEventSink 37 | { 38 | public: 39 | CCompartmentEventSink(_In_ CESCALLBACK pfnCallback, _In_ void *pv); 40 | ~CCompartmentEventSink(); 41 | 42 | // IUnknown 43 | STDMETHODIMP QueryInterface(REFIID riid, _Outptr_ void **ppvObj); 44 | STDMETHODIMP_(ULONG) AddRef(void); 45 | STDMETHODIMP_(ULONG) Release(void); 46 | 47 | // ITfCompartmentEventSink 48 | STDMETHODIMP OnChange(_In_ REFGUID guid); 49 | 50 | // function 51 | HRESULT _Advise(_In_ IUnknown *punk, _In_ REFGUID guidCompartment); 52 | HRESULT _Unadvise(); 53 | 54 | private: 55 | ITfCompartment *_pCompartment; 56 | DWORD _dwCookie; 57 | CESCALLBACK _pfnCallback; 58 | void *_pv; 59 | 60 | LONG _refCount; 61 | }; 62 | -------------------------------------------------------------------------------- /doc/FunctionProviderSink.md: -------------------------------------------------------------------------------- 1 | ## 3.11 扩展功能提供者 2 | 3 | 扩展功能提供者,提供各种函数对象。 4 | 5 | ## 3.11.1 安装扩展功能提供者 6 | 7 | Interface |Description 8 | -|- 9 | [ITfSourceSingle][1] |单一事件安装器,与ITfSource的不同之处在于,ITfSourceSingle安装的事件接收器一次仅支持一个事件接收器。 10 | 11 | [1]: https://github.com/ChineseInputMethod/Interface/blob/master/TSFmanager/ITfSourceSingle.md 12 | 13 | ```C++ 14 | if (SUCCEEDED(_pThreadMgr->QueryInterface(IID_ITfSourceSingle, (void **)&pSourceSingle))) 15 | { 16 | IUnknown* punk = nullptr; 17 | if (SUCCEEDED(QueryInterface(IID_IUnknown, (void **)&punk))) 18 | { 19 | if (SUCCEEDED(pSourceSingle->AdviseSingleSink(_tfClientId, IID_ITfFunctionProvider, punk))) 20 | { 21 | ... 22 | } 23 | punk->Release(); 24 | } 25 | pSourceSingle->Release(); 26 | } 27 | ``` 28 | 29 | ## 3.11.2 获取指定的函数对象 30 | 31 | 安装扩展功能提供者后,TSF管理器会调用CSampleIME::GetFunction()方法,获取指定的函数对象。 32 | 33 | Interface |Description 34 | -|- 35 | [ITfFunctionProvider][1] |扩展功能提供者,提供各种函数对象。 36 | 37 | [1]: https://github.com/ChineseInputMethod/Interface/blob/master/TextService/ITfThreadFocusSink.md 38 | 39 | 当前工程实现为:提供搜索集成服务。 40 | 41 | ```C++ 42 | HRESULT CSampleIME::GetFunction(__RPC__in REFGUID rguid, __RPC__in REFIID riid, __RPC__deref_out_opt IUnknown **ppunk) 43 | { 44 | HRESULT hr = E_NOINTERFACE; 45 | 46 | if ((IsEqualGUID(rguid, GUID_NULL)) 47 | && (IsEqualGUID(riid, __uuidof(ITfFnSearchCandidateProvider)))) 48 | { 49 | hr = _pITfFnSearchCandidateProvider->QueryInterface(riid, (void**)ppunk); 50 | } 51 | else if (IsEqualGUID(rguid, GUID_NULL)) 52 | { 53 | hr = QueryInterface(riid, (void **)ppunk); 54 | } 55 | 56 | return hr; 57 | } 58 | ``` 59 | 60 | ## 3.11.3 搜索集成 61 | 62 | >https://learn.microsoft.com/zh-cn/windows/apps/design/input/input-method-editor-requirements#ime-search-integration 63 | 64 | 在Windows 8中安装激活本工程,在磁贴页面点击搜索,即可观测上面链接描述的搜索集成。 -------------------------------------------------------------------------------- /SampleIME/SearchCandidateProvider.h: -------------------------------------------------------------------------------- 1 | // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF 2 | // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO 3 | // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 4 | // PARTICULAR PURPOSE. 5 | // 6 | // Copyright (c) Microsoft Corporation. All rights reserved 7 | 8 | #pragma once 9 | 10 | // 11 | // CSearchCandidateProvider 12 | // 13 | // TfFnSearchCandidateProvider is used for search integration feature, CSearchCandidateProvider 14 | // implements this interface that can be called by search integration, and offer the candidate list 15 | // as search integration suggestion. 16 | // 17 | #define FAKECANDIDATENUMBER (16) 18 | class CSearchCandidateProvider : public ITfFnSearchCandidateProvider 19 | { 20 | protected: 21 | // constructor/destructor 22 | CSearchCandidateProvider(_In_ ITfTextInputProcessorEx *ptip); 23 | virtual ~CSearchCandidateProvider(); 24 | 25 | public: 26 | // create instance 27 | static HRESULT CreateInstance(_Outptr_ ITfFnSearchCandidateProvider **ppobj, _In_ ITfTextInputProcessorEx *ptip); 28 | static HRESULT CreateInstance(REFIID riid, _Outptr_ void **ppvObj, _In_ ITfTextInputProcessorEx *ptip); 29 | 30 | // IUnknown methods 31 | STDMETHODIMP QueryInterface(REFIID riid, _Outptr_ void **ppvObj); 32 | STDMETHODIMP_(ULONG) AddRef(void); 33 | STDMETHODIMP_(ULONG) Release(void); 34 | 35 | // ITfFunction methods 36 | STDMETHODIMP GetDisplayName(_Out_ BSTR *pbstrName); 37 | 38 | // ITfFnSearchCandidateProvider methods 39 | // 40 | // GetSearchCandidates is responsible for supporting the candidates to caller, search integration 41 | // SetResult is not used 42 | // 43 | STDMETHODIMP GetSearchCandidates(BSTR bstrQuery, BSTR bstrApplicationID, _Outptr_result_maybenull_ ITfCandidateList **pplist); 44 | STDMETHODIMP SetResult(BSTR bstrQuery, BSTR bstrApplicationID, BSTR bstrResult); 45 | 46 | private: 47 | LONG _refCount; 48 | ITfTextInputProcessorEx* _pTip; 49 | }; 50 | 51 | -------------------------------------------------------------------------------- /doc/appendix/完成汉字输入.md: -------------------------------------------------------------------------------- 1 | ## B.3 完成汉字输入 2 | 3 | 加载2023年10月22日版本的track.xml断点文件,调试时会包含如下跟踪信息。该信息包含了将汉字输出到文档中的主要函数调用过程。 4 | 5 | Function |Description 6 | -|- 7 | CSampleIME::_TerminateComposition(unsigned long, ITfContext *, int) |完成汉字输入 8 | CSampleIME::_ClearCompositionDisplayAttributes(unsigned long, ITfContext *) |清除显示属性 9 | 10 | Function |Description 11 | -|- 12 | CSampleIME::OnEndEdit(ITfContext *, unsigned long, ITfEditRecord *) |其他过程 13 | CSampleIME::_IsComposing(void) 0x0000000000000000 | 14 | */ | 15 | CSampleIME::OnKeyDown(ITfContext *, unsigned __int64, __int64, int *) end | 16 | /* | 17 | CSampleIME::OnKeyUp(ITfContext *, unsigned __int64, __int64, int *) | 18 | Global::UpdateModifiers(unsigned __int64, __int64) | 19 | CSampleIME::_IsKeyEaten(ITfContext *, unsigned int, unsigned int *, wchar_t *, _KEYSTROKE_STATE *) | 20 | CSampleIME::_IsKeyboardDisabled(void) | 21 | CSampleIME::ConvertVKey(unsigned int) | 22 | VKeyFromVKPacketAndWchar(unsigned int, wchar_t) | 23 | CSampleIME::_IsComposing(void) 0x0000000000000000 | 24 | CCompositionProcessorEngine::IsVirtualKeyNeed(unsigned int, wchar_t *, int, CANDIDATE_MODE, int, _KEYSTROKE_STATE *) | 25 | CCompositionProcessorEngine::IsVirtualKeyKeystrokeComposition(unsigned int, _KEYSTROKE_STATE *, KEYSTROKE_FUNCTION) | 26 | CCompositionProcessorEngine::IsVirtualKeyKeystrokeComposition(unsigned int, _KEYSTROKE_STATE *, KEYSTROKE_FUNCTION) | 27 | CCompositionProcessorEngine::IsKeystrokeRange(unsigned int, _KEYSTROKE_STATE *, CANDIDATE_MODE) | 28 | CCompositionProcessorEngine::IsVirtualKeyKeystrokeComposition(unsigned int, _KEYSTROKE_STATE *, KEYSTROKE_FUNCTION) | 29 | CCompositionProcessorEngine::IsPunctuation(wchar_t) | 30 | CSampleIME::OnKeyUp(ITfContext *, unsigned __int64, __int64, int *) end | 31 | */ | -------------------------------------------------------------------------------- /doc/uiless/uiless.md: -------------------------------------------------------------------------------- 1 | ## 3.41 无界面模式 2 | 3 | 无界面模式,由应用程序显示UI元素。 4 | 5 | ## 3.41.1 调试 6 | 7 | 该仓库下:https://github.com/yangyuan/meow 8 | 9 | 有个https://github.com/yangyuan/meow/tree/master/src/meow-uiless工程,可以用于调试输入法。 10 | 11 | 我在https://github.com/ChineseInputMethod/SampleIME/tree/master/doc/uiless目录下放置了一个编译好的该工程。 12 | 13 | 如图设置,即可调试无界面模式。 14 | 15 | ![DEBUG](debug.png) 16 | 17 | ## 3.41.2 启动和按键处理 18 | 19 | 无界面模式的启动过程和按键处理函数调用,请参考:[启动过程](启动过程.md) [按键处理](按键处理.md) 20 | 21 | 启动过程和按键处理与普通模式几乎完全相同,唯一不同的地方是,在ITfTextInputProcessorEx::ActivateEx()方法中,dwFlags的状态值不同。 22 | 23 | ## 3.41.3 候选列表 24 | 25 | 无界面模式的候选列表函数调用过程,请参考:[候选列表](候选列表.md) 26 | 27 | 在CCandidateListUIPresenter::BeginUIElement()函数中,输入法调用ITfUIElementMgr::BeginUIElement()方法,向应用程序查询是否显示候选窗口。 28 | 29 | 当应用程序自己显示候选窗口时,CCandidateListUIPresenter::_UpdateUIElement()函数将被调用。 30 | 31 | 在CCandidateListUIPresenter::_UpdateUIElement()函数中,调用ITfUIElementMgr::UpdateUIElement()方法,通知应用程序更新UI元素。 32 | 33 | Interface |Description 34 | -|- 35 | [ITfUIElementMgr][1] |UI元素管理器,文本服务调用ITfUIElementMgr,操作应用程序UI元素。 36 | 37 | [1]: https://github.com/ChineseInputMethod/Interface/blob/master/TSFmanager/ITfUIElementMgr.md 38 | 39 | ```C++ 40 | HRESULT CCandidateListUIPresenter::_UpdateUIElement() 41 | { 42 | HRESULT hr = S_OK; 43 | 44 | ITfThreadMgr* pThreadMgr = _pTextService->_GetThreadMgr(); 45 | if (nullptr == pThreadMgr) 46 | { 47 | return S_OK; 48 | } 49 | 50 | ITfUIElementMgr* pUIElementMgr = nullptr; 51 | 52 | hr = pThreadMgr->QueryInterface(IID_ITfUIElementMgr, (void **)&pUIElementMgr); 53 | if (hr == S_OK) 54 | { 55 | pUIElementMgr->UpdateUIElement(_uiElementId); 56 | pUIElementMgr->Release(); 57 | } 58 | 59 | return S_OK; 60 | } 61 | ``` 62 | 63 | 随后,应用程序调用输入法ITfCandidateListUIElement候选列表UI元素接口。 64 | 65 | Interface |Description 66 | -|- 67 | [ITfCandidateListUIElement][2] |候选列表UI元素,向应用程序提供UI元素。 68 | 69 | [2]: https://github.com/ChineseInputMethod/Interface/blob/master/TSFmanager/ITfUIElementMgr.md 70 | 71 | 当完成汉字输入后,输入法调用ITfUIElementMgr::EndUIElement()方法,通知应用程序隐藏UI元素。 -------------------------------------------------------------------------------- /doc/BaseWindow.md: -------------------------------------------------------------------------------- 1 | ## 3.31 窗口基类 2 | 3 | 窗口基类,为派生类调用API函数。
4 | 候选窗口类均派生自窗口基类,窗口基类为子窗口类提供辅助函数。 5 | 6 | ## 3.31.1 创建窗口 7 | 8 | 在CBaseWindow::_Create()函数中,窗口基类为子窗口类调用API函数,创建窗口。 9 | 10 | ```C++ 11 | BOOL CBaseWindow::_Create(ATOM atom, DWORD dwExStyle, DWORD dwStyle, _In_opt_ CBaseWindow *pParentWnd, int wndWidth, int wndHeight, _In_opt_ HWND parentWndHandle) 12 | { 13 | _pParentWnd = pParentWnd;//候选窗口此参数为NULL,子窗口此参数为候选窗口。也就是说,候选窗口的父窗口为文档窗口,子窗口的所有者窗口为候选窗口 14 | 15 | if (atom != 0) 16 | { 17 | // create real window 18 | 19 | _wndHandle = CreateWindowEx(dwExStyle,//扩展窗口样式 20 | (LPCTSTR)atom,//类原子 21 | NULL,//窗口名称 22 | dwStyle,//窗口样式 23 | 0, 0,//窗口位置 24 | wndWidth, wndHeight,//窗口尺寸 25 | _pParentWnd ? _pParentWnd->_GetWnd() : parentWndHandle, // parentWndHandle 26 | NULL,//菜单句柄 27 | Global::dllInstanceHandle,//实例句柄 28 | this); // lpParam 29 | 30 | if (!_wndHandle) 31 | { 32 | return FALSE; 33 | } 34 | } 35 | 36 | return TRUE; 37 | } 38 | ``` 39 | 40 | >注意类的继承关系,和窗口的继承关系。 41 | 42 | ## 3.31.2 窗口过程 43 | 44 | 窗口基类,将窗口消息分发给其派生类的窗口过程。 45 | 46 | ```C++ 47 | LRESULT CALLBACK CBaseWindow::_WindowProc(_In_ HWND wndHandle, UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam) 48 | { 49 | if (uMsg == WM_CREATE) 50 | { 51 | _SetThis(wndHandle, ((CREATESTRUCT*)lParam)->lpCreateParams); 52 | } 53 | 54 | CBaseWindow* pv = _GetThis(wndHandle); 55 | if (!pv) 56 | { 57 | return DefWindowProc(wndHandle, uMsg, wParam, lParam); 58 | } 59 | 60 | if (uMsg == WM_TIMER) 61 | { 62 | switch (wParam) 63 | { 64 | case idTimer_UIObject: 65 | if (pv->_GetTimerObject() != nullptr) 66 | { 67 | pv->_GetTimerObject()->_OnTimer(); 68 | } 69 | break; 70 | } 71 | return 0; 72 | } 73 | else 74 | { 75 | return pv->_WindowProcCallback(wndHandle, uMsg, wParam, lParam); 76 | } 77 | } 78 | ``` -------------------------------------------------------------------------------- /SampleIME/File.h: -------------------------------------------------------------------------------- 1 | // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF 2 | // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO 3 | // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 4 | // PARTICULAR PURPOSE. 5 | // 6 | // Copyright (c) Microsoft Corporation. All rights reserved 7 | 8 | #pragma once 9 | 10 | class CFile 11 | { 12 | public: 13 | CFile(UINT codePage = CP_ACP); 14 | virtual ~CFile(); 15 | 16 | BOOL CreateFile(_In_ PCWSTR pFileName, DWORD desiredAccess, DWORD creationDisposition, 17 | DWORD sharedMode = 0, _Inout_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes = nullptr, DWORD flagsAndAttributes = 0, _Inout_opt_ HANDLE templateFileHandle = nullptr); 18 | 19 | BOOL IsEndOfFile(); 20 | VOID NextLine(); 21 | 22 | _Ret_maybenull_ 23 | const WCHAR *GetReadBufferPointer() 24 | { 25 | if (!_pReadBuffer) 26 | { 27 | if (!SetupReadBuffer()) 28 | { 29 | return nullptr; 30 | } 31 | } 32 | return _pReadBuffer; 33 | } 34 | 35 | DWORD_PTR GetFileSize() { return _fileSize;} 36 | 37 | LPCWSTR GetFileName() { return _pFileName;} 38 | 39 | protected: 40 | virtual BOOL SetupReadBuffer(); 41 | 42 | protected: 43 | const WCHAR* _pReadBuffer; // read buffer memory. 44 | DWORD_PTR _fileSize; // in byte. 45 | HANDLE _fileHandle; // file handle for CreateFile 46 | 47 | private: 48 | DWORD_PTR GetBufferInWCharLength() 49 | { 50 | if (_filePosPointer == 0 && _fileSize > 0) 51 | { 52 | // skip Unicode byte order mark 53 | GetReadBufferPointer(); 54 | } 55 | return(_fileSize - _filePosPointer) / sizeof(WCHAR); // in char count as a returned length. 56 | } 57 | 58 | const WCHAR *GetBufferInWChar() 59 | { 60 | const WCHAR *pwch = GetReadBufferPointer(); 61 | return(const WCHAR*)((BYTE*)pwch + _filePosPointer); 62 | } 63 | 64 | private: 65 | UINT _codePage; // used for MultiByteToWideChar 66 | DWORD_PTR _filePosPointer; // in byte. Always point start of line. 67 | LPWSTR _pFileName; 68 | }; 69 | -------------------------------------------------------------------------------- /SampleIME/SampleIMEStructureArray.h: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////// 2 | // 3 | // Copyright (C) Microsoft Corporation. All rights reserved. 4 | // 5 | // CSampleIMESampleIMEStructureArray.h 6 | // 7 | // CSampleIMEStructureArray declaration. 8 | // 9 | ////////////////////////////////////////////////////////////////////// 10 | 11 | #pragma once 12 | 13 | #include 14 | 15 | template 16 | class CSampleIMEStructureArray 17 | { 18 | typedef typename std::vector value_type; 19 | typedef const T& CONST_REF; 20 | typedef typename value_type CSampleIMEArray; 21 | typedef typename value_type::iterator CSampleIMEIter; 22 | 23 | public: 24 | CSampleIMEStructureArray(): _imeVector() 25 | { 26 | } 27 | 28 | explicit CSampleIMEStructureArray(size_t iCount): _imeVector(iCount) 29 | { 30 | } 31 | 32 | CSampleIMEStructureArray(size_t iCount, CONST_REF tVal): _imeVector(iCount, tVal) 33 | { 34 | } 35 | 36 | virtual ~CSampleIMEStructureArray() {} 37 | 38 | inline CONST_REF GetAt(size_t iIndex) const 39 | { 40 | assert(iIndex <= _imeVector.size()); 41 | assert(_imeVector.size() > 0); 42 | 43 | return _imeVector[iIndex]; 44 | } 45 | 46 | inline T& GetAt(size_t iIndex) 47 | { 48 | assert(iIndex <= _imeVector.size()); 49 | assert(_imeVector.size() > 0); 50 | 51 | return _imeVector[iIndex]; 52 | } 53 | 54 | void RemoveAt(size_t iIndex, size_t iElements) 55 | { 56 | assert(iIndex <= _imeVector.size()); 57 | assert(_imeVector.size() > 0); 58 | 59 | CSampleIMEIter beginIter = _imeVector.begin() + iIndex; 60 | CSampleIMEIter lastIter = beginIter + iElements - 1; 61 | 62 | _imeVector.erase(beginIter, lastIter); 63 | } 64 | 65 | size_t Count() const { return _imeVector.size(); } 66 | 67 | void Append(const T& tVal) 68 | { 69 | _imeVector.push_back(tVal); 70 | } 71 | 72 | void Clear() 73 | { 74 | _imeVector.clear(); 75 | } 76 | 77 | private: 78 | CSampleIMEArray _imeVector; // the actual array of data 79 | CSampleIMEIter _imeIter; 80 | }; 81 | -------------------------------------------------------------------------------- /SampleIME/Define.h: -------------------------------------------------------------------------------- 1 | // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF 2 | // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO 3 | // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 4 | // PARTICULAR PURPOSE. 5 | // 6 | // Copyright (c) Microsoft Corporation. All rights reserved 7 | 8 | #pragma once 9 | #include "resource.h" 10 | 11 | #define TEXTSERVICE_MODEL L"Apartment" 12 | #define TEXTSERVICE_LANGID MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED) 13 | #define TEXTSERVICE_ICON_INDEX -IDIS_SAMPLEIME 14 | #define TEXTSERVICE_DIC L"SampleIMESimplifiedQuanPin.txt" 15 | 16 | #define IME_MODE_ON_ICON_INDEX IDI_IME_MODE_ON 17 | #define IME_MODE_OFF_ICON_INDEX IDI_IME_MODE_OFF 18 | #define IME_DOUBLE_ON_INDEX IDI_DOUBLE_SINGLE_BYTE_ON 19 | #define IME_DOUBLE_OFF_INDEX IDI_DOUBLE_SINGLE_BYTE_OFF 20 | #define IME_PUNCTUATION_ON_INDEX IDI_PUNCTUATION_ON 21 | #define IME_PUNCTUATION_OFF_INDEX IDI_PUNCTUATION_OFF 22 | 23 | #define SAMPLEIME_FONT_DEFAULT L"Microsoft YaHei UI" 24 | 25 | //--------------------------------------------------------------------- 26 | // defined Candidated Window 27 | //--------------------------------------------------------------------- 28 | #define CANDWND_ROW_WIDTH (30) 29 | #define CANDWND_BORDER_COLOR (RGB(0x00, 0x00, 0x00)) 30 | #define CANDWND_BORDER_WIDTH (2) 31 | #define CANDWND_NUM_COLOR (RGB(0xB4, 0xB4, 0xB4)) 32 | #define CANDWND_SELECTED_ITEM_COLOR (RGB(0xFF, 0xFF, 0xFF)) 33 | #define CANDWND_SELECTED_BK_COLOR (RGB(0xA6, 0xA6, 0x00)) 34 | #define CANDWND_ITEM_COLOR (RGB(0x00, 0x00, 0x00)) 35 | 36 | //--------------------------------------------------------------------- 37 | // defined modifier 38 | //--------------------------------------------------------------------- 39 | #define _TF_MOD_ON_KEYUP_SHIFT_ONLY (0x00010000 | TF_MOD_ON_KEYUP) 40 | #define _TF_MOD_ON_KEYUP_CONTROL_ONLY (0x00020000 | TF_MOD_ON_KEYUP) 41 | #define _TF_MOD_ON_KEYUP_ALT_ONLY (0x00040000 | TF_MOD_ON_KEYUP) 42 | 43 | #define CAND_WIDTH (13) // * tmMaxCharWidth 44 | 45 | //--------------------------------------------------------------------- 46 | // string length of CLSID 47 | //--------------------------------------------------------------------- 48 | #define CLSID_STRLEN (38) // strlen("{xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx}") -------------------------------------------------------------------------------- /doc/TableDictionaryEngine.md: -------------------------------------------------------------------------------- 1 | ## 3.23 词典引擎 2 | 3 | 词典引擎,获取候选列表。 4 | 5 | 我最初一直以为输入法发明人追求低重码率是因为当时硬件条件的限制,现在才意识到,很可能从二十世纪中文打字机的时代,发明人就开始试图将汉字和键盘操作一一映射起来。
6 | 这一点也体现在输入法的专利分类上,由于不能将词典的检索方式申请为专利,所以输入法的专利都申请为了汉字的一种键盘输入方式。 7 | 8 | 这就将输入法具体成为了汉字的编码,然后具体到了键盘键位。发明人想尽了一切办法,把汉字用某种规则排列到键盘上。作者也是其中一员,作者还在以电脑输入法的开发思路,研究手机输入法。 9 | 直到使用了触屏手机很久很久,才知道哪里出了问题。 10 | 11 | 这可能是个语言学课题,那就是拼音文字是否是因为借用他国文字注音,不断抽象化的过程。
12 | 假设太平洋上还有很多国家,这些国家借用日韩的文字,来给自己的语言注音。其中一个国家后来影响力巨大,是否这个国家的文字会变成太平洋中的“拉丁字母”。 13 | 14 | 再假设人类是三头六臂,是否会发展出汉字->西夏文->汉字字母键盘这样的路线。
15 | 其实汉字的部件或部首,就相当于汉字的字母,这不过这种字母比较隐晦庞杂。 16 | 17 | 我看过几乎所有的输入法专利,包括当时的我自己,发明人几乎都被限制在了为了汉字和编码的映射关系上。
18 | 因为从机械键盘到电子键盘,别国文字都能和键盘形成一一映射关系。 19 | 20 | 但是汉字不能! 21 | 22 | 这应该就是上面那个课题的结论。因为别国文字都经过转借抽象化。而汉字保留了象形文字太多元素。
23 | 从造字方法上看,声旁、形旁这样的符号,就是汉字的字母。但是这些字母,没有进一步被抽象化。 24 | 25 | 因此,大多数输入法都承袭了林语堂的明快打字机的设计理念,那就是为了不重码,无理化。 26 | 27 | 相对于形码输入法,拼音输入法则走向了另一套道路,用今天流行语讲叫摆烂。
28 | 在简体中文地区更是进一步去掉了声调。 29 | 30 | 事实上,拼音输入法是最早的汉字输入法,一直是占有率最高的输入法。
31 | 只不过,在当时的发明人理念中,只有形码输入法才算输入法。 32 | 33 | 曾经有个发明人调查,有多少输入法发明人在用自己的输入法。
34 | 我说我一直用拼音,这个发明人非常诧异,认为一个输入法如果连发明人自己都不用,凭什么推广给用户。 35 | 36 | 事实上,我不但一直在用拼音输入法,而且用的是操作系统自带的全拼输入法。最初的版本,只能一个字一个字的输入。 37 | 因为,我观察到其他人也是如此。我感觉,大多数人宁愿忍受这种打字方式,而没有去学某种输入法,必然有其深刻原因。 38 | 39 | 有人知道了我在开发输入法,曾经人们总爱问,你的输入法打字快吗?我回答,很慢。
40 | 后来问这种问题的少了,改问,现在不是有什么什么吗,你还搞输入法干什么?
41 | 好在,我从来没被这些人干扰了思路。从没试图向他们解释。 42 | 43 | ## 3.23.1 获取候选列表 44 | 45 | 输入法调用CDictionarySearch的成员函数FindPhraseForWildcard(&pdret),将找到的搜索结果保存到候选列表中。
46 | 然后从搜索结果的下一位置继续查找,直到文件结束。 47 | 48 | ```C++ 49 | VOID CTableDictionaryEngine::CollectWordForWildcard(_In_ CStringRange *pKeyCode, _Inout_ CSampleImeArray *pItemList) 50 | { 51 | CDictionaryResult* pdret = nullptr; 52 | CDictionarySearch dshSearch(_locale, _pDictionaryFile, pKeyCode); 53 | 54 | while (dshSearch.FindPhraseForWildcard(&pdret)) 55 | { 56 | for (UINT iIndex = 0; iIndex < pdret->_FindPhraseList.Count(); iIndex++) 57 | { 58 | CCandidateListItem* pLI = nullptr; 59 | pLI = pItemList->Append(); 60 | if (pLI) 61 | { 62 | pLI->_ItemString.Set(*pdret->_FindPhraseList.GetAt(iIndex)); 63 | pLI->_FindKeyCode.Set(pdret->_FindKeyCode.Get(), pdret->_FindKeyCode.GetLength()); 64 | } 65 | } 66 | 67 | delete pdret; 68 | pdret = nullptr; 69 | } 70 | } 71 | ``` -------------------------------------------------------------------------------- /SampleIME/DictionarySearch.h: -------------------------------------------------------------------------------- 1 | // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF 2 | // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO 3 | // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 4 | // PARTICULAR PURPOSE. 5 | // 6 | // Copyright (c) Microsoft Corporation. All rights reserved 7 | 8 | 9 | #pragma once 10 | 11 | #include "File.h" 12 | #include "DictionaryParser.h" 13 | #include "SampleIMEBaseStructure.h" 14 | 15 | class CDictionaryResult; 16 | 17 | ////////////////////////////////////////////////////////////////////// 18 | // 19 | // CDictionarySearch declaration. 20 | // 21 | ////////////////////////////////////////////////////////////////////// 22 | 23 | class CDictionarySearch : CDictionaryParser 24 | { 25 | public: 26 | CDictionarySearch(LCID locale, _In_ CFile *pFile, _In_ CStringRange *pSearchKeyCode); 27 | virtual ~CDictionarySearch(); 28 | 29 | BOOL FindPhrase(_Out_ CDictionaryResult **ppdret); 30 | BOOL FindPhraseForWildcard(_Out_ CDictionaryResult **ppdret); 31 | 32 | BOOL FindConvertedStringForWildcard(CDictionaryResult **ppdret); 33 | 34 | CStringRange* _pSearchKeyCode; 35 | 36 | DWORD_PTR _charIndex; // in character. Always point start of line in dictionary file. 37 | 38 | private: 39 | BOOL FindWorker(BOOL isTextSearch, _Out_ CDictionaryResult **ppdret, BOOL isWildcardSearch); 40 | 41 | DWORD_PTR GetBufferInWCharLength() 42 | { 43 | return (_pFile->GetFileSize() / sizeof(WCHAR)) - _charIndex; // in char count as a returned length. 44 | } 45 | 46 | const WCHAR* GetBufferInWChar() 47 | { 48 | return _pFile->GetReadBufferPointer() + _charIndex; 49 | } 50 | 51 | CFile* _pFile; 52 | }; 53 | 54 | ////////////////////////////////////////////////////////////////////// 55 | // 56 | // CDictionaryResult declaration. 57 | // 58 | ////////////////////////////////////////////////////////////////////// 59 | 60 | class CDictionaryResult 61 | { 62 | public: 63 | CDictionaryResult() { } 64 | virtual ~CDictionaryResult() { } 65 | 66 | CDictionaryResult& operator=(CDictionaryResult& dret) 67 | { 68 | _FindKeyCode = dret._FindKeyCode; 69 | _FindPhraseList = dret._FindPhraseList; 70 | return *this; 71 | } 72 | 73 | CStringRange _SearchKeyCode; 74 | CStringRange _FindKeyCode; 75 | CSampleImeArray _FindPhraseList; 76 | }; 77 | -------------------------------------------------------------------------------- /doc/ThreadMgrEventSink.md: -------------------------------------------------------------------------------- 1 | ## 3.6 线程管理器事件接收器 2 | 3 | 线程管理器事件接收器,主要处理焦点事件。当输入法获取焦点后,OnSetFocus()方法将被调用。 4 | 5 | ## 3.6.1 安装线程管理器事件接收器 6 | 7 | Interface |Description 8 | -|- 9 | [ITfSource][1] |事件安装器。 10 | 11 | [1]: https://github.com/ChineseInputMethod/Interface/blob/master/TSFmanager/ITfSource.md 12 | 13 | 获取事件安装器。 14 | 15 | ```C++ 16 | _pThreadMgr->QueryInterface(IID_ITfSource, (void **)&pSource); 17 | ``` 18 | 19 | 安装线程管理器事件接收器。 20 | 21 | ```C++ 22 | pSource->AdviseSink(IID_ITfThreadMgrEventSink, (ITfThreadMgrEventSink *)this, &_threadMgrEventSinkCookie); 23 | ``` 24 | 25 | ## 3.6.2 线程焦点 26 | 27 | Interface |Description 28 | -|- 29 | [ITfContext][2] |上下文。 30 | 31 | [2]: https://github.com/ChineseInputMethod/Interface/blob/master/TSFmanager/ITfContext.md 32 | 33 | 在OnSetFocus()方法中,主要完成三个方面的工作。
34 | 重新安装编辑会话完成消息接收器: 35 | 36 | ```C++ 37 | _InitTextEditSink(pDocMgrFocus); 38 | ``` 39 | 40 | 更新托盘图标: 41 | 42 | ```C++ 43 | _UpdateLanguageBarOnSetFocus(pDocMgrFocus); 44 | ``` 45 | 46 | 调用候选窗口焦点事件处理函数: 47 | 48 | ```C++ 49 | if (_pCandidateListUIPresenter) 50 | { 51 | ITfDocumentMgr* pCandidateListDocumentMgr = nullptr; 52 | ITfContext* pTfContext = _pCandidateListUIPresenter->_GetContextDocument(); 53 | if ((nullptr != pTfContext) && SUCCEEDED(pTfContext->GetDocumentMgr(&pCandidateListDocumentMgr))) 54 | { 55 | if (pCandidateListDocumentMgr != pDocMgrFocus) 56 | { 57 | _pCandidateListUIPresenter->OnKillThreadFocus(); 58 | } 59 | else 60 | { 61 | _pCandidateListUIPresenter->OnSetThreadFocus(); 62 | } 63 | 64 | pCandidateListDocumentMgr->Release(); 65 | } 66 | } 67 | ``` 68 | 69 | ## 3.6.3 TSF框架和中文输入观念的不同 70 | 71 | Windows系统是英文开发者开发的,无论TSF框架还是过去的IME框架其实都是为日文系统设计的。这些语言的文字与键盘编码是一一对应的,没有汉字输入法的“汉字编码”概念。
72 | 最初的输入法设计者们也深受这种影响,想要发明出汉字与编码“XX”对应的输入法。这里的“XX”指发明人的各种理念。
73 | 74 | 与本小节相关的问题是,线程管理器事件接收器处理焦点事件,其实就是对应的更新输入窗口、候选窗口和状态窗口。
75 | 对于英文字处理软件,编码就是文字,文字就是编码。编码存在于输入法的输入窗口中,显然是违和的。
76 | 对于日文字处理软件,虽然有由罗马字转换为假名和汉字的这一“合成”过程,但是这种转换发生在字处理软件中,显然比先发生在输入法的窗口中,然后将转换结果发送给字处理软件,更加直接一些。 77 | 78 | 所以微软输入法的标志特点是:输入窗口和候选窗口是分离的。而其他几乎所有的中文输入法的输入窗口和候选窗口是在一起的。
79 | 实际上微软输入法才是TSF框架的标准实现方式。具体到本小节,在OnSetFocus()方法中,只重新安装了编辑会话完成消息接收器,并没有对输入编码进行处理。
80 | 在TSF框架里输入编码是由应用程序处理(UI部分)的。 81 | 82 | 现在的字处理软件已经可以很好的处理半角空格和全角空格,以及标点符号。但是,早期这是一个很麻烦的问题。当时的输入法需要一个状态条,指示当前的标点和空格状态。 83 | 现在的中文输入法开发者依然希望有一个状态条,以显示更多的信息。然而微软的设计理念是,只希望提供一个中英文状态的系统托盘图标。 84 | 85 | 微软甚至希望连候选窗口也消灭掉,因为由系统或应用程序来提供输入UI,可以带来一致性,保持程序的整体风格。而输入法窗口,更像一个悬浮窗口,其风格和显示位置是不受应用程序控制的。
86 | 过去经常会出现输入法窗口遮挡了应用程序,用户不得不拖动输入法窗口或关闭输入法,以执行某项操作。现在这个问题,已经有所改观不是那么严重了。 -------------------------------------------------------------------------------- /doc/Compartment.md: -------------------------------------------------------------------------------- 1 | ## 3.14 公共缓冲池 2 | 3 | 公共缓冲池,用来保存输入法状态开关。 4 | 5 | >作者并不真正理解Compartment的含义,这里也有可能是COM的套间技术,总之有个隔离区域保存输入法的配置。 6 | 7 | ## 3.14.1 公共缓冲池管理器 8 | 9 | Interface |Description 10 | -|- 11 | [ITfCompartmentMgr][1] |公共缓冲池管理器,用于管理客户端之间的共享数据。 12 | 13 | [1]: https://github.com/ChineseInputMethod/Interface/blob/master/TSFmanager/ITfCompartmentMgr.md 14 | 15 | ```C++ 16 | HRESULT CCompartment::_GetCompartment(_Outptr_ ITfCompartment **ppCompartment) 17 | { 18 | HRESULT hr = S_OK; 19 | ITfCompartmentMgr* pCompartmentMgr = nullptr; 20 | 21 | hr = _punk->QueryInterface(IID_ITfCompartmentMgr, (void **)&pCompartmentMgr); 22 | if (SUCCEEDED(hr)) 23 | { 24 | hr = pCompartmentMgr->GetCompartment(_guidCompartment, ppCompartment); 25 | pCompartmentMgr->Release(); 26 | } 27 | 28 | return hr; 29 | } 30 | ``` 31 | 32 | ## 3.14.2 操作公共缓冲池 33 | 34 | Interface |Description 35 | -|- 36 | [ITfCompartment][2] |公共缓冲池,用于获取和设置公共缓冲池中的数据。 37 | 38 | [2]: https://github.com/ChineseInputMethod/Interface/blob/master/TSFmanager/ITfCompartment.md 39 | 40 | ```C++ 41 | HRESULT CCompartment::_SetCompartmentBOOL(_In_ BOOL flag) 42 | { 43 | HRESULT hr = S_OK; 44 | ITfCompartment* pCompartment = nullptr; 45 | 46 | hr = _GetCompartment(&pCompartment); 47 | if (SUCCEEDED(hr)) 48 | { 49 | VARIANT var; 50 | var.vt = VT_I4; 51 | var.lVal = flag; 52 | hr = pCompartment->SetValue(_tfClientId, &var); 53 | pCompartment->Release(); 54 | } 55 | 56 | return hr; 57 | } 58 | ``` 59 | 60 | ## 3.14.3 公共缓冲池事件接收器 61 | 62 | Interface |Description 63 | -|- 64 | [ITfCompartmentEventSink][3] |公共缓冲池事件接收器。 65 | 66 | [3]: https://github.com/ChineseInputMethod/Interface/blob/master/TextService/ITfCompartmentEventSink.md 67 | 68 | 安装ITfCompartmentEventSink公共缓冲池事件接收器后,当公共缓冲池发生改变时,ITfCompartmentEventSink::OnChange()函数将被调用。 69 | 70 | ```C++ 71 | HRESULT CCompartmentEventSink::_Advise(_In_ IUnknown *punk, _In_ REFGUID guidCompartment) 72 | { 73 | HRESULT hr = S_OK; 74 | ITfCompartmentMgr* pCompartmentMgr = nullptr; 75 | ITfSource* pSource = nullptr; 76 | 77 | hr = punk->QueryInterface(IID_ITfCompartmentMgr, (void **)&pCompartmentMgr); 78 | if (FAILED(hr)) 79 | { 80 | return hr; 81 | } 82 | 83 | hr = pCompartmentMgr->GetCompartment(guidCompartment, &_pCompartment); 84 | if (SUCCEEDED(hr)) 85 | { 86 | hr = _pCompartment->QueryInterface(IID_ITfSource, (void **)&pSource); 87 | if (SUCCEEDED(hr)) 88 | { 89 | hr = pSource->AdviseSink(IID_ITfCompartmentEventSink, this, &_dwCookie); 90 | pSource->Release(); 91 | } 92 | } 93 | 94 | pCompartmentMgr->Release(); 95 | 96 | return hr; 97 | } 98 | ``` -------------------------------------------------------------------------------- /SampleIME/LanguageBar.h: -------------------------------------------------------------------------------- 1 | // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF 2 | // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO 3 | // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 4 | // PARTICULAR PURPOSE. 5 | // 6 | // Copyright (c) Microsoft Corporation. All rights reserved 7 | 8 | #pragma once 9 | 10 | class CCompartment; 11 | class CCompartmentEventSink; 12 | 13 | class CLangBarItemButton : public ITfLangBarItemButton, 14 | public ITfSource 15 | { 16 | public: 17 | CLangBarItemButton(REFGUID guidLangBar, LPCWSTR description, LPCWSTR tooltip, DWORD onIconIndex, DWORD offIconIndex, BOOL isSecureMode); 18 | ~CLangBarItemButton(); 19 | 20 | // IUnknown 21 | STDMETHODIMP QueryInterface(REFIID riid, _Outptr_ void **ppvObj); 22 | STDMETHODIMP_(ULONG) AddRef(void); 23 | STDMETHODIMP_(ULONG) Release(void); 24 | 25 | // ITfLangBarItem 26 | STDMETHODIMP GetInfo(_Out_ TF_LANGBARITEMINFO *pInfo); 27 | STDMETHODIMP GetStatus(_Out_ DWORD *pdwStatus); 28 | STDMETHODIMP Show(BOOL fShow); 29 | STDMETHODIMP GetTooltipString(_Out_ BSTR *pbstrToolTip); 30 | 31 | // ITfLangBarItemButton 32 | STDMETHODIMP OnClick(TfLBIClick click, POINT pt, _In_ const RECT *prcArea); 33 | STDMETHODIMP InitMenu(_In_ ITfMenu *pMenu); 34 | STDMETHODIMP OnMenuSelect(UINT wID); 35 | STDMETHODIMP GetIcon(_Out_ HICON *phIcon); 36 | STDMETHODIMP GetText(_Out_ BSTR *pbstrText); 37 | 38 | // ITfSource 39 | STDMETHODIMP AdviseSink(__RPC__in REFIID riid, __RPC__in_opt IUnknown *punk, __RPC__out DWORD *pdwCookie); 40 | STDMETHODIMP UnadviseSink(DWORD dwCookie); 41 | 42 | // Add/Remove languagebar item 43 | HRESULT _AddItem(_In_ ITfThreadMgr *pThreadMgr); 44 | HRESULT _RemoveItem(_In_ ITfThreadMgr *pThreadMgr); 45 | 46 | // Register compartment for button On/Off switch 47 | BOOL _RegisterCompartment(_In_ ITfThreadMgr *pThreadMgr, TfClientId tfClientId, REFGUID guidCompartment); 48 | BOOL _UnregisterCompartment(_In_ ITfThreadMgr *pThreadMgr); 49 | 50 | void CleanUp(); 51 | 52 | void SetStatus(DWORD dwStatus, BOOL fSet); 53 | 54 | private: 55 | ITfLangBarItemSink* _pLangBarItemSink; 56 | 57 | TF_LANGBARITEMINFO _tfLangBarItemInfo; 58 | LPCWSTR _pTooltipText; 59 | DWORD _onIconIndex; 60 | DWORD _offIconIndex; 61 | 62 | BOOL _isAddedToLanguageBar; 63 | BOOL _isSecureMode; 64 | DWORD _status; 65 | 66 | CCompartment* _pCompartment; 67 | CCompartmentEventSink* _pCompartmentEventSink; 68 | static HRESULT _CompartmentCallback(_In_ void *pv, REFGUID guidCompartment); 69 | 70 | // The cookie for the sink to CLangBarItemButton. 71 | static const DWORD _cookie = 0; 72 | 73 | LONG _refCount; 74 | }; 75 | -------------------------------------------------------------------------------- /SampleIME/DisplayAttributeInfo.h: -------------------------------------------------------------------------------- 1 | // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF 2 | // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO 3 | // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 4 | // PARTICULAR PURPOSE. 5 | // 6 | // Copyright (c) Microsoft Corporation. All rights reserved 7 | 8 | 9 | #pragma once 10 | 11 | //+--------------------------------------------------------------------------- 12 | // 13 | // CDisplayAttributeInfo class 14 | // 15 | //---------------------------------------------------------------------------- 16 | 17 | class CDisplayAttributeInfo : public ITfDisplayAttributeInfo 18 | { 19 | public: 20 | CDisplayAttributeInfo(); 21 | ~CDisplayAttributeInfo(); 22 | 23 | // IUnknown 24 | STDMETHODIMP QueryInterface(REFIID riid, _Outptr_ void **ppvObj); 25 | STDMETHODIMP_(ULONG) AddRef(void); 26 | STDMETHODIMP_(ULONG) Release(void); 27 | 28 | // ITfDisplayAttributeInfo 29 | STDMETHODIMP GetGUID(_Out_ GUID *pguid); 30 | STDMETHODIMP GetDescription(_Out_ BSTR *pbstrDesc); 31 | STDMETHODIMP GetAttributeInfo(_Out_ TF_DISPLAYATTRIBUTE *pTSFDisplayAttr); 32 | STDMETHODIMP SetAttributeInfo(_In_ const TF_DISPLAYATTRIBUTE *ptfDisplayAttr); 33 | STDMETHODIMP Reset(); 34 | 35 | protected: 36 | const GUID* _pguid; 37 | const TF_DISPLAYATTRIBUTE* _pDisplayAttribute; 38 | const WCHAR* _pDescription; 39 | const WCHAR* _pValueName; 40 | 41 | private: 42 | LONG _refCount; // COM ref count 43 | }; 44 | 45 | //+--------------------------------------------------------------------------- 46 | // 47 | // CDisplayAttributeInfoInput class 48 | // 49 | //---------------------------------------------------------------------------- 50 | 51 | class CDisplayAttributeInfoInput : public CDisplayAttributeInfo 52 | { 53 | public: 54 | CDisplayAttributeInfoInput() 55 | { 56 | _pguid = &Global::SampleIMEGuidDisplayAttributeInput; 57 | _pDisplayAttribute = &_s_DisplayAttribute; 58 | _pDescription = _s_szDescription; 59 | _pValueName = _s_szValueName; 60 | } 61 | 62 | static const TF_DISPLAYATTRIBUTE _s_DisplayAttribute; 63 | static const WCHAR _s_szDescription[]; 64 | static const WCHAR _s_szValueName[]; 65 | }; 66 | 67 | //+--------------------------------------------------------------------------- 68 | // 69 | // CDisplayAttributeInfoConverted class 70 | // 71 | //---------------------------------------------------------------------------- 72 | 73 | class CDisplayAttributeInfoConverted : public CDisplayAttributeInfo 74 | { 75 | public: 76 | CDisplayAttributeInfoConverted() 77 | { 78 | _pguid = &Global::SampleIMEGuidDisplayAttributeConverted; 79 | _pDisplayAttribute = &_s_DisplayAttribute; 80 | _pDescription = _s_szDescription; 81 | _pValueName = _s_szValueName; 82 | } 83 | 84 | static const TF_DISPLAYATTRIBUTE _s_DisplayAttribute; 85 | static const WCHAR _s_szDescription[]; 86 | static const WCHAR _s_szValueName[]; 87 | }; 88 | -------------------------------------------------------------------------------- /doc/SampleIME.md: -------------------------------------------------------------------------------- 1 | ## 3.5 TSF文本服务框架 2 | 3 | CSampleIME继承的接口分为两类。一类是隐式声明具备的能力(例如注册的类别)。当需要调用相应接口时,TSF管理器会查询调用的接口是否实现: 4 | 5 | Interface |Description 6 | -|- 7 | [ITfTextInputProcessorEx][1] |文本输入处理器,继承ITfTextInputProcessor,扩展了文本服务的激活方式。 8 | [ITfDisplayAttributeProvider][2] |显示属性提供者,在显示属性专题讲解。当输入法注册了显示属性类别后,由客户端查询显示属性。 9 | [ITfCompositionSink][3] |输入组合终止消息接收器,在输入合成专题讲解。意外终止合成时被调用。 10 | [ITfFnGetPreferredTouchKeyboardLayout][4] |获取首选触摸键盘布局,指定Windows 8触摸键盘支持的键盘布局。 11 | 12 | [1]: https://github.com/ChineseInputMethod/Interface/blob/master/TextService/ITfTextInputProcessorEx.md 13 | [2]: https://github.com/ChineseInputMethod/Interface/blob/master/TextService/ITfDisplayAttributeProvider.md 14 | [3]: https://github.com/ChineseInputMethod/Interface/blob/master/TextService/ITfCompositionSink.md 15 | [4]: https://learn.microsoft.com/zh-cn/windows/win32/api/ctffunc/nn-ctffunc-itffngetpreferredtouchkeyboardlayout 16 | 17 | 另一类是在输入法激活时显式安装的事件接收器,当发生相应事件后,TSF管理器会调用相应的事件接收器: 18 | 19 | Interface |Description 20 | -|- 21 | [ITfThreadMgrEventSink][5] |线程管理器事件接收器,主要处理焦点事件。 22 | [ITfTextEditSink][6] |编辑会话完成消息接收器,用于感知其他文本服务对编辑内容的更改。 23 | [ITfKeyEventSink][7] |键盘事件接收器,用于接收按键和虚拟按键事件。 24 | [ITfActiveLanguageProfileNotifySink][8] |语言配置激活消息接收器,当更改激活语言配置文件时,框架调用接收器。 25 | [ITfThreadFocusSink][9] |线程输入焦点消息接收器,在线程接收或失去UI焦点时接收通知。 26 | [ITfFunctionProvider][10] |扩展功能提供者,提供各种函数对象。 27 | 28 | [5]: https://github.com/ChineseInputMethod/Interface/blob/master/TextService/ITfThreadMgrEventSink.md 29 | [6]: https://github.com/ChineseInputMethod/Interface/blob/master/TextService/ITfTextEditSink.md 30 | [7]: https://github.com/ChineseInputMethod/Interface/blob/master/TextService/ITfKeyEventSink.md 31 | [8]: https://github.com/ChineseInputMethod/Interface/blob/master/Application/ITfActiveLanguageProfileNotifySink.md 32 | [9]: https://github.com/ChineseInputMethod/Interface/blob/master/TextService/ITfThreadFocusSink.md 33 | [10]: https://github.com/ChineseInputMethod/Interface/blob/master/TextService/ITfFunctionProvider.md 34 | 35 | ## 3.5.1 激活输入法 36 | 37 | 当输入法被激活时(键盘布局被载入),ITfTextInputProcessorEx::ActivateEx方法首先被调用。 38 | ITfTextInputProcessorEx是ITfTextInputProcessor的升级版本,扩展了激活方式,例如增加了对UILess模式的支持。 39 | 40 | 激活输入法的主要任务是安装事件接收器和初始化输入法。 41 | 42 | Function |Description 43 | -|- 44 | _InitThreadMgrEventSink() |安装线程管理器事件接收器。 45 | _InitTextEditSink() |安装编辑会话完成消息接收器。 46 | _InitKeyEventSink() |安装键盘事件接收器。 47 | _InitActiveLanguageProfileNotifySink() |安装语言配置激活消息接收器,当更改激活语言配置文件时,框架调用接收器。 48 | _InitThreadFocusSink() |安装线程输入焦点消息接收器。 49 | _InitFunctionProviderSink() |安装扩展功能提供者,创建一个搜索集成引擎。 50 | 51 | Function |Description 52 | -|- 53 | _InitDisplayAttributeGuidAtom() |注册显示属性标识符。 54 | _AddTextProcessorEngine() |添加文本处理引擎。创建一个编码分词引擎。 55 | 56 | ## 3.5.2 触摸键盘布局 57 | 58 | Interface |Description 59 | -|- 60 | [ITfFunction][11] |扩展功能对象,ITfFunction接口是各个函数接口的基接口。 61 | 62 | [11]: https://github.com/ChineseInputMethod/Interface/blob/master/TextService/ITfFunction.md 63 | 64 | >https://learn.microsoft.com/zh-cn/windows/apps/design/input/input-method-editor-requirements#ime-and-touch-keyboard -------------------------------------------------------------------------------- /doc/TfTextLayoutSink.md: -------------------------------------------------------------------------------- 1 | ## 3.28 文本布局消息接收器 2 | 3 | 文本布局消息接收器,通过响应布局更改消息,实现光标跟随。 4 | 5 | ## 3.28.1 安装文本布局消息接收器 6 | 7 | Interface |Description 8 | -|- 9 | [ITfTextLayoutSink][1] |当上下文视图的布局发生更改时,会收到通知。 10 | 11 | [1]: https://github.com/ChineseInputMethod/Interface/blob/master/TextService/ITfTextLayoutSink.md 12 | 13 | ```C++ 14 | HRESULT CTfTextLayoutSink::_AdviseTextLayoutSink() 15 | { 16 | HRESULT hr = S_OK; 17 | ITfSource* pSource = nullptr; 18 | 19 | hr = _pContextDocument->QueryInterface(IID_ITfSource, (void **)&pSource); 20 | if (FAILED(hr)) 21 | { 22 | return hr; 23 | } 24 | 25 | hr = pSource->AdviseSink(IID_ITfTextLayoutSink, (ITfTextLayoutSink *)this, &_dwCookieTextLayoutSink); 26 | if (FAILED(hr)) 27 | { 28 | pSource->Release(); 29 | return hr; 30 | } 31 | 32 | pSource->Release(); 33 | 34 | return hr; 35 | } 36 | ``` 37 | 38 | ## 3.28.2 文本布局更改事件 39 | 40 | 当上下文视图的布局发生更改时,OnLayoutChange()函数会被调用。 41 | 42 | 在TSF处理逻辑上,输入编码被写入合成,就会触发文本布局更改事件。输入法响应事件,申请编辑会话,更改候选窗口内容和位置。
43 | 但是,第三方输入法一般自己管理输入编码,不会将输入编码实际写入合成,所以实现光标跟随,需要一点技巧。具体如何实现,将在下一章讲解。 44 | 45 | ```C++ 46 | STDAPI CTfTextLayoutSink::OnLayoutChange(_In_ ITfContext *pContext, TfLayoutCode lcode, _In_ ITfContextView *pContextView) 47 | { 48 | // we're interested in only document context. 49 | if (pContext != _pContextDocument) 50 | { 51 | return S_OK; 52 | } 53 | 54 | switch (lcode) 55 | { 56 | case TF_LC_CHANGE: 57 | { 58 | CGetTextExtentEditSession* pEditSession = nullptr; 59 | pEditSession = new (std::nothrow) CGetTextExtentEditSession(_pTextService, pContext, pContextView, _pRangeComposition, this); 60 | if (nullptr != (pEditSession)) 61 | { 62 | HRESULT hr = S_OK; 63 | pContext->RequestEditSession(_pTextService->_GetClientId(), pEditSession, TF_ES_SYNC | TF_ES_READ, &hr); 64 | 65 | pEditSession->Release(); 66 | } 67 | } 68 | break; 69 | 70 | case TF_LC_DESTROY: 71 | _LayoutDestroyNotification(); 72 | break; 73 | 74 | } 75 | return S_OK; 76 | } 77 | ``` 78 | 79 | ## 3.28.3 获取屏幕坐标 80 | 81 | Interface |Description 82 | -|- 83 | [ITfContextView][2] |上下文视图对象,由客户端(应用程序或文本服务)用来获取上下文视图的信息。 84 | 85 | [2]: https://github.com/ChineseInputMethod/Interface/blob/master/TSFmanager/ITfContextView.md 86 | 87 | ```C++ 88 | HRESULT CTfTextLayoutSink::_GetTextExt(_Out_ RECT *lpRect) 89 | { 90 | HRESULT hr = S_OK; 91 | BOOL isClipped = TRUE; 92 | ITfContextView* pContextView = nullptr; 93 | 94 | hr = _pContextDocument->GetActiveView(&pContextView); 95 | if (FAILED(hr)) 96 | { 97 | return hr; 98 | } 99 | 100 | if (FAILED(hr = pContextView->GetTextExt(_tfEditCookie, _pRangeComposition, lpRect, &isClipped))) 101 | { 102 | return hr; 103 | } 104 | 105 | pContextView->Release(); 106 | 107 | return S_OK; 108 | } 109 | ``` -------------------------------------------------------------------------------- /doc/SampleIMEBaseStructure.md: -------------------------------------------------------------------------------- 1 | ## 3.22 数据结构 2 | 3 | 数据结构,输入法自定义数据结构。 4 | 5 | ## 3.22.1 CStringRange 6 | 7 | 输入法自定义的字符串类,用于以字符串形式保存编码、汉字等数据。 8 | 9 | ```C++ 10 | class CStringRange 11 | { 12 | public: 13 | CStringRange(); 14 | ~CStringRange(); 15 | 16 | const WCHAR *Get() const; 17 | const DWORD_PTR GetLength() const; 18 | void Clear(); 19 | void Set(const WCHAR *pwch, DWORD_PTR dwLength); 20 | void Set(CStringRange &sr); 21 | CStringRange& operator=(const CStringRange& sr); 22 | void CharNext(_Inout_ CStringRange* pCharNext); 23 | static int Compare(LCID locale, _In_ CStringRange* pString1, _In_ CStringRange* pString2); 24 | static BOOL WildcardCompare(LCID locale, _In_ CStringRange* stringWithWildcard, _In_ CStringRange* targetString); 25 | 26 | protected: 27 | DWORD_PTR _stringBufLen; // Length is in character count. 28 | const WCHAR *_pStringBuf; // Buffer which is not add zero terminate. 29 | }; 30 | ``` 31 | 32 | ## 3.22.2 CCandidateListItem 33 | 34 | 候选列表项,将汉字和编码定义成一个结构,以做为候选列表的一个元素。 35 | 36 | ```C++ 37 | struct CCandidateListItem 38 | { 39 | CStringRange _ItemString; 40 | CStringRange _FindKeyCode; 41 | 42 | CCandidateListItem& CCandidateListItem::operator =( const CCandidateListItem& rhs) 43 | { 44 | _ItemString = rhs._ItemString; 45 | _FindKeyCode = rhs._FindKeyCode; 46 | return *this; 47 | } 48 | }; 49 | ``` 50 | 51 | ## 3.22.3 CSampleImeArray 52 | 53 | 以向量实现的自定义容器,将模板类型声明为候选列表项,做为候选列表。 54 | 55 | ```C++ 56 | template 57 | class CSampleImeArray 58 | { 59 | typedef typename std::vector CSampleImeInnerArray; 60 | typedef typename std::vector::iterator CSampleImeInnerIter; 61 | 62 | public: 63 | CSampleImeArray(): _innerVect() 64 | { 65 | } 66 | 67 | explicit CSampleImeArray(size_t count): _innerVect(count) 68 | { 69 | } 70 | 71 | virtual ~CSampleImeArray() 72 | { 73 | } 74 | 75 | inline T* GetAt(size_t index) 76 | { 77 | assert(index >= 0); 78 | assert(index < _innerVect.size()); 79 | 80 | T& curT = _innerVect.at(index); 81 | 82 | return &(curT); 83 | } 84 | 85 | inline const T* GetAt(size_t index) const 86 | { 87 | assert(index >= 0); 88 | assert(index < _innerVect.size()); 89 | 90 | T& curT = _innerVect.at(index); 91 | 92 | return &(curT); 93 | } 94 | 95 | void RemoveAt(size_t index) 96 | { 97 | assert(index >= 0); 98 | assert(index < _innerVect.size()); 99 | 100 | CSampleImeInnerIter iter = _innerVect.begin(); 101 | _innerVect.erase(iter + index); 102 | } 103 | 104 | UINT Count() const 105 | { 106 | return static_cast(_innerVect.size()); 107 | } 108 | 109 | T* Append() 110 | { 111 | T newT; 112 | _innerVect.push_back(newT); 113 | T& backT = _innerVect.back(); 114 | 115 | return &(backT); 116 | } 117 | 118 | void reserve(size_t Count) 119 | { 120 | _innerVect.reserve(Count); 121 | } 122 | 123 | void Clear() 124 | { 125 | _innerVect.clear(); 126 | } 127 | 128 | private: 129 | CSampleImeInnerArray _innerVect; 130 | }; 131 | ``` -------------------------------------------------------------------------------- /SampleIME/TipCandidateString.cpp: -------------------------------------------------------------------------------- 1 | // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF 2 | // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO 3 | // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 4 | // PARTICULAR PURPOSE. 5 | // 6 | // Copyright (c) Microsoft Corporation. All rights reserved 7 | 8 | #include "private.h" 9 | #include "TipCandidateString.h" 10 | 11 | HRESULT CTipCandidateString::CreateInstance(_Outptr_ CTipCandidateString **ppobj) 12 | { 13 | if (ppobj == nullptr) 14 | { 15 | return E_INVALIDARG; 16 | } 17 | *ppobj = nullptr; 18 | 19 | *ppobj = new (std::nothrow) CTipCandidateString(); 20 | if (*ppobj == nullptr) 21 | { 22 | return E_OUTOFMEMORY; 23 | } 24 | 25 | return S_OK; 26 | } 27 | 28 | HRESULT CTipCandidateString::CreateInstance(REFIID riid, _Outptr_ void **ppvObj) 29 | { 30 | if (ppvObj == nullptr) 31 | { 32 | return E_INVALIDARG; 33 | } 34 | *ppvObj = nullptr; 35 | 36 | *ppvObj = new (std::nothrow) CTipCandidateString(); 37 | if (*ppvObj == nullptr) 38 | { 39 | return E_OUTOFMEMORY; 40 | } 41 | 42 | return ((CTipCandidateString*)(*ppvObj))->QueryInterface(riid, ppvObj); 43 | } 44 | 45 | CTipCandidateString::CTipCandidateString(void) 46 | { 47 | _refCount = 0; 48 | _index = 0; 49 | } 50 | 51 | CTipCandidateString::~CTipCandidateString() 52 | { 53 | } 54 | 55 | // IUnknown methods 56 | STDMETHODIMP CTipCandidateString::QueryInterface(REFIID riid, _Outptr_ void **ppvObj) 57 | { 58 | if (ppvObj == nullptr) 59 | { 60 | return E_POINTER; 61 | } 62 | *ppvObj = nullptr; 63 | 64 | if (IsEqualIID(riid, IID_IUnknown)) 65 | { 66 | *ppvObj = (CTipCandidateString*)this; 67 | } 68 | else if (IsEqualIID(riid, IID_ITfCandidateString)) 69 | { 70 | *ppvObj = (CTipCandidateString*)this; 71 | } 72 | 73 | if (*ppvObj == nullptr) 74 | { 75 | return E_NOINTERFACE; 76 | } 77 | 78 | AddRef(); 79 | return S_OK; 80 | } 81 | 82 | STDMETHODIMP_(ULONG) CTipCandidateString::AddRef(void) 83 | { 84 | return (ULONG)InterlockedIncrement((LONG*)&_refCount); 85 | } 86 | 87 | STDMETHODIMP_(ULONG) CTipCandidateString::Release(void) 88 | { 89 | ULONG refT = (ULONG)InterlockedDecrement((LONG*)&_refCount); 90 | if (0 < refT) 91 | { 92 | return refT; 93 | } 94 | 95 | delete this; 96 | 97 | return 0; 98 | } 99 | 100 | // ITfCandidateString methods 101 | STDMETHODIMP CTipCandidateString::GetString(BSTR *pbstr) 102 | { 103 | *pbstr = SysAllocString(_candidateStr.c_str()); 104 | return S_OK; 105 | } 106 | 107 | STDMETHODIMP CTipCandidateString::GetIndex(_Out_ ULONG *pnIndex) 108 | { 109 | if (pnIndex == nullptr) 110 | { 111 | return E_POINTER; 112 | } 113 | 114 | *pnIndex = _index; 115 | return S_OK; 116 | } 117 | 118 | STDMETHODIMP CTipCandidateString::SetIndex(ULONG uIndex) 119 | { 120 | _index = uIndex; 121 | return S_OK; 122 | } 123 | 124 | STDMETHODIMP CTipCandidateString::SetString(_In_ const WCHAR *pch, DWORD_PTR length) 125 | { 126 | _candidateStr.assign(pch, 0, length); 127 | return S_OK; 128 | } 129 | -------------------------------------------------------------------------------- /doc/uiless/按键处理.md: -------------------------------------------------------------------------------- 1 | ## C.2 按键处理 2 | 3 | 当按下编码键后,输入法判断是否处理该按键。如果需要处理该按键,则发起编辑会话。 4 | 5 | Function |Description 6 | -|- 7 | /* 8 | CSampleIME::OnKeyDown(ITfContext *, unsigned __int64, __int64, int *) |按键按下事件 9 | Global::UpdateModifiers(unsigned __int64, __int64) | 10 | CSampleIME::_IsKeyEaten(ITfContext *, unsigned int, unsigned int *, wchar_t *, _KEYSTROKE_STATE *) |是否处理该按键 11 | CSampleIME::_IsKeyboardDisabled(void) | 12 | CSampleIME::ConvertVKey(unsigned int) | 13 | VKeyFromVKPacketAndWchar(unsigned int, wchar_t) | 14 | CSampleIME::_IsComposing(void) 0x0000000000000000 | 15 | CCompositionProcessorEngine::IsVirtualKeyNeed(unsigned int, wchar_t *, int, CANDIDATE_MODE, int, _KEYSTROKE_STATE *) |是否处理该虚键 16 | CCompositionProcessorEngine::IsVirtualKeyKeystrokeComposition(unsigned int, _KEYSTROKE_STATE *, KEYSTROKE_FUNCTION) |是否处理该编码键 17 | Global::CheckModifiers(unsigned int, unsigned int) | 18 | modCurrent ==0x00000000 19 | function ==FUNCTION_NONE (0x00000000) 20 | CSampleIME::_InvokeKeyHandler(ITfContext *, unsigned int, wchar_t, unsigned long, _KEYSTROKE_STATE) 0x00000041 0x0061 'a' |发起编辑会话 21 | 22 | 当输入法需要处理按键时,发起编辑会话,在编辑会话中处理按键。 23 | 24 | Function |Description 25 | -|- 26 | CKeyHandlerEditSession::DoEditSession(unsigned long) 0x412dd00c |在编辑会话中处理按键 27 | CKeyStateCategoryFactory::Instance(void) | 28 | CKeyStateCategoryFactory::MakeKeyStateCategory(KEYSTROKE_CATEGORY, CSampleIME *) |将按键分类 29 | CKeyStateCategory::KeyStateHandler(KEYSTROKE_FUNCTION, KeyHandlerEditSessionDTO) |按照按键类别调用相应的处理函数 30 | CKeyStateComposing::HandleKeyInput(KeyHandlerEditSessionDTO) |当前按键为输入状态 31 | CSampleIME::_HandleCompositionInput(unsigned long, ITfContext *, wchar_t) 0x0061 'a' |处理合成 32 | 33 | 在合成处理中,如果没有开始合成,则开始合成。 34 | 35 | Function |Description 36 | -|- 37 | CSampleIME::_IsComposing(void) 0x0000000000000000 |是否处于合成状态 38 | CSampleIME::_StartComposition(ITfContext *) |发起合成会话 39 | CStartCompositionEditSession::DoEditSession(unsigned long) |在会话中创建合成 40 | CSampleIME::_SetComposition(ITfComposition *) |保存合成 41 | CSampleIME::_SaveCompositionContext(ITfContext *) |保存上下文 42 | CSampleIME::_IsRangeCovered(unsigned long, ITfRange *, ITfRange *) | 43 | CCompositionProcessorEngine::AddVirtualKey(wchar_t) |保存输入的编码 44 | 45 | 在处理编码的合成处理部分,将编码和合成属性写入到上下文中。 46 | 47 | Function |Description 48 | -|- 49 | CSampleIME::_HandleCompositionInputWorker(CCompositionProcessorEngine *, unsigned long, ITfContext *) |编码处理 50 | CCompositionProcessorEngine::GetReadingStrings(CSampleImeArray *, int *) |获取输入编码 51 | CSampleIME::_AddComposingAndChar(unsigned long, ITfContext *, CStringRange *) |处理输入编码 52 | CSampleIME::_FindComposingRange(unsigned long, ITfContext *, ITfRange *, ITfRange * *) |查找已有合成 53 | CSampleIME::_SetInputString(unsigned long, ITfContext *, ITfRange *, CStringRange *, int) |覆盖文本范围内容 54 | CSampleIME::_InsertAtSelection(unsigned long, ITfContext *, CStringRange *, ITfRange * *) | 55 | CSampleIME::_SetCompositionLanguage(unsigned long, ITfContext *) | 56 | CSampleIME::_SetCompositionDisplayAttributes(unsigned long, ITfContext *, unsigned long) |设置合成显示属性 -------------------------------------------------------------------------------- /doc/appendix/按键处理.md: -------------------------------------------------------------------------------- 1 | ## A.2 按键处理 2 | 3 | 加载2023年10月7日版本的track.xml断点文件,调试时会输出如下跟踪信息。该信息包含了从按下一个编码键到将输入编码添加到合成中的主要函数调用过程。 4 | 5 | 当按下编码键后,输入法判断是否处理该按键。如果需要处理该按键,则发起编辑会话。 6 | 7 | Function |Description 8 | -|- 9 | /* 10 | CSampleIME::OnKeyDown(ITfContext *, unsigned __int64, __int64, int *) |按键按下事件 11 | Global::UpdateModifiers(unsigned __int64, __int64) | 12 | CSampleIME::_IsKeyEaten(ITfContext *, unsigned int, unsigned int *, wchar_t *, _KEYSTROKE_STATE *) |是否处理该按键 13 | CSampleIME::_IsKeyboardDisabled(void) | 14 | CSampleIME::ConvertVKey(unsigned int) | 15 | VKeyFromVKPacketAndWchar(unsigned int, wchar_t) | 16 | CSampleIME::_IsComposing(void) 0x0000000000000000 | 17 | CCompositionProcessorEngine::IsVirtualKeyNeed(unsigned int, wchar_t *, int, CANDIDATE_MODE, int, _KEYSTROKE_STATE *) |是否处理该虚键 18 | CCompositionProcessorEngine::IsVirtualKeyKeystrokeComposition(unsigned int, _KEYSTROKE_STATE *, KEYSTROKE_FUNCTION) |是否处理该编码键 19 | Global::CheckModifiers(unsigned int, unsigned int) | 20 | modCurrent ==0x00000000 21 | function ==FUNCTION_NONE (0x00000000) 22 | CSampleIME::_InvokeKeyHandler(ITfContext *, unsigned int, wchar_t, unsigned long, _KEYSTROKE_STATE) 0x00000041 0x0061 'a' |发起编辑会话 23 | 24 | 当输入法需要处理按键时,发起编辑会话,在编辑会话中处理按键。 25 | 26 | Function |Description 27 | -|- 28 | CKeyHandlerEditSession::DoEditSession(unsigned long) 0x412dd00c |在编辑会话中处理按键 29 | CKeyStateCategoryFactory::Instance(void) | 30 | CKeyStateCategoryFactory::MakeKeyStateCategory(KEYSTROKE_CATEGORY, CSampleIME *) |将按键分类 31 | CKeyStateCategory::KeyStateHandler(KEYSTROKE_FUNCTION, KeyHandlerEditSessionDTO) |按照按键类别调用相应的处理函数 32 | CKeyStateComposing::HandleKeyInput(KeyHandlerEditSessionDTO) |当前按键为输入状态 33 | CSampleIME::_HandleCompositionInput(unsigned long, ITfContext *, wchar_t) 0x0061 'a' |处理合成 34 | 35 | 在合成处理中,如果没有开始合成,则开始合成。 36 | 37 | Function |Description 38 | -|- 39 | CSampleIME::_IsComposing(void) 0x0000000000000000 |是否处于合成状态 40 | CSampleIME::_StartComposition(ITfContext *) |发起合成会话 41 | CStartCompositionEditSession::DoEditSession(unsigned long) |在会话中创建合成 42 | CSampleIME::_SetComposition(ITfComposition *) |保存合成 43 | CSampleIME::_SaveCompositionContext(ITfContext *) |保存上下文 44 | CSampleIME::_IsRangeCovered(unsigned long, ITfRange *, ITfRange *) | 45 | CCompositionProcessorEngine::AddVirtualKey(wchar_t) |保存输入的编码 46 | 47 | 在处理编码的合成处理部分,将编码和合成属性写入到上下文中。 48 | 49 | Function |Description 50 | -|- 51 | CSampleIME::_HandleCompositionInputWorker(CCompositionProcessorEngine *, unsigned long, ITfContext *) |编码处理 52 | CCompositionProcessorEngine::GetReadingStrings(CSampleImeArray *, int *) |获取输入编码 53 | CSampleIME::_AddComposingAndChar(unsigned long, ITfContext *, CStringRange *) |处理输入编码 54 | CSampleIME::_FindComposingRange(unsigned long, ITfContext *, ITfRange *, ITfRange * *) |查找已有合成 55 | CSampleIME::_SetInputString(unsigned long, ITfContext *, ITfRange *, CStringRange *, int) |覆盖文本范围内容 56 | CSampleIME::_InsertAtSelection(unsigned long, ITfContext *, CStringRange *, ITfRange * *) | 57 | CSampleIME::_SetCompositionLanguage(unsigned long, ITfContext *) | 58 | CSampleIME::_SetCompositionDisplayAttributes(unsigned long, ITfContext *, unsigned long) |设置合成显示属性 -------------------------------------------------------------------------------- /doc/LanguageBar.md: -------------------------------------------------------------------------------- 1 | ## 3.15 语言栏 2 | 3 | 语言栏,设置输入法状态开关。 4 | 5 | ## 3.15.1 语言栏项管理器 6 | 7 | Interface |Description 8 | -|- 9 | [ITfLangBarItemMgr][1] |语言栏项管理器,用于管理语言栏中的项。 10 | 11 | [1]: https://github.com/ChineseInputMethod/Interface/blob/master/LanguageBar/ITfLangBarItemMgr.md 12 | 13 | ```C++ 14 | HRESULT CLangBarItemButton::_AddItem(_In_ ITfThreadMgr *pThreadMgr) 15 | { 16 | HRESULT hr = S_OK; 17 | ITfLangBarItemMgr* pLangBarItemMgr = nullptr; 18 | 19 | if (_isAddedToLanguageBar) 20 | { 21 | return S_OK; 22 | } 23 | 24 | hr = pThreadMgr->QueryInterface(IID_ITfLangBarItemMgr, (void **)&pLangBarItemMgr); 25 | if (SUCCEEDED(hr)) 26 | { 27 | hr = pLangBarItemMgr->AddItem(this); 28 | if (SUCCEEDED(hr)) 29 | { 30 | _isAddedToLanguageBar = TRUE; 31 | } 32 | pLangBarItemMgr->Release(); 33 | } 34 | 35 | return hr; 36 | } 37 | ``` 38 | 39 | ## 3.15.2 语言栏项信息 40 | 41 | Interface |Description 42 | -|- 43 | [ITfLangBarItem][2] |语言栏项信息,由语言栏管理器用来获取语言栏项的详细信息。 44 | 45 | [2]: https://github.com/ChineseInputMethod/Interface/blob/master/TextService/ITfLangBarItem.md 46 | 47 | ```C++ 48 | STDAPI CLangBarItemButton::GetInfo(_Out_ TF_LANGBARITEMINFO *pInfo) 49 | { 50 | _tfLangBarItemInfo.dwStyle |= TF_LBI_STYLE_SHOWNINTRAY; 51 | *pInfo = _tfLangBarItemInfo; 52 | return S_OK; 53 | } 54 | ``` 55 | 56 | ## 3.15.3 语言栏项消息接收器 57 | 58 | Interface |Description 59 | -|- 60 | [ITfLangBarItemSink][3] |语言栏项消息接收器,用于将语言栏项中的更改通知语言栏。 61 | 62 | [3]: https://github.com/ChineseInputMethod/Interface/blob/master/LanguageBar/ITfLangBarItemSink.md 63 | 64 | 当调用ITfLangBarItemMgr::AddItem()方法,将语言栏项(输入法)安装到语言栏后,语言栏(系统)立即调用ITfSource::AdviseSink()方法,将ITfLangBarItemSink语言栏项消息接收器安装到语言栏项(输入法)中。 65 | 66 | ```C++ 67 | STDAPI CLangBarItemButton::AdviseSink(__RPC__in REFIID riid, __RPC__in_opt IUnknown *punk, __RPC__out DWORD *pdwCookie) 68 | { 69 | // We allow only ITfLangBarItemSink interface. 70 | if (!IsEqualIID(IID_ITfLangBarItemSink, riid)) 71 | { 72 | return CONNECT_E_CANNOTCONNECT; 73 | } 74 | 75 | // We support only one sink once. 76 | if (_pLangBarItemSink != nullptr) 77 | { 78 | return CONNECT_E_ADVISELIMIT; 79 | } 80 | 81 | // Query the ITfLangBarItemSink interface and store it into _pLangBarItemSink. 82 | if (punk == nullptr) 83 | { 84 | return E_INVALIDARG; 85 | } 86 | if (punk->QueryInterface(IID_ITfLangBarItemSink, (void **)&_pLangBarItemSink) != S_OK) 87 | { 88 | _pLangBarItemSink = nullptr; 89 | return E_NOINTERFACE; 90 | } 91 | 92 | // return our cookie. 93 | *pdwCookie = _cookie; 94 | return S_OK; 95 | } 96 | ``` 97 | 98 | 当更改输入法状态时,输入法调用ITfLangBarItemSink::OnUpdate()方法,通知语言栏(系统)。 99 | 100 | ## 3.15.4 语言栏按钮项信息 101 | 102 | Interface |Description 103 | -|- 104 | [ITfLangBarItemButton][4] |语言栏按钮项信息,由语言栏管理器用来获取语言栏上的按钮项信息。 105 | 106 | [4]: https://github.com/ChineseInputMethod/Interface/blob/master/TextService/ITfLangBarItemButton.md 107 | 108 | ```C++ 109 | STDAPI CLangBarItemButton::OnClick(TfLBIClick click, POINT pt, _In_ const RECT *prcArea) 110 | { 111 | click;pt; 112 | prcArea; 113 | 114 | BOOL isOn = FALSE; 115 | 116 | _pCompartment->_GetCompartmentBOOL(isOn); 117 | _pCompartment->_SetCompartmentBOOL(isOn ? FALSE : TRUE); 118 | 119 | return S_OK; 120 | } 121 | ``` -------------------------------------------------------------------------------- /SampleIME/SampleIME.rc: -------------------------------------------------------------------------------- 1 | // Microsoft Visual C++ generated resource script. 2 | // 3 | #include "resource.h" 4 | ///////////////////////////////////////////////////////////////////////////// 5 | // English (United States) resources 6 | 7 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) 8 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US 9 | #pragma code_page(1252) 10 | 11 | ///////////////////////////////////////////////////////////////////////////// 12 | // 13 | // Icon 14 | // 15 | 16 | // Icon with lowest ID value placed first to ensure application icon 17 | // remains consistent on all systems. 18 | IDIS_SAMPLEIME ICON "image\\SampleIme.ico" 19 | IDI_IME_MODE_ON ICON "image\\ImeModeOn.ico" 20 | IDI_IME_MODE_OFF ICON "image\\ImeModeOff.ico" 21 | IDI_DOUBLE_SINGLE_BYTE_ON ICON "image\\DoubleSingleByteOn.ico" 22 | IDI_DOUBLE_SINGLE_BYTE_OFF ICON "image\\DoubleSingleByteOff.ico" 23 | IDI_PUNCTUATION_ON ICON "image\\PunctuationOn.ico" 24 | IDI_PUNCTUATION_OFF ICON "image\\PunctuationOff.ico" 25 | 26 | ///////////////////////////////////////////////////////////////////////////// 27 | // 28 | // Version 29 | // 30 | 31 | IDR_VERSION2 VERSIONINFO 32 | FILEVERSION 1,0,0,1 33 | PRODUCTVERSION 1,0,0,1 34 | FILEFLAGSMASK 0x3fL 35 | #ifdef _DEBUG 36 | FILEFLAGS 0x1L 37 | #else 38 | FILEFLAGS 0x0L 39 | #endif 40 | FILEOS 0x40004L 41 | FILETYPE 0x2L 42 | FILESUBTYPE 0x0L 43 | BEGIN 44 | BLOCK "StringFileInfo" 45 | BEGIN 46 | BLOCK "040904b0" 47 | BEGIN 48 | VALUE "CompanyName", "MSFT" 49 | VALUE "FileDescription", "The Sample code of Windows 8 IME" 50 | VALUE "FileVersion", "1.0.0.1" 51 | VALUE "InternalName", "SampleIM.dll" 52 | VALUE "LegalCopyright", "Copyright (C)" 53 | VALUE "OriginalFilename", "SampleIM.dll" 54 | VALUE "ProductName", "SampleIME" 55 | VALUE "ProductVersion", "1.0.0.1" 56 | END 57 | END 58 | BLOCK "VarFileInfo" 59 | BEGIN 60 | VALUE "Translation", 0x409, 1200 61 | END 62 | END 63 | 64 | 65 | #ifdef APSTUDIO_INVOKED 66 | ///////////////////////////////////////////////////////////////////////////// 67 | // 68 | // TEXTINCLUDE 69 | // 70 | 71 | 1 TEXTINCLUDE 72 | BEGIN 73 | "resource.h\0" 74 | END 75 | 76 | 2 TEXTINCLUDE 77 | BEGIN 78 | "\0" 79 | END 80 | 81 | 3 TEXTINCLUDE 82 | BEGIN 83 | "\r\n" 84 | "\0" 85 | END 86 | 87 | #endif // APSTUDIO_INVOKED 88 | 89 | 90 | ///////////////////////////////////////////////////////////////////////////// 91 | // 92 | // String Table 93 | // 94 | 95 | STRINGTABLE 96 | BEGIN 97 | IDIS_SAMPLEIME "Chinese Simplified QuanPin (version 6.0)" 98 | IDS_DEFAULT_FONT "Microsoft YaHei UI" 99 | END 100 | 101 | STRINGTABLE 102 | BEGIN 103 | IDS_IME_MODE "Chinese/English input (Shift)" 104 | IDS_DOUBLE_SINGLE_BYTE "Double/Single byte (Shift+Space)" 105 | IDS_PUNCTUATION "Chinese/English punctuation (Ctrl+.)" 106 | END 107 | 108 | #endif // English (United States) resources 109 | ///////////////////////////////////////////////////////////////////////////// 110 | 111 | 112 | 113 | #ifndef APSTUDIO_INVOKED 114 | ///////////////////////////////////////////////////////////////////////////// 115 | // 116 | // Generated from the TEXTINCLUDE 3 resource. 117 | // 118 | 119 | 120 | ///////////////////////////////////////////////////////////////////////////// 121 | #endif // not APSTUDIO_INVOKED 122 | 123 | -------------------------------------------------------------------------------- /doc/TextEditSink.md: -------------------------------------------------------------------------------- 1 | ## 3.7 编辑会话完成消息接收器 2 | 3 | 编辑会话完成消息接收器,用于感知其他文本服务对编辑内容的更改。 4 | 5 | ## 3.7.1 安装编辑会话完成消息接收器 6 | 7 | Interface |Description 8 | -|- 9 | [ITfDocumentMgr][1] |文档管理器,主要用来创建和管理上下文。 10 | 11 | [1]: https://github.com/ChineseInputMethod/Interface/blob/master/TSFmanager/ITfDocumentMgr.md 12 | 13 | 首先清除已安装的编辑会话完成消息接收器。 14 | 15 | ```C++ 16 | if (_textEditSinkCookie != TF_INVALID_COOKIE) 17 | { 18 | if (SUCCEEDED(_pTextEditSinkContext->QueryInterface(IID_ITfSource, (void **)&pSource))) 19 | { 20 | pSource->UnadviseSink(_textEditSinkCookie); 21 | pSource->Release(); 22 | } 23 | 24 | _pTextEditSinkContext->Release(); 25 | _pTextEditSinkContext = nullptr; 26 | _textEditSinkCookie = TF_INVALID_COOKIE; 27 | } 28 | ``` 29 | 30 | 然后得到文档顶部的上下文。 31 | 32 | ```C++ 33 | if (FAILED(pDocMgr->GetTop(&_pTextEditSinkContext))) 34 | { 35 | return FALSE; 36 | } 37 | ``` 38 | 39 | 最后在上下文中安装编辑会话完成消息接收器。 40 | 41 | ```C++ 42 | if (SUCCEEDED(_pTextEditSinkContext->QueryInterface(IID_ITfSource, (void **)&pSource))) 43 | { 44 | if (SUCCEEDED(pSource->AdviseSink(IID_ITfTextEditSink, (ITfTextEditSink *)this, &_textEditSinkCookie))) 45 | { 46 | ret = TRUE; 47 | } 48 | else 49 | { 50 | _textEditSinkCookie = TF_INVALID_COOKIE; 51 | } 52 | pSource->Release(); 53 | } 54 | ``` 55 | 56 | ## 3.7.2 处理编辑会话完成消息 57 | 58 | Interface |Description 59 | -|- 60 | [ITfEditRecord][2] |编辑记录,用来确定编辑会话期间更改的内容。 61 | 62 | [2]: https://github.com/ChineseInputMethod/Interface/blob/master/TSFmanager/ITfEditRecord.md 63 | 64 | 安装ITfTextEditSink编辑会话完成消息接收器后,每当写访问文档锁被释放时,TSF管理器调用OnEndEdit()方法。 65 | 66 | 首先判断编辑内容是否已更改。 67 | 68 | ```C++ 69 | if (SUCCEEDED(pEditRecord->GetSelectionStatus(&isSelectionChanged)) && 70 | isSelectionChanged) 71 | { 72 | ``` 73 | 74 | 当编辑内容已更改,且处于输入状态时,获取文档中的选定内容。 75 | 76 | ```C++ 77 | if (_IsComposing()) 78 | { 79 | TF_SELECTION tfSelection; 80 | ULONG fetched = 0; 81 | 82 | if (pContext == nullptr) 83 | { 84 | return E_INVALIDARG; 85 | } 86 | if (FAILED(pContext->GetSelection(ecReadOnly, TF_DEFAULT_SELECTION, 1, &tfSelection, &fetched)) || fetched != 1) 87 | { 88 | return S_FALSE; 89 | } 90 | ``` 91 | 92 | 然后获取一个范围对象,该对象包含合成所涵盖的文本。 93 | 94 | ```C++ 95 | ITfRange* pRangeComposition = nullptr; 96 | if (SUCCEEDED(_pComposition->GetRange(&pRangeComposition))) 97 | { 98 | ``` 99 | 100 | 如果其他文本服务更改的编辑内容覆盖了输入法正在编辑的文本范围,则结束合成。 101 | 102 | ```C++ 103 | if (!_IsRangeCovered(ecReadOnly, tfSelection.range, pRangeComposition)) 104 | { 105 | _EndComposition(pContext); 106 | } 107 | ``` 108 | 109 | ## 3.7.3 文本范围 110 | 111 | Interface |Description 112 | -|- 113 | [ITfRange][3] |文本范围,用来引用和操作给定上下文中的文本 114 | 115 | [3]: https://github.com/ChineseInputMethod/Interface/blob/master/TSFmanager/ITfRange.md 116 | 117 | TSF不使用绝对索引来定位文本流的位置,而是使用被称为ITfRange文本范围的相对位置。 118 | 119 | 这段代码的意思是,如果输入组合的起点在更改后的文本范围起点之前,则返回FALSE。 120 | 121 | ```C++ 122 | if (FAILED(pRangeCover->CompareStart(ec, pRangeTest, TF_ANCHOR_START, &lResult)) 123 | || (lResult > 0)) 124 | { 125 | return FALSE; 126 | } 127 | ``` 128 | 129 | 下面这段代码的意思是,如果输入组合的终点在更改后的文本范围终点之后,则返回FALSE。 130 | 131 | ```C++ 132 | if (FAILED(pRangeCover->CompareEnd(ec, pRangeTest, TF_ANCHOR_END, &lResult)) 133 | || (lResult < 0)) 134 | { 135 | return FALSE; 136 | } 137 | ``` 138 | 139 | 作者并不明白这个覆盖的含义,大致意思是说,只要输入组合的端点超出了修改后文本范围,就要结束合成。 -------------------------------------------------------------------------------- /doc/appendix/选择候选字词.md: -------------------------------------------------------------------------------- 1 | ## B.1 选择候选字词 2 | 3 | 加载2023年10月22日版本的track.xml断点文件,调试时会包含如下跟踪信息。该信息包含了从按下空格键选择候选字词到将汉字输出到文档中的主要函数调用过程。 4 | 5 | 候选窗口开启时,当按下数字键或使用鼠标选择候选字词时,输入法会计算当前候选项的索引,将当前候选字词输出到文档中。本文只跟踪了其中最简单的场景:按下空格键选择候选字词。 6 | 7 | 按下空格键后,输入法判断是否处理该按键。如果需要处理该按键,则发起编辑会话。 8 | 9 | Function |Description 10 | -|- 11 | /* | 12 | CSampleIME::OnKeyDown(ITfContext *, unsigned __int64, __int64, int *) |按下空格键 13 | Global::UpdateModifiers(unsigned __int64, __int64) | 14 | CSampleIME::_IsKeyEaten(ITfContext *, unsigned int, unsigned int *, wchar_t *, _KEYSTROKE_STATE *) |判断是否需要处理 15 | CSampleIME::_IsKeyboardDisabled(void) | 16 | CSampleIME::ConvertVKey(unsigned int) | 17 | VKeyFromVKPacketAndWchar(unsigned int, wchar_t) | 18 | CSampleIME::_IsComposing(void) 0x000002cfa2ba1310 <无可用信息,未为 TextInputFramework.dll 加载任何符号> | 19 | CCompositionProcessorEngine::IsVirtualKeyNeed(unsigned int, wchar_t *, int, CANDIDATE_MODE, int, _KEYSTROKE_STATE *) | 20 | CCompositionProcessorEngine::IsVirtualKeyKeystrokeComposition(unsigned int, _KEYSTROKE_STATE *, KEYSTROKE_FUNCTION) | 21 | CCompositionProcessorEngine::IsVirtualKeyKeystrokeCandidate(unsigned int, _KEYSTROKE_STATE *, CANDIDATE_MODE, int *, CSampleImeArray *) | 22 | CSampleIME::_InvokeKeyHandler(ITfContext *, unsigned int, wchar_t, unsigned long, _KEYSTROKE_STATE) 0x00000020 0x0020 ' ' | 23 | 24 | 发起编辑会话,在编辑会话中处理按键。 25 | 26 | Function |Description 27 | -|- 28 | CKeyHandlerEditSession::DoEditSession(unsigned long) 0xae2e5010 |编辑会话 29 | CKeyStateCategoryFactory::Instance(void) | 30 | CKeyStateCategoryFactory::MakeKeyStateCategory(KEYSTROKE_CATEGORY, CSampleIME *) |按键分类 31 | CKeyStateCategory::KeyStateHandler(KEYSTROKE_FUNCTION, KeyHandlerEditSessionDTO) | 32 | CKeyStateCandidate::HandleKeyConvert(KeyHandlerEditSessionDTO) | 33 | CSampleIME::_HandleCandidateConvert(unsigned long, ITfContext *) | 34 | 35 | 获取当前候选字词,处理候选列表,实现联想输入。 36 | 37 | Function |Description 38 | -|- 39 | CSampleIME::_HandleCandidateWorker(unsigned long, ITfContext *) 40 | CCandidateListUIPresenter::_GetSelectedCandidateString(const wchar_t * *) |获取当前候选字词 41 | CCandidateWindow::_GetSelectedCandidateString(const wchar_t * *) | 42 | CCompositionProcessorEngine::GetCandidateStringInConverted(CStringRange &, CSampleImeArray *) |联想输入 43 | CTableDictionaryEngine::CollectWordFromConvertedStringForWildcard(CStringRange *, CSampleImeArray *) | 44 | CDictionarySearch::FindConvertedStringForWildcard(CDictionaryResult * *) | 45 | CDictionarySearch::FindConvertedStringForWildcard(CDictionaryResult * *) | 46 | CDictionarySearch::FindConvertedStringForWildcard(CDictionaryResult * *) | 47 | CBaseDictionaryEngine::SortListItemByFindKeyCode(CSampleImeArray | 48 | CCandidateListUIPresenter::RemoveSpecificCandidateFromList(unsigned long, CSampleImeArray &, CStringRange &) | 49 | 50 | 将当前候选字词写入到合成中。 51 | 52 | Function |Description 53 | -|- 54 | CSampleIME::_HandleCandidateFinalize(unsigned long, ITfContext *) |完成候选列表处理 55 | CCandidateListUIPresenter::_GetSelectedCandidateString(const wchar_t * *) | 56 | CCandidateWindow::_GetSelectedCandidateString(const wchar_t * *) |获取当前候选字词 57 | CSampleIME::_AddComposingAndChar(unsigned long, ITfContext *, CStringRange *) |将当前候选字词写入到合成中 58 | CSampleIME::_FindComposingRange(unsigned long, ITfContext *, ITfRange *, ITfRange * *) | 59 | CSampleIME::_SetInputString(unsigned long, ITfContext *, ITfRange *, CStringRange *, int) | 60 | CSampleIME::_SetCompositionLanguage(unsigned long, ITfContext *) | 61 | CSampleIME::_SetCompositionDisplayAttributes(unsigned long, ITfContext *, unsigned long) | -------------------------------------------------------------------------------- /doc/DisplayAttribute.md: -------------------------------------------------------------------------------- 1 | ## 3.38 显示属性 2 | 3 | 显示属性,合成中编码或汉字的显示样式。 4 | 5 | TSF框架由TSF管理器管理输入编码和转换字符(日韩输入法中的概念)的显示,输入法负责向TSF管理器提供显示的内容和样式。
6 | 内容就是合成中的输入编码和转换字符。样式就是本主题介绍的显示属性。 7 | 8 | ## 3.38.1 激活 9 | 10 | Interface |Description 11 | -|- 12 | [ITfCategoryMgr][1] |类别管理器,为输入法管理类别。 13 | 14 | [1]: https://github.com/ChineseInputMethod/Interface/blob/master/TSFmanager/ITfCategoryMgr.md 15 | 16 | 因为效率的原因,输入法在激活的时候,需要调用ITfCategoryMgr::RegisterGUID()方法,将GUID添加到内部表,并获取GUID的原子。 17 | 18 | ```C++ 19 | BOOL CSampleIME::_InitDisplayAttributeGuidAtom() 20 | { 21 | ITfCategoryMgr* pCategoryMgr = nullptr; 22 | HRESULT hr = CoCreateInstance(CLSID_TF_CategoryMgr, nullptr, CLSCTX_INPROC_SERVER, IID_ITfCategoryMgr, (void**)&pCategoryMgr); 23 | 24 | if (FAILED(hr)) 25 | { 26 | return FALSE; 27 | } 28 | 29 | // register the display attribute for input text. 30 | hr = pCategoryMgr->RegisterGUID(Global::SampleIMEGuidDisplayAttributeInput, &_gaDisplayAttributeInput); 31 | if (FAILED(hr)) 32 | { 33 | goto Exit; 34 | } 35 | // register the display attribute for the converted text. 36 | hr = pCategoryMgr->RegisterGUID(Global::SampleIMEGuidDisplayAttributeConverted, &_gaDisplayAttributeConverted); 37 | if (FAILED(hr)) 38 | { 39 | goto Exit; 40 | } 41 | 42 | Exit: 43 | pCategoryMgr->Release(); 44 | 45 | return (hr == S_OK); 46 | } 47 | ``` 48 | 49 | ## 3.38.2 合成 50 | 51 | 在CSampleIME::_SetInputString()函数中,将编码或汉字写入到合成后,调用_SetCompositionDisplayAttributes()函数,为文本范围设置显示属性。 52 | 53 | 首先,获取合成中的文本范围。然后,为该文本范围设置显示属性。 54 | 55 | ```C++ 56 | BOOL CSampleIME::_SetCompositionDisplayAttributes(TfEditCookie ec, _In_ ITfContext *pContext, TfGuidAtom gaDisplayAttribute) 57 | { 58 | ITfRange* pRangeComposition = nullptr; 59 | ITfProperty* pDisplayAttributeProperty = nullptr; 60 | HRESULT hr = S_OK; 61 | 62 | // we need a range and the context it lives in 63 | hr = _pComposition->GetRange(&pRangeComposition); 64 | if (FAILED(hr)) 65 | { 66 | return FALSE; 67 | } 68 | 69 | hr = E_FAIL; 70 | 71 | // get our the display attribute property 72 | if (SUCCEEDED(pContext->GetProperty(GUID_PROP_ATTRIBUTE, &pDisplayAttributeProperty))) 73 | { 74 | VARIANT var; 75 | // set the value over the range 76 | // the application will use this guid atom to lookup the acutal rendering information 77 | var.vt = VT_I4; // we're going to set a TfGuidAtom 78 | var.lVal = gaDisplayAttribute; 79 | 80 | hr = pDisplayAttributeProperty->SetValue(ec, pRangeComposition, &var); 81 | 82 | pDisplayAttributeProperty->Release(); 83 | } 84 | 85 | pRangeComposition->Release(); 86 | return (hr == S_OK); 87 | } 88 | ``` 89 | 90 | 输入法调用ITfProperty::SetValue()方法后,TSF管理器会获取ITfDisplayAttributeProvider接口, 91 | 调用ITfDisplayAttributeProvider::GetDisplayAttributeInfo()方法,获取显示属性信息。 92 | 93 | ## 3.38.3 清除 94 | 95 | Interface |Description 96 | -|- 97 | [ITfProperty][2] |属性设置,由客户端(应用程序或文本服务)用来修改显示属性。 98 | 99 | [2]: https://github.com/ChineseInputMethod/Interface/blob/master/TSFmanager/ITfProperty.md 100 | 101 | 在结束合成之前,输入法调用CSampleIME::_ClearCompositionDisplayAttributes()函数,清除合成显示属性。 102 | 103 | ```C++ 104 | void CSampleIME::_ClearCompositionDisplayAttributes(TfEditCookie ec, _In_ ITfContext *pContext) 105 | { 106 | ITfRange* pRangeComposition = nullptr; 107 | ITfProperty* pDisplayAttributeProperty = nullptr; 108 | 109 | // get the compositon range. 110 | if (FAILED(_pComposition->GetRange(&pRangeComposition))) 111 | { 112 | return; 113 | } 114 | 115 | // get our the display attribute property 116 | if (SUCCEEDED(pContext->GetProperty(GUID_PROP_ATTRIBUTE, &pDisplayAttributeProperty))) 117 | { 118 | // clear the value over the range 119 | pDisplayAttributeProperty->Clear(ec, pRangeComposition); 120 | 121 | pDisplayAttributeProperty->Release(); 122 | } 123 | 124 | pRangeComposition->Release(); 125 | } 126 | ``` -------------------------------------------------------------------------------- /doc/composition/StartComposition.md: -------------------------------------------------------------------------------- 1 | ## 3.35 开始合成 2 | 3 | 开始合成,在上下文中创建一个合成。 4 | 5 | ## 3.35.1 输出汉字的第一种方式 6 | 7 | 将StartComposition.cpp文件的第48行注释掉,取消第47行和52行的注释。 8 | 将CStartCompositionEditSession::DoEditSession()函数修改为下图所示。 9 | 10 | ![输出汉字的第一种方式](输出汉字的第一种方式.png) 11 | 12 | 在字处理软件中激活本工程输入法,按下编码键,字符串“输出汉字的第一种方式”会被插入到当前输入焦点。 13 | 这是输出汉字的第一种方式。 14 | 15 | ## 3.35.2 输出汉字的第二种方式 16 | 17 | 取消53行到59行的注释,将CStartCompositionEditSession::DoEditSession()函数修改为下图所示。 18 | 19 | ![输出汉字的第二种方式](输出汉字的第二种方式.png) 20 | 21 | 在字处理软件中激活本工程输入法,按下编码键,字符串“输出汉字的第二种方式”会被插入到当前输入焦点。 22 | 这是输出汉字的第二种方式。 23 | 24 | 可以看到第54行到58行代码,与70行到75行代码相似。这是TSF输入法输出汉字与第二种输出汉字方式的相似之处。 25 | 不同的是,TSF输入法调用ITfContextComposition::StartComposition()方法,在上下文中创建了一个ITfComposition合成。 26 | 27 | ## 3.35.3 什么是合成 28 | 29 | 类似于页面布局,元素被放在标签内,用标记语言进行排版。铅字印刷时代对文字也需要进行一定的排版。 30 | 或是因为字体的不同,或是因为磨损的原因,需要对字模在xyz轴上进行调整。如果是以字母为单位,效率不高,效果也不好。 31 | 所以一般都是以单词为单位,调整页面布局。 32 | 33 | 早期的字处理软件,类似于Markdown这样的语法,有可见字符,也有控制字符。控制字符控制可见字符的显示。 34 | 这种模式非常方便字母文字的输入。但是输入东亚文字则有一点不便。(作者只了解一点日韩文字) 35 | 36 | ucdos时代,页面底部有个控制条,可以在控制条里输入编码,选择想要输出的汉字。这种方式显然不适合字母文字的录入方式。字母文字在编辑区直接可以输出字母。 37 | 日韩文字就夹在这两种模式之间。 38 | 39 | 在Windows 95系统中,提出了合成概念。与现在TSF合成概念不同的是,win95中的合成,包含了输入编码的多种状态。 40 | 具体的讲,就是输入文字的编码时,此时是输入状态。结束输入编码后,自动或手动转换为想要输入的文字,此时是转换状态。 41 | 如果输入的是系统为收录的自造词,还需要管理已转换编码和未转换编码。 42 | 43 | 作者当时并不懂这些,很长时间都搞不懂,怎么输出一个汉字。现在才明白,如果从日韩输入法的角度看,就好理解了。 44 | 因为日韩文字一般不需要候选列表,罗马字拼写与文字是强一一对应关系。所以在编辑区直接编辑其不同状态,是非常便捷直观的。 45 | 46 | 而汉字输入法中的形码输入法,其编码与文字的对应关系是附会的。在编辑区出现汉字的输入编码就显得有点突兀。 47 | 另一方面,汉字输入法中的拼音输入法,存在着无法完全避免的同音字,所以在编码之后拖着一个候选窗口。 48 | 49 | 当时,作者很多都想不明白的事,现在都明白了其背后的原因。例如智能ABC输入完编码,需要按下空格才出现其他候选字词。 50 | 这应该和注音输入法和日韩输入法有关,将候选窗口隐藏了起来。 51 | 例如形码输入法,追求一个顶屏功能(输入完全码,如果没有重码字,自动上屏)。这有意或者无意的受了日韩输入法的影响。 52 | 53 | 什么是合成? 54 | 55 | 就是在上下文中,将当前正在输入的内容,添加一个属性,表示这段内容是待确定的。 56 | 当这段内容完成了从编码到文字的转换,就从这段内容中删除这个待确定属性。这段内容正式被添加到上下文中(其实一直在上下文中,只不过去掉了这个待确定属性)。 57 | 58 | 这段内容,在TSF术语里就是ITfRange。这个待确定属性就是ITfComposition。 59 | TSF输入法调用ITfContextComposition::StartComposition()方法,在上下文中创建了一个ITfComposition合成。 60 | 就是在上下文中,将当前正在输入的内容,添加一个属性,表示这段内容是待确定的。 61 | 62 | ## 3.35.4 开始合成 63 | 64 | Interface |Description 65 | -|- 66 | [ITfInsertAtSelection][1] |在选定位置插入内容,用于在上下文中插入文本或嵌入对象。 67 | [ITfContextComposition][2] |上下文合成,用于创建ITfComposition合成。 68 | 69 | [1]: https://github.com/ChineseInputMethod/Interface/blob/master/TSFmanager/ITfInsertAtSelection.md 70 | [2]: https://github.com/ChineseInputMethod/Interface/blob/master/TSFmanager/ITfContextComposition.md 71 | 72 | 当用户按下编码键,如果输入状态不是处于合成状态,那么创建一个编辑会话。在编辑会话中创建一个合成。 73 | 74 | 首先,获取在选定位置插入内容接口; 75 | 然后,调用ITfInsertAtSelection::InsertTextAtSelection()方法,通过插入一个空字符,获得ITfRange文本范围; 76 | 接着,获取上下文合成接口; 77 | 然后,调用ITfContextComposition::StartComposition()方法,将所选文本范围标记为GUID_PROP_COMPOSING属性; 78 | 最后,调用ITfContext::SetSelection()方法,将标记后的文本范围设置为上下文中的选定内容。 79 | 80 | ```C++ 81 | STDAPI CStartCompositionEditSession::DoEditSession(TfEditCookie ec) 82 | { 83 | ITfInsertAtSelection* pInsertAtSelection = nullptr; 84 | ITfRange* pRangeInsert = nullptr; 85 | ITfContextComposition* pContextComposition = nullptr; 86 | ITfComposition* pComposition = nullptr; 87 | 88 | if (FAILED(_pContext->QueryInterface(IID_ITfInsertAtSelection, (void **)&pInsertAtSelection))) 89 | { 90 | goto Exit; 91 | } 92 | 93 | if (FAILED(pInsertAtSelection->InsertTextAtSelection(ec, TF_IAS_QUERYONLY, NULL, 0, &pRangeInsert))) 94 | { 95 | goto Exit; 96 | } 97 | 98 | if (FAILED(_pContext->QueryInterface(IID_ITfContextComposition, (void **)&pContextComposition))) 99 | { 100 | goto Exit; 101 | } 102 | 103 | if (SUCCEEDED(pContextComposition->StartComposition(ec, pRangeInsert, _pTextService, &pComposition)) && (nullptr != pComposition)) 104 | { 105 | _pTextService->_SetComposition(pComposition); 106 | 107 | // set selection to the adjusted range 108 | TF_SELECTION tfSelection; 109 | tfSelection.range = pRangeInsert; 110 | tfSelection.style.ase = TF_AE_NONE; 111 | tfSelection.style.fInterimChar = FALSE; 112 | 113 | _pContext->SetSelection(ec, 1, &tfSelection); 114 | _pTextService->_SaveCompositionContext(_pContext); 115 | } 116 | 117 | ... 118 | ``` -------------------------------------------------------------------------------- /doc/DictionarySearch.md: -------------------------------------------------------------------------------- 1 | ## 3.24 词典搜索 2 | 3 | 词典搜索,在词典中搜索编码为输入编码的汉字。 4 | 5 | ## 3.24.1 搜索词典 6 | 7 | 本工程将整个码表文件映射为了一维数组。
8 | 首先,获取数组的长度和首地址。 9 | 10 | ```C++ 11 | BOOL CDictionarySearch::FindWorker(BOOL isTextSearch, _Out_ CDictionaryResult **ppdret, BOOL isWildcardSearch) 12 | { 13 | DWORD_PTR dwTotalBufLen = GetBufferInWCharLength(); // in char 14 | if (dwTotalBufLen == 0) { ... } 15 | 16 | const WCHAR *pwch = GetBufferInWChar(); 17 | DWORD_PTR indexTrace = 0; // in char 18 | *ppdret = nullptr; 19 | BOOL isFound = FALSE; 20 | DWORD_PTR bufLenOneLine = 0; 21 | ... 22 | ``` 23 | 24 | 然后从当前地址,读取剩余长度,至回车符。
25 | 例如,第一次调用,当前地址就是首地址,剩余长度就是数组长度,从当前位置直到首个回车符为一行。
26 | 第二次调用indexTrace += bufLenOneLine;dwTotalBufLen -= bufLenOneLine;
27 | 也就是&pwch[indexTrace]偏移了bufLenOneLine个字符,从&pwch[indexTrace]开始,读取dwTotalBufLen长度,直到遇到首个回车符。
28 | (因为还要处理换行符,在FindNextLine:部分,还要多偏移一个字符) 29 | 30 | ```C++ 31 | ... 32 | TryAgain: 33 | bufLenOneLine = GetOneLine(&pwch[indexTrace], dwTotalBufLen); 34 | ... 35 | ``` 36 | 37 | 接着调用第四个参数使用默认实参的ParseLine(&pwch[indexTrace], bufLenOneLine, &keyword)函数,获得文件中一行码表中的编码部分。 38 | 39 | ```C++ 40 | ... 41 | if (bufLenOneLine == 0) { ... } 42 | else 43 | { 44 | CParserStringRange keyword; 45 | DWORD_PTR bufLen = 0; 46 | LPWSTR pText = nullptr; 47 | 48 | if (!ParseLine(&pwch[indexTrace], bufLenOneLine, &keyword)) { ... } 49 | ... 50 | ``` 51 | 52 | 然后进行通配符比较。如果通配符比较返回FALSE,则继续查找下一行码表。 53 | 54 | ```C++ 55 | ... 56 | if (!isTextSearch) 57 | { 58 | // Compare Dictionary key code and input key code 59 | if (!isWildcardSearch) { ... } 60 | else 61 | { 62 | // Wildcard search 63 | if (!CStringRange::WildcardCompare(_locale, _pSearchKeyCode, &keyword)) 64 | { 65 | if (bufLen) { ... } 66 | goto FindNextLine; 67 | } 68 | } 69 | } 70 | else { ... } 71 | 72 | if (bufLen) { ... } 73 | ... 74 | ``` 75 | 76 | 如果通配符比较返回TRUE,则调用使用实参初始化第四个参数的ParseLine(&pwch[indexTrace], bufLenOneLine, &keyword, &valueStrings)函数。 77 | 78 | ```C++ 79 | ... 80 | // Prepare return's CDictionaryResult 81 | *ppdret = new (std::nothrow) CDictionaryResult(); 82 | if (!*ppdret) { ... } 83 | 84 | CSampleImeArray valueStrings; 85 | if (!ParseLine(&pwch[indexTrace], bufLenOneLine, &keyword, &valueStrings)) { ... } 86 | ... 87 | ``` 88 | 89 | 然后将查找结果保存到搜索结果中。 90 | 91 | ```C++ 92 | ... 93 | (*ppdret)->_FindKeyCode = keyword; 94 | (*ppdret)->_SearchKeyCode = *_pSearchKeyCode; 95 | 96 | for (UINT i = 0; i < valueStrings.Count(); i++) 97 | { 98 | CStringRange* findPhrase = (*ppdret)->_FindPhraseList.Append(); 99 | if (findPhrase) 100 | { 101 | *findPhrase = *valueStrings.GetAt(i); 102 | } 103 | } 104 | 105 | // Seek to next line 106 | isFound = TRUE; 107 | } 108 | ... 109 | ``` 110 | 111 | 如果找到了搜索结果,则退出。否则继续搜索。 112 | 113 | ```C++ 114 | ... 115 | FindNextLine: 116 | dwTotalBufLen -= bufLenOneLine; 117 | if (dwTotalBufLen == 0) 118 | { 119 | indexTrace += bufLenOneLine; 120 | _charIndex += indexTrace; 121 | 122 | if (!isFound && *ppdret) 123 | { 124 | delete *ppdret; 125 | *ppdret = nullptr; 126 | } 127 | return (isFound ? TRUE : FALSE); // End of file 128 | } 129 | 130 | indexTrace += bufLenOneLine; 131 | if (pwch[indexTrace] == L'\r' || pwch[indexTrace] == L'\n' || pwch[indexTrace] == L'\0') 132 | { 133 | bufLenOneLine = 1; 134 | goto FindNextLine; 135 | } 136 | 137 | if (isFound) 138 | { 139 | _charIndex += indexTrace; 140 | return TRUE; 141 | } 142 | 143 | goto TryAgain; 144 | } 145 | ``` 146 | 147 | 本工程的候选列表默认搜索方式,在输入法中被称为省码或者简码。
148 | 例如,输入编码“wom”,用户不用输全最后一个字的全部拼音,就可以显示候选词,被称为省码。
149 | 这一方式与字处理软件的自动补全形式相同,但输入法的处理更加复杂。(本工程未表现这种复杂性) 150 | 151 | 响应用户操作提供候选字词,是输入法的核心内容。判断一个输入法优秀与否,其最具决定性的因素就是:
152 | 用户是否能“XX”的输入TA想要输入的汉字。 153 | 154 | 这里的XX,不同的发明人,不同的用户,可能有不同的定义。也许舒适比较能描述这一技术特点。 -------------------------------------------------------------------------------- /doc/appendix/销毁候选窗口.md: -------------------------------------------------------------------------------- 1 | ## B.2 销毁候选窗口 2 | 3 | 加载2023年10月22日版本的track.xml断点文件,调试时会包含如下跟踪信息。该信息包含了从按下空格键选择候选字词后,销毁候选窗口的主要函数调用过程。 4 | 5 | Function |Description 6 | -|- 7 | CSampleIME::_HandleComplete(unsigned long, ITfContext *) |完成处理 8 | CSampleIME::_DeleteCandidateList(int, ITfContext *) | 9 | CCompositionProcessorEngine::PurgeVirtualKey(void) | 10 | CCandidateListUIPresenter::_EndCandidateList(void) | 11 | CCandidateListUIPresenter::EndUIElement(void) | 12 | CCandidateListUIPresenter::QueryInterface(const _GUID &, void * *) {EA1EA138-19DF-11D7-A6D2-00065B84435C} | 13 | CCandidateListUIPresenter::QueryInterface(const _GUID &, void * *) {EA1EA139-19DF-11D7-A6D2-00065B84435C} | 14 | CCandidateListUIPresenter::DisposeCandidateWindow(void) |销毁候选窗口 15 | CBaseWindow::_Destroy(void) | 16 | CBaseWindow::_WindowProc(HWND__ *, unsigned int, unsigned __int64, __int64) 0x00000090 | 17 | CCandidateWindow::_WindowProcCallback(HWND__ *, unsigned int, unsigned __int64, __int64) 0x00000090 | 18 | CBaseWindow::_WindowProc(HWND__ *, unsigned int, unsigned __int64, __int64) 0x00000046 | 19 | CCandidateWindow::_WindowProcCallback(HWND__ *, unsigned int, unsigned __int64, __int64) 0x00000046 | 20 | CShadowWindow::_Show(int) | 21 | CShadowWindow::_OnOwnerWndMoved(int) | 22 | CBaseWindow::_IsWindowVisible(void) | 23 | CShadowWindow::_AdjustWindowPos(void) | 24 | CBaseWindow::_WindowProc(HWND__ *, unsigned int, unsigned __int64, __int64) 0x00000046 | 25 | CShadowWindow::_WindowProcCallback(HWND__ *, unsigned int, unsigned __int64, __int64) 0x00000046 | 26 | CShadowWindow::_InitShadow(void) | 27 | CBaseWindow::_WindowProc(HWND__ *, unsigned int, unsigned __int64, __int64) 0x0000007c | 28 | CShadowWindow::_WindowProcCallback(HWND__ *, unsigned int, unsigned __int64, __int64) 0x0000007c | 29 | CBaseWindow::_WindowProc(HWND__ *, unsigned int, unsigned __int64, __int64) 0x0000007d | 30 | CShadowWindow::_WindowProcCallback(HWND__ *, unsigned int, unsigned __int64, __int64) 0x0000007d | 31 | CBaseWindow::_GetWindowRect(tagRECT *) | 32 | CBaseWindow::_Show(int) | 33 | CBaseWindow::_WindowProc(HWND__ *, unsigned int, unsigned __int64, __int64) 0x00000018 | 34 | CShadowWindow::_WindowProcCallback(HWND__ *, unsigned int, unsigned __int64, __int64) 0x00000018 | 35 | CBaseWindow::_WindowProc(HWND__ *, unsigned int, unsigned __int64, __int64) 0x00000046 | 36 | CShadowWindow::_WindowProcCallback(HWND__ *, unsigned int, unsigned __int64, __int64) 0x00000046 | 37 | CBaseWindow::_WindowProc(HWND__ *, unsigned int, unsigned __int64, __int64) 0x00000047 | 38 | CShadowWindow::_WindowProcCallback(HWND__ *, unsigned int, unsigned __int64, __int64) 0x00000047 | 39 | CShadowWindow::_OnOwnerWndMoved(int) | 40 | CBaseWindow::_IsWindowVisible(void) | 41 | CBaseWindow::_WindowProc(HWND__ *, unsigned int, unsigned __int64, __int64) 0x00000047 | 42 | CCandidateWindow::_WindowProcCallback(HWND__ *, unsigned int, unsigned __int64, __int64) 0x00000047 | 43 | CShadowWindow::_OnOwnerWndMoved(int) | 44 | CBaseWindow::_IsWindowVisible(void) | 45 | CCandidateWindow::_FireMessageToLightDismiss(HWND__ *, tagWINDOWPOS *) | 46 | CBaseWindow::_WindowProc(HWND__ *, unsigned int, unsigned __int64, __int64) 0x00000090 | 47 | CShadowWindow::_WindowProcCallback(HWND__ *, unsigned int, unsigned __int64, __int64) 0x00000090 | 48 | CBaseWindow::_WindowProc(HWND__ *, unsigned int, unsigned __int64, __int64) 0x00000002 | 49 | CShadowWindow::_WindowProcCallback(HWND__ *, unsigned int, unsigned __int64, __int64) 0x00000002 | 50 | CBaseWindow::_WindowProc(HWND__ *, unsigned int, unsigned __int64, __int64) 0x00000082 | 51 | CShadowWindow::_WindowProcCallback(HWND__ *, unsigned int, unsigned __int64, __int64) 0x00000082 | 52 | CBaseWindow::_WindowProc(HWND__ *, unsigned int, unsigned __int64, __int64) 0x00000002 | 53 | CCandidateWindow::_WindowProcCallback(HWND__ *, unsigned int, unsigned __int64, __int64) 0x00000002 | 54 | CCandidateWindow::_DeleteShadowWnd(void) | 55 | CBaseWindow::_WindowProc(HWND__ *, unsigned int, unsigned __int64, __int64) 0x00000002 | 56 | CBaseWindow::_WindowProc(HWND__ *, unsigned int, unsigned __int64, __int64) 0x00000082 | 57 | CBaseWindow::_WindowProc(HWND__ *, unsigned int, unsigned __int64, __int64) 0x00000082 | 58 | CCandidateWindow::_WindowProcCallback(HWND__ *, unsigned int, unsigned __int64, __int64) 0x00000082 | 59 | CCandidateWindow::_ClearList(void) | 60 | CCandidateWindow::_DeleteShadowWnd(void) | 61 | CCandidateWindow::_DeleteVScrollBarWnd(void) | 62 | CTfTextLayoutSink::_EndLayout(void) | 63 | CTfTextLayoutSink::_UnadviseTextLayoutSink(void) | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 文件说明 2 | 3 | 本章输入法源自[Windows-classic-samples](https://github.com/microsoft/Windows-classic-samples/tree/main/Samples/IME)。 4 | 使用Visual Studio 2019编译。测试环境为Windows 7和Windows 10的64位版。 5 | 6 | Files |Description 7 | -|- 8 | track.xml |断点文件,跟踪主要函数的调用关系。 9 | install.bat |安装文件,安装debug64位版。 10 | uninstall.bat |卸载文件。 11 | 12 | ## COM组件框架 13 | 14 | Source Files |Document 15 | -|- 16 | [DllMain.cpp](doc/DllMain.md) |DLL入口点,注册候选窗口类。 17 | [Globals.cpp](doc/Globals.md) |全局设置,全局变量、类型、函数,对标点符号以及快捷键进行通用设置。 18 | [Server.cpp](doc/Server.md) |COM导出函数,注册输入法以及创建输入法实例。 19 | [Register.cpp](doc/Register.md) |注册输入法,注册TSF输入法的标准实现。 20 | 21 | ## TSF文本服务框架 22 | 23 | Source Files |Document 24 | -|- 25 | [SampleIME.cpp](doc/SampleIME.md) |TSF文本服务框架,扩展了对无界面元素程序和触摸键盘支持。 26 | [ThreadMgrEventSink.cpp](doc/ThreadMgrEventSink.md) |线程管理器事件接收器,主要处理焦点事件。 27 | [TextEditSink.cpp](doc/TextEditSink.md) |编辑会话完成消息接收器,用于感知其他文本服务对编辑内容的更改。 28 | [KeyEventSink.cpp](doc/KeyEventSink.md) |键盘事件接收器,用于接收按键和虚拟按键事件。 29 | [ActiveLanguageProfileNotifySink.cpp](doc/ActiveLanguageProfileNotifySink.md) |语言配置激活消息接收器,当更改激活语言配置文件时,框架调用接收器。 30 | [ThreadFocusSink.cpp](doc/ThreadFocusSink.md) |线程输入焦点消息接收器,在线程接收或失去UI焦点时接收通知。 31 | [FunctionProviderSink.cpp](doc/FunctionProviderSink.md) |扩展功能提供者,提供各种函数对象。 32 | 33 | ## 输入法配置 34 | 35 | Source Files |Document 36 | -|- 37 | [CompositionProcessorEngine.cpp](doc/CompositionProcessorEngine.md) |合成处理器引擎,输入法核心类。 38 | [TfInputProcessorProfile.cpp](doc/TfInputProcessorProfile.md) |文本服务语言配置,用来操作一个或多个文本服务的语言配置文件。 39 | [Compartment.cpp](doc/Compartment.md) |公共缓冲池,用来保存输入法状态开关。 40 | [LanguageBar.cpp](doc/LanguageBar.md) |语言栏,设置输入法状态开关。 41 | [File.cpp](doc/File.md) |文件,打开码表文件。 42 | [FileMapping.cpp](doc/FileMapping.md) |文件映射,将码表文件创建为文件映射对象。 43 | 44 | ## 按键事件 45 | 46 | Source Files |Document 47 | -|- 48 | [KeyHandler.cpp](doc/KeyHandler.md) |按键处理器,用来处理按键事件。 49 | [EditSession.cpp](doc/EditSession.md) |编辑会话基接口,本章工程所有编辑会话均继承此接口。 50 | [KeyHandlerEditSession.cpp](doc/KeyHandlerEditSession.md) |按键处理编辑会话,在会话中按照按键分类调用相应处理函数。 51 | [KeyStateCategory.cpp](doc/KeyStateCategory.md) |按键分类,按照按键分类调用相应处理函数。 52 | 53 | ## 候选列表 54 | 55 | Source Files |Document 56 | -|- 57 | [SampleIMEBaseStructure.h](doc/SampleIMEBaseStructure.md) |数据结构,输入法自定义数据结构。 58 | [TableDictionaryEngine.cpp](doc/TableDictionaryEngine.md) |词典引擎,获取候选列表。 59 | [DictionarySearch.cpp](doc/DictionarySearch.md) |词典搜索,在词典中搜索编码为输入编码的汉字。 60 | [DictionaryParser.cpp](doc/DictionaryParser.md) |词典搜索基类,为词典搜索提供辅助函数。 61 | [BaseDictionaryEngine.cpp](doc/BaseDictionaryEngine.md) |词典引擎基类,为词典引擎提供辅助函数。 62 | 63 | ## 候选窗口控制 64 | 65 | Source Files |Document 66 | -|- 67 | [CandidateListUIPresenter.cpp](doc/CandidateListUIPresenter.md) |候选窗口控制器,控制候选窗口的显示。 68 | [TfTextLayoutSink.cpp](doc/TfTextLayoutSink.md) |文本布局消息接收器,通过响应布局更改消息,实现光标跟随。 69 | [GetTextExtentEditSession.cpp](doc/GetTextExtentEditSession.md) |文本布局会话,处理文本布局消息。 70 | 71 | ## 候选窗口绘制 72 | 73 | Source Files |Document 74 | -|- 75 | [CandidateWindow.cpp](doc/CandidateWindow.md) |候选窗口,在主窗口中显示候选列表。 76 | [BaseWindow.cpp](doc/BaseWindow.md) |窗口基类,为派生类调用API函数。 77 | [ShadowWindow.cpp](doc/ShadowWindow.md) |阴影窗口,为窗口添加阴影。 78 | [ScrollBarWindow.cpp](doc/ScrollBarWindow.md) |滚动条窗口。 79 | [ButtonWindow.cpp](doc/ButtonWindow.md) |按钮窗口。 80 | 81 | ## 合成 82 | 83 | Source Files |Document 84 | -|- 85 | [StartComposition.cpp](doc/composition/StartComposition.md) |开始合成,在上下文中创建一个合成。 86 | [Composition.cpp](doc/composition/Composition.md) |合成,标记为GUID_PROP_COMPOSING属性的文本范围。 87 | [EndComposition.cpp](doc/composition/EndComposition.md) |结束合成,完成汉字输入。 88 | 89 | ## 显示属性 90 | 91 | Source Files |Document 92 | -|- 93 | [DisplayAttribute.cpp](doc/DisplayAttribute.md) |显示属性,合成中编码或汉字的显示样式。 94 | [DisplayAttributeProvider.cpp](doc/DisplayAttributeProvider.md) |显示属性提供者,由TSF管理器用来枚举和获取单个显示属性信息对象。 95 | [DisplayAttributeInfo.cpp](doc/DisplayAttributeInfo.md) |显示属性信息对象,为应用程序提供显示属性信息。 96 | [EnumDisplayAttributeInfo.cpp](doc/EnumDisplayAttributeInfo.md) |显示属性信息对象枚举器,未演示此接口。 97 | 98 | ## 无界面模式 99 | 100 | Source Files |Document 101 | -|- 102 | [UIless.md](doc/uiless/UIless.md) |无界面模式,由应用程序显示UI元素。 103 | 104 | ## 其他主题 105 | 106 | Source Files |Document 107 | -|- 108 | [RegKey.cpp](doc/RegKey.md) |注册表类,未讲解。 109 | [SearchCandidateProvider.cpp](doc/SearchCandidateProvider.md) |搜索候选提供者,未讲解。 110 | [TipCandidateList.cpp](doc/TipCandidateList.md) |候选列表对象,未讲解。 111 | [TipCandidateString.cpp](doc/TipCandidateString.md) |候选文字串对象,未讲解。 112 | [EnumTfCandidates.cpp](doc/EnumTfCandidates.md) |候选文字串对象枚举器,未讲解。 113 | 114 | ## 附录A 115 | 116 | 1. [启动过程](doc/appendix/启动过程.md) 117 | 2. [按键处理](doc/appendix/按键处理.md) 118 | 3. [候选列表](doc/appendix/候选列表.md) 119 | 120 | ## 附录B 121 | 122 | 1. [选择候选字词](doc/appendix/选择候选字词.md) 123 | 2. [销毁候选窗口](doc/appendix/销毁候选窗口.md) 124 | 3. [完成汉字输入](doc/appendix/完成汉字输入.md) 125 | 126 | ## 附录C 127 | 128 | 1. [启动过程](doc/uiless/启动过程.md) 129 | 2. [按键处理](doc/uiless/按键处理.md) 130 | 3. [候选列表](doc/uiless/候选列表.md) -------------------------------------------------------------------------------- /doc/CandidateWindow.md: -------------------------------------------------------------------------------- 1 | ## 3.30 候选窗口 2 | 3 | 候选窗口,在主窗口中显示候选列表。 4 | 5 | ## 3.30.1 候选窗口结构 6 | 7 | 首先,调用_CreateMainWindow()函数,创建主窗口。 8 | 然后,调用_CreateBackGroundShadowWindow()函数,创建背景阴影窗口。 9 | 接着,调用_CreateVScrollWindow()函数,创建滚动条窗口。 10 | 最后,调用_ResizeWindow()函数,调整窗口大小。 11 | 12 | ```C++ 13 | BOOL CCandidateWindow::_Create(ATOM atom, _In_ UINT wndWidth, _In_opt_ HWND parentWndHandle) 14 | { 15 | BOOL ret = FALSE; 16 | _wndWidth = wndWidth; 17 | 18 | ret = _CreateMainWindow(atom, parentWndHandle); 19 | if (FALSE == ret) 20 | { 21 | goto Exit; 22 | } 23 | 24 | ret = _CreateBackGroundShadowWindow(); 25 | if (FALSE == ret) 26 | { 27 | goto Exit; 28 | } 29 | 30 | ret = _CreateVScrollWindow(); 31 | if (FALSE == ret) 32 | { 33 | goto Exit; 34 | } 35 | 36 | _ResizeWindow(); 37 | 38 | Exit: 39 | return TRUE; 40 | } 41 | ``` 42 | 43 | ## 3.30.2 创建主窗口 44 | 45 | 首先保存当前候选窗口对象,做为主窗口对象。候选子窗口通过CBaseWindow::_GetTopmostUIWnd()函数,获取候选主窗口对象。
46 | 然后调用CBaseWindow::_Create()函数,创建候选主窗口。主窗口用于显示候选列表,并做为子窗口的所有者窗口。 47 | 48 | ```C++ 49 | BOOL CCandidateWindow::_CreateMainWindow(ATOM atom, _In_opt_ HWND parentWndHandle) 50 | { 51 | _SetUIWnd(this); 52 | 53 | if (!CBaseWindow::_Create(atom, 54 | WS_EX_TOPMOST | WS_EX_TOOLWINDOW, 55 | WS_BORDER | WS_POPUP, 56 | NULL, 0, 0, parentWndHandle)) 57 | { 58 | return FALSE; 59 | } 60 | 61 | return TRUE; 62 | } 63 | ``` 64 | 65 | ## 3.30.3 添加候选窗口数据 66 | 67 | CCandidateWindow::_AddString()函数只是简单为候选列表生成了一个副本。
68 | 在实际的输入法开发中,一般也应该为每个候选窗口生成一个副本。
69 | 这是因为输入法可能在多个软件中运行,每个候选窗口的候选列表应该是独立的。
70 | 71 | ```C++ 72 | void CCandidateWindow::_AddString(_Inout_ CCandidateListItem *pCandidateItem, _In_ BOOL isAddFindKeyCode) 73 | { 74 | DWORD_PTR dwItemString = pCandidateItem->_ItemString.GetLength(); 75 | const WCHAR* pwchString = nullptr; 76 | if (dwItemString) 77 | { 78 | pwchString = new (std::nothrow) WCHAR[ dwItemString ]; 79 | if (!pwchString) 80 | { 81 | return; 82 | } 83 | memcpy((void*)pwchString, pCandidateItem->_ItemString.Get(), dwItemString * sizeof(WCHAR)); 84 | } 85 | 86 | DWORD_PTR itemWildcard = pCandidateItem->_FindKeyCode.GetLength(); 87 | const WCHAR* pwchWildcard = nullptr; 88 | if (itemWildcard && isAddFindKeyCode) 89 | { 90 | pwchWildcard = new (std::nothrow) WCHAR[ itemWildcard ]; 91 | if (!pwchWildcard) 92 | { 93 | if (pwchString) 94 | { 95 | delete [] pwchString; 96 | } 97 | return; 98 | } 99 | memcpy((void*)pwchWildcard, pCandidateItem->_FindKeyCode.Get(), itemWildcard * sizeof(WCHAR)); 100 | } 101 | 102 | CCandidateListItem* pLI = nullptr; 103 | pLI = _candidateList.Append(); 104 | if (!pLI) 105 | { 106 | if (pwchString) 107 | { 108 | delete [] pwchString; 109 | pwchString = nullptr; 110 | } 111 | if (pwchWildcard) 112 | { 113 | delete [] pwchWildcard; 114 | pwchWildcard = nullptr; 115 | } 116 | return; 117 | } 118 | 119 | if (pwchString) 120 | { 121 | pLI->_ItemString.Set(pwchString, dwItemString); 122 | } 123 | if (pwchWildcard) 124 | { 125 | pLI->_FindKeyCode.Set(pwchWildcard, itemWildcard); 126 | } 127 | 128 | return; 129 | } 130 | ``` 131 | 132 | ## 3.30.4 绘制消息 133 | 134 | 当输入法调用_pCandidateWnd->_InvalidateRect()函数,失效工作区后,系统向窗口发送WM_PAINT消息。
135 | 窗口过程调用CCandidateWindow::_OnPaint()函数,处理WM_PAINT消息。 136 | 137 | 首先,调用_GetCurrentPage()函数,获取当前候选页。
138 | 然后,调用_AdjustPageIndex()函数,调整当前候选项。
139 | 最后,调用_DrawList()函数,绘制当前候选页面。 140 | 141 | ```C++ 142 | void CCandidateWindow::_OnPaint(_In_ HDC dcHandle, _In_ PAINTSTRUCT *pPaintStruct) 143 | { 144 | SetBkMode(dcHandle, TRANSPARENT);//透明 145 | 146 | HFONT hFontOld = (HFONT)SelectObject(dcHandle, Global::defaultlFontHandle); 147 | 148 | FillRect(dcHandle, &pPaintStruct->rcPaint, _brshBkColor);//COLOR_WINDOW+1 149 | 150 | UINT currentPageIndex = 0; 151 | UINT currentPage = 0; 152 | 153 | if (FAILED(_GetCurrentPage(¤tPage))) 154 | { 155 | goto cleanup; 156 | } 157 | 158 | _AdjustPageIndex(currentPage, currentPageIndex); 159 | 160 | _DrawList(dcHandle, currentPageIndex, &pPaintStruct->rcPaint); 161 | 162 | cleanup: 163 | SelectObject(dcHandle, hFontOld); 164 | } 165 | ``` 166 | 167 | ## 3.30.5 输出汉字 168 | 169 | 当用户按下数字键或者鼠标键选择候选字词时,输入法将用户选择的候选字词输出到合成中。 170 | 本文只演示了其中最简单的场景,既按下空格键选择候选字词。因此,跳过了所选候选字词索引的计算,只将默认的当前候选字词输出到合成中。 171 | 172 | ```C++ 173 | DWORD CCandidateWindow::_GetSelectedCandidateString(_Outptr_result_maybenull_ const WCHAR **ppwchCandidateString) 174 | { 175 | CCandidateListItem* pItemList = nullptr; 176 | 177 | if (_currentSelection >= _candidateList.Count()) 178 | { 179 | *ppwchCandidateString = nullptr; 180 | return 0; 181 | } 182 | 183 | pItemList = _candidateList.GetAt(_currentSelection); 184 | if (ppwchCandidateString) 185 | { 186 | *ppwchCandidateString = pItemList->_ItemString.Get(); 187 | } 188 | return (DWORD)pItemList->_ItemString.GetLength(); 189 | } 190 | ``` -------------------------------------------------------------------------------- /doc/CandidateListUIPresenter.md: -------------------------------------------------------------------------------- 1 | ## 3.27 候选窗口控制器 2 | 3 | 候选窗口控制器,控制候选窗口的显示。 4 | 5 | ## 3.27.1 候选窗口创建过程 6 | 7 | 当用户按下编码键,输入法生成候选列表后,调用CCandidateListUIPresenter::_StartCandidateList()函数,创建候选窗口。 8 | 9 | 首先,调用_StartLayout()函数,安装文本布局消息接收器。 10 | 然后,调用BeginUIElement()函数,确定是否应显示文本服务的UI。 11 | 接着,调用MakeCandidateWindow()函数,创建候选窗口。 12 | 然后,调用Show()函数,显示候选窗口。 13 | 接着,调用_GetTextExt()函数,获取屏幕坐标。 14 | 最后,调用_LayoutChangeNotification()函数,实现光标跟随。 15 | 16 | ```C++ 17 | HRESULT CCandidateListUIPresenter::_StartCandidateList(TfClientId tfClientId, _In_ ITfDocumentMgr *pDocumentMgr, _In_ ITfContext *pContextDocument, TfEditCookie ec, _In_ ITfRange *pRangeComposition, UINT wndWidth) 18 | { 19 | pDocumentMgr;tfClientId; 20 | 21 | HRESULT hr = E_FAIL; 22 | 23 | if (FAILED(_StartLayout(pContextDocument, ec, pRangeComposition))) 24 | { 25 | goto Exit; 26 | } 27 | 28 | BeginUIElement(); 29 | 30 | hr = MakeCandidateWindow(pContextDocument, wndWidth); 31 | if (FAILED(hr)) 32 | { 33 | goto Exit; 34 | } 35 | 36 | Show(_isShowMode); 37 | 38 | RECT rcTextExt; 39 | if (SUCCEEDED(_GetTextExt(&rcTextExt))) 40 | { 41 | _LayoutChangeNotification(&rcTextExt); 42 | } 43 | 44 | Exit: 45 | if (FAILED(hr)) 46 | { 47 | _EndCandidateList(); 48 | } 49 | return hr; 50 | } 51 | ``` 52 | 53 | ## 3.27.2 无界面模式 54 | 55 | Interface |Description 56 | -|- 57 | [ITfUIElementMgr][1] |UI元素管理器,文本服务调用ITfUIElementMgr,向应用程序查询UI可见性。 58 | 59 | [1]: https://github.com/ChineseInputMethod/Interface/blob/master/TSFmanager/ITfUIElementMgr.md 60 | 61 | ```C++ 62 | HRESULT CCandidateListUIPresenter::BeginUIElement() 63 | { 64 | HRESULT hr = S_OK; 65 | 66 | ITfThreadMgr* pThreadMgr = _pTextService->_GetThreadMgr(); 67 | if (nullptr ==pThreadMgr) 68 | { 69 | hr = E_FAIL; 70 | goto Exit; 71 | } 72 | 73 | ITfUIElementMgr* pUIElementMgr = nullptr; 74 | hr = pThreadMgr->QueryInterface(IID_ITfUIElementMgr, (void **)&pUIElementMgr); 75 | if (hr == S_OK) 76 | { 77 | pUIElementMgr->BeginUIElement(this, &_isShowMode, &_uiElementId); 78 | pUIElementMgr->Release(); 79 | } 80 | 81 | Exit: 82 | return hr; 83 | } 84 | ``` 85 | 86 | ## 3.27.3 创建候选窗口 87 | 88 | 输入法在MakeCandidateWindow()创建候选窗口类,开始创建候选窗口。 89 | 90 | ```C++ 91 | HRESULT CCandidateListUIPresenter::MakeCandidateWindow(_In_ ITfContext *pContextDocument, _In_ UINT wndWidth) 92 | { 93 | HRESULT hr = S_OK; 94 | 95 | if (nullptr != _pCandidateWnd) 96 | { 97 | return hr; 98 | } 99 | 100 | _pCandidateWnd = new (std::nothrow) CCandidateWindow(_CandWndCallback, this, _pIndexRange, _pTextService->_IsStoreAppMode()); 101 | if (_pCandidateWnd == nullptr) 102 | { 103 | hr = E_OUTOFMEMORY; 104 | goto Exit; 105 | } 106 | 107 | HWND parentWndHandle = nullptr; 108 | ITfContextView* pView = nullptr; 109 | if (SUCCEEDED(pContextDocument->GetActiveView(&pView))) 110 | { 111 | pView->GetWnd(&parentWndHandle); 112 | } 113 | 114 | if (!_pCandidateWnd->_Create(_atom, wndWidth, parentWndHandle)) 115 | { 116 | hr = E_OUTOFMEMORY; 117 | goto Exit; 118 | } 119 | 120 | Exit: 121 | return hr; 122 | } 123 | ``` 124 | 125 | ## 3.27.4 显示候选列表 126 | 127 | 当用户按下编码键,输入法生成候选列表,创建候选窗口,然后将候选列表添加到候选窗口中。 128 | 129 | 首先,调用AddCandidateToCandidateListUI()函数,将候选列表复制一份副本给候选窗口。 130 | 然后,调用SetPageIndexWithScrollInfo()函数,设置候选页索引。 131 | 最后,调用_pCandidateWnd->_InvalidateRect()函数,失效工作区。 132 | 133 | ```C++ 134 | void CCandidateListUIPresenter::_SetText(_In_ CSampleImeArray *pCandidateList, BOOL isAddFindKeyCode) 135 | { 136 | AddCandidateToCandidateListUI(pCandidateList, isAddFindKeyCode); 137 | 138 | SetPageIndexWithScrollInfo(pCandidateList); 139 | 140 | if (_isShowMode) 141 | { 142 | _pCandidateWnd->_InvalidateRect(); 143 | } 144 | else 145 | { 146 | _updatedFlags = TF_CLUIE_COUNT | 147 | TF_CLUIE_SELECTION | 148 | TF_CLUIE_STRING | 149 | TF_CLUIE_PAGEINDEX | 150 | TF_CLUIE_CURRENTPAGE; 151 | _UpdateUIElement(); 152 | } 153 | } 154 | ``` 155 | 156 | ## 3.27.5 选择候选字词 157 | 158 | 当用户输入完编码,按下空格键选择候选字词,CSampleIME::_HandleCandidateWorker()函数将被调用。
159 | _HandleCandidateWorker()函数,实现了联想功能,本文不讲解此部分。
160 | 在_HandleCandidateWorker()函数的最后,调用了_HandleCandidateFinalize函数,完成汉字的输出。 161 | 162 | 首先,调用_pCandidateListUIPresenter->_GetSelectedCandidateString()函数,获取当前选择的候选字词。 163 | 然后,调用_AddComposingAndChar()函数,将候选字词添加到上下文中。 164 | 最后,调用_HandleComplete()函数,在_HandleComplete()函数中调用_TerminateComposition()函数,结束合成。 165 | 166 | ```C++ 167 | HRESULT CSampleIME::_HandleCandidateFinalize(TfEditCookie ec, _In_ ITfContext *pContext) 168 | { 169 | HRESULT hr = S_OK; 170 | DWORD_PTR candidateLen = 0; 171 | const WCHAR* pCandidateString = nullptr; 172 | CStringRange candidateString; 173 | 174 | if (nullptr == _pCandidateListUIPresenter) 175 | { 176 | goto NoPresenter; 177 | } 178 | 179 | candidateLen = _pCandidateListUIPresenter->_GetSelectedCandidateString(&pCandidateString); 180 | 181 | candidateString.Set(pCandidateString, candidateLen); 182 | 183 | if (candidateLen) 184 | { 185 | hr = _AddComposingAndChar(ec, pContext, &candidateString); 186 | 187 | if (FAILED(hr)) 188 | { 189 | return hr; 190 | } 191 | } 192 | 193 | NoPresenter: 194 | 195 | _HandleComplete(ec, pContext); 196 | 197 | return hr; 198 | } 199 | ``` -------------------------------------------------------------------------------- /SampleIME/CandidateListUIPresenter.h: -------------------------------------------------------------------------------- 1 | // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF 2 | // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO 3 | // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 4 | // PARTICULAR PURPOSE. 5 | // 6 | // Copyright (c) Microsoft Corporation. All rights reserved 7 | 8 | 9 | #pragma once 10 | 11 | #include "KeyHandlerEditSession.h" 12 | #include "CandidateWindow.h" 13 | #include "TfTextLayoutSink.h" 14 | #include "SampleIME.h" 15 | #include "SampleIMEBaseStructure.h" 16 | 17 | class CReadingLine; 18 | 19 | //+--------------------------------------------------------------------------- 20 | // 21 | // CCandidateListUIPresenter 22 | // 23 | // ITfCandidateListUIElement / ITfIntegratableCandidateListUIElement is used for 24 | // UILess mode support 25 | // ITfCandidateListUIElementBehavior sends the Selection behavior message to 26 | // 3rd party IME. 27 | //---------------------------------------------------------------------------- 28 | 29 | class CCandidateListUIPresenter : public CTfTextLayoutSink, 30 | public ITfCandidateListUIElementBehavior, 31 | public ITfIntegratableCandidateListUIElement 32 | { 33 | public: 34 | CCandidateListUIPresenter(_In_ CSampleIME *pTextService, ATOM atom, 35 | KEYSTROKE_CATEGORY Category, 36 | _In_ CCandidateRange *pIndexRange, 37 | BOOL hideWindow); 38 | virtual ~CCandidateListUIPresenter(); 39 | 40 | // IUnknown 41 | STDMETHODIMP QueryInterface(REFIID riid, _Outptr_ void **ppvObj); 42 | STDMETHODIMP_(ULONG) AddRef(void); 43 | STDMETHODIMP_(ULONG) Release(void); 44 | 45 | // ITfUIElement 46 | STDMETHODIMP GetDescription(BSTR *pbstr); 47 | STDMETHODIMP GetGUID(GUID *pguid); 48 | STDMETHODIMP Show(BOOL showCandidateWindow); 49 | STDMETHODIMP IsShown(BOOL *pIsShow); 50 | 51 | // ITfCandidateListUIElement 52 | STDMETHODIMP GetUpdatedFlags(DWORD *pdwFlags); 53 | STDMETHODIMP GetDocumentMgr(ITfDocumentMgr **ppdim); 54 | STDMETHODIMP GetCount(UINT *pCandidateCount); 55 | STDMETHODIMP GetSelection(UINT *pSelectedCandidateIndex); 56 | STDMETHODIMP GetString(UINT uIndex, BSTR *pbstr); 57 | STDMETHODIMP GetPageIndex(UINT *pIndex, UINT uSize, UINT *puPageCnt); 58 | STDMETHODIMP SetPageIndex(UINT *pIndex, UINT uPageCnt); 59 | STDMETHODIMP GetCurrentPage(UINT *puPage); 60 | 61 | // ITfCandidateListUIElementBehavior methods 62 | STDMETHODIMP SetSelection(UINT nIndex); 63 | STDMETHODIMP Finalize(void); 64 | STDMETHODIMP Abort(void); 65 | 66 | // ITfIntegratableCandidateListUIElement 67 | STDMETHODIMP SetIntegrationStyle(GUID guidIntegrationStyle); 68 | STDMETHODIMP GetSelectionStyle(_Out_ TfIntegratableCandidateListSelectionStyle *ptfSelectionStyle); 69 | STDMETHODIMP OnKeyDown(_In_ WPARAM wParam, _In_ LPARAM lParam, _Out_ BOOL *pIsEaten); 70 | STDMETHODIMP ShowCandidateNumbers(_Out_ BOOL *pIsShow); 71 | STDMETHODIMP FinalizeExactCompositionString(); 72 | 73 | virtual HRESULT _StartCandidateList(TfClientId tfClientId, _In_ ITfDocumentMgr *pDocumentMgr, _In_ ITfContext *pContextDocument, TfEditCookie ec, _In_ ITfRange *pRangeComposition, UINT wndWidth); 74 | void _EndCandidateList(); 75 | 76 | void _SetText(_In_ CSampleImeArray *pCandidateList, BOOL isAddFindKeyCode); 77 | void _ClearList(); 78 | VOID _SetTextColor(COLORREF crColor, COLORREF crBkColor); 79 | VOID _SetFillColor(HBRUSH hBrush); 80 | 81 | DWORD_PTR _GetSelectedCandidateString(_Outptr_result_maybenull_ const WCHAR **ppwchCandidateString); 82 | BOOL _SetSelectionInPage(int nPos) { return _pCandidateWnd->_SetSelectionInPage(nPos); } 83 | 84 | BOOL _MoveSelection(_In_ int offSet); 85 | BOOL _SetSelection(_In_ int selectedIndex); 86 | BOOL _MovePage(_In_ int offSet); 87 | 88 | void _MoveWindowToTextExt(); 89 | 90 | // CTfTextLayoutSink 91 | virtual VOID _LayoutChangeNotification(_In_ RECT *lpRect); 92 | virtual VOID _LayoutDestroyNotification(); 93 | 94 | // Event for ITfThreadFocusSink 95 | virtual HRESULT OnSetThreadFocus(); 96 | virtual HRESULT OnKillThreadFocus(); 97 | 98 | void RemoveSpecificCandidateFromList(_In_ LCID Locale, _Inout_ CSampleImeArray &candidateList, _In_ CStringRange &srgCandidateString); 99 | void AdviseUIChangedByArrowKey(_In_ KEYSTROKE_FUNCTION arrowKey); 100 | 101 | private: 102 | virtual HRESULT CALLBACK _CandidateChangeNotification(_In_ enum CANDWND_ACTION action); 103 | 104 | static HRESULT _CandWndCallback(_In_ void *pv, _In_ enum CANDWND_ACTION action); 105 | 106 | friend COLORREF _AdjustTextColor(_In_ COLORREF crColor, _In_ COLORREF crBkColor); 107 | 108 | HRESULT _UpdateUIElement(); 109 | 110 | HRESULT ToShowCandidateWindow(); 111 | 112 | HRESULT ToHideCandidateWindow(); 113 | 114 | HRESULT BeginUIElement(); 115 | HRESULT EndUIElement(); 116 | 117 | HRESULT MakeCandidateWindow(_In_ ITfContext *pContextDocument, _In_ UINT wndWidth); 118 | void DisposeCandidateWindow(); 119 | 120 | void AddCandidateToCandidateListUI(_In_ CSampleImeArray *pCandidateList, BOOL isAddFindKeyCode); 121 | 122 | void SetPageIndexWithScrollInfo(_In_ CSampleImeArray *pCandidateList); 123 | 124 | protected: 125 | CCandidateWindow *_pCandidateWnd; 126 | BOOL _isShowMode; 127 | BOOL _hideWindow; 128 | 129 | private: 130 | 131 | HWND _parentWndHandle; 132 | ATOM _atom; 133 | CCandidateRange* _pIndexRange; 134 | KEYSTROKE_CATEGORY _Category; 135 | DWORD _updatedFlags; 136 | DWORD _uiElementId; 137 | CSampleIME* _pTextService; 138 | LONG _refCount; 139 | }; 140 | -------------------------------------------------------------------------------- /doc/CompositionProcessorEngine.md: -------------------------------------------------------------------------------- 1 | ## 3.12 合成处理器引擎 2 | 3 | 在输入法被激活时,调用CSampleIME::_AddTextProcessorEngine()方法,设置输入法配置。 4 | 5 | ## 3.12.1 获取语言配置文件 6 | 7 | 首先获取输入法的语言配置文件。 8 | 9 | ```C++ 10 | if (FAILED(profile.CreateInstance())) 11 | { 12 | return FALSE; 13 | } 14 | 15 | if (FAILED(profile.GetCurrentLanguage(&langid))) 16 | { 17 | return FALSE; 18 | } 19 | 20 | if (FAILED(profile.GetDefaultLanguageProfile(langid, GUID_TFCAT_TIP_KEYBOARD, &clsid, &guidProfile))) 21 | { 22 | return FALSE; 23 | } 24 | ``` 25 | 26 | ## 3.12.2 创建合成处理器引擎 27 | 28 | 然后创建合成处理器引擎,这是输入法的核心类。 29 | 30 | ```C++ 31 | if (_pCompositionProcessorEngine == nullptr) 32 | { 33 | _pCompositionProcessorEngine = new (std::nothrow) CCompositionProcessorEngine(); 34 | } 35 | ``` 36 | 37 | ## 3.12.3 设置语言配置文件 38 | 39 | 最后调用_pCompositionProcessorEngine->SetupLanguageProfile()方法,设置输入法的语言配置文件。 40 | 41 | Function |Description 42 | -|- 43 | SetupPreserved() |设置输入法状态开关快捷键。 44 | InitializeSampleIMECompartment() |将输入法默认状态设置到缓冲区。 45 | SetupPunctuationPair() |设置标点对。 46 | SetupLanguageBar() |设置输入法状态开关。 47 | SetupKeystroke() |设置编码键。 48 | SetupConfiguration() |设置候选栏。 49 | SetupDictionaryFile() |打开输入法词典文件。 50 | 51 | ## 3.12.4 完成初始化 52 | 53 | 调用_AddTextProcessorEngine()完成后,ActivateEx()方法退出,输入法完成初始化。 54 | 然后根据配置,开始进入工作状态。 55 | 56 | 首先ITfActiveLanguageProfileNotifySink::OnActivated()方法被调用。这是输入法做为普通应用程序响应键盘布局激活事件。 57 | 然后ITfLangBarItemButton接口被调用,这是因为输入法在语言栏中安装了三个按钮,语言栏调用按钮信息。 58 | 至此,输入法进入工作状态,等待响应事件。 59 | 60 | ## 3.12.5 获取候选列表 61 | 62 | 当用户按下编码键后,输入法调用CCompositionProcessorEngine::GetCandidateList()函数,获取候选列表。 63 | 64 | 首先,在编码后面添加一个通配符,只是输入法做的一个简单处理,例如没有拼音为“b”的编码,所以当用户按下b后,候选列表是空的,添加一个通配符防止出现这种情况。
65 | 然后,输入法调用CTableDictionaryEngine::CollectWordForWildcard()函数,获取候选列表。
66 | 接着,调用CBaseDictionaryEngine::SortListItemByFindKeyCode()函数,为这个候选列表排序。
67 | 最后,清除候选列表项编码的通配符。 68 | 69 | ```C++ 70 | void CCompositionProcessorEngine::GetCandidateList(_Inout_ CSampleImeArray *pCandidateList, BOOL isIncrementalWordSearch, BOOL isWildcardSearch) 71 | { 72 | if (!IsDictionaryAvailable()) 73 | { 74 | return; 75 | } 76 | 77 | if (isIncrementalWordSearch) 78 | { 79 | CStringRange wildcardSearch; 80 | DWORD_PTR keystrokeBufLen = _keystrokeBuffer.GetLength() + 2; 81 | PWCHAR pwch = new (std::nothrow) WCHAR[ keystrokeBufLen ]; 82 | if (!pwch) 83 | { 84 | return; 85 | } 86 | 87 | // check keystroke buffer already has wildcard char which end user want wildcard serach 88 | DWORD wildcardIndex = 0; 89 | BOOL isFindWildcard = FALSE; 90 | 91 | if (IsWildcard()) 92 | { 93 | for (wildcardIndex = 0; wildcardIndex < _keystrokeBuffer.GetLength(); wildcardIndex++) 94 | { 95 | if (IsWildcardChar(*(_keystrokeBuffer.Get() + wildcardIndex))) 96 | { 97 | isFindWildcard = TRUE; 98 | break; 99 | } 100 | } 101 | } 102 | 103 | StringCchCopyN(pwch, keystrokeBufLen, _keystrokeBuffer.Get(), _keystrokeBuffer.GetLength()); 104 | 105 | if (!isFindWildcard) 106 | { 107 | // add wildcard char for incremental search 108 | StringCchCat(pwch, keystrokeBufLen, L"*"); 109 | } 110 | 111 | size_t len = 0; 112 | if (StringCchLength(pwch, STRSAFE_MAX_CCH, &len) == S_OK) 113 | { 114 | wildcardSearch.Set(pwch, len); 115 | } 116 | else 117 | { 118 | return; 119 | } 120 | 121 | _pTableDictionaryEngine->CollectWordForWildcard(&wildcardSearch, pCandidateList); 122 | 123 | if (0 >= pCandidateList->Count()) 124 | { 125 | return; 126 | } 127 | 128 | if (IsKeystrokeSort()) 129 | { 130 | _pTableDictionaryEngine->SortListItemByFindKeyCode(pCandidateList); 131 | } 132 | 133 | // Incremental search would show keystroke data from all candidate list items 134 | // but wont show identical keystroke data for user inputted. 135 | for (UINT index = 0; index < pCandidateList->Count(); index++) 136 | { 137 | CCandidateListItem *pLI = pCandidateList->GetAt(index); 138 | DWORD_PTR keystrokeBufferLen = 0; 139 | 140 | if (IsWildcard()) 141 | { 142 | keystrokeBufferLen = wildcardIndex; 143 | } 144 | else 145 | { 146 | keystrokeBufferLen = _keystrokeBuffer.GetLength(); 147 | } 148 | 149 | CStringRange newFindKeyCode; 150 | newFindKeyCode.Set(pLI->_FindKeyCode.Get() + keystrokeBufferLen, pLI->_FindKeyCode.GetLength() - keystrokeBufferLen); 151 | pLI->_FindKeyCode.Set(newFindKeyCode); 152 | } 153 | 154 | delete [] pwch; 155 | } 156 | else if (isWildcardSearch) 157 | { 158 | _pTableDictionaryEngine->CollectWordForWildcard(&_keystrokeBuffer, pCandidateList); 159 | } 160 | else 161 | { 162 | _pTableDictionaryEngine->CollectWord(&_keystrokeBuffer, pCandidateList); 163 | } 164 | 165 | for (UINT index = 0; index < pCandidateList->Count();) 166 | { 167 | CCandidateListItem *pLI = pCandidateList->GetAt(index); 168 | CStringRange startItemString; 169 | CStringRange endItemString; 170 | 171 | startItemString.Set(pLI->_ItemString.Get(), 1); 172 | endItemString.Set(pLI->_ItemString.Get() + pLI->_ItemString.GetLength() - 1, 1); 173 | 174 | index++; 175 | } 176 | } 177 | ``` -------------------------------------------------------------------------------- /doc/composition/Composition.md: -------------------------------------------------------------------------------- 1 | ## 3.36 合成 2 | 3 | 合成,标记为GUID_PROP_COMPOSING属性的文本范围。 4 | 5 | 处理输入编码的函数调用过程,请参考:[附录A](../appendix/启动过程.md) 6 | 7 | ## 3.36.1 写入合成字符 8 | 9 | Interface |Description 10 | -|- 11 | [ITfContext][1] |上下文,用来创建和管理编辑上下文。 12 | [ITfRange][2] |文本范围,用来操作给定上下文中的文本。 13 | 14 | [1]: https://github.com/ChineseInputMethod/Interface/blob/master/TSFmanager/ITfContext.md 15 | [2]: https://github.com/ChineseInputMethod/Interface/blob/master/TSFmanager/ITfRange.md 16 | 17 | 当用户按下编码键,输入法调用CSampleIME::_AddComposingAndChar()函数,将输入编码写入到上下文中。 18 | 19 | 首先,调用_FindComposingRange()函数,查找已有合成。
20 | 然后,调用_SetInputString()函数,将输入编码写入上下文。 21 | 22 | ```C++ 23 | HRESULT CSampleIME::_AddComposingAndChar(TfEditCookie ec, _In_ ITfContext *pContext, _In_ CStringRange *pstrAddString) 24 | { 25 | HRESULT hr = S_OK; 26 | 27 | ULONG fetched = 0; 28 | TF_SELECTION tfSelection; 29 | //当前插入点 30 | if (pContext->GetSelection(ec, TF_DEFAULT_SELECTION, 1, &tfSelection, &fetched) != S_OK || fetched == 0) 31 | return S_FALSE; 32 | 33 | // 34 | // make range start to selection 35 | // 36 | ITfRange* pAheadSelection = nullptr; 37 | hr = pContext->GetStart(ec, &pAheadSelection);//上下文开始 38 | if (SUCCEEDED(hr)) 39 | {//将文本范围设置为从上下文开始到当前插入点的起始定位点 40 | hr = pAheadSelection->ShiftEndToRange(ec, tfSelection.range, TF_ANCHOR_START); 41 | if (SUCCEEDED(hr)) 42 | { 43 | ITfRange* pRange = nullptr; 44 | BOOL exist_composing = _FindComposingRange(ec, pContext, pAheadSelection, &pRange); 45 | 46 | _SetInputString(ec, pContext, pRange, pstrAddString, exist_composing); 47 | 48 | if (pRange) 49 | { 50 | pRange->Release(); 51 | } 52 | } 53 | } 54 | 55 | tfSelection.range->Release(); 56 | 57 | if (pAheadSelection) 58 | { 59 | pAheadSelection->Release(); 60 | } 61 | 62 | return S_OK; 63 | } 64 | ``` 65 | 66 | ## 3.36.2 查找已有合成 67 | 68 | Interface |Description 69 | -|- 70 | [ITfReadOnlyProperty][3] |只读属性,ITfProperty继承此接口。 71 | [IEnumTfRanges][4] |文本范围枚举器,枚举文本范围对象。 72 | 73 | [3]: https://github.com/ChineseInputMethod/Interface/blob/master/TSFmanager/ITfReadOnlyProperty.md 74 | [4]: https://github.com/ChineseInputMethod/Interface/blob/master/TSFmanager/IEnumTfRanges.md 75 | 76 | 第一次按下编码键会创建合成,并将第一个编码写入到上下文中,当按下第二个编码键时,输入法需要去上下文中查询是否存在合成。 77 | 这是因为COM框架的特性,输入法使用接口交互,而接口无法保存数据,所以输入法需要查询上下文中是否已经存在合成了。 78 | 79 | ## 3.36.3 覆盖文本范围内容 80 | 81 | 当在上下文中创建或查找到已有合成后,调用CSampleIME::_SetInputString()函数,将输入编码写入到上下文中。 82 | 83 | 当第一次按下编码键时,因为插入点之前没有GUID_PROP_COMPOSING属性,所以_InsertAtSelection()函数会被调用。 84 | 当按下第二个编码键时,因为插入点之前,也就是第一个编码被设置为了GUID_PROP_COMPOSING属性,所以_InsertAtSelection()函数不会被调用。 85 | 86 | 因为TSF框架的特性,第二个编码并不是追加到第一个编码的后面。例如,第一个编码输入"a",第二个编码输入"b"。 87 | ITfRange::SetText()方法,是用字符串"ab",替换覆盖掉原文本范围内容"a"。 88 | (从形式上看两者相同,但逻辑上不同) 89 | 90 | ```C++ 91 | HRESULT CSampleIME::_SetInputString(TfEditCookie ec, _In_ ITfContext *pContext, _Out_opt_ ITfRange *pRange, _In_ CStringRange *pstrAddString, BOOL exist_composing) 92 | { 93 | ITfRange* pRangeInsert = nullptr; 94 | if (!exist_composing) 95 | { 96 | _InsertAtSelection(ec, pContext, pstrAddString, &pRangeInsert); 97 | if (pRangeInsert == nullptr) 98 | { 99 | return S_OK; 100 | } 101 | pRange = pRangeInsert; 102 | } 103 | if (pRange != nullptr) 104 | { 105 | pRange->SetText(ec, 0, pstrAddString->Get(), (LONG)pstrAddString->GetLength()); 106 | } 107 | 108 | _SetCompositionLanguage(ec, pContext); 109 | 110 | _SetCompositionDisplayAttributes(ec, pContext, _gaDisplayAttributeInput); 111 | 112 | // update the selection, we'll make it an insertion point just past 113 | // the inserted text. 114 | ITfRange* pSelection = nullptr; 115 | TF_SELECTION sel; 116 | 117 | if ((pRange != nullptr) && (pRange->Clone(&pSelection) == S_OK)) 118 | { 119 | pSelection->Collapse(ec, TF_ANCHOR_END); 120 | 121 | sel.range = pSelection; 122 | sel.style.ase = TF_AE_NONE; 123 | sel.style.fInterimChar = FALSE; 124 | pContext->SetSelection(ec, 1, &sel); 125 | pSelection->Release(); 126 | } 127 | 128 | if (pRangeInsert) 129 | { 130 | pRangeInsert->Release(); 131 | } 132 | 133 | 134 | return S_OK; 135 | } 136 | ``` 137 | 138 | 然后,调用_SetCompositionLanguage()函数,为合成设置语言属性,本章未讲解此性质。
139 | 接着,调用_SetCompositionDisplayAttributes()函数,为合成设置显示属性,该性质在显示属性主题讲解。
140 | 最后,调用ITfRange::Collapse()方法,将文本范围的起始点设置为终止点。(移动插入点位置) 141 | 然后,调用ITfContext::SetSelection()方法,更新调整后的插入点。 142 | 143 | >注释掉239行,取消240行注释,可观察到向前插入输入编码。 144 | 注释掉239行,可观察到选中的输入编码。 145 | 146 | ## 3.36.4 输出汉字 147 | 148 | 当用户按下空格键选择候选字词后,CSampleIME::_AddComposingAndChar()函数再次被调用。
149 | 可以发现将编码或汉字写入到合成,是没有任何区别的,实际上TSF管理器也并不知道,输入法写入的是编码还是汉字。
150 | 不同之处是,在输出汉字后,输入法会调用ITfComposition::EndComposition()方法结束合成。 151 | 152 | >TSF输入法的核心就是上面短短的几句话。只不过被套上了一层又一层外壳。作者从IME时代就搞不懂,怎么输出一个汉字。找到了一个源码,然后打印出来看,因为作者没有电脑。 153 | 看了很长时间也看不懂。其实,输出汉字就是下面几个步骤。 154 | 155 | 1. 首先,从ITfContext中获取ITfInsertAtSelection,然后从ITfInsertAtSelection中获取ITfRange,备用。 156 | 2. 接着,从ITfContext中获取ITfContextComposition,将ITfRange放入ITfContextComposition,获取ITfComposition,备用。(开始合成) 157 | 3. 然后,从ITfContext中获取ITfRange,将输入编码写入ITfRange,再将修改后的当前选择放到ITfContext中。(合成中) 158 | 4. 接着,重复步骤3,完成汉字输入或继续重复步骤3,完成自造词、语句输入等功能。 159 | 5. 最后,结束ITfComposition,完成输入。(结束合成) 160 | 161 | 我现在一直在想,为什么这么简单的东西,我做了这么久。答案竟然是,我足够幸运,才最终找出了答案。 162 | 非常想记录下每一个节点,我的心理感受,然而我没有时间。 163 | 164 | ## 3.36.4 合成终止消息接收器 165 | 166 | Interface |Description 167 | -|- 168 | [ITfCompositionSink][5] |合成终止消息接收器,用于在终止ITfComposition合成时接收通知。 169 | 170 | [5]: https://github.com/ChineseInputMethod/Interface/blob/master/TextService/ITfCompositionSink.md 171 | 172 | 当合成被意外终止时,ITfCompositionSink::OnCompositionTerminated()方法将被调用。 173 | 174 | ```C++ 175 | STDAPI CSampleIME::OnCompositionTerminated(TfEditCookie ecWrite, _In_ ITfComposition *pComposition) 176 | { 177 | // Clear dummy composition 178 | _RemoveDummyCompositionForComposing(ecWrite, pComposition); 179 | 180 | // Clear display attribute and end composition, _EndComposition will release composition for us 181 | ITfContext* pContext = _pContext; 182 | if (pContext) 183 | { 184 | pContext->AddRef(); 185 | } 186 | 187 | _EndComposition(_pContext); 188 | 189 | _DeleteCandidateList(FALSE, pContext); 190 | 191 | if (pContext) 192 | { 193 | pContext->Release(); 194 | pContext = nullptr; 195 | } 196 | 197 | return S_OK; 198 | } 199 | ``` -------------------------------------------------------------------------------- /SampleIME/ScrollBarWindow.h: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////// 2 | // 3 | // Copyright (C) 2005 Microsoft Corporation. All rights reserved. 4 | // 5 | // ScrollBarWindow.h 6 | // 7 | // CScrollBarWindow declaration. 8 | // 9 | // 10 | ////////////////////////////////////////////////////////////////////// 11 | 12 | #pragma once 13 | 14 | #include "BaseWindow.h" 15 | #include "ButtonWindow.h" 16 | 17 | class CScrollButtonWindow; 18 | 19 | enum SCROLLBAR_DIRECTION 20 | { 21 | SCROLL_NONE_DIR, 22 | SCROLL_PAGEDOWN, // or page left 23 | SCROLL_PAGEUP // or page right 24 | }; 25 | 26 | enum SHELL_MODE 27 | { 28 | STOREAPP, 29 | DESKTOP, 30 | }; 31 | 32 | struct CScrollInfo 33 | { 34 | CScrollInfo() 35 | { 36 | nMax = 0; 37 | nPage = 0; 38 | nPos = 0; 39 | } 40 | 41 | int nMax; 42 | int nPage; 43 | int nPos; 44 | }; 45 | 46 | class CScrollBarWindow; 47 | 48 | class CScrollBarWindowFactory 49 | { 50 | public: 51 | static CScrollBarWindowFactory* Instance(); 52 | CScrollBarWindow* MakeScrollBarWindow(SHELL_MODE shellMode); 53 | void Release(); 54 | 55 | protected: 56 | CScrollBarWindowFactory(); 57 | 58 | private: 59 | static CScrollBarWindowFactory* _instance; 60 | 61 | }; 62 | ////////////////////////////////////////////////////////////////////// 63 | // 64 | // CScrollBarWindow declaration. 65 | // 66 | ////////////////////////////////////////////////////////////////////// 67 | 68 | class CScrollBarWindow : public CBaseWindow 69 | { 70 | public: 71 | CScrollBarWindow(); 72 | virtual ~CScrollBarWindow(); 73 | 74 | BOOL _Create(ATOM atom, DWORD dwExStyle, DWORD dwStyle, CBaseWindow *pParent = nullptr, int wndWidth = 0, int wndHeight = 0); 75 | 76 | void _Resize(int x, int y, int cx, int cy); 77 | void _Show(BOOL isShowWnd); 78 | void _Enable(BOOL isEnable); 79 | 80 | LRESULT CALLBACK _WindowProcCallback(_In_ HWND wndHandle, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam); 81 | virtual void _OnPaint(_In_ HDC dcHandle, _In_ PAINTSTRUCT *pps); 82 | virtual void _OnLButtonDown(POINT pt); 83 | virtual void _OnLButtonUp(POINT pt); 84 | virtual void _OnMouseMove(POINT pt); 85 | virtual void _OnTimer() { }; 86 | 87 | virtual void _OnOwnerWndMoved(BOOL isResized); 88 | 89 | virtual void _SetScrollInfo(_In_ CScrollInfo *lpsi); 90 | virtual void _GetScrollInfo(_Out_ CScrollInfo *lpsi); 91 | 92 | virtual BOOL _GetBtnUpRect(_Out_ RECT *prc); 93 | virtual BOOL _GetBtnDnRect(_Out_ RECT *prc); 94 | 95 | virtual void _ShiftLine(int nLine, BOOL isNotify) 96 | { 97 | DWORD dwSB = 0; 98 | 99 | dwSB = (nLine > 0 ? SB_LINEDOWN : 100 | nLine < 0 ? SB_LINEUP : 0); 101 | 102 | _SetCurPos(_scrollInfo.nPos + nLine, isNotify ? dwSB : -1); 103 | } 104 | 105 | virtual void _ShiftPage(int nPage, BOOL isNotify) 106 | { 107 | DWORD dwSB = 0; 108 | 109 | dwSB = (nPage > 0 ? SB_PAGEDOWN : 110 | nPage < 0 ? SB_PAGEUP : 0); 111 | 112 | _SetCurPos(_scrollInfo.nPos + _scrollInfo.nPage * nPage, isNotify ? dwSB : -1); 113 | } 114 | 115 | virtual void _ShiftPosition(int iPos, BOOL isNotify) 116 | { 117 | _SetCurPos(iPos, isNotify ? SB_THUMBPOSITION : -1); 118 | } 119 | 120 | virtual void _GetScrollArea(_Out_ RECT *prc); 121 | virtual void _SetCurPos(int nPos, int dwSB); 122 | 123 | private: 124 | void _AdjustWindowPos(); 125 | 126 | CScrollButtonWindow* _pBtnUp; 127 | CScrollButtonWindow* _pBtnDn; 128 | 129 | CScrollInfo _scrollInfo; 130 | SIZE _sizeOfScrollBtn; 131 | SCROLLBAR_DIRECTION _scrollDir; 132 | }; 133 | 134 | 135 | ////////////////////////////////////////////////////////////////////// 136 | // 137 | // CScrollBarNullWindow declaration. 138 | // 139 | ////////////////////////////////////////////////////////////////////// 140 | 141 | class CScrollBarNullWindow : public CScrollBarWindow 142 | { 143 | public: 144 | CScrollBarNullWindow() { }; 145 | virtual ~CScrollBarNullWindow() { }; 146 | 147 | virtual BOOL _Create(ATOM atom, DWORD dwExStyle, DWORD dwStyle, _In_opt_ CBaseWindow *pParent = nullptr, int wndWidth = 0, int wndHeight = 0) 148 | { 149 | atom; dwExStyle; dwStyle; pParent; wndWidth; wndHeight; 150 | return TRUE; 151 | }; 152 | 153 | virtual void _Destroy() { }; 154 | 155 | virtual void _Move(int x, int y) { x;y; }; 156 | virtual void _Resize(int x, int y, int cx, int cy) { x; y; cx; cy; }; 157 | virtual void _Show(BOOL isShowWnd) { isShowWnd; }; 158 | virtual BOOL _IsWindowVisible() { return TRUE; }; 159 | virtual void _Enable(BOOL isEnable) { isEnable; }; 160 | virtual BOOL _IsEnabled() { return TRUE; }; 161 | virtual void _InvalidateRect() { }; 162 | virtual BOOL _GetWindowRect(_Out_ LPRECT lpRect) { lpRect->bottom = 0; lpRect->left = 0; lpRect->right = 0; lpRect->top = 0; return TRUE; }; 163 | virtual BOOL _GetClientRect(_Out_ LPRECT lpRect) { lpRect->bottom = 0; lpRect->left = 0; lpRect->right = 0; lpRect->top = 0; return TRUE; }; 164 | 165 | virtual LRESULT CALLBACK _WindowProcCallback(_In_ HWND wndHandle, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam) 166 | { 167 | wndHandle; uMsg; wParam; lParam; 168 | return 0; 169 | }; 170 | virtual void _OnPaint(_In_ HDC dcHandle, _In_ PAINTSTRUCT *pps) { dcHandle;pps; }; 171 | virtual void _OnLButtonDown(POINT pt) { pt; }; 172 | virtual void _OnLButtonUp(POINT pt) { pt; }; 173 | virtual void _OnMouseMove(POINT pt) { pt; }; 174 | virtual void _OnTimer() { }; 175 | 176 | virtual void _OnOwnerWndMoved(BOOL isResized) { isResized; }; 177 | 178 | virtual void _SetScrollInfo(_In_ CScrollInfo *lpsi) { lpsi; }; 179 | virtual void _GetScrollInfo(_In_ CScrollInfo *lpsi) { lpsi; }; 180 | 181 | virtual BOOL _GetBtnUpRect(_Out_ RECT *prc) { prc->bottom = 0; prc->left = 0; prc->right = 0; prc->top = 0; return TRUE; }; 182 | virtual BOOL _GetBtnDnRect(_Out_ RECT *prc) { prc->bottom = 0; prc->left = 0; prc->right = 0; prc->top = 0; return TRUE; }; 183 | 184 | virtual void _ShiftLine(int nLine, BOOL isNotify) { nLine; isNotify; }; 185 | virtual void _ShiftPage(int nPage, BOOL isNotify) { nPage; isNotify; }; 186 | virtual void _ShiftPosition(int iPos, BOOL isNotify) { iPos; isNotify; }; 187 | virtual void _GetScrollArea(_Out_ RECT *prc) { prc->bottom = 0; prc->left = 0; prc->right = 0; prc->top = 0; }; 188 | virtual void _SetCurPos(int nPos, DWORD dwSB) { nPos; dwSB; }; 189 | }; 190 | 191 | ////////////////////////////////////////////////////////////////////// 192 | // 193 | // CScrollButtonWondow declaration. 194 | // 195 | ////////////////////////////////////////////////////////////////////// 196 | 197 | class CScrollButtonWindow : public CButtonWindow 198 | { 199 | public: 200 | CScrollButtonWindow(UINT uSubType) 201 | { 202 | subTypeOfControl = uSubType; 203 | } 204 | virtual ~CScrollButtonWindow() 205 | { 206 | } 207 | 208 | LRESULT CALLBACK _WindowProcCallback(_In_ HWND wndHandle, UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam) 209 | { 210 | wndHandle;uMsg;wParam;lParam; 211 | return 0; 212 | } 213 | virtual void _OnPaint(_In_ HDC dcHandle, _In_ PAINTSTRUCT *pps); 214 | virtual void _OnLButtonDown(POINT pt); 215 | virtual void _OnLButtonUp(POINT pt); 216 | virtual void _OnTimer(); 217 | 218 | private: 219 | UINT subTypeOfControl; // DFCS_SCROLLxxx for DrawFrameControl 220 | }; 221 | -------------------------------------------------------------------------------- /doc/KeyHandler.md: -------------------------------------------------------------------------------- 1 | ## 3.18 按键处理器 2 | 3 | 按键处理器,用来处理按键事件。 4 | 5 | ## 3.18.1 发起编辑会话 6 | 7 | 当按下键盘按键,TSF管理器会调用输入法的OnKeyDown()方法。 8 | 当输入法处理该按键时,调用_InvokeKeyHandler()方法处理按键事件。 9 | 输入法在_InvokeKeyHandler()方法中,发起ITfEditSession编辑会话。 10 | 11 | ```C++ 12 | HRESULT CSampleIME::_InvokeKeyHandler(_In_ ITfContext *pContext, UINT code, WCHAR wch, DWORD flags, _KEYSTROKE_STATE keyState) 13 | { 14 | flags; 15 | 16 | CKeyHandlerEditSession* pEditSession = nullptr; 17 | HRESULT hr = E_FAIL; 18 | 19 | // we'll insert a char ourselves in place of this keystroke 20 | pEditSession = new (std::nothrow) CKeyHandlerEditSession(this, pContext, code, wch, keyState); 21 | if (pEditSession == nullptr) 22 | { 23 | goto Exit; 24 | } 25 | 26 | // 27 | // Call CKeyHandlerEditSession::DoEditSession(). 28 | // 29 | // Do not specify TF_ES_SYNC so edit session is not invoked on WinWord 30 | // 31 | hr = pContext->RequestEditSession(_tfClientId, pEditSession, TF_ES_ASYNCDONTCARE | TF_ES_READWRITE, &hr); 32 | 33 | pEditSession->Release(); 34 | 35 | Exit: 36 | return hr; 37 | } 38 | ``` 39 | 40 | ## 3.18.2 处理编码键 41 | 42 | 当按下编码键后,如果没有开始合成,则开始合成,保存合成以及上下文。
43 | 然后获取当前插入范围,与合成范围比较,如果合成范围不覆盖插入点,则不继续处理。
44 | 然后将编码保存到合成处理器引擎。
45 | 最后调用_HandleCompositionInputWorker(),开始处理编码。 46 | 47 | ```C++ 48 | HRESULT CSampleIME::_HandleCompositionInput(TfEditCookie ec, _In_ ITfContext *pContext, WCHAR wch) 49 | { 50 | ITfRange* pRangeComposition = nullptr; 51 | TF_SELECTION tfSelection; 52 | ULONG fetched = 0; 53 | BOOL isCovered = TRUE; 54 | 55 | CCompositionProcessorEngine* pCompositionProcessorEngine = nullptr; 56 | pCompositionProcessorEngine = _pCompositionProcessorEngine; 57 | 58 | if ((_pCandidateListUIPresenter != nullptr) && (_candidateMode != CANDIDATE_INCREMENTAL)) 59 | { 60 | _HandleCompositionFinalize(ec, pContext, FALSE); 61 | } 62 | 63 | // Start the new (std::nothrow) compositon if there is no composition. 64 | if (!_IsComposing()) 65 | { 66 | _StartComposition(pContext); 67 | } 68 | 69 | // first, test where a keystroke would go in the document if we did an insert 70 | if (pContext->GetSelection(ec, TF_DEFAULT_SELECTION, 1, &tfSelection, &fetched) != S_OK || fetched != 1) 71 | { 72 | return S_FALSE; 73 | } 74 | 75 | // is the insertion point covered by a composition? 76 | if (SUCCEEDED(_pComposition->GetRange(&pRangeComposition))) 77 | { 78 | isCovered = _IsRangeCovered(ec, tfSelection.range, pRangeComposition); 79 | 80 | pRangeComposition->Release(); 81 | 82 | if (!isCovered) 83 | { 84 | goto Exit; 85 | } 86 | } 87 | 88 | // Add virtual key to composition processor engine 89 | pCompositionProcessorEngine->AddVirtualKey(wch); 90 | 91 | _HandleCompositionInputWorker(pCompositionProcessorEngine, ec, pContext); 92 | 93 | Exit: 94 | tfSelection.range->Release(); 95 | return S_OK; 96 | } 97 | ``` 98 | 99 | ## 3.18.3 处理编码 100 | 101 | 假设最简单的场景,既只输入一个编码“a”。那么输入法主要完成两项工作:
102 | 1、在输入窗口显示编码“a”。
103 | 2、在候选窗口显示汉字“啊……”。 104 | 105 | 在TSF框架里编码是由TSF管理器控制的,所以输入法仅仅是将编码写入Composition合成。
106 | Windows程序习惯将数据与视图分离,所以有单独的CandidateList候选列表数据处理过程。 107 | 108 | 在CSampleIME::_HandleCompositionInputWorker()函数中,首先从合成处理器引擎中获取汉字编码。
109 | 然后将汉字编码写入Composition合成。
110 | 接着获取CandidateList候选列表。
111 | 最后将CandidateList候选列表写入候选列表UI控制器。 112 | 113 | ```C++ 114 | HRESULT CSampleIME::_HandleCompositionInputWorker(_In_ CCompositionProcessorEngine *pCompositionProcessorEngine, TfEditCookie ec, _In_ ITfContext *pContext) 115 | { 116 | HRESULT hr = S_OK; 117 | CSampleImeArray readingStrings; 118 | BOOL isWildcardIncluded = TRUE; 119 | 120 | // 121 | // Get reading string from composition processor engine 122 | // 123 | pCompositionProcessorEngine->GetReadingStrings(&readingStrings, &isWildcardIncluded); 124 | 125 | for (UINT index = 0; index < readingStrings.Count(); index++) 126 | { 127 | hr = _AddComposingAndChar(ec, pContext, readingStrings.GetAt(index)); 128 | if (FAILED(hr)) 129 | { 130 | return hr; 131 | } 132 | } 133 | 134 | // 135 | // Get candidate string from composition processor engine 136 | // 137 | CSampleImeArray candidateList; 138 | 139 | pCompositionProcessorEngine->GetCandidateList(&candidateList, TRUE, FALSE); 140 | 141 | if ((candidateList.Count())) 142 | { 143 | hr = _CreateAndStartCandidate(pCompositionProcessorEngine, ec, pContext); 144 | if (SUCCEEDED(hr)) 145 | { 146 | _pCandidateListUIPresenter->_ClearList(); 147 | _pCandidateListUIPresenter->_SetText(&candidateList, TRUE); 148 | } 149 | } 150 | else if (_pCandidateListUIPresenter) 151 | { 152 | _pCandidateListUIPresenter->_ClearList(); 153 | } 154 | else if (readingStrings.Count() && isWildcardIncluded) 155 | { 156 | hr = _CreateAndStartCandidate(pCompositionProcessorEngine, ec, pContext); 157 | if (SUCCEEDED(hr)) 158 | { 159 | _pCandidateListUIPresenter->_ClearList(); 160 | } 161 | } 162 | return hr; 163 | } 164 | ``` 165 | 166 | >_AddComposingAndChar(ec, pContext, readingStrings.GetAt(index))将在合成主题讲解。 167 | >GetCandidateList(&candidateList, TRUE, FALSE)将在候选列表主题讲解。 168 | >_pCandidateListUIPresenter->_SetText(&candidateList, TRUE)将在候选窗口主题讲解。 169 | 170 | 其他更复杂的编码处理,将在后续主题讲解。 171 | 172 | ## 3.18.4 创建候选窗口 173 | 174 | 当用户按下编码键,输入法生成候选列表后,开始创建候选窗口。 175 | 176 | ```C++ 177 | HRESULT CSampleIME::_CreateAndStartCandidate(_In_ CCompositionProcessorEngine *pCompositionProcessorEngine, TfEditCookie ec, _In_ ITfContext *pContext) 178 | { 179 | HRESULT hr = S_OK; 180 | 181 | if (((_candidateMode == CANDIDATE_PHRASE) && (_pCandidateListUIPresenter)) 182 | || ((_candidateMode == CANDIDATE_NONE) && (_pCandidateListUIPresenter))) 183 | { 184 | // Recreate candidate list 185 | _pCandidateListUIPresenter->_EndCandidateList(); 186 | delete _pCandidateListUIPresenter; 187 | _pCandidateListUIPresenter = nullptr; 188 | 189 | _candidateMode = CANDIDATE_NONE; 190 | _isCandidateWithWildcard = FALSE; 191 | } 192 | 193 | if (_pCandidateListUIPresenter == nullptr) 194 | { 195 | _pCandidateListUIPresenter = new (std::nothrow) CCandidateListUIPresenter(this, Global::AtomCandidateWindow, 196 | CATEGORY_CANDIDATE, 197 | pCompositionProcessorEngine->GetCandidateListIndexRange(), 198 | FALSE); 199 | if (!_pCandidateListUIPresenter) 200 | { 201 | return E_OUTOFMEMORY; 202 | } 203 | 204 | _candidateMode = CANDIDATE_INCREMENTAL; 205 | _isCandidateWithWildcard = FALSE; 206 | 207 | // we don't cache the document manager object. So get it from pContext. 208 | ITfDocumentMgr* pDocumentMgr = nullptr; 209 | if (SUCCEEDED(pContext->GetDocumentMgr(&pDocumentMgr))) 210 | { 211 | // get the composition range. 212 | ITfRange* pRange = nullptr; 213 | if (SUCCEEDED(_pComposition->GetRange(&pRange))) 214 | { 215 | hr = _pCandidateListUIPresenter->_StartCandidateList(_tfClientId, pDocumentMgr, pContext, ec, pRange, pCompositionProcessorEngine->GetCandidateWindowWidth()); 216 | pRange->Release(); 217 | } 218 | pDocumentMgr->Release(); 219 | } 220 | } 221 | 222 | return hr; 223 | } 224 | ``` -------------------------------------------------------------------------------- /doc/uiless/启动过程.md: -------------------------------------------------------------------------------- 1 | ## C.1 启动过程 2 | 3 | 无界面模式的启动过程与普通模式几乎完全一样。 4 | 5 | Function |Description 6 | -|- 7 | DllMain(HINSTANCE__ *, unsigned long, void *) |DLL加载过程 8 | Global::RegisterWindowClass(void) | 9 | DllGetClassObject(const _GUID &, const _GUID &, void * *) | 10 | BuildGlobalObjects(void) | 11 | CClassFactory::CreateInstance(IUnknown *, const _GUID &, void * *) | 12 | CSampleIME::QueryInterface(const _GUID &, void * *) {AA80E7F7-2021-11D2-93E0-0060B067B86E} |ITfTextInputProcessor 13 | CSampleIME::QueryInterface(const _GUID &, void * *) {AA80E7F7-2021-11D2-93E0-0060B067B86E} |ITfTextInputProcessorEx 14 | CSampleIME::QueryInterface(const _GUID &, void * *) {6E4E2102-F9CD-433D-B496-303CE03A6507} | 15 | 16 | Function |Description 17 | -|- 18 | /* 19 | CSampleIME::ActivateEx(ITfThreadMgr *, unsigned long, unsigned long) 0x00000004 |无界面模式 20 | CSampleIME::_InitThreadMgrEventSink(void) | 21 | CSampleIME::QueryInterface(const _GUID &, void * *) {IID_ITfThreadMgrEventSink} | 22 | CSampleIME::_InitTextEditSink(ITfDocumentMgr *) | 23 | CSampleIME::QueryInterface(const _GUID &, void * *) {IID_ITfTextEditSink} | 24 | CSampleIME::_InitKeyEventSink(void) | 25 | CSampleIME::OnSetFocus(int) | 26 | CSampleIME::_InitActiveLanguageProfileNotifySink(void) | 27 | CSampleIME::QueryInterface(const _GUID &, void * *) {IID_ITfActiveLanguageProfileNotifySink} | 28 | CSampleIME::_InitThreadFocusSink(void) | 29 | CSampleIME::QueryInterface(const _GUID &, void * *) {C0F1DB0C-3A20-405C-A303-96B6010A885F} |ITfThreadFocusSink 30 | CSampleIME::_InitDisplayAttributeGuidAtom(void) | 31 | CSampleIME::_InitFunctionProviderSink(void) | 32 | CSampleIME::QueryInterface(const _GUID &, void * *) {IID_IUnknown} | 33 | CSampleIME::QueryInterface(const _GUID &, void * *) {IID_ITfFunctionProvider} | 34 | CSampleIME::GetFunction(const _GUID &, const _GUID &, IUnknown * *) | 35 | CSampleIME::_AddTextProcessorEngine(void) | 36 | CTfInputProcessorProfile::CreateInstance(void) | 37 | CTfInputProcessorProfile::GetCurrentLanguage(unsigned short *) | 38 | CTfInputProcessorProfile::GetDefaultLanguageProfile(unsigned short, const _GUID &, _GUID *, _GUID *) | 39 | CCompositionProcessorEngine::InitKeyStrokeTable(void) | 40 | CCompositionProcessorEngine::SetupLanguageProfile(unsigned short, const _GUID &, ITfThreadMgr *, unsigned long, int, int) | 41 | CCompositionProcessorEngine::InitializeSampleIMECompartment(ITfThreadMgr *, unsigned long) | 42 | CCompositionProcessorEngine::PrivateCompartmentsUpdated(ITfThreadMgr *) | 43 | CCompositionProcessorEngine::SetupPunctuationPair(void) | 44 | CCompositionProcessorEngine::SetupLanguageBar(ITfThreadMgr *, unsigned long, int) | 45 | CCompositionProcessorEngine::InitLanguageBar(CLangBarItemButton *, ITfThreadMgr *, unsigned long, const _GUID &) | 46 | CLangBarItemButton::_AddItem(ITfThreadMgr *) | 47 | CLangBarItemButton::GetInfo(TF_LANGBARITEMINFO *) | 48 | CLangBarItemButton::GetInfo(TF_LANGBARITEMINFO *) | 49 | CLangBarItemButton::QueryInterface(const _GUID &, void * *) {IID_ITfSource} | 50 | CLangBarItemButton::AdviseSink(const _GUID &, IUnknown *, unsigned long *) | 51 | CLangBarItemButton::_RegisterCompartment(ITfThreadMgr *, unsigned long, const _GUID &) | 52 | CCompartmentEventSink::_Advise(IUnknown *, const _GUID &) | 53 | CCompartmentEventSink::QueryInterface(const _GUID &, void * *) {IID_ITfCompartmentEventSink} | 54 | CCompositionProcessorEngine::InitLanguageBar(CLangBarItemButton *, ITfThreadMgr *, unsigned long, const _GUID &) | 55 | CLangBarItemButton::_AddItem(ITfThreadMgr *) | 56 | CLangBarItemButton::GetInfo(TF_LANGBARITEMINFO *) | 57 | CLangBarItemButton::GetInfo(TF_LANGBARITEMINFO *) | 58 | CLangBarItemButton::QueryInterface(const _GUID &, void * *) {IID_ITfSource} | 59 | CLangBarItemButton::AdviseSink(const _GUID &, IUnknown *, unsigned long *) | 60 | CLangBarItemButton::_RegisterCompartment(ITfThreadMgr *, unsigned long, const _GUID &) | 61 | CCompartmentEventSink::_Advise(IUnknown *, const _GUID &) | 62 | CCompartmentEventSink::QueryInterface(const _GUID &, void * *) {IID_ITfCompartmentEventSink} | 63 | CCompositionProcessorEngine::InitLanguageBar(CLangBarItemButton *, ITfThreadMgr *, unsigned long, const _GUID &) | 64 | CLangBarItemButton::_AddItem(ITfThreadMgr *) | 65 | CLangBarItemButton::GetInfo(TF_LANGBARITEMINFO *) | 66 | CLangBarItemButton::GetInfo(TF_LANGBARITEMINFO *) | 67 | CLangBarItemButton::QueryInterface(const _GUID &, void * *) {IID_ITfSource} | 68 | CLangBarItemButton::AdviseSink(const _GUID &, IUnknown *, unsigned long *) | 69 | CLangBarItemButton::_RegisterCompartment(ITfThreadMgr *, unsigned long, const _GUID &) | 70 | CCompartmentEventSink::_Advise(IUnknown *, const _GUID &) | 71 | CCompartmentEventSink::QueryInterface(const _GUID &, void * *) {IID_ITfCompartmentEventSink} | 72 | CCompartmentEventSink::_Advise(IUnknown *, const _GUID &) | 73 | CCompartmentEventSink::QueryInterface(const _GUID &, void * *) {IID_ITfCompartmentEventSink} | 74 | CCompartmentEventSink::_Advise(IUnknown *, const _GUID &) | 75 | CCompartmentEventSink::QueryInterface(const _GUID &, void * *) {IID_ITfCompartmentEventSink} | 76 | CCompartmentEventSink::_Advise(IUnknown *, const _GUID &) | 77 | CCompartmentEventSink::QueryInterface(const _GUID &, void * *) {IID_ITfCompartmentEventSink} | 78 | CCompartmentEventSink::_Advise(IUnknown *, const _GUID &) | 79 | CCompartmentEventSink::QueryInterface(const _GUID &, void * *) {IID_ITfCompartmentEventSink} | 80 | CCompositionProcessorEngine::SetupKeystroke(void) | 81 | CCompositionProcessorEngine::SetKeystrokeTable(CSampleImeArray *) | 82 | CCompositionProcessorEngine::SetupConfiguration(void) | 83 | CCompositionProcessorEngine::SetInitialCandidateListRange(void) | 84 | CCompositionProcessorEngine::SetDefaultCandidateTextFont(void) | 85 | CCompositionProcessorEngine::SetupDictionaryFile(void) | 86 | CFile::CreateFileW(const wchar_t *, unsigned long, unsigned long, unsigned long, _SECURITY_ATTRIBUTES *, unsigned long, void *) | 87 | CSampleIME::ActivateEx(ITfThreadMgr *, unsigned long, unsigned long) end | 88 | */ | 89 | 90 | Function |Description 91 | -|- 92 | CSampleIME::OnActivated(const _GUID &, const _GUID &, int) |响应激活事件 93 | /* | 94 | CSampleIME::_AddTextProcessorEngine(void) | 95 | CTfInputProcessorProfile::CreateInstance(void) | 96 | CTfInputProcessorProfile::GetCurrentLanguage(unsigned short *) | 97 | CTfInputProcessorProfile::GetDefaultLanguageProfile(unsigned short, const _GUID &, _GUID *, _GUID *) | 98 | CCompositionProcessorEngine::ShowAllLanguageBarIcons(void) | 99 | CCompositionProcessorEngine::SetLanguageBarStatus(unsigned long, int) | 100 | CCompositionProcessorEngine::ConversionModeCompartmentUpdated(ITfThreadMgr *) | 101 | CCompartment::_GetCompartmentDWORD(unsigned long &) 0x00000401 | 102 | CSampleIME::OnActivated(const _GUID &, const _GUID &, int) end | 103 | */ | 104 | 105 | Function |Description 106 | -|- 107 | CLangBarItemButton::GetInfo(TF_LANGBARITEMINFO *) |提供托盘信息 108 | CLangBarItemButton::GetTooltipString(wchar_t * *) | 109 | CLangBarItemButton::GetText(wchar_t * *) | 110 | CLangBarItemButton::GetIcon(HICON__ * *) | 111 | CLangBarItemButton::GetInfo(TF_LANGBARITEMINFO *) | 112 | CLangBarItemButton::GetTooltipString(wchar_t * *) | 113 | CLangBarItemButton::GetText(wchar_t * *) | 114 | CLangBarItemButton::GetIcon(HICON__ * *) | 115 | CLangBarItemButton::GetInfo(TF_LANGBARITEMINFO *) | 116 | CLangBarItemButton::GetTooltipString(wchar_t * *) | 117 | CLangBarItemButton::GetText(wchar_t * *) | 118 | CLangBarItemButton::GetIcon(HICON__ * *) | -------------------------------------------------------------------------------- /SampleIME/KeyStateCategory.h: -------------------------------------------------------------------------------- 1 | // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF 2 | // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO 3 | // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 4 | // PARTICULAR PURPOSE. 5 | // 6 | // Copyright (c) Microsoft Corporation. All rights reserved 7 | 8 | #pragma once 9 | #include "Globals.h" 10 | #include "Private.h" 11 | #include "SampleIME.h" 12 | 13 | class CKeyStateCategory; 14 | 15 | class CKeyStateCategoryFactory 16 | { 17 | public: 18 | static CKeyStateCategoryFactory* Instance(); 19 | CKeyStateCategory* MakeKeyStateCategory(KEYSTROKE_CATEGORY keyCategory, _In_ CSampleIME *pTextService); 20 | void Release(); 21 | 22 | protected: 23 | CKeyStateCategoryFactory(); 24 | 25 | private: 26 | static CKeyStateCategoryFactory* _instance; 27 | 28 | }; 29 | 30 | typedef struct KeyHandlerEditSessionDTO 31 | { 32 | KeyHandlerEditSessionDTO::KeyHandlerEditSessionDTO(TfEditCookie tFEC, _In_ ITfContext *pTfContext, UINT virualCode, WCHAR inputChar, KEYSTROKE_FUNCTION arrowKeyFunction) 33 | { 34 | ec = tFEC; 35 | pContext = pTfContext; 36 | code = virualCode; 37 | wch = inputChar; 38 | arrowKey = arrowKeyFunction; 39 | } 40 | 41 | TfEditCookie ec; 42 | ITfContext* pContext; 43 | UINT code; 44 | WCHAR wch; 45 | KEYSTROKE_FUNCTION arrowKey; 46 | }KeyHandlerEditSessionDTO; 47 | 48 | class CKeyStateCategory 49 | { 50 | public: 51 | CKeyStateCategory(_In_ CSampleIME *pTextService); 52 | 53 | protected: 54 | ~CKeyStateCategory(void); 55 | 56 | public: 57 | HRESULT KeyStateHandler(KEYSTROKE_FUNCTION function, KeyHandlerEditSessionDTO dto); 58 | void Release(void); 59 | 60 | protected: 61 | // HandleKeyInput 62 | virtual HRESULT HandleKeyInput(KeyHandlerEditSessionDTO dto); 63 | 64 | // HandleKeyFinalizeTextStoreAndInput 65 | virtual HRESULT HandleKeyFinalizeTextStoreAndInput(KeyHandlerEditSessionDTO dto); 66 | 67 | // HandleKeyFinalizeTextStore 68 | virtual HRESULT HandleKeyFinalizeTextStore(KeyHandlerEditSessionDTO dto); 69 | 70 | // HandleKeyFinalizeCandidatelistAndInput 71 | virtual HRESULT HandleKeyFinalizeCandidatelistAndInput(KeyHandlerEditSessionDTO dto); 72 | 73 | // HandleKeyFinalizeCandidatelist 74 | virtual HRESULT HandleKeyFinalizeCandidatelist(KeyHandlerEditSessionDTO dto); 75 | 76 | // HandleKeyConvert 77 | virtual HRESULT HandleKeyConvert(KeyHandlerEditSessionDTO dto); 78 | 79 | // HandleKeyConvertWild 80 | virtual HRESULT HandleKeyConvertWildCard(KeyHandlerEditSessionDTO dto); 81 | 82 | // HandleKeyCancel 83 | virtual HRESULT HandleKeyCancel(KeyHandlerEditSessionDTO dto); 84 | 85 | // HandleKeyBackspace 86 | virtual HRESULT HandleKeyBackspace(KeyHandlerEditSessionDTO dto); 87 | 88 | // HandleKeyArrow 89 | virtual HRESULT HandleKeyArrow(KeyHandlerEditSessionDTO dto); 90 | 91 | // HandleKeyDoubleSingleByte 92 | virtual HRESULT HandleKeyDoubleSingleByte(KeyHandlerEditSessionDTO dto); 93 | 94 | // HandleKeyPunctuation 95 | virtual HRESULT HandleKeyPunctuation(KeyHandlerEditSessionDTO dto); 96 | 97 | // HandleKeySelectByNumber 98 | virtual HRESULT HandleKeySelectByNumber(KeyHandlerEditSessionDTO dto); 99 | 100 | protected: 101 | CSampleIME* _pTextService; 102 | }; 103 | 104 | class CKeyStateComposing : public CKeyStateCategory 105 | { 106 | public: 107 | CKeyStateComposing(_In_ CSampleIME *pTextService); 108 | 109 | protected: 110 | // _HandleCompositionInput 111 | HRESULT HandleKeyInput(KeyHandlerEditSessionDTO dto); 112 | 113 | // HandleKeyCompositionFinalizeTextStoreAndInput 114 | HRESULT HandleKeyFinalizeTextStoreAndInput(KeyHandlerEditSessionDTO dto); 115 | 116 | // HandleKeyFinalizeTextStore 117 | HRESULT HandleKeyFinalizeTextStore(KeyHandlerEditSessionDTO dto); 118 | 119 | // HandleKeyCompositionFinalizeCandidatelistAndInput 120 | HRESULT HandleKeyFinalizeCandidatelistAndInput(KeyHandlerEditSessionDTO dto); 121 | 122 | // HandleKeyCompositionFinalizeCandidatelist 123 | HRESULT HandleKeyFinalizeCandidatelist(KeyHandlerEditSessionDTO dto); 124 | 125 | // HandleCompositionConvert 126 | HRESULT HandleKeyConvert(KeyHandlerEditSessionDTO dto); 127 | 128 | // HandleKeyCompositionConvertWildCard 129 | HRESULT HandleKeyConvertWildCard(KeyHandlerEditSessionDTO dto); 130 | 131 | // HandleCancel 132 | HRESULT HandleKeyCancel(KeyHandlerEditSessionDTO dto); 133 | 134 | // HandleCompositionBackspace 135 | HRESULT HandleKeyBackspace(KeyHandlerEditSessionDTO dto); 136 | 137 | // HandleArrowKey 138 | HRESULT HandleKeyArrow(KeyHandlerEditSessionDTO dto); 139 | 140 | // HandleKeyDoubleSingleByte 141 | HRESULT HandleKeyDoubleSingleByte(KeyHandlerEditSessionDTO dto); 142 | 143 | // HandleKeyCompositionPunctuation 144 | HRESULT HandleKeyPunctuation(KeyHandlerEditSessionDTO dto); 145 | }; 146 | 147 | class CKeyStateCandidate : public CKeyStateCategory 148 | { 149 | public: 150 | CKeyStateCandidate(_In_ CSampleIME *pTextService); 151 | 152 | protected: 153 | // HandleKeyFinalizeCandidatelist 154 | HRESULT HandleKeyFinalizeCandidatelist(KeyHandlerEditSessionDTO dto); 155 | 156 | // HandleKeyFinalizeCandidatelistAndInput 157 | HRESULT HandleKeyFinalizeCandidatelistAndInput(KeyHandlerEditSessionDTO dto); 158 | 159 | //_HandleCandidateConvert 160 | HRESULT HandleKeyConvert(KeyHandlerEditSessionDTO dto); 161 | 162 | //_HandleCancel 163 | HRESULT HandleKeyCancel(KeyHandlerEditSessionDTO dto); 164 | 165 | //_HandleCandidateArrowKey 166 | HRESULT HandleKeyArrow(KeyHandlerEditSessionDTO dto); 167 | 168 | //_HandleCandidateSelectByNumber 169 | HRESULT HandleKeySelectByNumber(KeyHandlerEditSessionDTO dto); 170 | }; 171 | 172 | class CKeyStatePhrase : public CKeyStateCategory 173 | { 174 | public: 175 | CKeyStatePhrase(_In_ CSampleIME *pTextService); 176 | 177 | protected: 178 | //_HandleCancel 179 | HRESULT HandleKeyFinalizeCandidatelist(KeyHandlerEditSessionDTO dto); 180 | 181 | //_HandleCancel 182 | HRESULT HandleKeyCancel(KeyHandlerEditSessionDTO dto); 183 | 184 | //_HandlePhraseArrowKey 185 | HRESULT HandleKeyArrow(KeyHandlerEditSessionDTO dto); 186 | 187 | //_HandlePhraseSelectByNumber 188 | HRESULT HandleKeySelectByNumber(KeyHandlerEditSessionDTO dto); 189 | }; 190 | 191 | //degeneration class 192 | class CKeyStateNull : public CKeyStateCategory 193 | { 194 | public: 195 | CKeyStateNull(_In_ CSampleIME *pTextService) : CKeyStateCategory(pTextService) {}; 196 | 197 | protected: 198 | // _HandleNullInput 199 | HRESULT HandleKeyInput(KeyHandlerEditSessionDTO dto) { return __super::HandleKeyInput(dto); }; 200 | 201 | // HandleKeyNullFinalizeTextStoreAndInput 202 | HRESULT HandleKeyFinalizeTextStoreAndInput(KeyHandlerEditSessionDTO dto) { return __super::HandleKeyFinalizeTextStoreAndInput(dto); }; 203 | 204 | // HandleKeyFinalizeTextStore 205 | HRESULT HandleKeyFinalizeTextStore(KeyHandlerEditSessionDTO dto) { return __super::HandleKeyFinalizeTextStore(dto); }; 206 | 207 | // HandleKeyNullFinalizeCandidatelistAndInput 208 | HRESULT HandleKeyFinalizeCandidatelistAndInput(KeyHandlerEditSessionDTO dto) { return __super::HandleKeyFinalizeCandidatelistAndInput(dto); }; 209 | 210 | // HandleKeyNullFinalizeCandidatelist 211 | HRESULT HandleKeyFinalizeCandidatelist(KeyHandlerEditSessionDTO dto) { return __super::HandleKeyFinalizeCandidatelist(dto); }; 212 | 213 | //_HandleNullConvert 214 | HRESULT HandleKeyConvert(KeyHandlerEditSessionDTO dto) { return __super::HandleKeyConvert(dto); }; 215 | 216 | //_HandleNullCancel 217 | HRESULT HandleKeyCancel(KeyHandlerEditSessionDTO dto) { return __super::HandleKeyCancel(dto); }; 218 | 219 | // HandleKeyNullConvertWild 220 | HRESULT HandleKeyConvertWildCard(KeyHandlerEditSessionDTO dto) { return __super::HandleKeyConvertWildCard(dto); }; 221 | 222 | //_HandleNullBackspace 223 | HRESULT HandleKeyBackspace(KeyHandlerEditSessionDTO dto) { return __super::HandleKeyBackspace(dto); }; 224 | 225 | //_HandleNullArrowKey 226 | HRESULT HandleKeyArrow(KeyHandlerEditSessionDTO dto) { return __super::HandleKeyArrow(dto); }; 227 | 228 | // HandleKeyDoubleSingleByte 229 | HRESULT HandleKeyDoubleSingleByte(KeyHandlerEditSessionDTO dto) { return __super::HandleKeyDoubleSingleByte(dto); }; 230 | 231 | // HandleKeyPunctuation 232 | HRESULT HandleKeyPunctuation(KeyHandlerEditSessionDTO dto) { return __super::HandleKeyPunctuation(dto); }; 233 | 234 | //_HandleNullCandidateSelectByNumber 235 | HRESULT HandleKeySelectByNumber(KeyHandlerEditSessionDTO dto) { return __super::HandleKeySelectByNumber(dto); }; 236 | }; --------------------------------------------------------------------------------