diff options
author | Lars Knoll <lars.knoll@nokia.com> | 2009-03-23 09:18:55 (GMT) |
---|---|---|
committer | Simon Hausmann <simon.hausmann@nokia.com> | 2009-03-23 09:18:55 (GMT) |
commit | e5fcad302d86d316390c6b0f62759a067313e8a9 (patch) | |
tree | c2afbf6f1066b6ce261f14341cf6d310e5595bc1 /src/gui/inputmethod/qwininputcontext_win.cpp | |
download | Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.zip Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.tar.gz Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.tar.bz2 |
Long live Qt 4.5!
Diffstat (limited to 'src/gui/inputmethod/qwininputcontext_win.cpp')
-rw-r--r-- | src/gui/inputmethod/qwininputcontext_win.cpp | 861 |
1 files changed, 861 insertions, 0 deletions
diff --git a/src/gui/inputmethod/qwininputcontext_win.cpp b/src/gui/inputmethod/qwininputcontext_win.cpp new file mode 100644 index 0000000..720f0b8 --- /dev/null +++ b/src/gui/inputmethod/qwininputcontext_win.cpp @@ -0,0 +1,861 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwininputcontext_p.h" +#include "qinputcontext_p.h" + +#include "qfont.h" +#include "qwidget.h" +#include "qapplication.h" +#include "qlibrary.h" +#include "qevent.h" +#include "qtextformat.h" + +//#define Q_IME_DEBUG + +/* Active Input method support on Win95/98/NT */ +#include <objbase.h> +#include <initguid.h> + +#ifdef Q_IME_DEBUG +#include "qdebug.h" +#endif + +QT_BEGIN_NAMESPACE + +extern bool qt_sendSpontaneousEvent(QObject*, QEvent*); +#if defined(Q_OS_WINCE) +extern void qt_wince_show_SIP(bool show); // defined in qguifunctions_wince.cpp +#endif + +DEFINE_GUID(IID_IActiveIMMApp, +0x08c0e040, 0x62d1, 0x11d1, 0x93, 0x26, 0x0, 0x60, 0xb0, 0x67, 0xb8, 0x6e); + + + +DEFINE_GUID(CLSID_CActiveIMM, +0x4955DD33, 0xB159, 0x11d0, 0x8F, 0xCF, 0x0, 0xAA, 0x00, 0x6B, 0xCC, 0x59); + + + +DEFINE_GUID(IID_IActiveIMMMessagePumpOwner, +0xb5cf2cfa, 0x8aeb, 0x11d1, 0x93, 0x64, 0x0, 0x60, 0xb0, 0x67, 0xb8, 0x6e); + + + +interface IEnumRegisterWordW; +interface IEnumInputContext; + + +bool qt_sendSpontaneousEvent(QObject*, QEvent*); + + +#define IFMETHOD HRESULT STDMETHODCALLTYPE + +interface IActiveIMMApp : public IUnknown +{ +public: + virtual IFMETHOD AssociateContext(HWND hWnd, HIMC hIME, HIMC __RPC_FAR *phPrev) = 0; + virtual IFMETHOD dummy_ConfigureIMEA() = 0; + virtual IFMETHOD ConfigureIMEW(HKL hKL, HWND hWnd, DWORD dwMode, REGISTERWORDW __RPC_FAR *pData) = 0; + virtual IFMETHOD CreateContext(HIMC __RPC_FAR *phIMC) = 0; + virtual IFMETHOD DestroyContext(HIMC hIME) = 0; + virtual IFMETHOD dummy_EnumRegisterWordA() = 0; + virtual IFMETHOD EnumRegisterWordW(HKL hKL, LPWSTR szReading, DWORD dwStyle, LPWSTR szRegister, LPVOID pData, + IEnumRegisterWordW __RPC_FAR *__RPC_FAR *pEnum) = 0; + virtual IFMETHOD dummy_EscapeA() = 0; + virtual IFMETHOD EscapeW(HKL hKL, HIMC hIMC, UINT uEscape, LPVOID pData, LRESULT __RPC_FAR *plResult) = 0; + virtual IFMETHOD dummy_GetCandidateListA() = 0; + virtual IFMETHOD GetCandidateListW(HIMC hIMC, DWORD dwIndex, UINT uBufLen, CANDIDATELIST __RPC_FAR *pCandList, + UINT __RPC_FAR *puCopied) = 0; + virtual IFMETHOD dummy_GetCandidateListCountA() = 0; + virtual IFMETHOD GetCandidateListCountW(HIMC hIMC, DWORD __RPC_FAR *pdwListSize, DWORD __RPC_FAR *pdwBufLen) = 0; + virtual IFMETHOD GetCandidateWindow(HIMC hIMC, DWORD dwIndex, CANDIDATEFORM __RPC_FAR *pCandidate) = 0; + virtual IFMETHOD dummy_GetCompositionFontA() = 0; + virtual IFMETHOD GetCompositionFontW(HIMC hIMC, LOGFONTW __RPC_FAR *plf) = 0; + virtual IFMETHOD dummy_GetCompositionStringA() = 0; + virtual IFMETHOD GetCompositionStringW(HIMC hIMC, DWORD dwIndex, DWORD dwBufLen, LONG __RPC_FAR *plCopied, LPVOID pBuf) = 0; + virtual IFMETHOD GetCompositionWindow(HIMC hIMC, COMPOSITIONFORM __RPC_FAR *pCompForm) = 0; + virtual IFMETHOD GetContext(HWND hWnd, HIMC __RPC_FAR *phIMC) = 0; + virtual IFMETHOD dummy_GetConversionListA() = 0; + virtual IFMETHOD GetConversionListW(HKL hKL, HIMC hIMC, LPWSTR pSrc, UINT uBufLen, UINT uFlag, + CANDIDATELIST __RPC_FAR *pDst, UINT __RPC_FAR *puCopied) = 0; + virtual IFMETHOD GetConversionStatus(HIMC hIMC, DWORD __RPC_FAR *pfdwConversion, DWORD __RPC_FAR *pfdwSentence) = 0; + virtual IFMETHOD GetDefaultIMEWnd(HWND hWnd, HWND __RPC_FAR *phDefWnd) = 0; + virtual IFMETHOD dummy_GetDescriptionA() = 0; + virtual IFMETHOD GetDescriptionW(HKL hKL, UINT uBufLen, LPWSTR szDescription, UINT __RPC_FAR *puCopied) = 0; + virtual IFMETHOD dummy_GetGuideLineA() = 0; + virtual IFMETHOD GetGuideLineW(HIMC hIMC, DWORD dwIndex, DWORD dwBufLen, LPWSTR pBuf, DWORD __RPC_FAR *pdwResult) = 0; + virtual IFMETHOD dummy_GetIMEFileNameA() = 0; + virtual IFMETHOD GetIMEFileNameW(HKL hKL, UINT uBufLen, LPWSTR szFileName, UINT __RPC_FAR *puCopied) = 0; + virtual IFMETHOD GetOpenStatus(HIMC hIMC) = 0; + virtual IFMETHOD GetProperty(HKL hKL, DWORD fdwIndex, DWORD __RPC_FAR *pdwProperty) = 0; + virtual IFMETHOD dummy_GetRegisterWordStyleA() = 0; + virtual IFMETHOD GetRegisterWordStyleW(HKL hKL, UINT nItem, STYLEBUFW __RPC_FAR *pStyleBuf, UINT __RPC_FAR *puCopied) = 0; + virtual IFMETHOD GetStatusWindowPos(HIMC hIMC, POINT __RPC_FAR *pptPos) = 0; + virtual IFMETHOD GetVirtualKey(HWND hWnd, UINT __RPC_FAR *puVirtualKey) = 0; + virtual IFMETHOD dummy_InstallIMEA() = 0; + virtual IFMETHOD InstallIMEW(LPWSTR szIMEFileName, LPWSTR szLayoutText, HKL __RPC_FAR *phKL) = 0; + virtual IFMETHOD IsIME(HKL hKL) = 0; + virtual IFMETHOD dummy_IsUIMessageA() = 0; + virtual IFMETHOD IsUIMessageW(HWND hWndIME, UINT msg, WPARAM wParam, LPARAM lParam) = 0; + virtual IFMETHOD NotifyIME(HIMC hIMC, DWORD dwAction, DWORD dwIndex, DWORD dwValue) = 0; + virtual IFMETHOD dummy_RegisterWordA() = 0; + virtual IFMETHOD RegisterWordW(HKL hKL, LPWSTR szReading, DWORD dwStyle, LPWSTR szRegister) = 0; + virtual IFMETHOD ReleaseContext(HWND hWnd, HIMC hIMC) = 0; + virtual IFMETHOD SetCandidateWindow(HIMC hIMC, CANDIDATEFORM __RPC_FAR *pCandidate) = 0; + virtual IFMETHOD SetCompositionFontA(HIMC hIMC, LOGFONTA __RPC_FAR *plf) = 0; + virtual IFMETHOD SetCompositionFontW(HIMC hIMC, LOGFONTW __RPC_FAR *plf) = 0; + virtual IFMETHOD dummy_SetCompositionStringA() = 0; + virtual IFMETHOD SetCompositionStringW(HIMC hIMC, DWORD dwIndex, LPVOID pComp, DWORD dwCompLen, + LPVOID pRead, DWORD dwReadLen) = 0; + virtual IFMETHOD SetCompositionWindow(HIMC hIMC, COMPOSITIONFORM __RPC_FAR *pCompForm) = 0; + virtual IFMETHOD SetConversionStatus(HIMC hIMC, DWORD fdwConversion, DWORD fdwSentence) = 0; + virtual IFMETHOD SetOpenStatus(HIMC hIMC, BOOL fOpen) = 0; + virtual IFMETHOD SetStatusWindowPos(HIMC hIMC, POINT __RPC_FAR *pptPos) = 0; + virtual IFMETHOD SimulateHotKey(HWND hWnd, DWORD dwHotKeyID) = 0; + virtual IFMETHOD dummy_UnregisterWordA() = 0; + virtual IFMETHOD UnregisterWordW(HKL hKL, LPWSTR szReading, DWORD dwStyle, LPWSTR szUnregister) = 0; + virtual IFMETHOD Activate(BOOL fRestoreLayout) = 0; + virtual IFMETHOD Deactivate(void) = 0; + virtual IFMETHOD OnDefWindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam, LRESULT __RPC_FAR *plResult) = 0; + virtual IFMETHOD FilterClientWindows(ATOM __RPC_FAR *aaClassList, UINT uSize) = 0; + virtual IFMETHOD dummy_GetCodePageA() = 0; + virtual IFMETHOD GetLangId(HKL hKL, LANGID __RPC_FAR *plid) = 0; + virtual IFMETHOD AssociateContextEx(HWND hWnd, HIMC hIMC, DWORD dwFlags) = 0; + virtual IFMETHOD DisableIME(DWORD idThread) = 0; + virtual IFMETHOD dummy_GetImeMenuItemsA() = 0; + virtual IFMETHOD GetImeMenuItemsW(HIMC hIMC, DWORD dwFlags, DWORD dwType, /*IMEMENUITEMINFOW*/ void __RPC_FAR *pImeParentMenu, + /*IMEMENUITEMINFOW*/ void __RPC_FAR *pImeMenu, DWORD dwSize, DWORD __RPC_FAR *pdwResult) = 0; + virtual IFMETHOD EnumInputContext(DWORD idThread, IEnumInputContext __RPC_FAR *__RPC_FAR *ppEnum) = 0; +}; + +interface IActiveIMMMessagePumpOwner : public IUnknown +{ +public: + virtual IFMETHOD Start(void) = 0; + virtual IFMETHOD End(void) = 0; + virtual IFMETHOD OnTranslateMessage(const MSG __RPC_FAR *pMsg) = 0; + virtual IFMETHOD Pause(DWORD __RPC_FAR *pdwCookie) = 0; + virtual IFMETHOD Resume(DWORD dwCookie) = 0; +}; + + +static IActiveIMMApp *aimm = 0; +static IActiveIMMMessagePumpOwner *aimmpump = 0; +static QString *imeComposition = 0; +static int imePosition = -1; +bool qt_use_rtl_extensions = false; +static bool haveCaret = false; + +#ifndef LGRPID_INSTALLED +#define LGRPID_INSTALLED 0x00000001 // installed language group ids +#define LGRPID_SUPPORTED 0x00000002 // supported language group ids +#endif + +#ifndef LGRPID_ARABIC +#define LGRPID_WESTERN_EUROPE 0x0001 // Western Europe & U.S. +#define LGRPID_CENTRAL_EUROPE 0x0002 // Central Europe +#define LGRPID_BALTIC 0x0003 // Baltic +#define LGRPID_GREEK 0x0004 // Greek +#define LGRPID_CYRILLIC 0x0005 // Cyrillic +#define LGRPID_TURKISH 0x0006 // Turkish +#define LGRPID_JAPANESE 0x0007 // Japanese +#define LGRPID_KOREAN 0x0008 // Korean +#define LGRPID_TRADITIONAL_CHINESE 0x0009 // Traditional Chinese +#define LGRPID_SIMPLIFIED_CHINESE 0x000a // Simplified Chinese +#define LGRPID_THAI 0x000b // Thai +#define LGRPID_HEBREW 0x000c // Hebrew +#define LGRPID_ARABIC 0x000d // Arabic +#define LGRPID_VIETNAMESE 0x000e // Vietnamese +#define LGRPID_INDIC 0x000f // Indic +#define LGRPID_GEORGIAN 0x0010 // Georgian +#define LGRPID_ARMENIAN 0x0011 // Armenian +#endif + +static DWORD WM_MSIME_MOUSE = 0; + +QWinInputContext::QWinInputContext(QObject *parent) + : QInputContext(parent), recursionGuard(false) +{ + if (QSysInfo::WindowsVersion < QSysInfo::WV_2000) { + // try to get the Active IMM COM object on Win95/98/NT, where english versions don't + // support the regular Windows input methods. + if (CoCreateInstance(CLSID_CActiveIMM, NULL, CLSCTX_INPROC_SERVER, + IID_IActiveIMMApp, (LPVOID *)&aimm) != S_OK) { + aimm = 0; + } + if (aimm && (aimm->QueryInterface(IID_IActiveIMMMessagePumpOwner, (LPVOID *)&aimmpump) != S_OK || + aimm->Activate(true) != S_OK)) { + aimm->Release(); + aimm = 0; + if (aimmpump) + aimmpump->Release(); + aimmpump = 0; + } + if (aimmpump) + aimmpump->Start(); + } + +#ifndef Q_OS_WINCE + QSysInfo::WinVersion ver = QSysInfo::windowsVersion(); + if (ver & QSysInfo::WV_NT_based && ver >= QSysInfo::WV_VISTA) { + // Since the IsValidLanguageGroup/IsValidLocale functions always return true on + // Vista, check the Keyboard Layouts for enabling RTL. + UINT nLayouts = GetKeyboardLayoutList(0, 0); + if (nLayouts) { + HKL *lpList = new HKL[nLayouts]; + GetKeyboardLayoutList(nLayouts, lpList); + for (int i = 0; i<(int)nLayouts; i++) { + WORD plangid = PRIMARYLANGID((quintptr)lpList[i]); + if (plangid == LANG_ARABIC + || plangid == LANG_HEBREW + || plangid == LANG_FARSI +#ifdef LANG_SYRIAC + || plangid == LANG_SYRIAC +#endif + ) { + qt_use_rtl_extensions = true; + break; + } + } + delete []lpList; + } + } else { + // figure out whether a RTL language is installed + typedef BOOL(WINAPI *PtrIsValidLanguageGroup)(DWORD,DWORD); + PtrIsValidLanguageGroup isValidLanguageGroup = (PtrIsValidLanguageGroup)QLibrary::resolve(QLatin1String("kernel32"), "IsValidLanguageGroup"); + if (isValidLanguageGroup) { + qt_use_rtl_extensions = isValidLanguageGroup(LGRPID_ARABIC, LGRPID_INSTALLED) + || isValidLanguageGroup(LGRPID_HEBREW, LGRPID_INSTALLED); + } + qt_use_rtl_extensions |= IsValidLocale(MAKELCID(MAKELANGID(LANG_ARABIC, SUBLANG_DEFAULT), SORT_DEFAULT), LCID_INSTALLED) + || IsValidLocale(MAKELCID(MAKELANGID(LANG_HEBREW, SUBLANG_DEFAULT), SORT_DEFAULT), LCID_INSTALLED) +#ifdef LANG_SYRIAC + || IsValidLocale(MAKELCID(MAKELANGID(LANG_SYRIAC, SUBLANG_DEFAULT), SORT_DEFAULT), LCID_INSTALLED) +#endif + || IsValidLocale(MAKELCID(MAKELANGID(LANG_FARSI, SUBLANG_DEFAULT), SORT_DEFAULT), LCID_INSTALLED); + } +#else + qt_use_rtl_extensions = false; +#endif + + WM_MSIME_MOUSE = QT_WA_INLINE(RegisterWindowMessage(L"MSIMEMouseOperation"), RegisterWindowMessageA("MSIMEMouseOperation")); +} + +QWinInputContext::~QWinInputContext() +{ + // release active input method if we have one + if (aimm) { + aimmpump->End(); + aimmpump->Release(); + aimm->Deactivate(); + aimm->Release(); + aimm = 0; + aimmpump = 0; + } + delete imeComposition; + imeComposition = 0; +} + +static HWND getDefaultIMEWnd(HWND wnd) +{ + HWND ime_wnd; + if(aimm) + aimm->GetDefaultIMEWnd(wnd, &ime_wnd); + else + ime_wnd = ImmGetDefaultIMEWnd(wnd); + return ime_wnd; +} + +static HIMC getContext(HWND wnd) +{ + HIMC imc; + if (aimm) + aimm->GetContext(wnd, &imc); + else + imc = ImmGetContext(wnd); + + return imc; +} + +static void releaseContext(HWND wnd, HIMC imc) +{ + if (aimm) + aimm->ReleaseContext(wnd, imc); + else + ImmReleaseContext(wnd, imc); +} + +static void notifyIME(HIMC imc, DWORD dwAction, DWORD dwIndex, DWORD dwValue) +{ + if (!imc) + return; + if (aimm) + aimm->NotifyIME(imc, dwAction, dwIndex, dwValue); + else + ImmNotifyIME(imc, dwAction, dwIndex, dwValue); +} + +static LONG getCompositionString(HIMC himc, DWORD dwIndex, LPVOID lpbuf, DWORD dBufLen, bool *unicode = 0) +{ + LONG len = 0; + if (unicode) + *unicode = true; + if (aimm) + aimm->GetCompositionStringW(himc, dwIndex, dBufLen, &len, lpbuf); + else + { + if(QSysInfo::WindowsVersion != QSysInfo::WV_95) { + len = ImmGetCompositionStringW(himc, dwIndex, lpbuf, dBufLen); + } +#if !defined(Q_OS_WINCE) + else { + len = ImmGetCompositionStringA(himc, dwIndex, lpbuf, dBufLen); + if (unicode) + *unicode = false; + } +#endif + } + return len; +} + +static int getCursorPosition(HIMC himc) +{ + return getCompositionString(himc, GCS_CURSORPOS, 0, 0); +} + +static QString getString(HIMC himc, DWORD dwindex, int *selStart = 0, int *selLength = 0) +{ + static char *buffer = 0; + static int buflen = 0; + + int len = getCompositionString(himc, dwindex, 0, 0) + 1; + if (!buffer || len > buflen) { + delete [] buffer; + buflen = qMin(len, 256); + buffer = new char[buflen]; + } + + bool unicode = true; + len = getCompositionString(himc, dwindex, buffer, buflen, &unicode); + + if (selStart) { + static char *attrbuffer = 0; + static int attrbuflen = 0; + int attrlen = getCompositionString(himc, dwindex, 0, 0) + 1; + if (!attrbuffer || attrlen> attrbuflen) { + delete [] attrbuffer; + attrbuflen = qMin(attrlen, 256); + attrbuffer = new char[attrbuflen]; + } + attrlen = getCompositionString(himc, GCS_COMPATTR, attrbuffer, attrbuflen); + *selStart = attrlen+1; + *selLength = -1; + for (int i = 0; i < attrlen; i++) { + if (attrbuffer[i] & ATTR_TARGET_CONVERTED) { + *selStart = qMin(*selStart, i); + *selLength = qMax(*selLength, i); + } + } + *selLength = qMax(0, *selLength - *selStart + 1); + } + + if (len <= 0) + return QString(); + if (unicode) { + return QString((QChar *)buffer, len/sizeof(QChar)); + } + else { + buffer[len] = 0; + WCHAR *wc = new WCHAR[len+1]; + int l = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, + buffer, len, wc, len+1); + QString res = QString((QChar *)wc, l); + delete [] wc; + return res; + } +} + +void QWinInputContext::TranslateMessage(const MSG *msg) +{ + if (!aimmpump || aimmpump->OnTranslateMessage(msg) != S_OK) + ::TranslateMessage(msg); +} + +LRESULT QWinInputContext::DefWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + LRESULT retval; + if (!aimm || aimm->OnDefWindowProc(hwnd, msg, wParam, lParam, &retval) != S_OK) + { + QT_WA({ + retval = ::DefWindowProc(hwnd, msg, wParam, lParam); + } , { + retval = ::DefWindowProcA(hwnd,msg, wParam, lParam); + }); + } + return retval; +} + + +void QWinInputContext::update() +{ + QWidget *w = focusWidget(); + if(!w) + return; + + Q_ASSERT(w->testAttribute(Qt::WA_WState_Created)); + HIMC imc = getContext(w->effectiveWinId()); + + if (!imc) + return; + + QFont f = qvariant_cast<QFont>(w->inputMethodQuery(Qt::ImFont)); + HFONT hf; + hf = f.handle(); + + QT_WA({ + LOGFONT lf; + if (GetObject(hf, sizeof(lf), &lf)) + if (aimm) + aimm->SetCompositionFontW(imc, &lf); + else + ImmSetCompositionFont(imc, &lf); + } , { + LOGFONTA lf; + if (GetObjectA(hf, sizeof(lf), &lf)) + if (aimm) + aimm->SetCompositionFontA(imc, &lf); + else + ImmSetCompositionFontA(imc, &lf); + }); + + QRect r = w->inputMethodQuery(Qt::ImMicroFocus).toRect(); + + // The ime window positions are based on the WinId with active focus. + QWidget *imeWnd = QWidget::find(::GetFocus()); + if (imeWnd && !aimm) { + QPoint pt (r.topLeft()); + pt = w->mapToGlobal(pt); + pt = imeWnd->mapFromGlobal(pt); + r.moveTo(pt); + } + + COMPOSITIONFORM cf; + // ### need X-like inputStyle config settings + cf.dwStyle = CFS_FORCE_POSITION; + cf.ptCurrentPos.x = r.x(); + cf.ptCurrentPos.y = r.y(); + + CANDIDATEFORM candf; + candf.dwIndex = 0; + candf.dwStyle = CFS_EXCLUDE; + candf.ptCurrentPos.x = r.x(); + candf.ptCurrentPos.y = r.y() + r.height(); + candf.rcArea.left = r.x(); + candf.rcArea.top = r.y(); + candf.rcArea.right = r.x() + r.width(); + candf.rcArea.bottom = r.y() + r.height(); + + if(haveCaret) + SetCaretPos(r.x(), r.y()); + + if (aimm) { + aimm->SetCompositionWindow(imc, &cf); + aimm->SetCandidateWindow(imc, &candf); + } else { + ImmSetCompositionWindow(imc, &cf); + ImmSetCandidateWindow(imc, &candf); + } + + releaseContext(w->effectiveWinId(), imc); +} + + +bool QWinInputContext::endComposition() +{ + QWidget *fw = focusWidget(); +#ifdef Q_IME_DEBUG + qDebug("endComposition! fw = %s", fw ? fw->className() : "(null)"); +#endif + bool result = true; + if(imePosition == -1 || recursionGuard) + return result; + + // Googles Pinyin Input Method likes to call endComposition again + // when we call notifyIME with CPS_CANCEL, so protect ourselves + // against that. + recursionGuard = true; + + if (fw) { + Q_ASSERT(fw->testAttribute(Qt::WA_WState_Created)); + HIMC imc = getContext(fw->effectiveWinId()); + notifyIME(imc, NI_COMPOSITIONSTR, CPS_CANCEL, 0); + releaseContext(fw->effectiveWinId(), imc); + if(haveCaret) { + DestroyCaret(); + haveCaret = false; + } + } + + if (!fw) + fw = qApp->focusWidget(); + + if (fw) { + QInputMethodEvent e; + result = qt_sendSpontaneousEvent(fw, &e); + } + + if (imeComposition) + imeComposition->clear(); + imePosition = -1; + + recursionGuard = false; + + return result; +} + +void QWinInputContext::reset() +{ + QWidget *fw = focusWidget(); + +#ifdef Q_IME_DEBUG + qDebug("sending accept to focus widget %s", fw ? fw->className() : "(null)"); +#endif + + if (fw && imePosition != -1) { + QInputMethodEvent e; + if (imeComposition) + e.setCommitString(*imeComposition); + imePosition = -1; + qt_sendSpontaneousEvent(fw, &e); + } + + if (imeComposition) + imeComposition->clear(); + imePosition = -1; + + if (fw) { + Q_ASSERT(fw->testAttribute(Qt::WA_WState_Created)); + HIMC imc = getContext(fw->effectiveWinId()); + notifyIME(imc, NI_COMPOSITIONSTR, CPS_CANCEL, 0); + releaseContext(fw->effectiveWinId(), imc); + } + +} + + +bool QWinInputContext::startComposition() +{ +#ifdef Q_IME_DEBUG + qDebug("startComposition"); +#endif + + if (!imeComposition) + imeComposition = new QString(); + + QWidget *fw = focusWidget(); + if (fw) { + Q_ASSERT(fw->testAttribute(Qt::WA_WState_Created)); + imePosition = 0; + haveCaret = CreateCaret(fw->effectiveWinId(), 0, 1, 1); + HideCaret(fw->effectiveWinId()); + update(); + } + return fw != 0; +} + +enum StandardFormat { + PreeditFormat, + SelectionFormat +}; + +bool QWinInputContext::composition(LPARAM lParam) +{ +#ifdef Q_IME_DEBUG + QString str; + if (lParam & GCS_RESULTSTR) + str += "RESULTSTR "; + if (lParam & GCS_COMPSTR) + str += "COMPSTR "; + if (lParam & GCS_COMPATTR) + str += "COMPATTR "; + if (lParam & GCS_CURSORPOS) + str += "CURSORPOS "; + if (lParam & GCS_COMPCLAUSE) + str += "COMPCLAUSE "; + if (lParam & CS_INSERTCHAR) + str += "INSERTCHAR "; + if (lParam & CS_NOMOVECARET) + str += "NOMOVECARET "; + qDebug("composition, lParam=(%x) %s imePosition=%d", lParam, str.latin1(), imePosition); +#endif + + bool result = true; + + if(!lParam) + // bogus event + return true; + + QWidget *fw = qApp->focusWidget(); + if (fw) { + Q_ASSERT(fw->testAttribute(Qt::WA_WState_Created)); + HIMC imc = getContext(fw->effectiveWinId()); + QInputMethodEvent e; + if (lParam & (GCS_COMPSTR | GCS_COMPATTR | GCS_CURSORPOS)) { + if (imePosition == -1) + // need to send a start event + startComposition(); + + // some intermediate composition result + int selStart, selLength; + *imeComposition = getString(imc, GCS_COMPSTR, &selStart, &selLength); + imePosition = getCursorPosition(imc); + if (lParam & CS_INSERTCHAR && lParam & CS_NOMOVECARET) { + // make korean work correctly. Hope this is correct for all IMEs + selStart = 0; + selLength = imeComposition->length(); + } + if(selLength == 0) + selStart = 0; + + QList<QInputMethodEvent::Attribute> attrs; + if (selStart > 0) + attrs << QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, 0, selStart, + standardFormat(PreeditFormat)); + if (selLength) + attrs << QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, selStart, selLength, + standardFormat(SelectionFormat)); + if (selStart + selLength < imeComposition->length()) + attrs << QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, selStart + selLength, + imeComposition->length() - selStart - selLength, + standardFormat(PreeditFormat)); + if(imePosition >= 0) + attrs << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, imePosition, selLength ? 0 : 1, QVariant()); + + e = QInputMethodEvent(*imeComposition, attrs); + } + if (lParam & GCS_RESULTSTR) { + if(imePosition == -1) + startComposition(); + // a fixed result, return the converted string + *imeComposition = getString(imc, GCS_RESULTSTR); + imePosition = -1; + e.setCommitString(*imeComposition); + imeComposition->clear(); + } + result = qt_sendSpontaneousEvent(fw, &e); + update(); + releaseContext(fw->effectiveWinId(), imc); + } +#ifdef Q_IME_DEBUG + qDebug("imecomposition: cursor pos at %d, str=%x", imePosition, str[0].unicode()); +#endif + return result; +} + +static HIMC defaultContext = 0; + +// checks whether widget is a popup +inline bool isPopup(QWidget *w) +{ + if (w && (w->windowFlags() & Qt::Popup) == Qt::Popup) + return true; + else + return false; +} +// checks whether widget is in a popup +inline bool isInPopup(QWidget *w) +{ + if (w && (isPopup(w) || isPopup(w->window()))) + return true; + else + return false; +} + +// find the parent widget, which is a non popup toplevel +// this is valid only if the widget is/in a popup +inline QWidget *findParentforPopup(QWidget *w) +{ + QWidget *e = QWidget::find(w->effectiveWinId()); + // check if this or its parent is a popup + while (isInPopup(e)) { + e = e->window()->parentWidget(); + if (!e) + break; + e = QWidget::find(e->effectiveWinId()); + } + if (e) + return e->window(); + else + return 0; +} + +// enables or disables the ime +inline void enableIme(QWidget *w, bool value) +{ + if (value) { + // enable ime + if (defaultContext) + ImmAssociateContext(w->effectiveWinId(), defaultContext); +#ifdef Q_OS_WINCE + if (qApp->autoSipEnabled()) + qt_wince_show_SIP(true); +#endif + } else { + // disable ime + HIMC oldimc = ImmAssociateContext(w->effectiveWinId(), 0); + if (!defaultContext) + defaultContext = oldimc; +#ifdef Q_OS_WINCE + if (qApp->autoSipEnabled()) + qt_wince_show_SIP(false); +#endif + } +} + + +void QInputContextPrivate::updateImeStatus(QWidget *w, bool hasFocus) +{ + if (!w) + return; + bool e = w->testAttribute(Qt::WA_InputMethodEnabled) && w->isEnabled(); + bool hasIme = e && hasFocus; +#ifdef Q_IME_DEBUG + qDebug("%s HasFocus = %d hasIme = %d e = %d ", w->className(), hasFocus, hasIme, e); +#endif + if (hasFocus || e) { + if (isInPopup(w)) + QWinInputContext::enablePopupChild(w, hasIme); + else + QWinInputContext::enable(w, hasIme); + } +} + +void QWinInputContext::enablePopupChild(QWidget *w, bool e) +{ + if (aimm) { + enable(w, e); + return; + } + + if (!w || !isInPopup(w)) + return; +#ifdef Q_IME_DEBUG + qDebug("enablePopupChild: w=%s, enable = %s", w ? w->className() : "(null)" , e ? "true" : "false"); +#endif + QWidget *parent = findParentforPopup(w); + if (parent) { + // update ime status of the normal toplevel parent of the popup + enableIme(parent, e); + } + QWidget *toplevel = w->window(); + if (toplevel) { + // update ime status of the toplevel popup + enableIme(toplevel, e); + } +} + +void QWinInputContext::enable(QWidget *w, bool e) +{ + if(w) { +#ifdef Q_IME_DEBUG + qDebug("enable: w=%s, enable = %s", w ? w->className() : "(null)" , e ? "true" : "false"); +#endif + if (!w->testAttribute(Qt::WA_WState_Created)) + return; + if(aimm) { + HIMC oldimc; + if (!e) { + aimm->AssociateContext(w->effectiveWinId(), 0, &oldimc); + if (!defaultContext) + defaultContext = oldimc; + } else if (defaultContext) { + aimm->AssociateContext(w->effectiveWinId(), defaultContext, &oldimc); + } + } else { + // update ime status on the widget + QWidget *p = QWidget::find(w->effectiveWinId()); + if (p) + enableIme(p, e); + } + } +} + +void QWinInputContext::setFocusWidget(QWidget *w) +{ + QInputContext::setFocusWidget(w); + update(); +} + +bool QWinInputContext::isComposing() const +{ + return imeComposition && !imeComposition->isEmpty(); +} + +void QWinInputContext::mouseHandler(int pos, QMouseEvent *e) +{ + if(e->type() != QEvent::MouseButtonPress) + return; + + if (pos < 0 || pos > imeComposition->length()) + reset(); + + // Probably should pass the correct button, but it seems to work fine like this. + DWORD button = MK_LBUTTON; + + QWidget *fw = focusWidget(); + if (fw) { + Q_ASSERT(fw->testAttribute(Qt::WA_WState_Created)); + HIMC himc = getContext(fw->effectiveWinId()); + HWND ime_wnd = getDefaultIMEWnd(fw->effectiveWinId()); + SendMessage(ime_wnd, WM_MSIME_MOUSE, MAKELONG(MAKEWORD(button, pos == 0 ? 2 : 1), pos), (LPARAM)himc); + releaseContext(fw->effectiveWinId(), himc); + } + //qDebug("mouseHandler: got value %d pos=%d", ret,pos); +} + +QString QWinInputContext::language() +{ + return QString(); +} + +QT_END_NAMESPACE |