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