NVDA with Japanese branch
Revisión | b7b4dc9c385dc9478316719dd80c2d371ddf4b09 (tree) |
---|---|
Tiempo | 2013-08-20 14:40:49 |
Autor | Takuya Nishimoto <nishimotz@gmai...> |
Commiter | Takuya Nishimoto |
merged tsf_japanese branch
@@ -0,0 +1,780 @@ | ||
1 | +/* | |
2 | +This file is a part of the NVDA project. | |
3 | +URL: http://www.nvda-project.org/ | |
4 | +Copyright 2010-2012 World Light Information Limited and Hong Kong Blind Union. | |
5 | + This program is free software: you can redistribute it and/or modify | |
6 | + it under the terms of the GNU General Public License version 2.0, as published by | |
7 | + the Free Software Foundation. | |
8 | + This program is distributed in the hope that it will be useful, | |
9 | + but WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | |
11 | +This license can be found at: | |
12 | +http://www.gnu.org/licenses/old-licenses/gpl-2.0.html | |
13 | +*/ | |
14 | + | |
15 | +#include <map> | |
16 | +#include <windows.h> | |
17 | +#include <wchar.h> | |
18 | +#include <msctf.h> | |
19 | +#include <common/log.h> | |
20 | +#include <common/lock.h> | |
21 | +#include "nvdaHelperRemote.h" | |
22 | +#include "nvdaControllerInternal.h" | |
23 | +#include "typedCharacter.h" | |
24 | +#include "ime.h" | |
25 | +#include "tsf.h" | |
26 | +#include "inputLangChange.h" | |
27 | + | |
28 | +using namespace std; | |
29 | + | |
30 | +CLSID curTSFClsID={0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0}}; | |
31 | +bool isUIElementMgrSafe=false; | |
32 | + | |
33 | + | |
34 | +bool fetchRangeExtent(ITfRange* pRange, long* start, ULONG* length) { | |
35 | + HRESULT res=S_OK; | |
36 | + if(!pRange) return false; | |
37 | + ITfRangeACP* pRangeACP=NULL; | |
38 | + res=pRange->QueryInterface(IID_ITfRangeACP,(void**)&pRangeACP); | |
39 | + if(res!=S_OK||!pRangeACP) return false; | |
40 | + res=pRangeACP->GetExtent(start,(long*)length); | |
41 | + pRangeACP->Release(); | |
42 | + return true?(res==S_OK):false; | |
43 | +} | |
44 | + | |
45 | +#define NVDAJP 1 | |
46 | + | |
47 | +#ifdef NVDAJP | |
48 | +// http://msdn.microsoft.com/en-us/library/windows/desktop/ms629224%28v=vs.85%29.aspx | |
49 | +HRESULT getDispAttrFromRange(ITfContext *pContext, | |
50 | + ITfRange *pRange, | |
51 | + TfEditCookie ec, | |
52 | + TF_DISPLAYATTRIBUTE *pDispAttr) | |
53 | +{ | |
54 | + HRESULT hr; | |
55 | + ITfCategoryMgr *pCategoryMgr; | |
56 | + hr = CoCreateInstance(CLSID_TF_CategoryMgr, | |
57 | + NULL, | |
58 | + CLSCTX_INPROC_SERVER, | |
59 | + IID_ITfCategoryMgr, | |
60 | + (LPVOID*)&pCategoryMgr); | |
61 | + if(FAILED(hr)){ | |
62 | + return hr; | |
63 | + } | |
64 | + ITfDisplayAttributeMgr *pDispMgr; | |
65 | + hr = CoCreateInstance(CLSID_TF_DisplayAttributeMgr, | |
66 | + NULL, | |
67 | + CLSCTX_INPROC_SERVER, | |
68 | + IID_ITfDisplayAttributeMgr, | |
69 | + (LPVOID*)&pDispMgr); | |
70 | + if(FAILED(hr)){ | |
71 | + pCategoryMgr->Release(); | |
72 | + return hr; | |
73 | + } | |
74 | + ITfProperty *pProp; | |
75 | + hr = pContext->GetProperty(GUID_PROP_ATTRIBUTE, &pProp); | |
76 | + if(SUCCEEDED(hr)){ | |
77 | + VARIANT var; | |
78 | + VariantInit(&var); | |
79 | + hr = pProp->GetValue(ec, pRange, &var); | |
80 | + if(S_OK == hr){ | |
81 | + if(VT_I4 == var.vt){ | |
82 | + GUID guid; | |
83 | + hr = pCategoryMgr->GetGUID((TfGuidAtom)var.lVal, &guid); | |
84 | + if(SUCCEEDED(hr)){ | |
85 | + ITfDisplayAttributeInfo *pDispInfo; | |
86 | + hr = pDispMgr->GetDisplayAttributeInfo(guid, &pDispInfo, NULL); | |
87 | + if(SUCCEEDED(hr)){ | |
88 | + hr = pDispInfo->GetAttributeInfo(pDispAttr); | |
89 | + if(SUCCEEDED(hr)){ | |
90 | + OutputDebugString(L"GetAttributeInfo() succeeded"); | |
91 | + } | |
92 | + pDispInfo->Release(); | |
93 | + } | |
94 | + } | |
95 | + } else { | |
96 | + hr = E_FAIL; | |
97 | + } | |
98 | + VariantClear(&var); | |
99 | + } | |
100 | + pProp->Release(); | |
101 | + } | |
102 | + pCategoryMgr->Release(); | |
103 | + pDispMgr->Release(); | |
104 | + return hr; | |
105 | +} | |
106 | +#endif // NVDAJP | |
107 | + | |
108 | + | |
109 | +class TsfSink; | |
110 | +typedef map<DWORD,TsfSink*> sinkMap_t; | |
111 | + | |
112 | +static DWORD gTsfIndex = TLS_OUT_OF_INDEXES; | |
113 | +static sinkMap_t gTsfSinks; | |
114 | +static LockableObject gTsfSinksLock; | |
115 | +static PVOID gLastCompStr = NULL; | |
116 | + | |
117 | +class TsfSink : public ITfThreadMgrEventSink, public ITfActiveLanguageProfileNotifySink, public ITfTextEditSink, public ITfUIElementSink, public ITfInputProcessorProfileActivationSink { | |
118 | +public: | |
119 | + TsfSink(); | |
120 | + ~TsfSink(); | |
121 | + | |
122 | + // Initializes object after creation | |
123 | + bool Initialize(); | |
124 | + | |
125 | + // Cleans up object before destruction | |
126 | + void CleanUp(); | |
127 | + | |
128 | + // IUnknown methods | |
129 | + STDMETHODIMP QueryInterface(REFIID, LPVOID*); | |
130 | + STDMETHODIMP_(ULONG) AddRef(); | |
131 | + STDMETHODIMP_(ULONG) Release(); | |
132 | + | |
133 | + // ITfThreadMgrEventSink methods | |
134 | + STDMETHODIMP OnInitDocumentMgr(ITfDocumentMgr*); | |
135 | + STDMETHODIMP OnUninitDocumentMgr(ITfDocumentMgr*); | |
136 | + STDMETHODIMP OnSetFocus(ITfDocumentMgr*, ITfDocumentMgr*); | |
137 | + STDMETHODIMP OnPushContext(ITfContext*); | |
138 | + STDMETHODIMP OnPopContext(ITfContext*); | |
139 | + | |
140 | + // ITfTextEditSink methods | |
141 | + STDMETHODIMP OnEndEdit(ITfContext*, TfEditCookie, ITfEditRecord*); | |
142 | + | |
143 | + STDMETHODIMP ITfActiveLanguageProfileNotifySink::OnActivated(REFCLSID, REFGUID, BOOL); | |
144 | + STDMETHODIMP ITfInputProcessorProfileActivationSink::OnActivated(DWORD dwProfileType, LANGID langId, REFCLSID rclsid, REFGUID catId, REFGUID guidProfile, HKL hkl, DWORD dwFlags); | |
145 | + | |
146 | + // ITfUIElementSink methods | |
147 | + STDMETHODIMP BeginUIElement(DWORD, BOOL*); | |
148 | + STDMETHODIMP UpdateUIElement(DWORD); | |
149 | + STDMETHODIMP EndUIElement(DWORD); | |
150 | + | |
151 | + //Is TSF actually being used for this thread? | |
152 | + bool hasActiveProfile; | |
153 | + | |
154 | +private: | |
155 | + LONG mRefCount; | |
156 | + ITfThreadMgr* mpThreadMgr; | |
157 | + ITfSource* mpTextEditSrc; | |
158 | + ITfUIElementMgr* mpUIElementMgr; | |
159 | + DWORD mThreadMgrCookie; | |
160 | + DWORD mLangProfCookie; | |
161 | + DWORD mTextEditCookie; | |
162 | + DWORD mUIElementCookie; | |
163 | + DWORD curReadingInformationUIElementId; | |
164 | + bool inComposition; | |
165 | + | |
166 | + void UpdateTextEditSink(ITfDocumentMgr* docMgr); | |
167 | + void RemoveTextEditSink(); | |
168 | + WCHAR* HandleCompositionView(ITfContext* pCtx, TfEditCookie cookie); | |
169 | + WCHAR* HandleEditRecord(TfEditCookie cookie, ITfEditRecord* pEditRec); | |
170 | + IEnumITfCompositionView* GetCompViewEnum(ITfContext* pCtx); | |
171 | + ITfRange* CombineCompRange(ITfContext* pCtx, TfEditCookie cookie); | |
172 | +}; | |
173 | + | |
174 | +typedef HRESULT (WINAPI* TF_GetThreadMgr_t)(ITfThreadMgr**); | |
175 | +typedef HRESULT (WINAPI* TF_CreateThreadMgr_t)(ITfThreadMgr**); | |
176 | + | |
177 | +ITfThreadMgr* | |
178 | +create_thread_manager() { | |
179 | + ITfThreadMgr* mgr = NULL; | |
180 | + HMODULE dll = LoadLibraryA("msctf.dll"); | |
181 | + if (!dll) return NULL; | |
182 | + TF_GetThreadMgr_t get_func = | |
183 | + (TF_GetThreadMgr_t)GetProcAddress(dll, "TF_GetThreadMgr"); | |
184 | + if (get_func) get_func(&mgr); | |
185 | + /* | |
186 | + if (!mgr) { | |
187 | + TF_CreateThreadMgr_t create_func = | |
188 | + (TF_CreateThreadMgr_t)GetProcAddress(dll, "TF_CreateThreadMgr"); | |
189 | + if (create_func) create_func(&mgr); | |
190 | + } | |
191 | + */ | |
192 | + FreeLibrary(dll); | |
193 | + return mgr; | |
194 | +} | |
195 | + | |
196 | +typedef HRESULT (WINAPI* TF_CreateInputProcessorProfiles_t)(ITfInputProcessorProfiles**); | |
197 | + | |
198 | +ITfInputProcessorProfiles* | |
199 | +create_input_processor_profiles() { | |
200 | + ITfInputProcessorProfiles* profiles = NULL; | |
201 | + HMODULE dll = LoadLibraryA("msctf.dll"); | |
202 | + if (!dll) return NULL; | |
203 | + TF_CreateInputProcessorProfiles_t func = | |
204 | + (TF_CreateInputProcessorProfiles_t)GetProcAddress(dll, "TF_CreateInputProcessorProfiles"); | |
205 | + if (func) func(&profiles); | |
206 | + FreeLibrary(dll); | |
207 | + return profiles; | |
208 | +} | |
209 | + | |
210 | +TsfSink::TsfSink() { | |
211 | + mRefCount = 1; | |
212 | + mpThreadMgr = NULL; | |
213 | + mpTextEditSrc = NULL; | |
214 | + mpUIElementMgr=NULL; | |
215 | + mThreadMgrCookie = TF_INVALID_COOKIE; | |
216 | + mLangProfCookie = TF_INVALID_COOKIE; | |
217 | + mTextEditCookie = TF_INVALID_COOKIE; | |
218 | + mUIElementCookie = TF_INVALID_COOKIE; | |
219 | + curReadingInformationUIElementId=-1; | |
220 | + inComposition=false; | |
221 | + int lastCompositionStartOffset=0; | |
222 | + hasActiveProfile=false; | |
223 | +} | |
224 | + | |
225 | +TsfSink::~TsfSink() { | |
226 | +} | |
227 | + | |
228 | +bool TsfSink::Initialize() { | |
229 | + mpThreadMgr = create_thread_manager(); | |
230 | + if(!mpThreadMgr) return false; | |
231 | + HRESULT hr = S_OK; | |
232 | + ITfSource* src = NULL; | |
233 | + if (hr == S_OK) { | |
234 | + hr = mpThreadMgr->QueryInterface(IID_ITfSource, (void**)&src); | |
235 | + } | |
236 | + if (src) { | |
237 | + if (hr == S_OK) { | |
238 | + hr = src->AdviseSink(IID_ITfThreadMgrEventSink, | |
239 | + (ITfThreadMgrEventSink*)this, &mThreadMgrCookie); | |
240 | + } | |
241 | + if (hr == S_OK) { | |
242 | + ///For profile activations use ITfInputProcessProfileActivationSink if its available, otherwise ITfActiveLanguageNotifySink (usually on XP). | |
243 | + hr = src->AdviseSink(IID_ITfInputProcessorProfileActivationSink,(ITfInputProcessorProfileActivationSink*)this, &mLangProfCookie); | |
244 | + if(hr!=S_OK||mLangProfCookie==TF_INVALID_COOKIE) { | |
245 | + LOG_DEBUGWARNING(L"Cannot register ITfInputProcessorProfileActivationSink, trying ITfActiveLanguageProfileNotifySink instead"); | |
246 | + hr = src->AdviseSink(IID_ITfActiveLanguageProfileNotifySink,(ITfActiveLanguageProfileNotifySink*)this, &mLangProfCookie); | |
247 | + } | |
248 | + } | |
249 | + if(isUIElementMgrSafe) { | |
250 | + if (hr == S_OK) { | |
251 | + hr = mpThreadMgr->QueryInterface(IID_ITfUIElementMgr,(void**)&mpUIElementMgr); | |
252 | + } | |
253 | + if (hr == S_OK) { | |
254 | + hr = src->AdviseSink(IID_ITfUIElementSink,(ITfUIElementSink*)this, &mUIElementCookie); | |
255 | + } | |
256 | + } | |
257 | + src->Release(); | |
258 | + src = NULL; | |
259 | + } | |
260 | + ITfDocumentMgr* doc_mgr = NULL; | |
261 | + mpThreadMgr->GetFocus(&doc_mgr); | |
262 | + if (doc_mgr) { | |
263 | + UpdateTextEditSink(doc_mgr); | |
264 | + doc_mgr->Release(); | |
265 | + } | |
266 | + //Check to see if there is an active TSF language profile and set hasActiveProfile accordingly. | |
267 | + ITfInputProcessorProfiles* profiles = create_input_processor_profiles(); | |
268 | + if(profiles) { | |
269 | + LANGID lang = 0; | |
270 | + profiles->GetCurrentLanguage(&lang); | |
271 | + if(lang) { | |
272 | + IEnumTfLanguageProfiles* pEnumTfLanguageProfiles=NULL; | |
273 | + profiles->EnumLanguageProfiles(lang,&pEnumTfLanguageProfiles); | |
274 | + if(pEnumTfLanguageProfiles) { | |
275 | + TF_LANGUAGEPROFILE profile; | |
276 | + ULONG fetched=0; | |
277 | + while(pEnumTfLanguageProfiles->Next(1,&profile,&fetched)==S_OK&&fetched==1) { | |
278 | + if(profile.fActive&&IsEqualCLSID(profile.catid,GUID_TFCAT_TIP_KEYBOARD)) { | |
279 | + hasActiveProfile=true; | |
280 | + break; | |
281 | + } | |
282 | + } | |
283 | + pEnumTfLanguageProfiles->Release(); | |
284 | + } | |
285 | + } | |
286 | + profiles->Release(); | |
287 | + } | |
288 | + return true; | |
289 | +} | |
290 | + | |
291 | +void TsfSink::CleanUp() { | |
292 | + RemoveTextEditSink(); | |
293 | + if (mpThreadMgr) { | |
294 | + ITfSource* src = NULL; | |
295 | + mpThreadMgr->QueryInterface(IID_ITfSource, (void**)&src); | |
296 | + if (src) | |
297 | + { | |
298 | + if (mUIElementCookie != TF_INVALID_COOKIE) { | |
299 | + src->UnadviseSink(mUIElementCookie); | |
300 | + mUIElementCookie = TF_INVALID_COOKIE; | |
301 | + } | |
302 | + if (mThreadMgrCookie != TF_INVALID_COOKIE) { | |
303 | + src->UnadviseSink(mThreadMgrCookie); | |
304 | + } | |
305 | + if (mLangProfCookie != TF_INVALID_COOKIE) { | |
306 | + src->UnadviseSink(mLangProfCookie); | |
307 | + } | |
308 | + src->Release(); | |
309 | + } | |
310 | + if(mpUIElementMgr) { | |
311 | + mpUIElementMgr->Release(); | |
312 | + mpUIElementMgr=NULL; | |
313 | + } | |
314 | + mThreadMgrCookie = TF_INVALID_COOKIE; | |
315 | + mLangProfCookie = TF_INVALID_COOKIE; | |
316 | + mpThreadMgr->Release(); | |
317 | + mpThreadMgr = NULL; | |
318 | + } | |
319 | + CoUninitialize(); | |
320 | +} | |
321 | + | |
322 | +void TsfSink::UpdateTextEditSink(ITfDocumentMgr* docMgr) { | |
323 | + RemoveTextEditSink(); | |
324 | + if (!docMgr) return; | |
325 | + ITfContext* ctx = NULL; | |
326 | + HRESULT hr = docMgr->GetBase(&ctx); | |
327 | + if (hr == S_OK) { | |
328 | + hr = ctx->QueryInterface(IID_ITfSource, (void**)&mpTextEditSrc); | |
329 | + ctx->Release(); | |
330 | + } | |
331 | + if (hr == S_OK) { | |
332 | + hr = mpTextEditSrc->AdviseSink(IID_ITfTextEditSink, | |
333 | + (ITfTextEditSink*)this, &mTextEditCookie); | |
334 | + } | |
335 | + if (hr != S_OK) { | |
336 | + RemoveTextEditSink(); | |
337 | + return; | |
338 | + } | |
339 | +} | |
340 | + | |
341 | +void TsfSink::RemoveTextEditSink() { | |
342 | + if (mTextEditCookie != TF_INVALID_COOKIE) { | |
343 | + mpTextEditSrc->UnadviseSink(mTextEditCookie); | |
344 | + mTextEditCookie = TF_INVALID_COOKIE; | |
345 | + } | |
346 | + if (mpTextEditSrc) { | |
347 | + mpTextEditSrc->Release(); | |
348 | + mpTextEditSrc = NULL; | |
349 | + } | |
350 | +} | |
351 | + | |
352 | +STDMETHODIMP TsfSink::QueryInterface(REFIID riid, LPVOID* ppvObj) { | |
353 | + if (!ppvObj) return E_INVALIDARG; | |
354 | + if (IsEqualIID(riid, IID_IUnknown) || | |
355 | + IsEqualIID(riid, IID_ITfThreadMgrEventSink)) { | |
356 | + *ppvObj = (ITfThreadMgrEventSink*)this; | |
357 | + } else if (IsEqualIID(riid, IID_ITfActiveLanguageProfileNotifySink)) { | |
358 | + *ppvObj = (ITfActiveLanguageProfileNotifySink*)this; | |
359 | + } else if (IsEqualIID(riid, IID_ITfInputProcessorProfileActivationSink)) { | |
360 | + *ppvObj = (ITfInputProcessorProfileActivationSink*)this; | |
361 | + } else if (IsEqualIID(riid, IID_ITfTextEditSink)) { | |
362 | + *ppvObj = (ITfTextEditSink*)this; | |
363 | + } else if (IsEqualIID(riid, IID_ITfUIElementSink)) { | |
364 | + *ppvObj = (ITfUIElementSink*)this; | |
365 | + } else { | |
366 | + *ppvObj = NULL; | |
367 | + return E_NOINTERFACE; | |
368 | + } | |
369 | + AddRef(); | |
370 | + return S_OK; | |
371 | +} | |
372 | + | |
373 | +STDMETHODIMP_(ULONG) TsfSink::AddRef() { | |
374 | + return ++mRefCount; | |
375 | +} | |
376 | + | |
377 | +STDMETHODIMP_(ULONG) TsfSink::Release() { | |
378 | + LONG count = --mRefCount; | |
379 | + if (count == 0) delete this; | |
380 | + return count; | |
381 | +} | |
382 | + | |
383 | +STDMETHODIMP TsfSink::OnInitDocumentMgr(ITfDocumentMgr* pDIM) { | |
384 | + return S_OK; | |
385 | +} | |
386 | + | |
387 | +STDMETHODIMP TsfSink::OnUninitDocumentMgr(ITfDocumentMgr* pDIM) { | |
388 | + return S_OK; | |
389 | +} | |
390 | + | |
391 | +STDMETHODIMP TsfSink::OnSetFocus( | |
392 | + ITfDocumentMgr* pDIM, ITfDocumentMgr* pPrevDIM) { | |
393 | + UpdateTextEditSink(pDIM); | |
394 | + return S_OK; | |
395 | +} | |
396 | + | |
397 | +STDMETHODIMP TsfSink::OnPushContext(ITfContext* pCtx) { | |
398 | + return S_OK; | |
399 | +} | |
400 | + | |
401 | +STDMETHODIMP TsfSink::OnPopContext(ITfContext* pCtx) { | |
402 | + return S_OK; | |
403 | +} | |
404 | + | |
405 | +IEnumITfCompositionView* TsfSink::GetCompViewEnum(ITfContext* pCtx) { | |
406 | + // Make sure there is a composition context | |
407 | + ITfContextComposition* ctx_comp = NULL; | |
408 | + pCtx->QueryInterface(IID_ITfContextComposition, (void**)&ctx_comp); | |
409 | + if (!ctx_comp) return NULL; | |
410 | + | |
411 | + // Obtain composition view enumerator | |
412 | + IEnumITfCompositionView* enum_view = NULL; | |
413 | + ctx_comp->EnumCompositions(&enum_view); | |
414 | + ctx_comp->Release(); | |
415 | + return enum_view; | |
416 | +} | |
417 | + | |
418 | +ITfRange* TsfSink::CombineCompRange(ITfContext* pCtx, TfEditCookie cookie) { | |
419 | + // Make sure there is a composition view enumerator | |
420 | + IEnumITfCompositionView* enum_view = GetCompViewEnum(pCtx); | |
421 | + if (!enum_view) return NULL; | |
422 | + | |
423 | + // Combine composition ranges from all views | |
424 | + ITfRange* range = NULL; | |
425 | + ITfCompositionView* view = NULL; | |
426 | + while (enum_view->Next(1, &view, NULL) == S_OK) { | |
427 | + ITfRange *view_range = NULL; | |
428 | + if (view->GetRange(&view_range) == S_OK) { | |
429 | + if (!range) { | |
430 | + view_range->Clone(&range); | |
431 | + } else { | |
432 | + range->ShiftEndToRange(cookie, view_range, TF_ANCHOR_END); | |
433 | + } | |
434 | + view_range->Release(); | |
435 | + } | |
436 | + view->Release(); | |
437 | + } | |
438 | + enum_view->Release(); | |
439 | + return range; | |
440 | +} | |
441 | + | |
442 | +WCHAR* TsfSink::HandleCompositionView(ITfContext* pCtx, TfEditCookie cookie) { | |
443 | + // Make sure there is a composition view enumerator | |
444 | + IEnumITfCompositionView* enum_view = GetCompViewEnum(pCtx); | |
445 | + if (!enum_view) return NULL; | |
446 | + | |
447 | + // Concatenate text in all composition views into composition string | |
448 | + WCHAR* comp_str = (WCHAR*)malloc(sizeof(WCHAR)); | |
449 | + int comp_len = 0; | |
450 | + ITfCompositionView* view = NULL; | |
451 | + while (enum_view->Next(1, &view, NULL) == S_OK) { | |
452 | + ITfRange *range; | |
453 | + if (view->GetRange(&range) == S_OK) { | |
454 | + BOOL empty; | |
455 | + while ((range->IsEmpty(cookie, &empty) == S_OK) && !empty) { | |
456 | + wchar_t buf[256]; | |
457 | + ULONG len = ARRAYSIZE(buf) - 1; | |
458 | + range->GetText(cookie, TF_TF_MOVESTART, buf, len, &len); | |
459 | + comp_str = (WCHAR*)realloc(comp_str, | |
460 | + (comp_len + len + 1) * sizeof(WCHAR)); | |
461 | + CopyMemory(comp_str + comp_len, buf, len * sizeof(WCHAR)); | |
462 | + comp_len += len; | |
463 | + } | |
464 | + range->Release(); | |
465 | + } | |
466 | + view->Release(); | |
467 | + } | |
468 | + enum_view->Release(); | |
469 | + | |
470 | + // Generate notification | |
471 | + comp_str[comp_len] = '\0'; | |
472 | + if (comp_len > 0) return comp_str; | |
473 | + free(comp_str); | |
474 | + return NULL; | |
475 | +} | |
476 | + | |
477 | +WCHAR* TsfSink::HandleEditRecord(TfEditCookie cookie, ITfEditRecord* pEditRec) { | |
478 | + // Make sure that are is a valid range enumerator | |
479 | +#ifdef NVDAJP | |
480 | + OutputDebugString(L"TsfSink::HandleEditRecord"); | |
481 | +#endif | |
482 | + IEnumTfRanges* enum_range = NULL; | |
483 | + HRESULT hr = pEditRec->GetTextAndPropertyUpdates( | |
484 | + TF_GTP_INCL_TEXT, NULL, 0, &enum_range); | |
485 | + if (!enum_range) return NULL; | |
486 | + | |
487 | + // Concatenate the text from all ranges | |
488 | + WCHAR* edit_str = (WCHAR*)malloc(sizeof(WCHAR)); | |
489 | + int edit_len = 0; | |
490 | + ITfRange* range = NULL; | |
491 | + ULONG count = 0; | |
492 | + while ((enum_range->Next(1, &range, &count) == S_OK) && count) { | |
493 | + BOOL empty; | |
494 | + while ((range->IsEmpty(cookie, &empty) == S_OK) && !empty) { | |
495 | + wchar_t buf[256]; | |
496 | + ULONG len = ARRAYSIZE(buf) - 1; | |
497 | + range->GetText(cookie, TF_TF_MOVESTART, buf, len, &len); | |
498 | + edit_str = (WCHAR*)realloc(edit_str, | |
499 | + (edit_len + len + 1) * sizeof(WCHAR)); | |
500 | + CopyMemory(edit_str + edit_len, buf, len * sizeof(WCHAR)); | |
501 | + edit_len += len; | |
502 | + } | |
503 | + range->Release(); | |
504 | + } | |
505 | + enum_range->Release(); | |
506 | + | |
507 | + // Generate notification | |
508 | + edit_str[edit_len] = '\0'; | |
509 | + if (edit_len > 0) return edit_str; | |
510 | + free(edit_str); | |
511 | + return NULL; | |
512 | +} | |
513 | + | |
514 | +STDMETHODIMP TsfSink::BeginUIElement(DWORD elementId, BOOL* pShow) { | |
515 | + if(mpUIElementMgr) { | |
516 | + ITfUIElement* pUIElement=NULL; | |
517 | + mpUIElementMgr->GetUIElement(elementId,&pUIElement); | |
518 | + if(pUIElement) { | |
519 | + ITfReadingInformationUIElement* pReadingInformationUIElement=NULL; | |
520 | + pUIElement->QueryInterface(IID_ITfReadingInformationUIElement,(void**)&pReadingInformationUIElement); | |
521 | + pUIElement->Release(); | |
522 | + if(pReadingInformationUIElement) { | |
523 | + curReadingInformationUIElementId=elementId; | |
524 | + pReadingInformationUIElement->Release(); | |
525 | + } | |
526 | + } | |
527 | + } | |
528 | + *pShow=(curReadingInformationUIElementId!=-1)?false:true; | |
529 | + return S_OK; | |
530 | +} | |
531 | + | |
532 | +STDMETHODIMP TsfSink::UpdateUIElement(DWORD elementId) { | |
533 | + if(elementId==curReadingInformationUIElementId&&mpUIElementMgr) { | |
534 | + ITfUIElement* pUIElement=NULL; | |
535 | + mpUIElementMgr->GetUIElement(elementId,&pUIElement); | |
536 | + if(pUIElement) { | |
537 | + ITfReadingInformationUIElement* pReadingInformationUIElement=NULL; | |
538 | + pUIElement->QueryInterface(IID_ITfReadingInformationUIElement,(void**)&pReadingInformationUIElement); | |
539 | + pUIElement->Release(); | |
540 | + if(pReadingInformationUIElement) { | |
541 | + BSTR read_str=NULL; | |
542 | + pReadingInformationUIElement->GetString(&read_str); | |
543 | + if(read_str) { | |
544 | + long len=SysStringLen(read_str); | |
545 | + if(len>0) { | |
546 | + nvdaControllerInternal_inputCompositionUpdate(read_str,len,len,1); | |
547 | + } | |
548 | + SysFreeString(read_str); | |
549 | + } | |
550 | + pReadingInformationUIElement->Release(); | |
551 | + } | |
552 | + } | |
553 | + } | |
554 | + return S_OK; | |
555 | +} | |
556 | + | |
557 | +STDMETHODIMP TsfSink::EndUIElement(DWORD elementId) { | |
558 | + if(elementId==curReadingInformationUIElementId) curReadingInformationUIElementId=-1; | |
559 | + return S_OK; | |
560 | +} | |
561 | + | |
562 | + | |
563 | +STDMETHODIMP TsfSink::OnEndEdit( | |
564 | + ITfContext* pCtx, TfEditCookie cookie, ITfEditRecord* pEditRec) { | |
565 | + // TSF input processor performing composition | |
566 | + ITfRange* pRange=CombineCompRange(pCtx,cookie); | |
567 | + if(!pRange) { | |
568 | + if(inComposition) { | |
569 | + inComposition=false; | |
570 | + if(!curIMEWindow) { | |
571 | + wchar_t* edit_str=HandleEditRecord(cookie, pEditRec); | |
572 | + nvdaControllerInternal_inputCompositionUpdate((edit_str?edit_str:L""),-1,-1,0); | |
573 | + if(edit_str) free(edit_str); | |
574 | + //Disable further typed character notifications produced by TSF | |
575 | + typedCharacter_window=NULL; | |
576 | + } | |
577 | + } | |
578 | + return S_OK; | |
579 | + } | |
580 | + inComposition=true; | |
581 | + wchar_t buf[256]; | |
582 | + ULONG len = ARRAYSIZE(buf) - 1; | |
583 | + pRange->GetText(cookie, 0, buf, len, &len); | |
584 | + buf[min(len,255)]=L'\0'; | |
585 | + long compStart=0; | |
586 | + fetchRangeExtent(pRange,&compStart,&len); | |
587 | + long selStart=compStart; | |
588 | + long selEnd=compStart; | |
589 | + TF_SELECTION tfSelection={0}; | |
590 | + if(pCtx->GetSelection(cookie,TF_DEFAULT_SELECTION,1,&tfSelection,&len)==S_OK&&tfSelection.range) { | |
591 | + if(fetchRangeExtent(tfSelection.range,&selStart,&len)) { | |
592 | + selEnd=selStart+len; | |
593 | + } | |
594 | + tfSelection.range->Release(); | |
595 | + } | |
596 | + selStart=max(0,selStart-compStart); | |
597 | + selEnd=max(0,selEnd-compStart); | |
598 | +#ifdef NVDAJP | |
599 | + TF_DISPLAYATTRIBUTE dispAttr; | |
600 | + TF_DA_ATTR_INFO attr = TF_ATTR_OTHER; // -1 | |
601 | + HRESULT hr = getDispAttrFromRange(pCtx, pRange, cookie, &dispAttr); | |
602 | + if (hr == S_OK) { | |
603 | + attr = dispAttr.bAttr; | |
604 | + } | |
605 | + wchar_t buf_[200]; | |
606 | + wsprintf(buf_, L"TsfSink::OnEndEdit %ld %ld %d %d (%s)", selStart, selEnd, hr, attr, buf); | |
607 | + OutputDebugString(buf_); | |
608 | +#endif | |
609 | + nvdaControllerInternal_inputCompositionUpdate(buf,selStart,selEnd,0); | |
610 | + return S_OK; | |
611 | +} | |
612 | + | |
613 | +//ITfActiveLanguageProfileNotifySink::OnActivated | |
614 | +//To notify NVDA (in XP) of a TSF profile change | |
615 | +STDMETHODIMP TsfSink::OnActivated(REFCLSID rClsID, REFGUID rProfGUID, BOOL activated) { | |
616 | + const CLSID null_clsid = {0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0}}; | |
617 | + if (!activated) { | |
618 | + curTSFClsID=null_clsid; | |
619 | + hasActiveProfile=false; | |
620 | + return S_OK; | |
621 | + } | |
622 | + //Re-enable IME conversion mode update reporting as input lang change window message disabled it while completing the switch | |
623 | + curTSFClsID=rClsID; | |
624 | + if (IsEqualCLSID(rClsID, null_clsid)) { | |
625 | + hasActiveProfile = false; | |
626 | + // When switching to non-TSF profile, resend last input language | |
627 | + wchar_t buf[KL_NAMELENGTH]; | |
628 | + GetKeyboardLayoutName(buf); | |
629 | + nvdaControllerInternal_inputLangChangeNotify(GetCurrentThreadId(), | |
630 | + (unsigned long)GetKeyboardLayout(0), buf); | |
631 | + handleIMEConversionModeUpdate(GetFocus(),true); | |
632 | + return S_OK; | |
633 | + } | |
634 | + hasActiveProfile = true; | |
635 | + ITfInputProcessorProfiles* profiles = create_input_processor_profiles(); | |
636 | + if (!profiles) return S_OK; | |
637 | + HRESULT hr = S_OK; | |
638 | + LANGID lang = 0; | |
639 | + if (hr == S_OK) | |
640 | + hr = profiles->GetCurrentLanguage(&lang); | |
641 | + if (hr == S_OK) { | |
642 | + BSTR desc = NULL; | |
643 | + profiles->GetLanguageProfileDescription(rClsID, lang, rProfGUID, &desc); | |
644 | + if (desc) { | |
645 | + nvdaControllerInternal_inputLangChangeNotify(GetCurrentThreadId(),(unsigned long)GetKeyboardLayout(0), desc); | |
646 | + SysFreeString(desc); | |
647 | + } | |
648 | + } | |
649 | + profiles->Release(); | |
650 | + handleIMEConversionModeUpdate(GetFocus(),true); | |
651 | + return S_OK; | |
652 | +} | |
653 | + | |
654 | +//ITfInputProcessorProfileActivationSink::OnActivated | |
655 | +//To notify NVDA (Win7 and above) of a TSF profile change | |
656 | +STDMETHODIMP TsfSink::OnActivated(DWORD dwProfileType, LANGID langId, REFCLSID rclsid, REFGUID catId, REFGUID guidProfile, HKL hkl, DWORD dwFlags) { | |
657 | + const CLSID null_clsid = {0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0}}; | |
658 | + if(dwProfileType==TF_PROFILETYPE_KEYBOARDLAYOUT) { | |
659 | + //This is a normal keyboard layout so forget any last active TSF profile | |
660 | + hasActiveProfile=false; | |
661 | + curTSFClsID=null_clsid; | |
662 | + if(dwFlags&TF_IPSINK_FLAG_ACTIVE) { | |
663 | + //As its activating, report the layout change to NVDA | |
664 | + wchar_t buf[KL_NAMELENGTH]; | |
665 | + GetKeyboardLayoutName(buf); | |
666 | + nvdaControllerInternal_inputLangChangeNotify(GetCurrentThreadId(),(unsigned long)GetKeyboardLayout(0), buf); | |
667 | + handleIMEConversionModeUpdate(GetFocus(),true); | |
668 | + } | |
669 | + return S_OK; | |
670 | + } | |
671 | + //From here on this is a text service change | |
672 | + if(!IsEqualCLSID(catId,GUID_TFCAT_TIP_KEYBOARD)) { | |
673 | + //We don't handle anything other than keyboard text services (no speech etc) | |
674 | + return S_OK; | |
675 | + } | |
676 | + if(!(dwFlags&TF_IPSINK_FLAG_ACTIVE)) { | |
677 | + //This keyboard text service is deactivating | |
678 | + curTSFClsID=null_clsid; | |
679 | + hasActiveProfile=false; | |
680 | + return S_OK; | |
681 | + } | |
682 | + curTSFClsID=rclsid; | |
683 | + hasActiveProfile = true; | |
684 | + ITfInputProcessorProfiles* profiles = create_input_processor_profiles(); | |
685 | + if (!profiles) return S_OK; | |
686 | + BSTR desc = NULL; | |
687 | + profiles->GetLanguageProfileDescription(rclsid, langId, guidProfile, &desc); | |
688 | + if (desc) { | |
689 | + nvdaControllerInternal_inputLangChangeNotify(GetCurrentThreadId(),(unsigned long)GetKeyboardLayout(0), desc); | |
690 | + SysFreeString(desc); | |
691 | + } | |
692 | + profiles->Release(); | |
693 | + handleIMEConversionModeUpdate(GetFocus(),true); | |
694 | + return S_OK; | |
695 | +} | |
696 | + | |
697 | +static void CALLBACK TSF_winEventHook(HWINEVENTHOOK hookID, DWORD eventID, HWND hwnd, long objectID, long childID, DWORD threadID, DWORD time) { | |
698 | + switch (eventID) | |
699 | + { | |
700 | + case EVENT_SYSTEM_FOREGROUND: | |
701 | + case EVENT_OBJECT_FOCUS: | |
702 | + // Create TSF sink when window gains focus | |
703 | + break; | |
704 | + default: | |
705 | + // Ignore all other events | |
706 | + return; | |
707 | + } | |
708 | + | |
709 | + // Create TSF sink now | |
710 | + if (TlsGetValue(gTsfIndex)) return; | |
711 | + TsfSink* sink = new TsfSink; | |
712 | + if (!sink) return; | |
713 | + if(!sink->Initialize()) { | |
714 | + sink->Release(); | |
715 | + return; | |
716 | + } | |
717 | + gTsfSinksLock.acquire(); | |
718 | + gTsfSinks[GetCurrentThreadId()] = sink; | |
719 | + gTsfSinksLock.release(); | |
720 | + TlsSetValue(gTsfIndex, sink); | |
721 | +} | |
722 | + | |
723 | +TsfSink* fetchCurrentTsfSink() { | |
724 | + if (gTsfIndex == TLS_OUT_OF_INDEXES) return NULL; | |
725 | + return (TsfSink*)TlsGetValue(gTsfIndex); | |
726 | +} | |
727 | + | |
728 | +void TSF_inProcess_initialize() { | |
729 | +#ifdef NVDAJP | |
730 | + OutputDebugString(L"TSF_inProcess_initialize"); | |
731 | +#endif | |
732 | + //Allow use of UIElementMgr on Vista and higher (crashes things on XP) | |
733 | + if((GetVersion()&0xff)>5) isUIElementMgrSafe=true; | |
734 | + // Initialize TLS and use window hook to create TSF sink in each thread | |
735 | + gTsfIndex = TlsAlloc(); | |
736 | + if (gTsfIndex != TLS_OUT_OF_INDEXES) | |
737 | + registerWinEventHook(TSF_winEventHook); | |
738 | +} | |
739 | + | |
740 | +void TSF_inProcess_terminate() { | |
741 | +#ifdef NVDAJP | |
742 | + OutputDebugString(L"TSF_inProcess_terminate"); | |
743 | +#endif | |
744 | + if (gTsfIndex == TLS_OUT_OF_INDEXES) return; | |
745 | + | |
746 | + // Remove window hook and clean up TLS | |
747 | + unregisterWinEventHook(TSF_winEventHook); | |
748 | + TlsFree(gTsfIndex); | |
749 | + gTsfIndex = TLS_OUT_OF_INDEXES; | |
750 | + | |
751 | + // Destroy all TSF sinks belonging to this process | |
752 | + gTsfSinksLock.acquire(); | |
753 | + sinkMap_t::const_iterator end = gTsfSinks.end(); | |
754 | + for (sinkMap_t::const_iterator i = gTsfSinks.begin(); i != end; ++i) { | |
755 | + TsfSink* sink = i->second; | |
756 | + sink->CleanUp(); | |
757 | + sink->Release(); | |
758 | + } | |
759 | + gTsfSinks.clear(); | |
760 | + gTsfSinksLock.release(); | |
761 | +} | |
762 | + | |
763 | +void TSF_thread_detached() { | |
764 | + TsfSink* sink=fetchCurrentTsfSink(); | |
765 | + // Remove TSF sink from the list | |
766 | + gTsfSinksLock.acquire(); | |
767 | + gTsfSinks.erase(GetCurrentThreadId()); | |
768 | + gTsfSinksLock.release(); | |
769 | + | |
770 | + // Destroy TSF sink belonging to this thread | |
771 | + TlsSetValue(gTsfIndex, NULL); | |
772 | + sink->CleanUp(); | |
773 | + sink->Release(); | |
774 | +} | |
775 | + | |
776 | +bool isTSFThread(bool checkActiveProfile) { | |
777 | +TsfSink* tsf=fetchCurrentTsfSink(); | |
778 | + if(!tsf) return false; | |
779 | + return checkActiveProfile?tsf->hasActiveProfile:true; | |
780 | +} |
@@ -42,6 +42,123 @@ bool fetchRangeExtent(ITfRange* pRange, long* start, ULONG* length) { | ||
42 | 42 | return true?(res==S_OK):false; |
43 | 43 | } |
44 | 44 | |
45 | +#define NVDAJP 1 | |
46 | + | |
47 | +#ifdef NVDAJP | |
48 | +// http://msdn.microsoft.com/en-us/library/windows/desktop/ms629224%28v=vs.85%29.aspx | |
49 | +HRESULT getDispAttrFromRange(ITfContext *pContext, | |
50 | + ITfRange *pRange, | |
51 | + TfEditCookie ec, | |
52 | + TF_DISPLAYATTRIBUTE *pDispAttr) | |
53 | +{ | |
54 | + HRESULT hr; | |
55 | + ITfCategoryMgr *pCategoryMgr; | |
56 | + hr = CoCreateInstance(CLSID_TF_CategoryMgr, | |
57 | + NULL, | |
58 | + CLSCTX_INPROC_SERVER, | |
59 | + IID_ITfCategoryMgr, | |
60 | + (LPVOID*)&pCategoryMgr); | |
61 | + if(FAILED(hr)){ | |
62 | + return hr; | |
63 | + } | |
64 | + ITfDisplayAttributeMgr *pDispMgr; | |
65 | + hr = CoCreateInstance(CLSID_TF_DisplayAttributeMgr, | |
66 | + NULL, | |
67 | + CLSCTX_INPROC_SERVER, | |
68 | + IID_ITfDisplayAttributeMgr, | |
69 | + (LPVOID*)&pDispMgr); | |
70 | + if(FAILED(hr)){ | |
71 | + pCategoryMgr->Release(); | |
72 | + return hr; | |
73 | + } | |
74 | + ITfProperty *pProp; | |
75 | + hr = pContext->GetProperty(GUID_PROP_ATTRIBUTE, &pProp); | |
76 | + if(SUCCEEDED(hr)){ | |
77 | + VARIANT var; | |
78 | + VariantInit(&var); | |
79 | + hr = pProp->GetValue(ec, pRange, &var); | |
80 | + if(S_OK == hr){ | |
81 | + if(VT_I4 == var.vt){ | |
82 | + GUID guid; | |
83 | + hr = pCategoryMgr->GetGUID((TfGuidAtom)var.lVal, &guid); | |
84 | + if(SUCCEEDED(hr)){ | |
85 | + ITfDisplayAttributeInfo *pDispInfo; | |
86 | + hr = pDispMgr->GetDisplayAttributeInfo(guid, &pDispInfo, NULL); | |
87 | + if(SUCCEEDED(hr)){ | |
88 | + hr = pDispInfo->GetAttributeInfo(pDispAttr); | |
89 | + if(SUCCEEDED(hr)){ | |
90 | + OutputDebugString(L"GetAttributeInfo() succeeded"); | |
91 | + } | |
92 | + pDispInfo->Release(); | |
93 | + } | |
94 | + } | |
95 | + } else { | |
96 | + hr = E_FAIL; | |
97 | + } | |
98 | + VariantClear(&var); | |
99 | + } | |
100 | + pProp->Release(); | |
101 | + } | |
102 | + pCategoryMgr->Release(); | |
103 | + pDispMgr->Release(); | |
104 | + return hr; | |
105 | +} | |
106 | +#endif // NVDAJP | |
107 | + | |
108 | +#ifdef NVDAJP | |
109 | +BOOL _FindComposingRange(TfEditCookie ec, ITfContext *pContext, ITfRange *pSelection, ITfRange **ppRange) | |
110 | +{ | |
111 | + if (ppRange == NULL) | |
112 | + { | |
113 | + return FALSE; | |
114 | + } | |
115 | + | |
116 | + *ppRange = NULL; | |
117 | + | |
118 | + // find GUID_PROP_COMPOSING | |
119 | + ITfProperty* pPropComp = NULL; | |
120 | + IEnumTfRanges* enumComp = NULL; | |
121 | + | |
122 | + HRESULT hr = pContext->GetProperty(GUID_PROP_COMPOSING, &pPropComp); | |
123 | + if (FAILED(hr) || pPropComp == NULL) | |
124 | + { | |
125 | + return FALSE; | |
126 | + } | |
127 | + | |
128 | + hr = pPropComp->EnumRanges(ec, &enumComp, pSelection); | |
129 | + if (FAILED(hr) || enumComp == NULL) | |
130 | + { | |
131 | + pPropComp->Release(); | |
132 | + return FALSE; | |
133 | + } | |
134 | + | |
135 | + BOOL isCompExist = FALSE; | |
136 | + VARIANT var; | |
137 | + ULONG fetched = 0; | |
138 | + | |
139 | + while (enumComp->Next(1, ppRange, &fetched) == S_OK && fetched == 1) | |
140 | + { | |
141 | + hr = pPropComp->GetValue(ec, *ppRange, &var); | |
142 | + if (hr == S_OK) | |
143 | + { | |
144 | + if (var.vt == VT_I4 && var.lVal != 0) | |
145 | + { | |
146 | + isCompExist = TRUE; | |
147 | + break; | |
148 | + } | |
149 | + } | |
150 | + (*ppRange)->Release(); | |
151 | + *ppRange = NULL; | |
152 | + } | |
153 | + | |
154 | + pPropComp->Release(); | |
155 | + enumComp->Release(); | |
156 | + | |
157 | + return isCompExist; | |
158 | +} | |
159 | +#endif // NVDAJP | |
160 | + | |
161 | + | |
45 | 162 | class TsfSink; |
46 | 163 | typedef map<DWORD,TsfSink*> sinkMap_t; |
47 | 164 |
@@ -412,6 +529,9 @@ WCHAR* TsfSink::HandleCompositionView(ITfContext* pCtx, TfEditCookie cookie) { | ||
412 | 529 | |
413 | 530 | WCHAR* TsfSink::HandleEditRecord(TfEditCookie cookie, ITfEditRecord* pEditRec) { |
414 | 531 | // Make sure that are is a valid range enumerator |
532 | +#ifdef NVDAJP | |
533 | + OutputDebugString(L"TsfSink::HandleEditRecord"); | |
534 | +#endif | |
415 | 535 | IEnumTfRanges* enum_range = NULL; |
416 | 536 | HRESULT hr = pEditRec->GetTextAndPropertyUpdates( |
417 | 537 | TF_GTP_INCL_TEXT, NULL, 0, &enum_range); |
@@ -492,6 +612,7 @@ STDMETHODIMP TsfSink::EndUIElement(DWORD elementId) { | ||
492 | 612 | return S_OK; |
493 | 613 | } |
494 | 614 | |
615 | + | |
495 | 616 | STDMETHODIMP TsfSink::OnEndEdit( |
496 | 617 | ITfContext* pCtx, TfEditCookie cookie, ITfEditRecord* pEditRec) { |
497 | 618 | // TSF input processor performing composition |
@@ -527,6 +648,17 @@ STDMETHODIMP TsfSink::OnEndEdit( | ||
527 | 648 | } |
528 | 649 | selStart=max(0,selStart-compStart); |
529 | 650 | selEnd=max(0,selEnd-compStart); |
651 | +#ifdef NVDAJP | |
652 | + TF_DISPLAYATTRIBUTE dispAttr; | |
653 | + TF_DA_ATTR_INFO attr = TF_ATTR_OTHER; // -1 | |
654 | + HRESULT hr = getDispAttrFromRange(pCtx, pRange, cookie, &dispAttr); | |
655 | + if (hr == S_OK) { | |
656 | + attr = dispAttr.bAttr; | |
657 | + } | |
658 | + wchar_t buf_[200]; | |
659 | + wsprintf(buf_, L"TsfSink::OnEndEdit %ld %ld %d %d (%s)", selStart, selEnd, hr, attr, buf); | |
660 | + OutputDebugString(buf_); | |
661 | +#endif | |
530 | 662 | nvdaControllerInternal_inputCompositionUpdate(buf,selStart,selEnd,0); |
531 | 663 | return S_OK; |
532 | 664 | } |
@@ -647,6 +779,9 @@ TsfSink* fetchCurrentTsfSink() { | ||
647 | 779 | } |
648 | 780 | |
649 | 781 | void TSF_inProcess_initialize() { |
782 | +#ifdef NVDAJP | |
783 | + OutputDebugString(L"TSF_inProcess_initialize"); | |
784 | +#endif | |
650 | 785 | //Allow use of UIElementMgr on Vista and higher (crashes things on XP) |
651 | 786 | if((GetVersion()&0xff)>5) isUIElementMgrSafe=true; |
652 | 787 | // Initialize TLS and use window hook to create TSF sink in each thread |
@@ -656,6 +791,9 @@ void TSF_inProcess_initialize() { | ||
656 | 791 | } |
657 | 792 | |
658 | 793 | void TSF_inProcess_terminate() { |
794 | +#ifdef NVDAJP | |
795 | + OutputDebugString(L"TSF_inProcess_terminate"); | |
796 | +#endif | |
659 | 797 | if (gTsfIndex == TLS_OUT_OF_INDEXES) return; |
660 | 798 | |
661 | 799 | // Remove window hook and clean up TLS |
@@ -180,7 +180,12 @@ def handleInputCompositionEnd(result): | ||
180 | 180 | if curInputComposition and not result: |
181 | 181 | result=curInputComposition.compositionString.lstrip(u'\u3000 ') |
182 | 182 | if result: |
183 | - speech.speakText(result,symbolLevel=characterProcessing.SYMLVL_ALL) | |
183 | + #nvdajp begin | |
184 | + #speech.speakText(result,symbolLevel=characterProcessing.SYMLVL_ALL) | |
185 | + import ui | |
186 | + #. Translators: a message when the IME cancelation status | |
187 | + ui.message(_("Clear")) | |
188 | + #nvdajp end | |
184 | 189 | |
185 | 190 | def handleInputCompositionStart(compositionString,selectionStart,selectionEnd,isReading): |
186 | 191 | import speech |
@@ -185,6 +185,11 @@ msgstr "日本語6点情報処理点字" | ||
185 | 185 | msgid "%s:%s" |
186 | 186 | msgstr "%s時%s分" |
187 | 187 | |
188 | +#. Translators: a message when the IME cancelation status | |
189 | +#: NVDAHelper.py: | |
190 | +msgid "Clear" | |
191 | +msgstr "クリア" | |
192 | + | |
188 | 193 | # end of nvdajp |
189 | 194 | |
190 | 195 | #. Translators: Message to indicate User Account Control (UAC) or other secure |