source: trunk/src/gui/inputmethod/qwininputcontext_win.cpp@ 441

Last change on this file since 441 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 28.9 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5**
6** This file is part of the QtGui module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you are unsure which license is appropriate for your use, please
37** contact the sales department at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qwininputcontext_p.h"
43#include "qinputcontext_p.h"
44
45#include "qfont.h"
46#include "qwidget.h"
47#include "qapplication.h"
48#include "qlibrary.h"
49#include "qevent.h"
50#include "qtextformat.h"
51
52//#define Q_IME_DEBUG
53
54/* Active Input method support on Win95/98/NT */
55#include <objbase.h>
56#include <initguid.h>
57
58#ifdef Q_IME_DEBUG
59#include "qdebug.h"
60#endif
61
62#if defined(Q_OS_WINCE)
63extern void qt_wince_show_SIP(bool show); // defined in qguifunctions_wince.cpp
64#endif
65
66QT_BEGIN_NAMESPACE
67
68extern bool qt_sendSpontaneousEvent(QObject*, QEvent*);
69
70
71DEFINE_GUID(IID_IActiveIMMApp,
720x08c0e040, 0x62d1, 0x11d1, 0x93, 0x26, 0x0, 0x60, 0xb0, 0x67, 0xb8, 0x6e);
73
74
75
76DEFINE_GUID(CLSID_CActiveIMM,
770x4955DD33, 0xB159, 0x11d0, 0x8F, 0xCF, 0x0, 0xAA, 0x00, 0x6B, 0xCC, 0x59);
78
79
80
81DEFINE_GUID(IID_IActiveIMMMessagePumpOwner,
820xb5cf2cfa, 0x8aeb, 0x11d1, 0x93, 0x64, 0x0, 0x60, 0xb0, 0x67, 0xb8, 0x6e);
83
84
85
86interface IEnumRegisterWordW;
87interface IEnumInputContext;
88
89
90bool qt_sendSpontaneousEvent(QObject*, QEvent*);
91
92
93#define IFMETHOD HRESULT STDMETHODCALLTYPE
94
95interface IActiveIMMApp : public IUnknown
96{
97public:
98 virtual IFMETHOD AssociateContext(HWND hWnd, HIMC hIME, HIMC __RPC_FAR *phPrev) = 0;
99 virtual IFMETHOD dummy_ConfigureIMEA() = 0;
100 virtual IFMETHOD ConfigureIMEW(HKL hKL, HWND hWnd, DWORD dwMode, REGISTERWORDW __RPC_FAR *pData) = 0;
101 virtual IFMETHOD CreateContext(HIMC __RPC_FAR *phIMC) = 0;
102 virtual IFMETHOD DestroyContext(HIMC hIME) = 0;
103 virtual IFMETHOD dummy_EnumRegisterWordA() = 0;
104 virtual IFMETHOD EnumRegisterWordW(HKL hKL, LPWSTR szReading, DWORD dwStyle, LPWSTR szRegister, LPVOID pData,
105 IEnumRegisterWordW __RPC_FAR *__RPC_FAR *pEnum) = 0;
106 virtual IFMETHOD dummy_EscapeA() = 0;
107 virtual IFMETHOD EscapeW(HKL hKL, HIMC hIMC, UINT uEscape, LPVOID pData, LRESULT __RPC_FAR *plResult) = 0;
108 virtual IFMETHOD dummy_GetCandidateListA() = 0;
109 virtual IFMETHOD GetCandidateListW(HIMC hIMC, DWORD dwIndex, UINT uBufLen, CANDIDATELIST __RPC_FAR *pCandList,
110 UINT __RPC_FAR *puCopied) = 0;
111 virtual IFMETHOD dummy_GetCandidateListCountA() = 0;
112 virtual IFMETHOD GetCandidateListCountW(HIMC hIMC, DWORD __RPC_FAR *pdwListSize, DWORD __RPC_FAR *pdwBufLen) = 0;
113 virtual IFMETHOD GetCandidateWindow(HIMC hIMC, DWORD dwIndex, CANDIDATEFORM __RPC_FAR *pCandidate) = 0;
114 virtual IFMETHOD dummy_GetCompositionFontA() = 0;
115 virtual IFMETHOD GetCompositionFontW(HIMC hIMC, LOGFONTW __RPC_FAR *plf) = 0;
116 virtual IFMETHOD dummy_GetCompositionStringA() = 0;
117 virtual IFMETHOD GetCompositionStringW(HIMC hIMC, DWORD dwIndex, DWORD dwBufLen, LONG __RPC_FAR *plCopied, LPVOID pBuf) = 0;
118 virtual IFMETHOD GetCompositionWindow(HIMC hIMC, COMPOSITIONFORM __RPC_FAR *pCompForm) = 0;
119 virtual IFMETHOD GetContext(HWND hWnd, HIMC __RPC_FAR *phIMC) = 0;
120 virtual IFMETHOD dummy_GetConversionListA() = 0;
121 virtual IFMETHOD GetConversionListW(HKL hKL, HIMC hIMC, LPWSTR pSrc, UINT uBufLen, UINT uFlag,
122 CANDIDATELIST __RPC_FAR *pDst, UINT __RPC_FAR *puCopied) = 0;
123 virtual IFMETHOD GetConversionStatus(HIMC hIMC, DWORD __RPC_FAR *pfdwConversion, DWORD __RPC_FAR *pfdwSentence) = 0;
124 virtual IFMETHOD GetDefaultIMEWnd(HWND hWnd, HWND __RPC_FAR *phDefWnd) = 0;
125 virtual IFMETHOD dummy_GetDescriptionA() = 0;
126 virtual IFMETHOD GetDescriptionW(HKL hKL, UINT uBufLen, LPWSTR szDescription, UINT __RPC_FAR *puCopied) = 0;
127 virtual IFMETHOD dummy_GetGuideLineA() = 0;
128 virtual IFMETHOD GetGuideLineW(HIMC hIMC, DWORD dwIndex, DWORD dwBufLen, LPWSTR pBuf, DWORD __RPC_FAR *pdwResult) = 0;
129 virtual IFMETHOD dummy_GetIMEFileNameA() = 0;
130 virtual IFMETHOD GetIMEFileNameW(HKL hKL, UINT uBufLen, LPWSTR szFileName, UINT __RPC_FAR *puCopied) = 0;
131 virtual IFMETHOD GetOpenStatus(HIMC hIMC) = 0;
132 virtual IFMETHOD GetProperty(HKL hKL, DWORD fdwIndex, DWORD __RPC_FAR *pdwProperty) = 0;
133 virtual IFMETHOD dummy_GetRegisterWordStyleA() = 0;
134 virtual IFMETHOD GetRegisterWordStyleW(HKL hKL, UINT nItem, STYLEBUFW __RPC_FAR *pStyleBuf, UINT __RPC_FAR *puCopied) = 0;
135 virtual IFMETHOD GetStatusWindowPos(HIMC hIMC, POINT __RPC_FAR *pptPos) = 0;
136 virtual IFMETHOD GetVirtualKey(HWND hWnd, UINT __RPC_FAR *puVirtualKey) = 0;
137 virtual IFMETHOD dummy_InstallIMEA() = 0;
138 virtual IFMETHOD InstallIMEW(LPWSTR szIMEFileName, LPWSTR szLayoutText, HKL __RPC_FAR *phKL) = 0;
139 virtual IFMETHOD IsIME(HKL hKL) = 0;
140 virtual IFMETHOD dummy_IsUIMessageA() = 0;
141 virtual IFMETHOD IsUIMessageW(HWND hWndIME, UINT msg, WPARAM wParam, LPARAM lParam) = 0;
142 virtual IFMETHOD NotifyIME(HIMC hIMC, DWORD dwAction, DWORD dwIndex, DWORD dwValue) = 0;
143 virtual IFMETHOD dummy_RegisterWordA() = 0;
144 virtual IFMETHOD RegisterWordW(HKL hKL, LPWSTR szReading, DWORD dwStyle, LPWSTR szRegister) = 0;
145 virtual IFMETHOD ReleaseContext(HWND hWnd, HIMC hIMC) = 0;
146 virtual IFMETHOD SetCandidateWindow(HIMC hIMC, CANDIDATEFORM __RPC_FAR *pCandidate) = 0;
147 virtual IFMETHOD SetCompositionFontA(HIMC hIMC, LOGFONTA __RPC_FAR *plf) = 0;
148 virtual IFMETHOD SetCompositionFontW(HIMC hIMC, LOGFONTW __RPC_FAR *plf) = 0;
149 virtual IFMETHOD dummy_SetCompositionStringA() = 0;
150 virtual IFMETHOD SetCompositionStringW(HIMC hIMC, DWORD dwIndex, LPVOID pComp, DWORD dwCompLen,
151 LPVOID pRead, DWORD dwReadLen) = 0;
152 virtual IFMETHOD SetCompositionWindow(HIMC hIMC, COMPOSITIONFORM __RPC_FAR *pCompForm) = 0;
153 virtual IFMETHOD SetConversionStatus(HIMC hIMC, DWORD fdwConversion, DWORD fdwSentence) = 0;
154 virtual IFMETHOD SetOpenStatus(HIMC hIMC, BOOL fOpen) = 0;
155 virtual IFMETHOD SetStatusWindowPos(HIMC hIMC, POINT __RPC_FAR *pptPos) = 0;
156 virtual IFMETHOD SimulateHotKey(HWND hWnd, DWORD dwHotKeyID) = 0;
157 virtual IFMETHOD dummy_UnregisterWordA() = 0;
158 virtual IFMETHOD UnregisterWordW(HKL hKL, LPWSTR szReading, DWORD dwStyle, LPWSTR szUnregister) = 0;
159 virtual IFMETHOD Activate(BOOL fRestoreLayout) = 0;
160 virtual IFMETHOD Deactivate(void) = 0;
161 virtual IFMETHOD OnDefWindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam, LRESULT __RPC_FAR *plResult) = 0;
162 virtual IFMETHOD FilterClientWindows(ATOM __RPC_FAR *aaClassList, UINT uSize) = 0;
163 virtual IFMETHOD dummy_GetCodePageA() = 0;
164 virtual IFMETHOD GetLangId(HKL hKL, LANGID __RPC_FAR *plid) = 0;
165 virtual IFMETHOD AssociateContextEx(HWND hWnd, HIMC hIMC, DWORD dwFlags) = 0;
166 virtual IFMETHOD DisableIME(DWORD idThread) = 0;
167 virtual IFMETHOD dummy_GetImeMenuItemsA() = 0;
168 virtual IFMETHOD GetImeMenuItemsW(HIMC hIMC, DWORD dwFlags, DWORD dwType, /*IMEMENUITEMINFOW*/ void __RPC_FAR *pImeParentMenu,
169 /*IMEMENUITEMINFOW*/ void __RPC_FAR *pImeMenu, DWORD dwSize, DWORD __RPC_FAR *pdwResult) = 0;
170 virtual IFMETHOD EnumInputContext(DWORD idThread, IEnumInputContext __RPC_FAR *__RPC_FAR *ppEnum) = 0;
171};
172
173interface IActiveIMMMessagePumpOwner : public IUnknown
174{
175public:
176 virtual IFMETHOD Start(void) = 0;
177 virtual IFMETHOD End(void) = 0;
178 virtual IFMETHOD OnTranslateMessage(const MSG __RPC_FAR *pMsg) = 0;
179 virtual IFMETHOD Pause(DWORD __RPC_FAR *pdwCookie) = 0;
180 virtual IFMETHOD Resume(DWORD dwCookie) = 0;
181};
182
183
184static IActiveIMMApp *aimm = 0;
185static IActiveIMMMessagePumpOwner *aimmpump = 0;
186static QString *imeComposition = 0;
187static int imePosition = -1;
188bool qt_use_rtl_extensions = false;
189static bool haveCaret = false;
190
191#ifndef LGRPID_INSTALLED
192#define LGRPID_INSTALLED 0x00000001 // installed language group ids
193#define LGRPID_SUPPORTED 0x00000002 // supported language group ids
194#endif
195
196#ifndef LGRPID_ARABIC
197#define LGRPID_WESTERN_EUROPE 0x0001 // Western Europe & U.S.
198#define LGRPID_CENTRAL_EUROPE 0x0002 // Central Europe
199#define LGRPID_BALTIC 0x0003 // Baltic
200#define LGRPID_GREEK 0x0004 // Greek
201#define LGRPID_CYRILLIC 0x0005 // Cyrillic
202#define LGRPID_TURKISH 0x0006 // Turkish
203#define LGRPID_JAPANESE 0x0007 // Japanese
204#define LGRPID_KOREAN 0x0008 // Korean
205#define LGRPID_TRADITIONAL_CHINESE 0x0009 // Traditional Chinese
206#define LGRPID_SIMPLIFIED_CHINESE 0x000a // Simplified Chinese
207#define LGRPID_THAI 0x000b // Thai
208#define LGRPID_HEBREW 0x000c // Hebrew
209#define LGRPID_ARABIC 0x000d // Arabic
210#define LGRPID_VIETNAMESE 0x000e // Vietnamese
211#define LGRPID_INDIC 0x000f // Indic
212#define LGRPID_GEORGIAN 0x0010 // Georgian
213#define LGRPID_ARMENIAN 0x0011 // Armenian
214#endif
215
216static DWORD WM_MSIME_MOUSE = 0;
217
218QWinInputContext::QWinInputContext(QObject *parent)
219 : QInputContext(parent), recursionGuard(false)
220{
221 if (QSysInfo::WindowsVersion < QSysInfo::WV_2000) {
222 // try to get the Active IMM COM object on Win95/98/NT, where english versions don't
223 // support the regular Windows input methods.
224 if (CoCreateInstance(CLSID_CActiveIMM, NULL, CLSCTX_INPROC_SERVER,
225 IID_IActiveIMMApp, (LPVOID *)&aimm) != S_OK) {
226 aimm = 0;
227 }
228 if (aimm && (aimm->QueryInterface(IID_IActiveIMMMessagePumpOwner, (LPVOID *)&aimmpump) != S_OK ||
229 aimm->Activate(true) != S_OK)) {
230 aimm->Release();
231 aimm = 0;
232 if (aimmpump)
233 aimmpump->Release();
234 aimmpump = 0;
235 }
236 if (aimmpump)
237 aimmpump->Start();
238 }
239
240#ifndef Q_OS_WINCE
241 QSysInfo::WinVersion ver = QSysInfo::windowsVersion();
242 if (ver & QSysInfo::WV_NT_based && ver >= QSysInfo::WV_VISTA) {
243 // Since the IsValidLanguageGroup/IsValidLocale functions always return true on
244 // Vista, check the Keyboard Layouts for enabling RTL.
245 UINT nLayouts = GetKeyboardLayoutList(0, 0);
246 if (nLayouts) {
247 HKL *lpList = new HKL[nLayouts];
248 GetKeyboardLayoutList(nLayouts, lpList);
249 for (int i = 0; i<(int)nLayouts; i++) {
250 WORD plangid = PRIMARYLANGID((quintptr)lpList[i]);
251 if (plangid == LANG_ARABIC
252 || plangid == LANG_HEBREW
253 || plangid == LANG_FARSI
254#ifdef LANG_SYRIAC
255 || plangid == LANG_SYRIAC
256#endif
257 ) {
258 qt_use_rtl_extensions = true;
259 break;
260 }
261 }
262 delete []lpList;
263 }
264 } else {
265 // figure out whether a RTL language is installed
266 typedef BOOL(WINAPI *PtrIsValidLanguageGroup)(DWORD,DWORD);
267 PtrIsValidLanguageGroup isValidLanguageGroup = (PtrIsValidLanguageGroup)QLibrary::resolve(QLatin1String("kernel32"), "IsValidLanguageGroup");
268 if (isValidLanguageGroup) {
269 qt_use_rtl_extensions = isValidLanguageGroup(LGRPID_ARABIC, LGRPID_INSTALLED)
270 || isValidLanguageGroup(LGRPID_HEBREW, LGRPID_INSTALLED);
271 }
272 qt_use_rtl_extensions |= IsValidLocale(MAKELCID(MAKELANGID(LANG_ARABIC, SUBLANG_DEFAULT), SORT_DEFAULT), LCID_INSTALLED)
273 || IsValidLocale(MAKELCID(MAKELANGID(LANG_HEBREW, SUBLANG_DEFAULT), SORT_DEFAULT), LCID_INSTALLED)
274#ifdef LANG_SYRIAC
275 || IsValidLocale(MAKELCID(MAKELANGID(LANG_SYRIAC, SUBLANG_DEFAULT), SORT_DEFAULT), LCID_INSTALLED)
276#endif
277 || IsValidLocale(MAKELCID(MAKELANGID(LANG_FARSI, SUBLANG_DEFAULT), SORT_DEFAULT), LCID_INSTALLED);
278 }
279#else
280 qt_use_rtl_extensions = false;
281#endif
282
283 WM_MSIME_MOUSE = QT_WA_INLINE(RegisterWindowMessage(L"MSIMEMouseOperation"), RegisterWindowMessageA("MSIMEMouseOperation"));
284}
285
286QWinInputContext::~QWinInputContext()
287{
288 // release active input method if we have one
289 if (aimm) {
290 aimmpump->End();
291 aimmpump->Release();
292 aimm->Deactivate();
293 aimm->Release();
294 aimm = 0;
295 aimmpump = 0;
296 }
297 delete imeComposition;
298 imeComposition = 0;
299}
300
301static HWND getDefaultIMEWnd(HWND wnd)
302{
303 HWND ime_wnd;
304 if(aimm)
305 aimm->GetDefaultIMEWnd(wnd, &ime_wnd);
306 else
307 ime_wnd = ImmGetDefaultIMEWnd(wnd);
308 return ime_wnd;
309}
310
311static HIMC getContext(HWND wnd)
312{
313 HIMC imc;
314 if (aimm)
315 aimm->GetContext(wnd, &imc);
316 else
317 imc = ImmGetContext(wnd);
318
319 return imc;
320}
321
322static void releaseContext(HWND wnd, HIMC imc)
323{
324 if (aimm)
325 aimm->ReleaseContext(wnd, imc);
326 else
327 ImmReleaseContext(wnd, imc);
328}
329
330static void notifyIME(HIMC imc, DWORD dwAction, DWORD dwIndex, DWORD dwValue)
331{
332 if (!imc)
333 return;
334 if (aimm)
335 aimm->NotifyIME(imc, dwAction, dwIndex, dwValue);
336 else
337 ImmNotifyIME(imc, dwAction, dwIndex, dwValue);
338}
339
340static LONG getCompositionString(HIMC himc, DWORD dwIndex, LPVOID lpbuf, DWORD dBufLen, bool *unicode = 0)
341{
342 LONG len = 0;
343 if (unicode)
344 *unicode = true;
345 if (aimm)
346 aimm->GetCompositionStringW(himc, dwIndex, dBufLen, &len, lpbuf);
347 else
348 {
349 if(QSysInfo::WindowsVersion != QSysInfo::WV_95) {
350 len = ImmGetCompositionStringW(himc, dwIndex, lpbuf, dBufLen);
351 }
352#if !defined(Q_OS_WINCE)
353 else {
354 len = ImmGetCompositionStringA(himc, dwIndex, lpbuf, dBufLen);
355 if (unicode)
356 *unicode = false;
357 }
358#endif
359 }
360 return len;
361}
362
363static int getCursorPosition(HIMC himc)
364{
365 return getCompositionString(himc, GCS_CURSORPOS, 0, 0);
366}
367
368static QString getString(HIMC himc, DWORD dwindex, int *selStart = 0, int *selLength = 0)
369{
370 static char *buffer = 0;
371 static int buflen = 0;
372
373 int len = getCompositionString(himc, dwindex, 0, 0) + 1;
374 if (!buffer || len > buflen) {
375 delete [] buffer;
376 buflen = qMin(len, 256);
377 buffer = new char[buflen];
378 }
379
380 bool unicode = true;
381 len = getCompositionString(himc, dwindex, buffer, buflen, &unicode);
382
383 if (selStart) {
384 static char *attrbuffer = 0;
385 static int attrbuflen = 0;
386 int attrlen = getCompositionString(himc, dwindex, 0, 0) + 1;
387 if (!attrbuffer || attrlen> attrbuflen) {
388 delete [] attrbuffer;
389 attrbuflen = qMin(attrlen, 256);
390 attrbuffer = new char[attrbuflen];
391 }
392 attrlen = getCompositionString(himc, GCS_COMPATTR, attrbuffer, attrbuflen);
393 *selStart = attrlen+1;
394 *selLength = -1;
395 for (int i = 0; i < attrlen; i++) {
396 if (attrbuffer[i] & ATTR_TARGET_CONVERTED) {
397 *selStart = qMin(*selStart, i);
398 *selLength = qMax(*selLength, i);
399 }
400 }
401 *selLength = qMax(0, *selLength - *selStart + 1);
402 }
403
404 if (len <= 0)
405 return QString();
406 if (unicode) {
407 return QString((QChar *)buffer, len/sizeof(QChar));
408 }
409 else {
410 buffer[len] = 0;
411 WCHAR *wc = new WCHAR[len+1];
412 int l = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED,
413 buffer, len, wc, len+1);
414 QString res = QString((QChar *)wc, l);
415 delete [] wc;
416 return res;
417 }
418}
419
420void QWinInputContext::TranslateMessage(const MSG *msg)
421{
422 if (!aimmpump || aimmpump->OnTranslateMessage(msg) != S_OK)
423 ::TranslateMessage(msg);
424}
425
426LRESULT QWinInputContext::DefWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
427{
428 LRESULT retval;
429 if (!aimm || aimm->OnDefWindowProc(hwnd, msg, wParam, lParam, &retval) != S_OK)
430 {
431 QT_WA({
432 retval = ::DefWindowProc(hwnd, msg, wParam, lParam);
433 } , {
434 retval = ::DefWindowProcA(hwnd,msg, wParam, lParam);
435 });
436 }
437 return retval;
438}
439
440
441void QWinInputContext::update()
442{
443 QWidget *w = focusWidget();
444 if(!w)
445 return;
446
447 Q_ASSERT(w->testAttribute(Qt::WA_WState_Created));
448 HIMC imc = getContext(w->effectiveWinId());
449
450 if (!imc)
451 return;
452
453 QFont f = qvariant_cast<QFont>(w->inputMethodQuery(Qt::ImFont));
454 HFONT hf;
455 hf = f.handle();
456
457 QT_WA({
458 LOGFONT lf;
459 if (GetObject(hf, sizeof(lf), &lf))
460 if (aimm)
461 aimm->SetCompositionFontW(imc, &lf);
462 else
463 ImmSetCompositionFont(imc, &lf);
464 } , {
465 LOGFONTA lf;
466 if (GetObjectA(hf, sizeof(lf), &lf))
467 if (aimm)
468 aimm->SetCompositionFontA(imc, &lf);
469 else
470 ImmSetCompositionFontA(imc, &lf);
471 });
472
473 QRect r = w->inputMethodQuery(Qt::ImMicroFocus).toRect();
474
475 // The ime window positions are based on the WinId with active focus.
476 QWidget *imeWnd = QWidget::find(::GetFocus());
477 if (imeWnd && !aimm) {
478 QPoint pt (r.topLeft());
479 pt = w->mapToGlobal(pt);
480 pt = imeWnd->mapFromGlobal(pt);
481 r.moveTo(pt);
482 }
483
484 COMPOSITIONFORM cf;
485 // ### need X-like inputStyle config settings
486 cf.dwStyle = CFS_FORCE_POSITION;
487 cf.ptCurrentPos.x = r.x();
488 cf.ptCurrentPos.y = r.y();
489
490 CANDIDATEFORM candf;
491 candf.dwIndex = 0;
492 candf.dwStyle = CFS_EXCLUDE;
493 candf.ptCurrentPos.x = r.x();
494 candf.ptCurrentPos.y = r.y() + r.height();
495 candf.rcArea.left = r.x();
496 candf.rcArea.top = r.y();
497 candf.rcArea.right = r.x() + r.width();
498 candf.rcArea.bottom = r.y() + r.height();
499
500 if(haveCaret)
501 SetCaretPos(r.x(), r.y());
502
503 if (aimm) {
504 aimm->SetCompositionWindow(imc, &cf);
505 aimm->SetCandidateWindow(imc, &candf);
506 } else {
507 ImmSetCompositionWindow(imc, &cf);
508 ImmSetCandidateWindow(imc, &candf);
509 }
510
511 releaseContext(w->effectiveWinId(), imc);
512}
513
514
515bool QWinInputContext::endComposition()
516{
517 QWidget *fw = focusWidget();
518#ifdef Q_IME_DEBUG
519 qDebug("endComposition! fw = %s", fw ? fw->className() : "(null)");
520#endif
521 bool result = true;
522 if(imePosition == -1 || recursionGuard)
523 return result;
524
525 // Googles Pinyin Input Method likes to call endComposition again
526 // when we call notifyIME with CPS_CANCEL, so protect ourselves
527 // against that.
528 recursionGuard = true;
529
530 if (fw) {
531 Q_ASSERT(fw->testAttribute(Qt::WA_WState_Created));
532 HIMC imc = getContext(fw->effectiveWinId());
533 notifyIME(imc, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
534 releaseContext(fw->effectiveWinId(), imc);
535 if(haveCaret) {
536 DestroyCaret();
537 haveCaret = false;
538 }
539 }
540
541 if (!fw)
542 fw = qApp->focusWidget();
543
544 if (fw) {
545 QInputMethodEvent e;
546 result = qt_sendSpontaneousEvent(fw, &e);
547 }
548
549 if (imeComposition)
550 imeComposition->clear();
551 imePosition = -1;
552
553 recursionGuard = false;
554
555 return result;
556}
557
558void QWinInputContext::reset()
559{
560 QWidget *fw = focusWidget();
561
562#ifdef Q_IME_DEBUG
563 qDebug("sending accept to focus widget %s", fw ? fw->className() : "(null)");
564#endif
565
566 if (fw && imePosition != -1) {
567 QInputMethodEvent e;
568 if (imeComposition)
569 e.setCommitString(*imeComposition);
570 imePosition = -1;
571 qt_sendSpontaneousEvent(fw, &e);
572 }
573
574 if (imeComposition)
575 imeComposition->clear();
576 imePosition = -1;
577
578 if (fw) {
579 Q_ASSERT(fw->testAttribute(Qt::WA_WState_Created));
580 HIMC imc = getContext(fw->effectiveWinId());
581 notifyIME(imc, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
582 releaseContext(fw->effectiveWinId(), imc);
583 }
584
585}
586
587
588bool QWinInputContext::startComposition()
589{
590#ifdef Q_IME_DEBUG
591 qDebug("startComposition");
592#endif
593
594 if (!imeComposition)
595 imeComposition = new QString();
596
597 QWidget *fw = focusWidget();
598 if (fw) {
599 Q_ASSERT(fw->testAttribute(Qt::WA_WState_Created));
600 imePosition = 0;
601 haveCaret = CreateCaret(fw->effectiveWinId(), 0, 1, 1);
602 HideCaret(fw->effectiveWinId());
603 update();
604 }
605 return fw != 0;
606}
607
608enum StandardFormat {
609 PreeditFormat,
610 SelectionFormat
611};
612
613bool QWinInputContext::composition(LPARAM lParam)
614{
615#ifdef Q_IME_DEBUG
616 QString str;
617 if (lParam & GCS_RESULTSTR)
618 str += "RESULTSTR ";
619 if (lParam & GCS_COMPSTR)
620 str += "COMPSTR ";
621 if (lParam & GCS_COMPATTR)
622 str += "COMPATTR ";
623 if (lParam & GCS_CURSORPOS)
624 str += "CURSORPOS ";
625 if (lParam & GCS_COMPCLAUSE)
626 str += "COMPCLAUSE ";
627 if (lParam & CS_INSERTCHAR)
628 str += "INSERTCHAR ";
629 if (lParam & CS_NOMOVECARET)
630 str += "NOMOVECARET ";
631 qDebug("composition, lParam=(%x) %s imePosition=%d", lParam, str.latin1(), imePosition);
632#endif
633
634 bool result = true;
635
636 if(!lParam)
637 // bogus event
638 return true;
639
640 QWidget *fw = qApp->focusWidget();
641 if (fw) {
642 Q_ASSERT(fw->testAttribute(Qt::WA_WState_Created));
643 HIMC imc = getContext(fw->effectiveWinId());
644 QInputMethodEvent e;
645 if (lParam & (GCS_COMPSTR | GCS_COMPATTR | GCS_CURSORPOS)) {
646 if (imePosition == -1)
647 // need to send a start event
648 startComposition();
649
650 // some intermediate composition result
651 int selStart, selLength;
652 *imeComposition = getString(imc, GCS_COMPSTR, &selStart, &selLength);
653 imePosition = getCursorPosition(imc);
654 if (lParam & CS_INSERTCHAR && lParam & CS_NOMOVECARET) {
655 // make korean work correctly. Hope this is correct for all IMEs
656 selStart = 0;
657 selLength = imeComposition->length();
658 }
659 if(selLength == 0)
660 selStart = 0;
661
662 QList<QInputMethodEvent::Attribute> attrs;
663 if (selStart > 0)
664 attrs << QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, 0, selStart,
665 standardFormat(PreeditFormat));
666 if (selLength)
667 attrs << QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, selStart, selLength,
668 standardFormat(SelectionFormat));
669 if (selStart + selLength < imeComposition->length())
670 attrs << QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, selStart + selLength,
671 imeComposition->length() - selStart - selLength,
672 standardFormat(PreeditFormat));
673 if(imePosition >= 0)
674 attrs << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, imePosition, selLength ? 0 : 1, QVariant());
675
676 e = QInputMethodEvent(*imeComposition, attrs);
677 }
678 if (lParam & GCS_RESULTSTR) {
679 if(imePosition == -1)
680 startComposition();
681 // a fixed result, return the converted string
682 *imeComposition = getString(imc, GCS_RESULTSTR);
683 imePosition = -1;
684 e.setCommitString(*imeComposition);
685 imeComposition->clear();
686 }
687 result = qt_sendSpontaneousEvent(fw, &e);
688 update();
689 releaseContext(fw->effectiveWinId(), imc);
690 }
691#ifdef Q_IME_DEBUG
692 qDebug("imecomposition: cursor pos at %d, str=%x", imePosition, str[0].unicode());
693#endif
694 return result;
695}
696
697static HIMC defaultContext = 0;
698
699// checks whether widget is a popup
700inline bool isPopup(QWidget *w)
701{
702 if (w && (w->windowFlags() & Qt::Popup) == Qt::Popup)
703 return true;
704 else
705 return false;
706}
707// checks whether widget is in a popup
708inline bool isInPopup(QWidget *w)
709{
710 if (w && (isPopup(w) || isPopup(w->window())))
711 return true;
712 else
713 return false;
714}
715
716// find the parent widget, which is a non popup toplevel
717// this is valid only if the widget is/in a popup
718inline QWidget *findParentforPopup(QWidget *w)
719{
720 QWidget *e = QWidget::find(w->effectiveWinId());
721 // check if this or its parent is a popup
722 while (isInPopup(e)) {
723 e = e->window()->parentWidget();
724 if (!e)
725 break;
726 e = QWidget::find(e->effectiveWinId());
727 }
728 if (e)
729 return e->window();
730 else
731 return 0;
732}
733
734// enables or disables the ime
735inline void enableIme(QWidget *w, bool value)
736{
737 if (value) {
738 // enable ime
739 if (defaultContext)
740 ImmAssociateContext(w->effectiveWinId(), defaultContext);
741#ifdef Q_OS_WINCE
742 if (qApp->autoSipEnabled())
743 qt_wince_show_SIP(true);
744#endif
745 } else {
746 // disable ime
747 HIMC oldimc = ImmAssociateContext(w->effectiveWinId(), 0);
748 if (!defaultContext)
749 defaultContext = oldimc;
750#ifdef Q_OS_WINCE
751 if (qApp->autoSipEnabled())
752 qt_wince_show_SIP(false);
753#endif
754 }
755}
756
757
758void QInputContextPrivate::updateImeStatus(QWidget *w, bool hasFocus)
759{
760 if (!w)
761 return;
762 bool e = w->testAttribute(Qt::WA_InputMethodEnabled) && w->isEnabled();
763 bool hasIme = e && hasFocus;
764#ifdef Q_IME_DEBUG
765 qDebug("%s HasFocus = %d hasIme = %d e = %d ", w->className(), hasFocus, hasIme, e);
766#endif
767 if (hasFocus || e) {
768 if (isInPopup(w))
769 QWinInputContext::enablePopupChild(w, hasIme);
770 else
771 QWinInputContext::enable(w, hasIme);
772 }
773}
774
775void QWinInputContext::enablePopupChild(QWidget *w, bool e)
776{
777 if (aimm) {
778 enable(w, e);
779 return;
780 }
781
782 if (!w || !isInPopup(w))
783 return;
784#ifdef Q_IME_DEBUG
785 qDebug("enablePopupChild: w=%s, enable = %s", w ? w->className() : "(null)" , e ? "true" : "false");
786#endif
787 QWidget *parent = findParentforPopup(w);
788 if (parent) {
789 // update ime status of the normal toplevel parent of the popup
790 enableIme(parent, e);
791 }
792 QWidget *toplevel = w->window();
793 if (toplevel) {
794 // update ime status of the toplevel popup
795 enableIme(toplevel, e);
796 }
797}
798
799void QWinInputContext::enable(QWidget *w, bool e)
800{
801 if(w) {
802#ifdef Q_IME_DEBUG
803 qDebug("enable: w=%s, enable = %s", w ? w->className() : "(null)" , e ? "true" : "false");
804#endif
805 if (!w->testAttribute(Qt::WA_WState_Created))
806 return;
807 if(aimm) {
808 HIMC oldimc;
809 if (!e) {
810 aimm->AssociateContext(w->effectiveWinId(), 0, &oldimc);
811 if (!defaultContext)
812 defaultContext = oldimc;
813 } else if (defaultContext) {
814 aimm->AssociateContext(w->effectiveWinId(), defaultContext, &oldimc);
815 }
816 } else {
817 // update ime status on the widget
818 QWidget *p = QWidget::find(w->effectiveWinId());
819 if (p)
820 enableIme(p, e);
821 }
822 }
823}
824
825void QWinInputContext::setFocusWidget(QWidget *w)
826{
827 QInputContext::setFocusWidget(w);
828 update();
829}
830
831bool QWinInputContext::isComposing() const
832{
833 return imeComposition && !imeComposition->isEmpty();
834}
835
836void QWinInputContext::mouseHandler(int pos, QMouseEvent *e)
837{
838 if(e->type() != QEvent::MouseButtonPress)
839 return;
840
841 if (pos < 0 || pos > imeComposition->length())
842 reset();
843
844 // Probably should pass the correct button, but it seems to work fine like this.
845 DWORD button = MK_LBUTTON;
846
847 QWidget *fw = focusWidget();
848 if (fw) {
849 Q_ASSERT(fw->testAttribute(Qt::WA_WState_Created));
850 HIMC himc = getContext(fw->effectiveWinId());
851 HWND ime_wnd = getDefaultIMEWnd(fw->effectiveWinId());
852 SendMessage(ime_wnd, WM_MSIME_MOUSE, MAKELONG(MAKEWORD(button, pos == 0 ? 2 : 1), pos), (LPARAM)himc);
853 releaseContext(fw->effectiveWinId(), himc);
854 }
855 //qDebug("mouseHandler: got value %d pos=%d", ret,pos);
856}
857
858QString QWinInputContext::language()
859{
860 return QString();
861}
862
863QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.