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