author | Eckhart Koeppen <eckhart.koppen@nokia.com> |
Wed, 21 Apr 2010 11:15:19 +0300 | |
branch | RCL_3 |
changeset 11 | 25a739ee40f4 |
parent 4 | 3b1da2848fc7 |
permissions | -rw-r--r-- |
0 | 1 |
/**************************************************************************** |
2 |
** |
|
4
3b1da2848fc7
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
3
diff
changeset
|
3 |
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). |
0 | 4 |
** All rights reserved. |
5 |
** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 |
** |
|
7 |
** This file is part of the QtGui module of the Qt Toolkit. |
|
8 |
** |
|
9 |
** $QT_BEGIN_LICENSE:LGPL$ |
|
10 |
** No Commercial Usage |
|
11 |
** This file contains pre-release code and may not be distributed. |
|
12 |
** You may use this file in accordance with the terms and conditions |
|
13 |
** contained in the Technology Preview License Agreement accompanying |
|
14 |
** this package. |
|
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 |
** If you have questions regarding the use of this file, please contact |
|
29 |
** Nokia at qt-info@nokia.com. |
|
30 |
** |
|
31 |
** |
|
32 |
** |
|
33 |
** |
|
34 |
** |
|
35 |
** |
|
36 |
** |
|
37 |
** |
|
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) |
|
59 |
extern void qt_wince_show_SIP(bool show); // defined in qguifunctions_wince.cpp |
|
60 |
#endif |
|
61 |
||
62 |
QT_BEGIN_NAMESPACE |
|
63 |
||
64 |
extern bool qt_sendSpontaneousEvent(QObject*, QEvent*); |
|
65 |
||
66 |
||
67 |
DEFINE_GUID(IID_IActiveIMMApp, |
|
68 |
0x08c0e040, 0x62d1, 0x11d1, 0x93, 0x26, 0x0, 0x60, 0xb0, 0x67, 0xb8, 0x6e); |
|
69 |
||
70 |
||
71 |
||
72 |
DEFINE_GUID(CLSID_CActiveIMM, |
|
73 |
0x4955DD33, 0xB159, 0x11d0, 0x8F, 0xCF, 0x0, 0xAA, 0x00, 0x6B, 0xCC, 0x59); |
|
74 |
||
75 |
||
76 |
||
77 |
DEFINE_GUID(IID_IActiveIMMMessagePumpOwner, |
|
78 |
0xb5cf2cfa, 0x8aeb, 0x11d1, 0x93, 0x64, 0x0, 0x60, 0xb0, 0x67, 0xb8, 0x6e); |
|
79 |
||
80 |
||
81 |
||
82 |
interface IEnumRegisterWordW; |
|
83 |
interface IEnumInputContext; |
|
84 |
||
85 |
||
86 |
bool qt_sendSpontaneousEvent(QObject*, QEvent*); |
|
87 |
||
88 |
||
89 |
#define IFMETHOD HRESULT STDMETHODCALLTYPE |
|
90 |
||
91 |
interface IActiveIMMApp : public IUnknown |
|
92 |
{ |
|
93 |
public: |
|
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 |
||
169 |
interface IActiveIMMMessagePumpOwner : public IUnknown |
|
170 |
{ |
|
171 |
public: |
|
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 |
||
180 |
static IActiveIMMApp *aimm = 0; |
|
181 |
static IActiveIMMMessagePumpOwner *aimmpump = 0; |
|
182 |
static QString *imeComposition = 0; |
|
183 |
static int imePosition = -1; |
|
184 |
bool qt_use_rtl_extensions = false; |
|
185 |
static 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 |
||
212 |
static DWORD WM_MSIME_MOUSE = 0; |
|
213 |
||
214 |
QWinInputContext::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 |
||
259 |
QWinInputContext::~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 |
||
274 |
static 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 |
||
284 |
static 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 |
||
295 |
static void releaseContext(HWND wnd, HIMC imc) |
|
296 |
{ |
|
297 |
if (aimm) |
|
298 |
aimm->ReleaseContext(wnd, imc); |
|
299 |
else |
|
300 |
ImmReleaseContext(wnd, imc); |
|
301 |
} |
|
302 |
||
303 |
static 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 |
||
313 |
static 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 |
||
323 |
static int getCursorPosition(HIMC himc) |
|
324 |
{ |
|
325 |
return getCompositionString(himc, GCS_CURSORPOS, 0, 0); |
|
326 |
} |
|
327 |
||
328 |
static QString getString(HIMC himc, DWORD dwindex, int *selStart = 0, int *selLength = 0) |
|
329 |
{ |
|
3
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
330 |
const int bufferSize = 256; |
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
331 |
wchar_t buffer[bufferSize]; |
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
332 |
int len = getCompositionString(himc, dwindex, buffer, bufferSize * sizeof(wchar_t)); |
0 | 333 |
|
334 |
if (selStart) { |
|
3
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
335 |
char attrbuffer[bufferSize]; |
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
336 |
int attrlen = getCompositionString(himc, GCS_COMPATTR, attrbuffer, bufferSize); |
0 | 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 |
||
354 |
void QWinInputContext::TranslateMessage(const MSG *msg) |
|
355 |
{ |
|
356 |
if (!aimmpump || aimmpump->OnTranslateMessage(msg) != S_OK) |
|
357 |
::TranslateMessage(msg); |
|
358 |
} |
|
359 |
||
360 |
LRESULT 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 |
||
371 |
void 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 |
||
437 |
bool 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 |
||
480 |
void 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 |
||
510 |
bool 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 |
||
530 |
enum StandardFormat { |
|
531 |
PreeditFormat, |
|
532 |
SelectionFormat |
|
533 |
}; |
|
534 |
||
535 |
bool 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 |
||
619 |
static HIMC defaultContext = 0; |
|
620 |
||
621 |
// checks whether widget is a popup |
|
622 |
inline 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 |
|
630 |
inline 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 |
|
640 |
inline 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 |
|
657 |
inline 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 |
||
680 |
void 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 |
||
702 |
void 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 |
||
726 |
void 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 |
||
752 |
void 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 |
||
767 |
bool QWinInputContext::isComposing() const |
|
768 |
{ |
|
769 |
return imeComposition && !imeComposition->isEmpty(); |
|
770 |
} |
|
771 |
||
772 |
void 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 |
||
794 |
QString QWinInputContext::language() |
|
795 |
{ |
|
796 |
return QString(); |
|
797 |
} |
|
798 |
||
799 |
int 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 |
||
847 |
QT_END_NAMESPACE |