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

Last change on this file since 605 was 561, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.1 sources.

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