x
Yes
No
Do you want to visit DriveHQ English website?
首页
产品服务
价格
免费试用
下载客户端
关于我们
云文件服务
|
云备份服务
|
FTP服务
|
企业邮箱服务
|
网站托管
|
客户端软件
云文件服务
云备份服务
FTP服务
企业级邮箱服务
网站托管
客户端软件
ImeUi.cpp - Hosted on DriveHQ Cloud IT Platform
返回上层目录
上传
下载
共享
发布
新建文件夹
新建文件
复制
剪切
删除
粘贴
评论
升级服务
路径: \\game3dprogramming\materials\DarkPuzzle\DXUT\Optional\ImeUi.cpp
旋转
特效
属性
历史版本
//-------------------------------------------------------------------------------------- // File: ImeUi.cpp // // Copyright (c) Microsoft Corporation. All rights reserved. //-------------------------------------------------------------------------------------- #include "dxut.h" #include "ImeUi.h" #include
#include
#include
#include
// Ignore typecast warnings #pragma warning( disable : 4312 ) #pragma warning( disable : 4244 ) #pragma warning( disable : 4311 ) #define MAX_CANDIDATE_LENGTH 256 #define COUNTOF(a) ( sizeof( a ) / sizeof( ( a )[0] ) ) #define POSITION_UNINITIALIZED ((DWORD)-1) #define LANG_CHT MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL) #define LANG_CHS MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED) #define MAKEIMEVERSION(major,minor) ( (DWORD)( ( (BYTE)( major ) << 24 ) | ( (BYTE)( minor ) << 16 ) ) ) #define IMEID_VER(dwId) ( ( dwId ) & 0xffff0000 ) #define IMEID_LANG(dwId) ( ( dwId ) & 0x0000ffff ) #define _CHT_HKL_DAYI ( (HKL)0xE0060404 ) // DaYi #define _CHT_HKL_NEW_PHONETIC ( (HKL)0xE0080404 ) // New Phonetic #define _CHT_HKL_NEW_CHANG_JIE ( (HKL)0xE0090404 ) // New Chang Jie #define _CHT_HKL_NEW_QUICK ( (HKL)0xE00A0404 ) // New Quick #define _CHT_HKL_HK_CANTONESE ( (HKL)0xE00B0404 ) // Hong Kong Cantonese #define _CHT_IMEFILENAME "TINTLGNT.IME" // New Phonetic #define _CHT_IMEFILENAME2 "CINTLGNT.IME" // New Chang Jie #define _CHT_IMEFILENAME3 "MSTCIPHA.IME" // Phonetic 5.1 #define IMEID_CHT_VER42 ( LANG_CHT | MAKEIMEVERSION( 4, 2 ) ) // New(Phonetic/ChanJie)IME98 : 4.2.x.x // Win98 #define IMEID_CHT_VER43 ( LANG_CHT | MAKEIMEVERSION( 4, 3 ) ) // New(Phonetic/ChanJie)IME98a : 4.3.x.x // Win2k #define IMEID_CHT_VER44 ( LANG_CHT | MAKEIMEVERSION( 4, 4 ) ) // New ChanJie IME98b : 4.4.x.x // WinXP #define IMEID_CHT_VER50 ( LANG_CHT | MAKEIMEVERSION( 5, 0 ) ) // New(Phonetic/ChanJie)IME5.0 : 5.0.x.x // WinME #define IMEID_CHT_VER51 ( LANG_CHT | MAKEIMEVERSION( 5, 1 ) ) // New(Phonetic/ChanJie)IME5.1 : 5.1.x.x // IME2002(w/OfficeXP) #define IMEID_CHT_VER52 ( LANG_CHT | MAKEIMEVERSION( 5, 2 ) ) // New(Phonetic/ChanJie)IME5.2 : 5.2.x.x // IME2002a(w/WinXP) #define IMEID_CHT_VER60 ( LANG_CHT | MAKEIMEVERSION( 6, 0 ) ) // New(Phonetic/ChanJie)IME6.0 : 6.0.x.x // New IME 6.0(web download) #define IMEID_CHT_VER_VISTA ( LANG_CHT | MAKEIMEVERSION( 7, 0 ) ) // All TSF TIP under Cicero UI-less mode: a hack to make GetImeId() return non-zero value #define _CHS_HKL ( (HKL)0xE00E0804 ) // MSPY #define _CHS_IMEFILENAME "PINTLGNT.IME" // MSPY1.5/2/3 #define _CHS_IMEFILENAME2 "MSSCIPYA.IME" // MSPY3 for OfficeXP #define IMEID_CHS_VER41 ( LANG_CHS | MAKEIMEVERSION( 4, 1 ) ) // MSPY1.5 // SCIME97 or MSPY1.5 (w/Win98, Office97) #define IMEID_CHS_VER42 ( LANG_CHS | MAKEIMEVERSION( 4, 2 ) ) // MSPY2 // Win2k/WinME #define IMEID_CHS_VER53 ( LANG_CHS | MAKEIMEVERSION( 5, 3 ) ) // MSPY3 // WinXP static CHAR signature[] = "%%%IMEUILIB:070111%%%"; static IMEUI_APPEARANCE gSkinIME = { 0, // symbolColor; 0x404040, // symbolColorOff; 0xff000000, // symbolColorText; 24, // symbolHeight; 0xa0, // symbolTranslucence; 0, // symbolPlacement; NULL, // symbolFont; 0xffffffff, // candColorBase; 0xff000000, // candColorBorder; 0, // candColorText; 0x00ffff00, // compColorInput; 0x000000ff, // compColorTargetConv; 0x0000ff00, // compColorConverted; 0x00ff0000, // compColorTargetNotConv; 0x00ff0000, // compColorInputErr; 0x80, // compTranslucence; 0, // compColorText; 2, // caretWidth; 1, // caretYMargin; }; struct _SkinCompStr { DWORD colorInput; DWORD colorTargetConv; DWORD colorConverted; DWORD colorTargetNotConv; DWORD colorInputErr; }; _SkinCompStr gSkinCompStr; // Definition from Win98DDK version of IMM.H typedef struct tagINPUTCONTEXT2 { HWND hWnd; BOOL fOpen; POINT ptStatusWndPos; POINT ptSoftKbdPos; DWORD fdwConversion; DWORD fdwSentence; union { LOGFONTA A; LOGFONTW W; } lfFont; COMPOSITIONFORM cfCompForm; CANDIDATEFORM cfCandForm[4]; HIMCC hCompStr; HIMCC hCandInfo; HIMCC hGuideLine; HIMCC hPrivate; DWORD dwNumMsgBuf; HIMCC hMsgBuf; DWORD fdwInit; DWORD dwReserve[3]; } INPUTCONTEXT2, *PINPUTCONTEXT2, NEAR *NPINPUTCONTEXT2, FAR *LPINPUTCONTEXT2; // Class to disable Cicero in case ImmDisableTextFrameService() doesn't disable it completely class CDisableCicero { public: CDisableCicero() : m_ptim( NULL ), m_bComInit( false ) { } ~CDisableCicero() { Uninitialize(); } void Initialize() { if ( m_bComInit ) { return; } HRESULT hr; hr = CoInitializeEx( NULL, COINIT_APARTMENTTHREADED ); if ( SUCCEEDED( hr ) ) { m_bComInit = true; hr = CoCreateInstance( CLSID_TF_ThreadMgr, NULL, CLSCTX_INPROC_SERVER, __uuidof(ITfThreadMgr), (void**)&m_ptim ); } } void Uninitialize() { if ( m_ptim ) { m_ptim->Release(); m_ptim = NULL; } if ( m_bComInit ) CoUninitialize(); m_bComInit = false; } void DisableCiceroOnThisWnd( HWND hwnd ) { if ( m_ptim == NULL ) return; ITfDocumentMgr* pdimPrev; // the dim that is associated previously. // Associate NULL dim to the window. // When this window gets the focus, Cicero does not work and IMM32 IME // will be activated. if ( SUCCEEDED( m_ptim->AssociateFocus( hwnd, NULL, &pdimPrev ) ) ) { if ( pdimPrev ) pdimPrev->Release(); } } private: ITfThreadMgr* m_ptim; bool m_bComInit; }; static CDisableCicero g_disableCicero; #define _IsLeadByte(x) ( LeadByteTable[(BYTE)( x )] ) static void _PumpMessage(); static BYTE LeadByteTable[256]; #define _ImmGetContext ImmGetContext #define _ImmReleaseContext ImmReleaseContext #define _ImmAssociateContext ImmAssociateContext static LONG (WINAPI* _ImmGetCompositionString)( HIMC himc, DWORD dwIndex, LPVOID lpBuf, DWORD dwBufLen ); #define _ImmGetOpenStatus ImmGetOpenStatus #define _ImmSetOpenStatus ImmSetOpenStatus #define _ImmGetConversionStatus ImmGetConversionStatus static DWORD (WINAPI* _ImmGetCandidateList)( HIMC himc, DWORD deIndex, LPCANDIDATELIST lpCandList, DWORD dwBufLen ); static LPINPUTCONTEXT2 (WINAPI* _ImmLockIMC)(HIMC hIMC); static BOOL (WINAPI* _ImmUnlockIMC)(HIMC hIMC); static LPVOID (WINAPI* _ImmLockIMCC)(HIMCC hIMCC); static BOOL (WINAPI* _ImmUnlockIMCC)(HIMCC hIMCC); #define _ImmGetDefaultIMEWnd ImmGetDefaultIMEWnd #define _ImmGetIMEFileNameA ImmGetIMEFileNameA #define _ImmGetVirtualKey ImmGetVirtualKey #define _ImmNotifyIME ImmNotifyIME #define _ImmSetConversionStatus ImmSetConversionStatus #define _ImmSimulateHotKey ImmSimulateHotKey #define _ImmIsIME ImmIsIME // private API provided by CHT IME. Available on version 6.0 or later. UINT (WINAPI*_GetReadingString)(HIMC himc, UINT uReadingBufLen, LPWSTR lpwReadingBuf, PINT pnErrorIndex, BOOL* pfIsVertical, PUINT puMaxReadingLen); BOOL (WINAPI*_ShowReadingWindow)(HIMC himc, BOOL bShow); // Callbacks void (CALLBACK *ImeUiCallback_DrawRect)( int x1, int y1, int x2, int y2, DWORD color ); void (CALLBACK *ImeUiCallback_DrawFans)(const IMEUI_VERTEX* paVertex, UINT uNum); void* (__cdecl *ImeUiCallback_Malloc)( size_t bytes ); void (__cdecl *ImeUiCallback_Free)( void* ptr ); void (CALLBACK *ImeUiCallback_OnChar)( WCHAR wc ); static void (*_SendCompString)(); static LRESULT (WINAPI* _SendMessage)(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) = SendMessageA; static DWORD (* _GetCandidateList)(HIMC himc, DWORD dwIndex, LPCANDIDATELIST* ppCandList); static HWND g_hwndMain; static HWND g_hwndCurr; static HIMC g_himcOrg; static bool g_bImeEnabled = false; static TCHAR g_szCompositionString[256]; static BYTE g_szCompAttrString[256]; static DWORD g_IMECursorBytes = 0; static DWORD g_IMECursorChars = 0; static TCHAR g_szCandidate[MAX_CANDLIST][MAX_CANDIDATE_LENGTH]; static DWORD g_dwSelection, g_dwCount; static UINT g_uCandPageSize; static DWORD g_bDisableImeCompletely = false; static DWORD g_dwIMELevel; static DWORD g_dwIMELevelSaved; static TCHAR g_szMultiLineCompString[ 256 * ( 3 - sizeof(TCHAR) ) ]; static bool g_bReadingWindow = false; static bool g_bHorizontalReading = false; static bool g_bVerticalCand = true; static UINT g_uCaretBlinkTime = 0; static UINT g_uCaretBlinkLast = 0; static bool g_bCaretDraw = false; static bool g_bChineseIME; static bool g_bInsertMode = true; static TCHAR g_szReadingString[32]; // Used only in case of horizontal reading window static int g_iReadingError; // Used only in case of horizontal reading window static UINT g_screenWidth, g_screenHeight; static DWORD g_dwPrevFloat; static bool bIsSendingKeyMessage = false; static OSVERSIONINFOA g_osi; static bool g_bInitialized = false; static bool g_bCandList = false; static DWORD g_dwCandX, g_dwCandY; static DWORD g_dwCaretX, g_dwCaretY; static DWORD g_hCompChar; static int g_iCandListIndexBase; static DWORD g_dwImeUiFlags = IMEUI_FLAG_SUPPORT_CARET; static bool g_bUILessMode = false; static HMODULE g_hImmDll = NULL; #define IsNT() (g_osi.dwPlatformId == VER_PLATFORM_WIN32_NT) struct CompStringAttribute { UINT caretX; UINT caretY; CImeUiFont_Base* pFont; DWORD colorComp; DWORD colorCand; RECT margins; }; static CompStringAttribute g_CaretInfo; static DWORD g_dwState = IMEUI_STATE_OFF; static DWORD swirl = 0; static double lastSwirl; #define INDICATOR_NON_IME 0 #define INDICATOR_CHS 1 #define INDICATOR_CHT 2 #define INDICATOR_KOREAN 3 #define INDICATOR_JAPANESE 4 #define GETLANG() LOWORD(g_hklCurrent) #define GETPRIMLANG() ((WORD)PRIMARYLANGID(GETLANG())) #define GETSUBLANG() SUBLANGID(GETLANG()) #define LANG_CHS MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED) #define LANG_CHT MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL) static HKL g_hklCurrent = 0; static UINT g_uCodePage = 0; static LPTSTR g_aszIndicator[] = { TEXT("A"), #ifdef UNICODE L"\x7B80", L"\x7E41", L"\xac00", L"\x3042", #else "\xd6\xd0", "\xa4\xa4", "\xb0\xa1", "\x82\xa0", #endif }; static LPTSTR g_pszIndicatior = g_aszIndicator[0]; static void GetReadingString(HWND hWnd); static DWORD GetImeId( UINT uIndex = 0 ); static void CheckToggleState(); static void DrawImeIndicator(); static void DrawCandidateList(); static void DrawCompositionString( bool bDrawCompAttr ); static void GetReadingWindowOrientation( DWORD dwId ); static void OnInputLangChangeWorker(); static void OnInputLangChange(); static void SetImeApi(); static void CheckInputLocale(); static void SetSupportLevel( DWORD dwImeLevel ); void ImeUi_SetSupportLevel(DWORD dwImeLevel); // // local helper functions // inline LRESULT SendKeyMsg(HWND hwnd, UINT msg, WPARAM wp) { bIsSendingKeyMessage = true; LRESULT lRc = _SendMessage(hwnd, msg, wp, 1); bIsSendingKeyMessage = false; return lRc; } #define SendKeyMsg_DOWN(hwnd,vk) SendKeyMsg(hwnd, WM_KEYDOWN, vk) #define SendKeyMsg_UP(hwnd,vk) SendKeyMsg(hwnd, WM_KEYUP, vk) /////////////////////////////////////////////////////////////////////////////// // // CTsfUiLessMode // Handles IME events using Text Service Framework (TSF). Before Vista, // IMM (Input Method Manager) API has been used to handle IME events and // inqueries. Some IMM functions lose backward compatibility due to design // of TSF, so we have to use new TSF interfaces. // /////////////////////////////////////////////////////////////////////////////// class CTsfUiLessMode { protected: // Sink receives event notifications class CUIElementSink : public ITfUIElementSink, public ITfInputProcessorProfileActivationSink, public ITfCompartmentEventSink { public: CUIElementSink(); ~CUIElementSink(); // IUnknown STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj); STDMETHODIMP_(ULONG) AddRef(void); STDMETHODIMP_(ULONG) Release(void); // ITfUIElementSink // Notifications for Reading Window events. We could process candidate as well, but we'll use IMM for simplicity sake. STDMETHODIMP BeginUIElement(DWORD dwUIElementId, BOOL *pbShow); STDMETHODIMP UpdateUIElement(DWORD dwUIElementId); STDMETHODIMP EndUIElement(DWORD dwUIElementId); // ITfInputProcessorProfileActivationSink // Notification for keyboard input locale change STDMETHODIMP OnActivated(DWORD dwProfileType, LANGID langid, REFCLSID clsid, REFGUID catid, REFGUID guidProfile, HKL hkl, DWORD dwFlags); // ITfCompartmentEventSink // Notification for open mode (toggle state) change STDMETHODIMP OnChange(REFGUID rguid); private: LONG _cRef; }; static void MakeReadingInformationString(ITfReadingInformationUIElement* preading); static void MakeCandidateStrings(ITfCandidateListUIElement* pcandidate); static ITfUIElement* GetUIElement(DWORD dwUIElementId); static BOOL GetCompartments( ITfCompartmentMgr** ppcm, ITfCompartment** ppTfOpenMode, ITfCompartment** ppTfConvMode ); static BOOL SetupCompartmentSinks( BOOL bResetOnly = FALSE, ITfCompartment* pTfOpenMode = NULL, ITfCompartment* ppTfConvMode = NULL ); static ITfThreadMgrEx* m_tm; static DWORD m_dwUIElementSinkCookie; static DWORD m_dwAlpnSinkCookie; static DWORD m_dwOpenModeSinkCookie; static DWORD m_dwConvModeSinkCookie; static CUIElementSink *m_TsfSink; static int m_nCandidateRefCount; // Some IME shows multiple candidate lists but the Library doesn't support multiple candidate list. // So track open / close events to make sure the candidate list opened last is shown. CTsfUiLessMode() {} // this class can't be instanciated public: static BOOL SetupSinks(); static void ReleaseSinks(); static BOOL CurrentInputLocaleIsIme(); static void UpdateImeState(BOOL bResetCompartmentEventSink = FALSE); static void EnableUiUpdates(bool bEnable); }; ITfThreadMgrEx* CTsfUiLessMode::m_tm; DWORD CTsfUiLessMode::m_dwUIElementSinkCookie = TF_INVALID_COOKIE; DWORD CTsfUiLessMode::m_dwAlpnSinkCookie = TF_INVALID_COOKIE; DWORD CTsfUiLessMode::m_dwOpenModeSinkCookie = TF_INVALID_COOKIE; DWORD CTsfUiLessMode::m_dwConvModeSinkCookie = TF_INVALID_COOKIE; CTsfUiLessMode::CUIElementSink* CTsfUiLessMode::m_TsfSink = NULL; int CTsfUiLessMode::m_nCandidateRefCount = NULL; static unsigned long _strtoul( LPCSTR psz, LPTSTR*, int ) { if ( !psz ) return 0; ULONG ulRet = 0; if ( psz[0] == '0' && ( psz[1] == 'x' || psz[1] == 'X' ) ) { psz += 2; ULONG ul = 0; while ( *psz ) { if ( '0' <= *psz && *psz <= '9' ) ul = *psz - '0'; else if ( 'A' <= *psz && *psz <= 'F' ) ul = *psz - 'A' + 10; else if ( 'a' <= *psz && *psz <= 'f' ) ul = *psz - 'a' + 10; else break; ulRet = ulRet * 16 + ul; psz++; } } else { while ( *psz && ( '0' <= *psz && *psz <= '9' ) ) { ulRet = ulRet * 10 + ( *psz - '0' ); psz++; } } return ulRet; } #ifdef UNICODE #define GetCharCount(psz) lstrlen(psz) #define GetCharCountFromBytes(psz,iBytes) (iBytes) static void AW_SendCompString() { int i, iLen; if ( ImeUiCallback_OnChar ) { for ( i = 0; g_szCompositionString[i]; i++ ) { ImeUiCallback_OnChar( g_szCompositionString[i] ); } return; } BYTE szCompStr[COUNTOF(g_szCompositionString) * 2]; iLen = WideCharToMultiByte(g_uCodePage, 0, g_szCompositionString, -1, (LPSTR)szCompStr, COUNTOF(szCompStr), NULL, NULL) - 1; // don't need to send NUL terminator; for (i = 0; i < iLen; i++) { SendKeyMsg(g_hwndCurr, WM_CHAR, szCompStr[i]); } } // The following AW_Imm* functions are there to support Win95/98 first version. // They can be deleted if the game doesn't supports them (i.e. games requires Win98 SE or later). static DWORD AW_GetCandidateList(HIMC himc, DWORD dwIndex, LPCANDIDATELIST* ppCandList) { DWORD dwBufLen = ImmGetCandidateListA( himc, dwIndex, NULL, 0 ); if (dwBufLen) { LPCANDIDATELIST pCandList = (LPCANDIDATELIST)ImeUiCallback_Malloc(dwBufLen); if (pCandList) { dwBufLen = ImmGetCandidateListA( himc, dwIndex, pCandList, dwBufLen ); if (dwBufLen) { int i; int wideBufLen = 0; for (i = 0; i < (int)pCandList->dwCount; i++) { wideBufLen += MultiByteToWideChar(g_uCodePage, 0, (LPSTR)pCandList + pCandList->dwOffset[i], -1, NULL, 0) * sizeof(WCHAR); } wideBufLen += pCandList->dwOffset[0]; *ppCandList = (LPCANDIDATELIST)ImeUiCallback_Malloc(wideBufLen); LPCANDIDATELIST pCandListW = *ppCandList; memcpy(pCandListW, pCandList, pCandList->dwOffset[0]); LPWSTR pwz = (LPWSTR)((LPSTR)pCandListW + pCandList->dwOffset[0]); for (i = 0; i < (int)pCandList->dwCount; i++) { pCandListW->dwOffset[i] = (LPSTR)pwz - (LPSTR)pCandListW; pwz += MultiByteToWideChar(g_uCodePage, 0, (LPSTR)pCandList + pCandList->dwOffset[i], -1, pwz, 256); } dwBufLen = wideBufLen; } ImeUiCallback_Free(pCandList); } } return dwBufLen; } static LONG WINAPI AW_ImmGetCompositionString(HIMC himc, DWORD dwIndex, LPVOID lpBuf, DWORD dwBufLen) { char pszMb[COUNTOF(g_szCompositionString) * 2]; DWORD dwRet = ImmGetCompositionStringA(himc, dwIndex, pszMb, sizeof(pszMb)); switch (dwIndex) { case GCS_RESULTSTR: case GCS_COMPSTR: if (dwRet) { pszMb[dwRet] = 0; dwRet = (DWORD)MultiByteToWideChar(g_uCodePage, 0, pszMb, -1, (LPWSTR)lpBuf, dwBufLen); if (dwRet) { // Note that ImmGetCompositionString() returns number of bytes copied, regardless of the width of character. dwRet = (dwRet - 1) * sizeof(WCHAR); } } break; case GCS_CURSORPOS: dwRet /= 2; break; case GCS_COMPATTR: { char pszMb2[COUNTOF(g_szCompositionString) * 2]; DWORD dwRet2 = ImmGetCompositionStringA(himc, GCS_COMPSTR, pszMb2, sizeof(pszMb2)); if (!dwRet2) { dwRet2 = ImmGetCompositionStringA(himc, GCS_RESULTSTR, pszMb2, sizeof(pszMb2)); if (!dwRet2) { return 0; } } char* pOut = (char*)lpBuf; for (DWORD i = 0; i < dwRet; i++) { *pOut++ = pszMb[i]; // copy attribute if (_IsLeadByte(pszMb2[i])) i++; } dwRet = pOut - (char*)lpBuf; } break; } return dwRet; } #else // !UNICODE // returns number of characters from number of bytes static int GetCharCountFromBytes(LPCSTR pszString, int iBytes) { int iCount = 0; int i; for (i = 0; pszString[i] && i < iBytes; i++) { iCount++; if (_IsLeadByte(pszString[i])) i++; } if (i != iBytes) iCount = -iCount; // indicate error - iBytes specifies wrong boundary (i.e. the last byte is leadbyte) return iCount; } static int GetCharCount(LPTSTR psz) { int i = 0; while (*psz) { if (_IsLeadByte(*psz)) { psz++; } psz++; i++; } return i; } static DWORD WA_GetCandidateList(HIMC himc, DWORD dwIndex, LPCANDIDATELIST* ppCandList) { DWORD dwBufLen = ImmGetCandidateListW( himc, dwIndex, NULL, 0 ); if (dwBufLen) { LPCANDIDATELIST pCandList = (LPCANDIDATELIST)ImeUiCallback_Malloc(dwBufLen); if (pCandList) { dwBufLen = ImmGetCandidateListW( himc, dwIndex, pCandList, dwBufLen ); if (dwBufLen) { int i; int mbBufLen = 0; for ( i = 0; i < (int)pCandList->dwCount; i++ ) { mbBufLen += WideCharToMultiByte( g_uCodePage, 0, (LPWSTR)((LPSTR)pCandList + pCandList->dwOffset[i]), -1, NULL, 0, NULL, NULL ); } mbBufLen += pCandList->dwOffset[0]; *ppCandList = (LPCANDIDATELIST)ImeUiCallback_Malloc(mbBufLen); LPCANDIDATELIST pCandListA = *ppCandList; memcpy(pCandListA, pCandList, pCandList->dwOffset[0]); LPSTR psz = (LPSTR)pCandListA + pCandList->dwOffset[0]; for (i = 0; i < (int)pCandList->dwCount; i++) { pCandListA->dwOffset[i] = (LPSTR)psz - (LPSTR)pCandListA; psz += WideCharToMultiByte( g_uCodePage, 0, (LPWSTR)((LPSTR)pCandList + pCandList->dwOffset[i]), -1, psz, 256, NULL, NULL ); } dwBufLen = mbBufLen; } ImeUiCallback_Free(pCandList); } } return dwBufLen; } static LONG WINAPI WA_ImmGetCompositionString(HIMC himc, DWORD dwIndex, LPVOID lpBuf, DWORD dwBufLen) { WCHAR pwzUc[COUNTOF(g_szCompositionString)]; DWORD dwRet = ImmGetCompositionStringW(himc, dwIndex, pwzUc, sizeof(pwzUc)); switch (dwIndex) { case GCS_RESULTSTR: case GCS_COMPSTR: if (dwRet) { pwzUc[dwRet / sizeof(WCHAR)] = 0; dwRet = (DWORD)WideCharToMultiByte( g_uCodePage, 0, pwzUc, -1, (LPSTR)lpBuf, dwBufLen, NULL, NULL ); if (dwRet) { dwRet = dwRet - 1; } } break; case GCS_CURSORPOS: { WCHAR pwzUc2[COUNTOF(g_szCompositionString)]; DWORD dwRet2 = ImmGetCompositionStringW(himc, GCS_COMPSTR, pwzUc2, sizeof(pwzUc2)); if (!dwRet2) { dwRet2 = ImmGetCompositionStringW(himc, GCS_RESULTSTR, pwzUc2, sizeof(pwzUc2)); if (!dwRet2) { return 0; } } dwRet2 /= 2; //The return value of WideCharToMultiByte() should probably be checked/asserted for success. //bounds violation (overflow) 'pszMb[iRc]' const int bufSize = COUNTOF(g_szCompositionString) * 2; char pszMb[bufSize]; int iRc = WideCharToMultiByte( g_uCodePage, 0, pwzUc2, dwRet2, pszMb, sizeof( pszMb ), NULL, NULL ); assert( iRc > 0 ); //WideCharToMultiByte returns 0 if it failed, and it should *never* be negative in the first place if (iRc >= bufSize) //if we wrote more bytes than the length of the buffer, we need to terminate it { pszMb[ bufSize - 1] = 0; //0 terminate the end of the buffer } else { pszMb[ iRc ] = 0; } char* psz = pszMb; for ( dwRet2 = 0; dwRet2 != dwRet; dwRet2++ ) { if ( _IsLeadByte( *psz ) ) psz++; psz++; } dwRet = psz - pszMb; } break; case GCS_COMPATTR: { WCHAR pwzUc2[COUNTOF(g_szCompositionString)]; DWORD dwRet2 = ImmGetCompositionStringW(himc, GCS_COMPSTR, pwzUc2, sizeof(pwzUc2)); if (!dwRet2) { dwRet2 = ImmGetCompositionStringW(himc, GCS_RESULTSTR, pwzUc2, sizeof(pwzUc2)); if (!dwRet2) { return 0; } } dwRet2 /= 2; const int bufSize = COUNTOF(g_szCompositionString) * 2; char pszMb[bufSize]; int iRc = WideCharToMultiByte( g_uCodePage, 0, pwzUc2, dwRet2, pszMb, sizeof( pszMb ), NULL, NULL ); assert( iRc > 0 ); //WideCharToMultiByte returns 0 if it failed, and it should *never* be negative in the first place if (iRc >= bufSize) //if we wrote more bytes than the length of the buffer, we need to terminate it { pszMb[ bufSize - 1] = 0; //0 terminate the end of the buffer } else { pszMb[ iRc ] = 0; } char* pSrc = (char*)pwzUc; char* pOut = (char*)lpBuf; for (char* psz = pszMb; *psz; psz++, pSrc++) { *pOut++ = *pSrc; // copy attribute if ( _IsLeadByte( *psz ) ) { *pOut++ = *pSrc; psz++; } // buffer overrun protection, pOut is incremented in the loop, but not part of the // loop invariant test. To make the code more readable we have a test rather than // rolling this into the for stmt. if ((DWORD)(pOut-(char*)lpBuf) >=dwBufLen) break; } dwRet = pOut - (char*)lpBuf; } break; } return dwRet; } #endif // UNICODE static void ComposeCandidateLine( int index, LPCTSTR pszCandidate ) { LPTSTR psz = g_szCandidate[index]; *psz++ = (TCHAR)( TEXT( '0' ) + ( ( index + g_iCandListIndexBase ) % 10 ) ); if ( g_bVerticalCand ) { *psz++ = TEXT( ' ' ); } while ( *pszCandidate && (COUNTOF(g_szCandidate[index]) > (psz-g_szCandidate[index])) ) { *psz++ = *pszCandidate++; } *psz = 0; } static void SendCompString() { int i, iLen = lstrlen(g_szCompositionString); if ( ImeUiCallback_OnChar ) { LPCWSTR pwz; #ifdef UNICODE pwz = g_szCompositionString; #else WCHAR szUnicode[ COUNTOF( g_szCompositionString ) ]; pwz = szUnicode; iLen = MultiByteToWideChar( g_uCodePage, 0, g_szCompositionString, -1, szUnicode, COUNTOF(szUnicode) ) - 1; #endif for ( i = 0; i < iLen; i++ ) { ImeUiCallback_OnChar( pwz[i] ); } return; } for ( i = 0; i < iLen; i++ ) { SendKeyMsg(g_hwndCurr, WM_CHAR, #ifdef UNICODE (WPARAM)g_szCompositionString[i] #else (WPARAM)(BYTE)g_szCompositionString[i] #endif ); } } static DWORD GetCandidateList(HIMC himc, DWORD dwIndex, LPCANDIDATELIST* ppCandList) { DWORD dwBufLen = _ImmGetCandidateList( himc, dwIndex, NULL, 0 ); if ( dwBufLen ) { *ppCandList = (LPCANDIDATELIST)ImeUiCallback_Malloc(dwBufLen); dwBufLen = _ImmGetCandidateList( himc, dwIndex, *ppCandList, dwBufLen ); } return dwBufLen; } static void SendControlKeys(UINT vk, UINT num) { if (num == 0) return; for (UINT i = 0; i < num ; i++) { SendKeyMsg_DOWN(g_hwndCurr, vk); } SendKeyMsg_UP(g_hwndCurr, vk); } // send key messages to erase composition string. static void CancelCompString(HWND hwnd, bool bUseBackSpace = true, int iNewStrLen = 0) { if (g_dwIMELevel != 3) return; int cc = GetCharCount(g_szCompositionString); int i; // move caret to the end of composition string SendControlKeys(VK_RIGHT, cc - g_IMECursorChars); if (bUseBackSpace || g_bInsertMode) iNewStrLen = 0; // The caller sets bUseBackSpace to false if there's possibility of sending // new composition string to the app right after this function call. // // If the app is in overwriting mode and new comp string is // shorter than current one, delete previous comp string // till it's same long as the new one. Then move caret to the beginning of comp string. // New comp string will overwrite old one. if (iNewStrLen < cc) { for (i = 0; i < cc - iNewStrLen; i++) { SendKeyMsg_DOWN(hwnd, VK_BACK); SendKeyMsg(hwnd, WM_CHAR, 8); //Backspace character } SendKeyMsg_UP(hwnd, VK_BACK); } else iNewStrLen = cc; SendControlKeys(VK_LEFT, iNewStrLen); } // initialize composition string data. static void InitCompStringData(void) { g_IMECursorBytes = 0; g_IMECursorChars = 0; memset(&g_szCompositionString, 0, sizeof(g_szCompositionString)); memset(&g_szCompAttrString, 0, sizeof(g_szCompAttrString)); } static void DrawCaret(DWORD x, DWORD y, DWORD height) { if (g_bCaretDraw && ImeUiCallback_DrawRect) ImeUiCallback_DrawRect(x, y + gSkinIME.caretYMargin, x + gSkinIME.caretWidth, y + height - gSkinIME.caretYMargin, g_CaretInfo.colorComp); } // // Apps that draw the composition string on top of composition string attribute // in level 3 support should call this function twice in rendering a frame. // // Draw edit box UI; // ImeUi_RenderUI(true, false); // paint composition string attribute; // // Draw text in the edit box; // ImeUi_RenderUi(false, true); // paint the rest of IME UI; // void ImeUi_RenderUI(bool bDrawCompAttr, bool bDrawOtherUi) { if (!g_bInitialized || !g_bImeEnabled || !g_CaretInfo.pFont) return; if (!bDrawCompAttr && !bDrawOtherUi) return; // error case if (g_dwIMELevel == 2) { if (!bDrawOtherUi) return; // 1st call for level 3 support } if (bDrawOtherUi) DrawImeIndicator(); DrawCompositionString( bDrawCompAttr ); if ( bDrawOtherUi ) DrawCandidateList(); } static void DrawImeIndicator() { bool bOn = g_dwState != IMEUI_STATE_OFF; IMEUI_VERTEX PieData[17]; float SizeOfPie = (float) gSkinIME.symbolHeight; memset( PieData, 0, sizeof( PieData ) ); switch(gSkinIME.symbolPlacement) { case 0: // vertical centering IME indicator { if (SizeOfPie + g_CaretInfo.margins.right+4 > g_screenWidth) { PieData[0].sx=(-SizeOfPie/2) + g_CaretInfo.margins.left-4; PieData[0].sy= (float) g_CaretInfo.margins.top + (g_CaretInfo.margins.bottom - g_CaretInfo.margins.top)/2; } else { PieData[0].sx=-(SizeOfPie/2) + g_CaretInfo.margins.right+gSkinIME.symbolHeight+4; PieData[0].sy= (float) g_CaretInfo.margins.top + (g_CaretInfo.margins.bottom - g_CaretInfo.margins.top)/2; } break; } case 1: // upperleft PieData[0].sx = 4+ (SizeOfPie/2); PieData[0].sy = 4+ (SizeOfPie/2); break; case 2: // upperright PieData[0].sx = g_screenWidth - (4+ (SizeOfPie/2)); PieData[0].sy = 4+ (SizeOfPie/2); break; case 3: // lowerright PieData[0].sx = g_screenWidth - (4+ (SizeOfPie/2)); PieData[0].sy = g_screenHeight - (4+ (SizeOfPie/2)); break; case 4: // lowerleft PieData[0].sx = 4+ (SizeOfPie/2); PieData[0].sy = g_screenHeight - (4+ (SizeOfPie/2)); break; } PieData[0].rhw=1.0f; if (bOn) { if ( GetTickCount() - lastSwirl > 250 ) { swirl++; lastSwirl = GetTickCount(); if (swirl > 13) swirl = 0; } } else swirl = 0; for( int t1 = 1; t1 < 16; t1++ ) { float radian = 2.0f * 3.1415926f * ( t1 - 1 + ( bOn * swirl ) ) / 14.0f; PieData[t1].sx = (float)( PieData[0].sx + SizeOfPie / 2 * cos( radian ) ); PieData[t1].sy = (float)( PieData[0].sy + SizeOfPie / 2 * sin( radian ) ); PieData[t1].rhw = 1.0f; } PieData[0].color=0xffffff + (((DWORD)gSkinIME.symbolTranslucence)<<24); if ( !gSkinIME.symbolColor && bOn ) { { PieData[1].color=0xff0000 + (((DWORD)gSkinIME.symbolTranslucence)<<24); PieData[2].color=0xff3000 + (((DWORD)gSkinIME.symbolTranslucence)<<24); PieData[3].color=0xff6000 + (((DWORD)gSkinIME.symbolTranslucence)<<24); PieData[4].color=0xff9000 + (((DWORD)gSkinIME.symbolTranslucence)<<24); PieData[5].color=0xffC000 + (((DWORD)gSkinIME.symbolTranslucence)<<24); PieData[6].color=0xffff00 + (((DWORD)gSkinIME.symbolTranslucence)<<24); PieData[7].color=0xC0ff00 + (((DWORD)gSkinIME.symbolTranslucence)<<24); PieData[8].color=0x90ff00 + (((DWORD)gSkinIME.symbolTranslucence)<<24); PieData[9].color=0x60ff00 + (((DWORD)gSkinIME.symbolTranslucence)<<24); PieData[10].color=0x30c0ff + (((DWORD)gSkinIME.symbolTranslucence)<<24); PieData[11].color=0x00a0ff + (((DWORD)gSkinIME.symbolTranslucence)<<24); PieData[12].color=0x3090ff + (((DWORD)gSkinIME.symbolTranslucence)<<24); PieData[13].color=0x6060ff + (((DWORD)gSkinIME.symbolTranslucence)<<24); PieData[14].color=0x9030ff + (((DWORD)gSkinIME.symbolTranslucence)<<24); PieData[15].color=0xc000ff + (((DWORD)gSkinIME.symbolTranslucence)<<24); } } else { DWORD dwColor = bOn ? gSkinIME.symbolColor : gSkinIME.symbolColorOff; for( int t1=1; t1<16; t1++ ) { PieData[t1].color= dwColor + (((DWORD)gSkinIME.symbolTranslucence)<<24); } } PieData[16] = PieData[1]; if( ImeUiCallback_DrawFans ) ImeUiCallback_DrawFans( PieData, 17 ); float fHeight = gSkinIME.symbolHeight*0.625f; // fix for Ent Gen #120 - reduce the height of character when Korean IME is on if ( GETPRIMLANG() == LANG_KOREAN && bOn ) { fHeight *= 0.8f; } if (gSkinIME.symbolFont) { #ifdef DS2 // save the font height here since DS2 shares editbox font and indicator font DWORD _w, _h; g_CaretInfo.pFont->GetTextExtent( TEXT(" "), &_w, &_h ); #endif //DS2 // GOS deals height in points that is 1/72nd inch and assumes display device is 96dpi. fHeight = fHeight * 96 / 72; gSkinIME.symbolFont->SetHeight((UINT)fHeight); gSkinIME.symbolFont->SetColor((((DWORD)gSkinIME.symbolTranslucence)<<24) | gSkinIME.symbolColorText); // // draw the proper symbol over the fan // DWORD w, h; LPCTSTR cszSymbol = ( g_dwState == IMEUI_STATE_ON ) ? g_pszIndicatior : g_aszIndicator[0]; gSkinIME.symbolFont->GetTextExtent(cszSymbol, &w, &h); gSkinIME.symbolFont->SetPosition((int)(PieData[0].sx)-w/2, (int)(PieData[0].sy)-h/2); gSkinIME.symbolFont->DrawText( cszSymbol ); #ifdef DS2 // revert the height. g_CaretInfo.pFont->SetHeight( _h ); // Double-check: Confirm match by testing a range of font heights to find best fit DWORD _h2; g_CaretInfo.pFont->GetTextExtent( TEXT(" "), &_w, &_h2 ); if ( _h2 < _h ) { for ( int i=1; _h2<_h && i<10; i++ ) { g_CaretInfo.pFont->SetHeight( _h+i ); g_CaretInfo.pFont->GetTextExtent( TEXT(" "), &_w, &_h2 ); } } else if ( _h2 > _h ) { for ( int i=1; _h2>_h && i<10; i++ ) { g_CaretInfo.pFont->SetHeight( _h-i ); g_CaretInfo.pFont->GetTextExtent( TEXT(" "), &_w, &_h2 ); } } #endif //DS2 } } static void DrawCompositionString( bool bDrawCompAttr ) { // Process timer for caret blink UINT uCurrentTime = GetTickCount(); if (uCurrentTime - g_uCaretBlinkLast > g_uCaretBlinkTime) { g_uCaretBlinkLast = uCurrentTime; g_bCaretDraw = !g_bCaretDraw; } int i = 0; g_CaretInfo.pFont->SetColor(g_CaretInfo.colorComp); DWORD uDummy; int len = lstrlen(g_szCompositionString); DWORD bgX = g_CaretInfo.caretX; DWORD bgY = g_CaretInfo.caretY; g_dwCaretX = POSITION_UNINITIALIZED; g_dwCaretY = POSITION_UNINITIALIZED; DWORD candX = POSITION_UNINITIALIZED; DWORD candY = 0; LPTSTR pszMlcs = g_szMultiLineCompString; DWORD wCompChar = 0; DWORD hCompChar = 0; g_CaretInfo.pFont->GetTextExtent( TEXT(" "), &uDummy, &hCompChar ); if (g_dwIMELevel == 3 && g_IMECursorBytes && g_szCompositionString[0]) { // shift starting point of drawing composition string according to the current caret position. TCHAR temp = g_szCompositionString[g_IMECursorBytes]; g_szCompositionString[g_IMECursorBytes] = 0; g_CaretInfo.pFont->GetTextExtent(g_szCompositionString, &wCompChar, &hCompChar); g_szCompositionString[g_IMECursorBytes] = temp; bgX -= wCompChar; } // // Draw the background colors for IME text nuggets // bool saveCandPos = false; DWORD cType = 1; LPTSTR pszCurrentCompLine = g_szCompositionString; DWORD dwCompLineStart = bgX; DWORD bgXnext = bgX; if (GETPRIMLANG() != LANG_KOREAN || g_bCaretDraw) // Korean uses composition attribute as blinking block caret for (i = 0; i < len ; i += cType) { DWORD bgColor = 0x00000000; TCHAR szChar[3]; szChar[0] = g_szCompositionString[i]; szChar[1] = szChar[2] = 0; #ifndef UNICODE cType = 1 + ((_IsLeadByte(g_szCompositionString[i])) ? 1 : 0); if (cType == 2 && g_szCompositionString[i+1]) // in case we have 0 in trailbyte, we don't count it. szChar[1] = g_szCompositionString[i+1]; #endif bgX = bgXnext; TCHAR cSave = g_szCompositionString[i + cType]; g_szCompositionString[i + cType] = 0; g_CaretInfo.pFont->GetTextExtent( pszCurrentCompLine, &bgXnext, &hCompChar ); g_szCompositionString[i + cType] = cSave; bgXnext += dwCompLineStart; wCompChar = bgXnext - bgX; switch(g_szCompAttrString[i]) { case ATTR_INPUT: bgColor = gSkinCompStr.colorInput; break; case ATTR_TARGET_CONVERTED: bgColor = gSkinCompStr.colorTargetConv; if ( IMEID_LANG( GetImeId() ) != LANG_CHS ) saveCandPos = true; break; case ATTR_CONVERTED: bgColor = gSkinCompStr.colorConverted; break; case ATTR_TARGET_NOTCONVERTED: // // This is the one the user is working with currently // bgColor = gSkinCompStr.colorTargetNotConv; break; case ATTR_INPUT_ERROR: bgColor = gSkinCompStr.colorInputErr; break; default: // STOP( TEXT( "Attributes on IME characters are wrong" ) ); break; } if (g_dwIMELevel == 3 && bDrawCompAttr) { if ((LONG)bgX >= g_CaretInfo.margins.left && (LONG)bgX <= g_CaretInfo.margins.right) { if ( g_dwImeUiFlags & IMEUI_FLAG_SUPPORT_CARET ) { if( ImeUiCallback_DrawRect ) ImeUiCallback_DrawRect(bgX, bgY, bgX + wCompChar, bgY + hCompChar, bgColor); } else { if( ImeUiCallback_DrawRect ) ImeUiCallback_DrawRect(bgX - wCompChar, bgY, bgX, bgY + hCompChar, bgColor); } } } else if (g_dwIMELevel == 2) { // make sure enough buffer space (possible space, NUL for current line, possible DBCS, 2 more NUL) // are available in multiline composition string buffer bool bWrite = ( pszMlcs - g_szMultiLineCompString < COUNTOF( g_szMultiLineCompString ) - 5 * ( 3 - sizeof(TCHAR) ) ); if ((LONG)(bgX + wCompChar) >= g_CaretInfo.margins.right) { bgX = dwCompLineStart = bgXnext = g_CaretInfo.margins.left; bgY = bgY + hCompChar; pszCurrentCompLine = g_szCompositionString + i; if (bWrite) { if (pszMlcs == g_szMultiLineCompString || pszMlcs[-1] == 0) *pszMlcs++ = ' '; // to avoid zero length line *pszMlcs++ = 0; } } if( ImeUiCallback_DrawRect ) ImeUiCallback_DrawRect(bgX, bgY, bgX + wCompChar, bgY + hCompChar, bgColor); if (bWrite) { *pszMlcs++ = g_szCompositionString[i]; #ifndef UNICODE if (cType == 2) *pszMlcs++ = g_szCompositionString[i+1]; #endif } if ((DWORD)i == g_IMECursorBytes) { g_dwCaretX = bgX; g_dwCaretY = bgY; } } if ( ( saveCandPos && candX == POSITION_UNINITIALIZED ) || ( IMEID_LANG( GetImeId() ) == LANG_CHS && i / ( 3 - sizeof(TCHAR) ) == (int)g_IMECursorChars ) ) { candX = bgX; candY = bgY; } saveCandPos = false; } bgX = bgXnext; if (g_dwIMELevel == 2) { // in case the caret in composition string is at the end of it, draw it here if (len != 0 && (DWORD)i == g_IMECursorBytes) { g_dwCaretX = bgX; g_dwCaretY = bgY; } // Draw composition string. //assert(pszMlcs - g_szMultiLineCompString <= // sizeof(g_szMultiLineCompString) / sizeof(g_szMultiLineCompString[0]) - 2); *pszMlcs++ = 0; *pszMlcs++ = 0; DWORD x, y; x = g_CaretInfo.caretX; y = g_CaretInfo.caretY; pszMlcs = g_szMultiLineCompString; while (*pszMlcs && pszMlcs - g_szMultiLineCompString < sizeof(g_szMultiLineCompString) / sizeof(g_szMultiLineCompString[0])) { g_CaretInfo.pFont->SetPosition(x, y); g_CaretInfo.pFont->DrawText(pszMlcs); pszMlcs += lstrlen( pszMlcs ) + 1; x = g_CaretInfo.margins.left; y += hCompChar; } } // for changing z-order of caret if ( g_dwCaretX != POSITION_UNINITIALIZED && g_dwCaretY != POSITION_UNINITIALIZED ) { DrawCaret(g_dwCaretX, g_dwCaretY, hCompChar); } g_dwCandX = candX; g_dwCandY = candY; g_hCompChar = hCompChar; } static void DrawCandidateList() { DWORD candX = g_dwCandX; DWORD candY = g_dwCandY; DWORD hCompChar = g_hCompChar; int i; // draw candidate list / reading window if ( !g_dwCount || g_szCandidate[0][0] == 0 ) { return; } // If position of candidate list is not initialized yet, set it here. if (candX == POSITION_UNINITIALIZED) { // CHT IME in Vista doesn't have ATTR_TARGET_CONVERTED attribute while typing, // so display the candidate list near the caret in the composition string if (GETLANG() == LANG_CHT && GetImeId() != 0 && g_dwCaretX != POSITION_UNINITIALIZED) { candX = g_dwCaretX; candY = g_dwCaretY; } else { candX = g_CaretInfo.caretX; candY = g_CaretInfo.caretY; } } SIZE largest = {0,0}; static DWORD uDigitWidth = 0; DWORD uSpaceWidth = 0; static DWORD uDigitWidthList[10]; static CImeUiFont_Base* pPrevFont = NULL; // find out the widest width of the digits if ( pPrevFont != g_CaretInfo.pFont ) { pPrevFont = g_CaretInfo.pFont; for(int cnt=0; cnt<=9; cnt++) { DWORD uDW = 0; DWORD uDH = 0; TCHAR ss[8]; StringCchPrintf(ss, COUNTOF(ss), TEXT("%d"), cnt); g_CaretInfo.pFont->GetTextExtent( ss, &uDW, &uDH ); uDigitWidthList[cnt] = uDW; if ( uDW > uDigitWidth ) uDigitWidth = uDW; if ( (signed)uDH > largest.cy) largest.cy = uDH; } } uSpaceWidth = uDigitWidth; DWORD dwMarginX = (uSpaceWidth + 1) / 2; DWORD adwCandWidth[ MAX_CANDLIST ]; // Find out the widest width of the candidate strings DWORD dwCandWidth = 0; if (g_bReadingWindow && g_bHorizontalReading) g_CaretInfo.pFont->GetTextExtent(g_szReadingString, (DWORD*)&largest.cx, (DWORD*)&largest.cy); else { for (i = 0; g_szCandidate[i][0] && i < (int)g_uCandPageSize; i++) { DWORD tx = 0; DWORD ty = 0; if (g_bReadingWindow) g_CaretInfo.pFont->GetTextExtent(g_szCandidate[i], &tx, &ty); else { if (g_bVerticalCand) g_CaretInfo.pFont->GetTextExtent(g_szCandidate[i]+2, &tx, &ty); else g_CaretInfo.pFont->GetTextExtent(g_szCandidate[i]+1, &tx, &ty); tx = tx + uDigitWidth + uSpaceWidth; } if ((signed)tx > largest.cx) largest.cx = tx; if ((signed)ty > largest.cy) largest.cy = ty; adwCandWidth[ i ] = tx; dwCandWidth += tx; } } DWORD slotsUsed; if (g_bReadingWindow && g_dwCount < g_uCandPageSize) slotsUsed = g_dwCount; else slotsUsed = g_uCandPageSize; // Show candidate list above composition string if there isn't enough room below. DWORD dwCandHeight; if (g_bVerticalCand && !(g_bReadingWindow && g_bHorizontalReading)) dwCandHeight = slotsUsed * largest.cy + 2; else dwCandHeight = largest.cy + 2; if ( candY + hCompChar + dwCandHeight > g_screenHeight) candY -= dwCandHeight; else candY += hCompChar; if ( (int)candY < 0 ) candY = 0; // Move candidate list horizontally to keep it inside of screen if ( !g_bReadingWindow && IMEID_LANG( GetImeId() ) == LANG_CHS ) dwCandWidth += dwMarginX * (slotsUsed-1); else if ( g_bReadingWindow && g_bHorizontalReading ) dwCandWidth = largest.cx + 2 + dwMarginX * 2; else if ( g_bVerticalCand || g_bReadingWindow ) dwCandWidth = largest.cx + 2 + dwMarginX * 2; else dwCandWidth = slotsUsed * (largest.cx + 1) + 1; if (candX + dwCandWidth > g_screenWidth) candX = g_screenWidth - dwCandWidth; if ( (int)candX < 0 ) candX = 0; // Draw frame and background of candidate list / reading window int seperateLineX = 0; int left = candX; int top = candY; int right = candX + dwCandWidth; int bottom = candY + dwCandHeight; if( ImeUiCallback_DrawRect ) ImeUiCallback_DrawRect(left, top, right, bottom, gSkinIME.candColorBorder); left++; top++; right--; bottom--; if ( g_bReadingWindow || IMEID_LANG( GetImeId() ) == LANG_CHS ) { if( ImeUiCallback_DrawRect ) ImeUiCallback_DrawRect(left,top, right, bottom, gSkinIME.candColorBase); } else if ( g_bVerticalCand ) { // uDigitWidth is the max width of all digits. if ( !g_bReadingWindow ) { seperateLineX = left + dwMarginX + uDigitWidth + uSpaceWidth / 2; if( ImeUiCallback_DrawRect ) { ImeUiCallback_DrawRect(left, top, seperateLineX-1, bottom, gSkinIME.candColorBase); ImeUiCallback_DrawRect(seperateLineX, top, right, bottom, gSkinIME.candColorBase); } } } else { for (i = 0; (DWORD)i < slotsUsed; i++) { if( ImeUiCallback_DrawRect ) ImeUiCallback_DrawRect(left, top, left + largest.cx, bottom, gSkinIME.candColorBase); left += largest.cx + 1; } } // Draw candidates / reading strings candX++; candY++; if (g_bReadingWindow && g_bHorizontalReading) { int iStart = -1, iEnd = -1, iDummy; candX += dwMarginX; // draw background of error character if it exists TCHAR szTemp[ COUNTOF( g_szReadingString ) ]; if (g_iReadingError >= 0) { StringCchCopy(szTemp, COUNTOF(szTemp), g_szReadingString); LPTSTR psz = szTemp + g_iReadingError; #ifdef UNICODE psz++; #else psz += ( _IsLeadByte( szTemp[g_iReadingError] ) ) ? 2 : 1; #endif *psz = 0; g_CaretInfo.pFont->GetTextExtent(szTemp, (DWORD*)&iEnd, (DWORD*)&iDummy); TCHAR cSave = szTemp[ g_iReadingError ]; szTemp[g_iReadingError] = 0; g_CaretInfo.pFont->GetTextExtent(szTemp, (DWORD*)&iStart, (DWORD*)&iDummy); szTemp[g_iReadingError] = cSave; if( ImeUiCallback_DrawRect ) ImeUiCallback_DrawRect(candX + iStart, candY, candX + iEnd, candY + largest.cy, gSkinIME.candColorBorder); } g_CaretInfo.pFont->SetPosition(candX , candY); g_CaretInfo.pFont->SetColor(g_CaretInfo.colorCand); g_CaretInfo.pFont->DrawText(g_szReadingString); // draw error character if it exists if (iStart >= 0) { g_CaretInfo.pFont->SetPosition(candX + iStart, candY); if ( gSkinIME.candColorBase != 0xffffffff || gSkinIME.candColorBorder != 0xff000000 ) g_CaretInfo.pFont->SetColor(g_CaretInfo.colorCand); else g_CaretInfo.pFont->SetColor(0xff000000 + (~((0x00ffffff) & g_CaretInfo.colorCand))); g_CaretInfo.pFont->DrawText(szTemp + g_iReadingError); } } else { for (i = 0; i < (int)g_uCandPageSize && (DWORD)i < g_dwCount; i++) { if (g_dwSelection == (DWORD)i) { if ( gSkinIME.candColorBase != 0xffffffff || gSkinIME.candColorBorder != 0xff000000 ) g_CaretInfo.pFont->SetColor(g_CaretInfo.colorCand); else g_CaretInfo.pFont->SetColor(0xff000000 + (~((0x00ffffff) & g_CaretInfo.colorCand))); if( ImeUiCallback_DrawRect ) { if ( g_bReadingWindow || g_bVerticalCand ) ImeUiCallback_DrawRect( candX, candY + i * largest.cy, candX - 1 + dwCandWidth, candY + (i + 1) * largest.cy, gSkinIME.candColorBorder ); else ImeUiCallback_DrawRect( candX, candY, candX + adwCandWidth[i], candY + largest.cy, gSkinIME.candColorBorder ); } } else g_CaretInfo.pFont->SetColor(g_CaretInfo.colorCand); if (g_szCandidate[i][0] != 0) { if (!g_bReadingWindow && g_bVerticalCand) { TCHAR szOneDigit[2] = { g_szCandidate[i][0], 0 }; int nOneDigit = g_szCandidate[i][0] - TEXT('0'); TCHAR *szCandidateBody = g_szCandidate[i] + 2; int dx = candX + (seperateLineX - candX - uDigitWidthList[nOneDigit]) / 2; int dy = candY + largest.cy * i; g_CaretInfo.pFont->SetPosition( dx, dy ); g_CaretInfo.pFont->DrawText(szOneDigit); g_CaretInfo.pFont->SetPosition( seperateLineX+dwMarginX, dy ); g_CaretInfo.pFont->DrawText(szCandidateBody); } else if ( g_bReadingWindow ) { g_CaretInfo.pFont->SetPosition( dwMarginX + candX, candY + i * largest.cy ); g_CaretInfo.pFont->DrawText(g_szCandidate[i]); } else { g_CaretInfo.pFont->SetPosition( uSpaceWidth / 2 + candX, candY ); g_CaretInfo.pFont->DrawText(g_szCandidate[i]); } } if ( !g_bReadingWindow && !g_bVerticalCand ) { if ( IMEID_LANG( GetImeId() ) == LANG_CHS ) candX += adwCandWidth[i] + dwMarginX; else candX += largest.cx + 1; } } } } static void CloseCandidateList() { g_bCandList = false; if (!g_bReadingWindow) // fix for Ent Gen #120. { g_dwCount = 0; memset(&g_szCandidate, 0, sizeof(g_szCandidate)); } } // // ProcessIMEMessages() // Processes IME related messages and acquire information // LPARAM ImeUi_ProcessMessage( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM& lParam, bool * trapped ) { HIMC himc; int len; static LPARAM lAlt = 0x80000000, lCtrl = 0x80000000, lShift = 0x80000000; *trapped = false; if (!g_bInitialized || g_bDisableImeCompletely) { return 0; } switch( uMsg ) { // // IME Handling // case WM_INPUTLANGCHANGE: OnInputLangChange(); break; case WM_IME_SETCONTEXT: // // We don't want anything to display, so we have to clear lParam and pass it to DefWindowProc(). // Expecially important in Vista to receive IMN_CHANGECANDIDATE correctly. // lParam = 0; break; case WM_IME_STARTCOMPOSITION: InitCompStringData(); *trapped = true; break; case WM_IME_COMPOSITION: { LONG lRet; TCHAR szCompStr[COUNTOF(g_szCompositionString)]; *trapped = true; if (NULL == (himc = _ImmGetContext(hWnd))) { break; } // ResultStr must be processed before composition string. if ( lParam & GCS_RESULTSTR ) { lRet = (LONG)_ImmGetCompositionString( himc, GCS_RESULTSTR, szCompStr, COUNTOF( szCompStr ) ) / sizeof(TCHAR); szCompStr[lRet] = 0; CancelCompString( g_hwndCurr, false, GetCharCount( szCompStr ) ); StringCchCopy(g_szCompositionString, COUNTOF(g_szCompositionString), szCompStr); _SendCompString(); InitCompStringData(); } // // Reads in the composition string. // if ( lParam & GCS_COMPSTR ) { ////////////////////////////////////////////////////// // Retrieve the latest user-selected IME candidates lRet = (LONG)_ImmGetCompositionString( himc, GCS_COMPSTR, szCompStr, COUNTOF( szCompStr ) ) / sizeof(TCHAR); szCompStr[lRet] = 0; // // Remove the whole of the string // CancelCompString(g_hwndCurr, false, GetCharCount( szCompStr ) ); StringCchCopy(g_szCompositionString, COUNTOF(g_szCompositionString), szCompStr); lRet = _ImmGetCompositionString( himc, GCS_COMPATTR, g_szCompAttrString, COUNTOF( g_szCompAttrString ) ); g_szCompAttrString[lRet] = 0; // Older CHT IME uses composition string for reading string if ( GETLANG() == LANG_CHT && !GetImeId() ) { int i, chars = lstrlen(g_szCompositionString) / (3 - sizeof(TCHAR)); if (chars) { g_dwCount = 4; g_dwSelection = (DWORD)-1; // don't select any candidate for (i = 3; i >= 0; i--) { if (i > chars - 1) g_szCandidate[i][0] = 0; else { #ifdef UNICODE g_szCandidate[i][0] = g_szCompositionString[i]; g_szCandidate[i][1] = 0; #else g_szCandidate[i][0] = g_szCompositionString[i*2]; g_szCandidate[i][1] = g_szCompositionString[i*2+1]; g_szCandidate[i][2] = 0; #endif } } g_uCandPageSize = MAX_CANDLIST; memset(g_szCompositionString, 0, 8); g_bReadingWindow = true; GetReadingWindowOrientation( 0 ); if (g_bHorizontalReading) { g_iReadingError = -1; g_szReadingString[0] = 0; for (i = 0; i < (int)g_dwCount; i++) { if (g_dwSelection == (DWORD)i) g_iReadingError = lstrlen(g_szReadingString); LPCTSTR pszTmp = g_szCandidate[i]; StringCchCat(g_szReadingString, COUNTOF(g_szReadingString), pszTmp); } } } else g_dwCount = 0; } // get caret position in composition string g_IMECursorBytes = _ImmGetCompositionString(himc, GCS_CURSORPOS, NULL, 0); g_IMECursorChars = GetCharCountFromBytes(g_szCompositionString, g_IMECursorBytes); if (g_dwIMELevel == 3) { // send composition string via WM_CHAR _SendCompString(); // move caret to appropreate location len = GetCharCount(g_szCompositionString + g_IMECursorBytes); SendControlKeys(VK_LEFT, len); } } _ImmReleaseContext(hWnd, himc); } break; case WM_IME_ENDCOMPOSITION: CancelCompString(g_hwndCurr); InitCompStringData(); break; case WM_IME_NOTIFY: switch (wParam) { case IMN_SETCONVERSIONMODE: { // Disable CHT IME software keyboard. static bool bNoReentrance = false; if (LANG_CHT == GETLANG() && !bNoReentrance) { bNoReentrance = true; DWORD dwConvMode, dwSentMode; _ImmGetConversionStatus( g_himcOrg, &dwConvMode, &dwSentMode ); const DWORD dwFlag = IME_CMODE_SOFTKBD | IME_CMODE_SYMBOL; if ( dwConvMode & dwFlag ) _ImmSetConversionStatus( g_himcOrg, dwConvMode & ~dwFlag, dwSentMode ); } bNoReentrance = false; } // fall through case IMN_SETOPENSTATUS: if (g_bUILessMode) break; CheckToggleState(); break; case IMN_OPENCANDIDATE: case IMN_CHANGECANDIDATE: if ( g_bUILessMode ) { break; } { g_bCandList = true; *trapped = true; if (NULL == (himc = _ImmGetContext(hWnd))) break; LPCANDIDATELIST lpCandList; DWORD dwIndex, dwBufLen; g_bReadingWindow = false; dwIndex = 0; dwBufLen = _GetCandidateList( himc, dwIndex, &lpCandList ); if ( dwBufLen ) { g_dwSelection = lpCandList->dwSelection; g_dwCount = lpCandList->dwCount; int startOfPage = 0; if (GETLANG() == LANG_CHS && GetImeId()) { // MSPY (CHS IME) has variable number of candidates in candidate window // find where current page starts, and the size of current page const int maxCandChar = 18 * (3 - sizeof(TCHAR)); UINT cChars = 0; UINT i; for (i = 0; i < g_dwCount; i++) { UINT uLen = lstrlen( (LPTSTR)((DWORD)lpCandList + lpCandList->dwOffset[i])) + (3 - sizeof(TCHAR)); if (uLen + cChars > maxCandChar) { if (i > g_dwSelection) { break; } startOfPage = i; cChars = uLen; } else { cChars += uLen; } } g_uCandPageSize = i - startOfPage; } else { g_uCandPageSize = min( lpCandList->dwPageSize, MAX_CANDLIST ); startOfPage = g_bUILessMode ? lpCandList->dwPageStart : ( g_dwSelection / g_uCandPageSize ) * g_uCandPageSize; } g_dwSelection = ( GETLANG() == LANG_CHS && !GetImeId() ) ? (DWORD)-1 : g_dwSelection - startOfPage; memset(&g_szCandidate, 0, sizeof(g_szCandidate)); for (UINT i = startOfPage, j = 0; (DWORD)i < lpCandList->dwCount && j < g_uCandPageSize; i++, j++) { ComposeCandidateLine( j, (LPTSTR)( (DWORD)lpCandList + lpCandList->dwOffset[i] ) ); } ImeUiCallback_Free( (HANDLE)lpCandList ); _ImmReleaseContext(hWnd, himc); // don't display selection in candidate list in case of Korean and old Chinese IME. if ( GETPRIMLANG() == LANG_KOREAN || GETLANG() == LANG_CHT && !GetImeId() ) g_dwSelection = (DWORD)-1; } break; } case IMN_CLOSECANDIDATE: if ( g_bUILessMode ) { break; } CloseCandidateList(); *trapped = true; break; // Jun.16,2000 05:21 by yutaka. case IMN_PRIVATE: { if ( !g_bCandList ) { GetReadingString( hWnd ); } // Trap some messages to hide reading window DWORD dwId = GetImeId(); switch (dwId) { case IMEID_CHT_VER42: case IMEID_CHT_VER43: case IMEID_CHT_VER44: case IMEID_CHS_VER41: case IMEID_CHS_VER42: if ((lParam == 1) || (lParam == 2)) { *trapped = true; } break; case IMEID_CHT_VER50: case IMEID_CHT_VER51: case IMEID_CHT_VER52: case IMEID_CHT_VER60: case IMEID_CHS_VER53: if ((lParam == 16) || (lParam == 17) || (lParam == 26) || (lParam == 27) || (lParam == 28)) { *trapped = true; } break; } } break; default: *trapped = true; break; } break; // fix for #15386 - When Text Service Framework is installed in Win2K, Alt+Shift and Ctrl+Shift combination (to switch // input locale / keyboard layout) doesn't send WM_KEYUP message for the key that is released first. We need to check // if these keys are actually up whenever we receive key up message for other keys. case WM_KEYUP: case WM_SYSKEYUP: if ( !( lAlt & 0x80000000 ) && wParam != VK_MENU && ( GetAsyncKeyState( VK_MENU ) & 0x8000 ) == 0 ) { PostMessageA( GetFocus(), WM_KEYUP, (WPARAM)VK_MENU, ( lAlt & 0x01ff0000 ) | 0xC0000001 ); } else if ( !( lCtrl & 0x80000000 ) && wParam != VK_CONTROL && ( GetAsyncKeyState( VK_CONTROL ) & 0x8000 ) == 0 ) { PostMessageA( GetFocus(), WM_KEYUP, (WPARAM)VK_CONTROL, ( lCtrl & 0x01ff0000 ) | 0xC0000001 ); } else if ( !( lShift & 0x80000000 ) && wParam != VK_SHIFT && ( GetAsyncKeyState( VK_SHIFT ) & 0x8000 ) == 0 ) { PostMessageA( GetFocus(), WM_KEYUP, (WPARAM)VK_SHIFT, ( lShift & 0x01ff0000 ) | 0xC0000001 ); } // fall through WM_KEYDOWN / WM_SYSKEYDOWN case WM_KEYDOWN: case WM_SYSKEYDOWN: { switch ( wParam ) { case VK_MENU: lAlt = lParam; break; case VK_SHIFT: lShift = lParam; break; case VK_CONTROL: lCtrl = lParam; break; } } break; } return 0; } void ImeUi_SetCaretPosition(UINT x, UINT y) { if (!g_bInitialized) return; g_CaretInfo.caretX = x; g_CaretInfo.caretY = y; } void ImeUi_SetCompStringAppearance( CImeUiFont_Base* pFont, DWORD color, const RECT* prc ) { if (!g_bInitialized) return; g_CaretInfo.pFont = pFont; g_CaretInfo.margins = *prc; if (0 == gSkinIME.candColorText) g_CaretInfo.colorCand = color; else g_CaretInfo.colorCand = gSkinIME.candColorText; if (0 == gSkinIME.compColorText) g_CaretInfo.colorComp = color; else g_CaretInfo.colorComp = gSkinIME.compColorText; } void ImeUi_SetState( DWORD dwState ) { if (!g_bInitialized) return; HIMC himc; if ( dwState == IMEUI_STATE_ON ) { ImeUi_EnableIme( true ); } if (NULL != (himc = _ImmGetContext(g_hwndCurr))) { if (g_bDisableImeCompletely) dwState = IMEUI_STATE_OFF; bool bOn = dwState == IMEUI_STATE_ON; // for non-Chinese IME switch ( GETPRIMLANG() ) { case LANG_CHINESE: { // toggle Chinese IME DWORD dwId; DWORD dwConvMode = 0, dwSentMode = 0; if ( ( g_bChineseIME && dwState == IMEUI_STATE_OFF ) || ( !g_bChineseIME && dwState != IMEUI_STATE_OFF ) ) { _ImmSimulateHotKey( g_hwndCurr, IME_THOTKEY_IME_NONIME_TOGGLE ); _PumpMessage(); } if ( dwState != IMEUI_STATE_OFF ) { dwId = GetImeId(); if ( dwId ) { _ImmGetConversionStatus( himc, &dwConvMode, &dwSentMode ); dwConvMode = ( dwState == IMEUI_STATE_ON ) ? ( dwConvMode | IME_CMODE_NATIVE ) : ( dwConvMode & ~IME_CMODE_NATIVE ); _ImmSetConversionStatus( himc, dwConvMode, dwSentMode ); } } break; } case LANG_KOREAN: // toggle Korean IME if ( ( bOn && g_dwState != IMEUI_STATE_ON ) || ( !bOn && g_dwState == IMEUI_STATE_ON ) ) { _ImmSimulateHotKey(g_hwndCurr, IME_KHOTKEY_ENGLISH); } break; case LANG_JAPANESE: _ImmSetOpenStatus(himc, bOn); break; } _ImmReleaseContext(g_hwndCurr, himc); CheckToggleState(); } } DWORD ImeUi_GetState() { if (!g_bInitialized) return IMEUI_STATE_OFF; CheckToggleState(); return g_dwState; } void ImeUi_EnableIme( bool bEnable ) { if (!g_bInitialized || !g_hwndCurr) return; if (g_bDisableImeCompletely) bEnable = false; if (g_hwndCurr == g_hwndMain) { HIMC himcDbg; himcDbg = _ImmAssociateContext( g_hwndCurr, bEnable? g_himcOrg : NULL ); } g_bImeEnabled = bEnable; if ( bEnable ) { CheckToggleState(); } CTsfUiLessMode::EnableUiUpdates(bEnable); } bool ImeUi_IsEnabled( void ) { return g_bImeEnabled; } bool ImeUi_Initialize( HWND hwnd, bool bDisable ) { if ( g_bInitialized ) { return true; } g_hwndMain = hwnd; g_disableCicero.Initialize(); g_osi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA); GetVersionExA(&g_osi); bool bUnicodeImm = false; // IMM in NT or Win98 supports Unicode if ( g_osi.dwPlatformId == VER_PLATFORM_WIN32_NT || ( g_osi.dwMajorVersion > 4 ) || ( g_osi.dwMajorVersion == 4 ) && ( g_osi.dwMinorVersion > 0 ) ) { bUnicodeImm = true; } g_hImmDll = LoadLibraryA( "imm32.dll" ); g_bDisableImeCompletely = false; if ( g_hImmDll ) { _ImmLockIMC = (LPINPUTCONTEXT2 (WINAPI*)(HIMC hIMC)) GetProcAddress(g_hImmDll, "ImmLockIMC"); _ImmUnlockIMC = (BOOL (WINAPI*)(HIMC hIMC)) GetProcAddress(g_hImmDll, "ImmUnlockIMC"); _ImmLockIMCC = (LPVOID (WINAPI*)(HIMCC hIMCC)) GetProcAddress(g_hImmDll, "ImmLockIMCC"); _ImmUnlockIMCC = (BOOL (WINAPI*)(HIMCC hIMCC)) GetProcAddress(g_hImmDll, "ImmUnlockIMCC"); BOOL (WINAPI* _ImmDisableTextFrameService)(DWORD) = (BOOL (WINAPI*)(DWORD))GetProcAddress(g_hImmDll, "ImmDisableTextFrameService"); if ( _ImmDisableTextFrameService ) { _ImmDisableTextFrameService( (DWORD)-1 ); } } else { g_bDisableImeCompletely = true; return false; } #ifdef UNICODE if ( bUnicodeImm ) { _ImmGetCompositionString = ImmGetCompositionStringW; _ImmGetCandidateList = ImmGetCandidateListW; _GetCandidateList = GetCandidateList; } else { _ImmGetCandidateList = ImmGetCandidateListA; _ImmGetCompositionString = AW_ImmGetCompositionString; _GetCandidateList = AW_GetCandidateList; } #else if ( bUnicodeImm ) { _ImmGetCompositionString = WA_ImmGetCompositionString; _ImmGetCandidateList = ImmGetCandidateListA; _GetCandidateList = WA_GetCandidateList; } else { _ImmGetCompositionString = ImmGetCompositionStringA; _ImmGetCandidateList = ImmGetCandidateListA; _GetCandidateList = GetCandidateList; } #endif // There are the following combinations of code config, window type, and the method of sending characters. // Wnd: Unicode, Code: Unicode, Method: SendMessageW (SendMessageW must be supported since RegisterClassW is successful) // Wnd: non Uni, Code: Unicode, Method: AW_SendCompString (Send characters in multibyte after W->A conversion) // Wnd: Unicode, Code: non Uni, Method: SendMessageA (System does A->W conversion) - possible, but unlikely to be used. // Wnd: non Uni, Code: non Uni, Method: SendMessageA #ifdef UNICODE if ( !IsWindowUnicode( hwnd ) ) { _SendCompString = AW_SendCompString; } else #endif { _SendCompString = SendCompString; #ifdef UNICODE _SendMessage = SendMessageW; #endif } // turn init flag on so that subsequent calls to ImeUi functions work. g_bInitialized = true; ImeUi_SetWindow( g_hwndMain ); g_himcOrg = _ImmGetContext( g_hwndMain ); _ImmReleaseContext( g_hwndMain, g_himcOrg ); if ( !g_himcOrg ) { bDisable = true; } // the following pointers to function has to be initialized before this function is called. if ( bDisable || !ImeUiCallback_Malloc || !ImeUiCallback_Free ) { g_bDisableImeCompletely = true; ImeUi_EnableIme(false); g_bInitialized = bDisable; return false; } g_uCaretBlinkTime = GetCaretBlinkTime(); #ifndef UNICODE // Check if system is SBCS system CPINFO cpi; BOOL bRc = GetCPInfo(CP_ACP, &cpi); if (bRc) { if (cpi.MaxCharSize == 1) { g_bDisableImeCompletely = true; // SBCS system. Disable IME. } } #endif g_CaretInfo.caretX = 0; g_CaretInfo.caretY = 0; g_CaretInfo.pFont = 0; g_CaretInfo.colorComp = 0; g_CaretInfo.colorCand = 0; g_CaretInfo.margins.left = 0; g_CaretInfo.margins.right = 640; g_CaretInfo.margins.top = 0; g_CaretInfo.margins.bottom = 480; CheckInputLocale(); OnInputLangChangeWorker(); ImeUi_SetSupportLevel(2); // SetupTSFSinks has to be called before CheckToggleState to make it work correctly. g_bUILessMode = CTsfUiLessMode::SetupSinks() != FALSE; CheckToggleState(); if ( g_bUILessMode ) { g_bChineseIME = ( GETPRIMLANG() == LANG_CHINESE ) && CTsfUiLessMode::CurrentInputLocaleIsIme(); CTsfUiLessMode::UpdateImeState(); } ImeUi_EnableIme(false); return true; } void ImeUi_Uninitialize() { if ( !g_bInitialized ) { return; } CTsfUiLessMode::ReleaseSinks(); if ( g_hwndMain ) { ImmAssociateContext(g_hwndMain, g_himcOrg); } g_hwndMain = NULL; g_himcOrg = NULL; if ( g_hImmDll ) { FreeLibrary(g_hImmDll); g_hImmDll = NULL; } g_disableCicero.Uninitialize(); g_bInitialized = false; } // // GetImeId( UINT uIndex ) // returns // returned value: // 0: In the following cases // - Non Chinese IME input locale // - Older Chinese IME // - Other error cases // // Othewise: // When uIndex is 0 (default) // bit 31-24: Major version // bit 23-16: Minor version // bit 15-0: Language ID // When uIndex is 1 // pVerFixedInfo->dwFileVersionLS // // Use IMEID_VER and IMEID_LANG macro to extract version and language information. // static DWORD GetImeId( UINT uIndex ) { static HKL hklPrev = 0; static DWORD dwRet[2] = {0, 0}; DWORD dwVerSize; DWORD dwVerHandle; LPVOID lpVerBuffer; LPVOID lpVerData; UINT cbVerData; char szTmp[1024]; if ( uIndex >= sizeof( dwRet ) / sizeof( dwRet[0] ) ) return 0; HKL kl = g_hklCurrent; if (hklPrev == kl) { return dwRet[uIndex]; } hklPrev = kl; DWORD dwLang = ((DWORD)kl & 0xffff); if ( g_bUILessMode && GETLANG() == LANG_CHT ) { // In case of Vista, artifitial value is returned so that it's not considered as older IME. dwRet[0] = IMEID_CHT_VER_VISTA; dwRet[1] = 0; return dwRet[0]; } if ( kl != _CHT_HKL_NEW_PHONETIC && kl != _CHT_HKL_NEW_CHANG_JIE && kl != _CHT_HKL_NEW_QUICK && kl != _CHT_HKL_HK_CANTONESE && kl != _CHS_HKL ) { goto error; } if ( _ImmGetIMEFileNameA( kl, szTmp, sizeof( szTmp ) - 1 ) <= 0 ) { goto error; } if ( !_GetReadingString ) // IME that doesn't implement private API { #define LCID_INVARIANT MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT) if ( ( CompareStringA( LCID_INVARIANT, NORM_IGNORECASE, szTmp, -1, _CHT_IMEFILENAME, -1 ) != 2 ) && ( CompareStringA( LCID_INVARIANT, NORM_IGNORECASE, szTmp, -1, _CHT_IMEFILENAME2, -1 ) != 2 ) && ( CompareStringA( LCID_INVARIANT, NORM_IGNORECASE, szTmp, -1, _CHT_IMEFILENAME3, -1 ) != 2 ) && ( CompareStringA( LCID_INVARIANT, NORM_IGNORECASE, szTmp, -1, _CHS_IMEFILENAME, -1 ) != 2 ) && ( CompareStringA( LCID_INVARIANT, NORM_IGNORECASE, szTmp, -1, _CHS_IMEFILENAME2, -1 ) != 2 ) ) { goto error; } } dwVerSize = GetFileVersionInfoSizeA( szTmp, &dwVerHandle ); if ( dwVerSize ) { lpVerBuffer = (LPVOID) ImeUiCallback_Malloc(dwVerSize); if ( lpVerBuffer ) { if ( GetFileVersionInfoA( szTmp, dwVerHandle, dwVerSize, lpVerBuffer ) ) { if ( VerQueryValueA( lpVerBuffer, "\\", &lpVerData, &cbVerData ) ) { #define pVerFixedInfo ((VS_FIXEDFILEINFO FAR*)lpVerData) DWORD dwVer = pVerFixedInfo->dwFileVersionMS; dwVer = ( dwVer & 0x00ff0000 ) << 8 | ( dwVer & 0x000000ff ) << 16; if ( _GetReadingString || dwLang == LANG_CHT && ( dwVer == MAKEIMEVERSION(4, 2) || dwVer == MAKEIMEVERSION(4, 3) || dwVer == MAKEIMEVERSION(4, 4) || dwVer == MAKEIMEVERSION(5, 0) || dwVer == MAKEIMEVERSION(5, 1) || dwVer == MAKEIMEVERSION(5, 2) || dwVer == MAKEIMEVERSION(6, 0) ) || dwLang == LANG_CHS && ( dwVer == MAKEIMEVERSION(4, 1) || dwVer == MAKEIMEVERSION(4, 2) || dwVer == MAKEIMEVERSION(5, 3) ) ) { dwRet[0] = dwVer | dwLang; dwRet[1] = pVerFixedInfo->dwFileVersionLS; ImeUiCallback_Free(lpVerBuffer); return dwRet[0]; } #undef pVerFixedInfo } } } ImeUiCallback_Free(lpVerBuffer); } // The flow comes here in the following conditions // - Non Chinese IME input locale // - Older Chinese IME // - Other error cases error: dwRet[0] = dwRet[1] = 0; return dwRet[uIndex]; } static void GetReadingString(HWND hWnd) { if ( g_bUILessMode ) { return; } DWORD dwId = GetImeId(); if ( ! dwId ) { return; } HIMC himc; himc = _ImmGetContext(hWnd); if ( !himc ) return; DWORD dwlen = 0; DWORD dwerr = 0; WCHAR wzBuf[16]; // We believe 16 wchars are big enough to hold reading string after having discussion with CHT IME team. WCHAR *wstr = wzBuf; bool unicode = FALSE; LPINPUTCONTEXT2 lpIMC = NULL; if ( _GetReadingString ) { BOOL bVertical; UINT uMaxUiLen; dwlen = _GetReadingString( himc, 0, NULL, (PINT)&dwerr, &bVertical, &uMaxUiLen); if ( dwlen ) { if ( dwlen > COUNTOF(wzBuf) ) { dwlen = COUNTOF(wzBuf); } dwlen = _GetReadingString( himc, dwlen, wstr, (PINT)&dwerr, &bVertical, &uMaxUiLen); } g_bHorizontalReading = bVertical == 0; unicode = true; } else // IMEs that doesn't implement Reading String API { lpIMC = _ImmLockIMC(himc); // *** hacking code from Michael Yang *** LPBYTE p = 0; switch ( dwId ) { case IMEID_CHT_VER42: // New(Phonetic/ChanJie)IME98 : 4.2.x.x // Win98 case IMEID_CHT_VER43: // New(Phonetic/ChanJie)IME98a : 4.3.x.x // WinMe, Win2k case IMEID_CHT_VER44: // New ChanJie IME98b : 4.4.x.x // WinXP p = *(LPBYTE *)((LPBYTE)_ImmLockIMCC(lpIMC->hPrivate) + 24); if (!p) break; dwlen = *(DWORD *)(p + 7*4 + 32*4); //m_dwInputReadStrLen dwerr = *(DWORD *)(p + 8*4 + 32*4); //m_dwErrorReadStrStart wstr = (WCHAR *)(p + 56); unicode = TRUE; break; case IMEID_CHT_VER50: // 5.0.x.x // WinME p = *(LPBYTE *)((LPBYTE)_ImmLockIMCC(lpIMC->hPrivate) + 3*4); // PCKeyCtrlManager if (!p) break; p = *(LPBYTE *)((LPBYTE)p + 1*4 + 5*4 + 4*2 ); // = PCReading = &STypingInfo if (!p) break; dwlen = *(DWORD *)(p + 1*4 + (16*2+2*4) + 5*4 + 16); //m_dwDisplayStringLength; dwerr = *(DWORD *)(p + 1*4 + (16*2+2*4) + 5*4 + 16 + 1*4); //m_dwDisplayErrorStart; wstr = (WCHAR *)(p + 1*4 + (16*2+2*4) + 5*4); unicode = FALSE; break; case IMEID_CHT_VER51: // 5.1.x.x // IME2002(w/OfficeXP) case IMEID_CHT_VER52: // 5.2.x.x // (w/whistler) case IMEID_CHS_VER53: // 5.3.x.x // SCIME2k or MSPY3 (w/OfficeXP and Whistler) p = *(LPBYTE *)((LPBYTE)_ImmLockIMCC(lpIMC->hPrivate) + 4); // PCKeyCtrlManager if (!p) break; p = *(LPBYTE *)((LPBYTE)p + 1*4 + 5*4); // = PCReading = &STypingInfo if (!p) break; dwlen = *(DWORD *)(p + 1*4 + (16*2+2*4) + 5*4 + 16 * 2); //m_dwDisplayStringLength; dwerr = *(DWORD *)(p + 1*4 + (16*2+2*4) + 5*4 + 16 * 2 + 1*4); //m_dwDisplayErrorStart; wstr = (WCHAR *) (p + 1*4 + (16*2+2*4) + 5*4); unicode = TRUE; break; // the code tested only with Win 98 SE (MSPY 1.5/ ver 4.1.0.21) case IMEID_CHS_VER41: { int offset; offset = ( GetImeId( 1 ) >= 0x00000002 ) ? 8 : 7; p = *(LPBYTE *)((LPBYTE)_ImmLockIMCC(lpIMC->hPrivate) + offset * 4); if (!p) break; dwlen = *(DWORD *)(p + 7*4 + 16*2*4); dwerr = *(DWORD *)(p + 8*4 + 16*2*4); dwerr = min(dwerr, dwlen); wstr = (WCHAR *)(p + 6*4 + 16*2*1); unicode = TRUE; break; } case IMEID_CHS_VER42: // 4.2.x.x // SCIME98 or MSPY2 (w/Office2k, Win2k, WinME, etc) { int nTcharSize = IsNT() ? sizeof(WCHAR) : sizeof(char); p = *(LPBYTE *)((LPBYTE)_ImmLockIMCC(lpIMC->hPrivate) + 1*4 + 1*4 + 6*4); // = PCReading = &STypintInfo if (!p) break; dwlen = *(DWORD *)(p + 1*4 + (16*2+2*4) + 5*4 + 16 * nTcharSize); //m_dwDisplayStringLength; dwerr = *(DWORD *)(p + 1*4 + (16*2+2*4) + 5*4 + 16 * nTcharSize + 1*4); //m_dwDisplayErrorStart; wstr = (WCHAR *) (p + 1*4 + (16*2+2*4) + 5*4); //m_tszDisplayString unicode = IsNT() ? TRUE : FALSE; } } // switch g_szCandidate[0][0] = 0; g_szCandidate[1][0] = 0; g_szCandidate[2][0] = 0; g_szCandidate[3][0] = 0; } g_dwCount = dwlen; g_dwSelection = (DWORD)-1; // do not select any char if ( unicode ) { int i; for ( i=0; (DWORD)i < dwlen ; i++ ) // dwlen > 0, if known IME : yutakah { if ( dwerr<=(DWORD)i && g_dwSelection==(DWORD)-1 ){ // select error char g_dwSelection = i; } #ifdef UNICODE g_szCandidate[i][0] = wstr[i]; g_szCandidate[i][1] = 0; #else char mbc[3]; mbc[1] = 0; mbc[2] = 0; WideCharToMultiByte(g_uCodePage, 0, wstr + i, 1, mbc, sizeof(mbc), NULL, NULL); g_szCandidate[i][0] = mbc[0]; g_szCandidate[i][1] = mbc[1]; g_szCandidate[i][2] = 0; #endif } g_szCandidate[i][0] = 0; } else { char *p = (char *)wstr; int i, j; for ( i=0, j=0; (DWORD)i < dwlen ; i++,j++ ) // dwlen > 0, if known IME : yutakah { if ( dwerr<=(DWORD)i && g_dwSelection==(DWORD)-1 ){ g_dwSelection = (DWORD)j; } #ifdef UNICODE MultiByteToWideChar( g_uCodePage, 0, p + i, 1 + ( _IsLeadByte( p[i] ) ? 1 : 0 ), g_szCandidate[j], 1 ); if ( _IsLeadByte( p[i] ) ) { i++; } #else g_szCandidate[j][0] = p[i]; g_szCandidate[j][1] = 0; g_szCandidate[j][2] = 0; if (_IsLeadByte(p[i])) { i++; g_szCandidate[j][1] = p[i]; } #endif } g_szCandidate[j][0] = 0; g_dwCount = j; } if ( !_GetReadingString ) { _ImmUnlockIMCC( lpIMC->hPrivate ); _ImmUnlockIMC( himc ); GetReadingWindowOrientation( dwId ); } _ImmReleaseContext(hWnd, himc); g_bReadingWindow = true; if (g_bHorizontalReading) { g_iReadingError = -1; g_szReadingString[0] = 0; for (UINT i = 0; i < g_dwCount; i++) { if (g_dwSelection == (DWORD)i) g_iReadingError = lstrlen(g_szReadingString); LPCTSTR pszTmp = g_szCandidate[i]; StringCchCat(g_szReadingString, COUNTOF(g_szReadingString), pszTmp); } } g_uCandPageSize = MAX_CANDLIST; } static struct { bool m_bCtrl; bool m_bShift; bool m_bAlt; UINT m_uVk; } aHotKeys[] = { false, false, false, VK_APPS, true, false, false, '8', true, false, false, 'Y', true, false, false, VK_DELETE, true, false, false, VK_F7, true, false, false, VK_F9, true, false, false, VK_F10, true, false, false, VK_F11, true, false, false, VK_F12, false, false, false, VK_F2, false, false, false, VK_F3, false, false, false, VK_F4, false, false, false, VK_F5, false, false, false, VK_F10, false, true, false, VK_F6, false, true, false, VK_F7, false, true, false, VK_F8, true, true, false, VK_F10, true, true, false, VK_F11, true, false, false, VK_CONVERT, true, false, false, VK_SPACE, true, false, true, 0xbc, // Alt + Ctrl + ',': SW keyboard for Trad. Chinese IME true, false, false, VK_TAB, // ATOK2005's Ctrl+TAB }; // // Ignores specific keys when IME is on. Returns true if the message is a hot key to ignore. // - Caller doesn't have to check whether IME is on. // - This function must be called before TranslateMessage() is called. // bool ImeUi_IgnoreHotKey( const MSG* pmsg ) { if ( !g_bInitialized || !pmsg ) return false; if (pmsg->wParam == VK_PROCESSKEY && (pmsg->message == WM_KEYDOWN || pmsg->message == WM_SYSKEYDOWN)) { bool bCtrl, bShift, bAlt; UINT uVkReal = _ImmGetVirtualKey(pmsg->hwnd); // special case #1 - VK_JUNJA toggles half/full width input mode in Korean IME. // This VK (sent by Alt+'=' combo) is ignored regardless of the modifier state. if ( uVkReal == VK_JUNJA ) { return true; } // special case #2 - disable right arrow key that switches the candidate list to expanded mode in CHT IME. if ( uVkReal == VK_RIGHT && g_bCandList && GETLANG() == LANG_CHT) { return true; } #ifndef ENABLE_HANJA_KEY // special case #3 - we disable VK_HANJA key because 1. some Korean fonts don't Hanja and 2. to reduce testing cost. if ( uVkReal == VK_HANJA && GETPRIMLANG() == LANG_KOREAN ) { return true; } #endif bCtrl = (GetKeyState(VK_CONTROL) & 0x8000) ? true : false; bShift = (GetKeyState(VK_SHIFT) & 0x8000) ? true : false; bAlt = (GetKeyState(VK_MENU) & 0x8000) ? true : false; for (int i = 0; i < COUNTOF(aHotKeys); i++) { if (aHotKeys[i].m_bCtrl == bCtrl && aHotKeys[i].m_bShift == bShift && aHotKeys[i].m_bAlt == bAlt && aHotKeys[i].m_uVk == uVkReal) return true; } } return false; } void ImeUi_FinalizeString(bool bSend) { HIMC himc; static bool bProcessing = false; // to avoid infinite recursion if ( !g_bInitialized || bProcessing || NULL == ( himc = _ImmGetContext( g_hwndCurr ) ) ) return; bProcessing = true; if (g_dwIMELevel == 2 && bSend) { // Send composition string to app. LONG lRet = lstrlen( g_szCompositionString ); assert( lRet >= 2); // In case of CHT IME, don't send the trailing double byte space, if it exists. #ifdef UNICODE if ( GETLANG() == LANG_CHT && (lRet >= 1) && g_szCompositionString[lRet - 1] == 0x3000 ) { lRet--; } #else if ( GETLANG() == LANG_CHT && (lRet >= 2) && (BYTE)( g_szCompositionString[lRet - 2] ) == 0xa1 && (BYTE)( g_szCompositionString[lRet - 1] ) == 0x40 ) { lRet -= 2; } #endif _SendCompString(); } InitCompStringData(); // clear composition string in IME _ImmNotifyIME(himc, NI_COMPOSITIONSTR, CPS_CANCEL, 0); if (g_bUILessMode) { // For some reason ImmNotifyIME doesn't work on DaYi and Array CHT IMEs. Cancel composition string by setting zero-length string. ImmSetCompositionString(himc, SCS_SETSTR, TEXT(""), sizeof(TCHAR), TEXT(""), sizeof(TCHAR)); } // the following line is necessary as Korean IME doesn't close cand list when comp string is cancelled. _ImmNotifyIME( himc, NI_CLOSECANDIDATE, 0, 0 ); _ImmReleaseContext(g_hwndCurr, himc); // Zooty2 RAID #4759: Sometimes application doesn't receive IMN_CLOSECANDIDATE on Alt+Tab // So the same code for IMN_CLOSECANDIDATE is replicated here. CloseCandidateList(); bProcessing = false; return; } static void SetCompStringColor() { // change color setting according to current IME level. DWORD dwTranslucency = (g_dwIMELevel == 2) ? 0xff000000 : ((DWORD)gSkinIME.compTranslucence << 24); gSkinCompStr.colorInput = dwTranslucency | gSkinIME.compColorInput; gSkinCompStr.colorTargetConv = dwTranslucency | gSkinIME.compColorTargetConv; gSkinCompStr.colorConverted = dwTranslucency | gSkinIME.compColorConverted; gSkinCompStr.colorTargetNotConv = dwTranslucency | gSkinIME.compColorTargetNotConv; gSkinCompStr.colorInputErr = dwTranslucency | gSkinIME.compColorInputErr; } static void SetSupportLevel( DWORD dwImeLevel ) { if ( dwImeLevel < 2 || 3 < dwImeLevel ) return; if ( GETPRIMLANG() == LANG_KOREAN ) { dwImeLevel = 3; } g_dwIMELevel = dwImeLevel; // cancel current composition string. ImeUi_FinalizeString(); SetCompStringColor(); } void ImeUi_SetSupportLevel(DWORD dwImeLevel) { if ( !g_bInitialized ) return; g_dwIMELevelSaved = dwImeLevel; SetSupportLevel( dwImeLevel ); } void ImeUi_SetAppearance( const IMEUI_APPEARANCE* pia ) { if ( !g_bInitialized || NULL == pia ) return; gSkinIME = *pia; gSkinIME.symbolColor &= 0xffffff; // mask translucency gSkinIME.symbolColorOff &= 0xffffff; // mask translucency gSkinIME.symbolColorText &= 0xffffff; // mask translucency gSkinIME.compColorInput &= 0xffffff; // mask translucency gSkinIME.compColorTargetConv &= 0xffffff; // mask translucency gSkinIME.compColorConverted &= 0xffffff; // mask translucency gSkinIME.compColorTargetNotConv &= 0xffffff; // mask translucency gSkinIME.compColorInputErr &= 0xffffff; // mask translucency SetCompStringColor(); } void ImeUi_GetAppearance( IMEUI_APPEARANCE* pia ) { if ( g_bInitialized && pia ) { *pia = gSkinIME; } } static void CheckToggleState() { CheckInputLocale(); // In Vista, we have to use TSF since few IMM functions don't work as expected. // WARNING: Because of timing, g_dwState and g_bChineseIME may not be updated // immediately after the change on IME states by user. if ( g_bUILessMode ) { return; } bool bIme = _ImmIsIME( g_hklCurrent ) != 0 && ( ( 0xF0000000 & (DWORD)g_hklCurrent ) == 0xE0000000 ); // Hack to detect IME correctly. When IME is running as TIP, ImmIsIME() returns true for CHT US keyboard. g_bChineseIME = ( GETPRIMLANG() == LANG_CHINESE ) && bIme; HIMC himc; if (NULL != (himc = _ImmGetContext(g_hwndCurr))) { if (g_bChineseIME) { DWORD dwConvMode, dwSentMode; _ImmGetConversionStatus(himc, &dwConvMode, &dwSentMode); g_dwState = ( dwConvMode & IME_CMODE_NATIVE ) ? IMEUI_STATE_ON : IMEUI_STATE_ENGLISH; } else { g_dwState = ( bIme && _ImmGetOpenStatus( himc ) != 0 ) ? IMEUI_STATE_ON : IMEUI_STATE_OFF; } _ImmReleaseContext(g_hwndCurr, himc); } else g_dwState = IMEUI_STATE_OFF; } void ImeUi_SetInsertMode(bool bInsert) { if ( !g_bInitialized ) return; g_bInsertMode = bInsert; } bool ImeUi_GetCaretStatus() { return !g_bInitialized || !g_szCompositionString[0]; } void ImeUi_SetScreenDimension(UINT width, UINT height) { if ( !g_bInitialized ) return; g_screenWidth = width; g_screenHeight = height; } // this function is used only in brief time in CHT IME handling, so accelerator isn't processed. static void _PumpMessage() { MSG msg; while (PeekMessageA(&msg, NULL, 0, 0, PM_NOREMOVE)) { if (!GetMessageA(&msg, NULL, 0, 0)) { PostQuitMessage(msg.wParam); return; } // if (0 == TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) { TranslateMessage(&msg); DispatchMessageA(&msg); // } } } static void GetReadingWindowOrientation( DWORD dwId ) { g_bHorizontalReading = ( g_hklCurrent == _CHS_HKL ) || ( g_hklCurrent == _CHT_HKL_NEW_CHANG_JIE ) || ( dwId == 0 ); if (!g_bHorizontalReading && IMEID_LANG( dwId ) == LANG_CHT ) { char szRegPath[MAX_PATH]; HKEY hkey; DWORD dwVer = IMEID_VER( dwId ); StringCchCopyA(szRegPath, COUNTOF(szRegPath), "software\\microsoft\\windows\\currentversion\\"); StringCchCatA(szRegPath, COUNTOF(szRegPath), (dwVer >= MAKEIMEVERSION(5, 1)) ? "MSTCIPH" : "TINTLGNT"); LONG lRc = RegOpenKeyExA(HKEY_CURRENT_USER, szRegPath, 0, KEY_READ, &hkey); if (lRc == ERROR_SUCCESS) { DWORD dwSize = sizeof(DWORD), dwMapping, dwType; lRc = RegQueryValueExA(hkey, "keyboard mapping", NULL, &dwType, (PBYTE)&dwMapping, &dwSize); if (lRc == ERROR_SUCCESS) { if ( ( dwVer <= MAKEIMEVERSION( 5, 0 ) && ((BYTE)dwMapping == 0x22 || (BYTE)dwMapping == 0x23) ) || ( ( dwVer == MAKEIMEVERSION( 5, 1 ) || dwVer == MAKEIMEVERSION( 5, 2 ) ) && ((BYTE)dwMapping >= 0x22 && (BYTE)dwMapping <= 0x24) ) ) { g_bHorizontalReading = true; } } RegCloseKey( hkey ); } } } void ImeUi_ToggleLanguageBar(BOOL bRestore) { static BOOL prevRestore = TRUE; bool bCheck = ( prevRestore == TRUE || bRestore == TRUE ); prevRestore = bRestore; if ( !bCheck ) return; static int iShowStatusWindow = -1; if ( iShowStatusWindow == -1 ) { iShowStatusWindow = IsNT() && g_osi.dwMajorVersion >= 5 && ( g_osi.dwMinorVersion > 1 || ( g_osi.dwMinorVersion == 1 && lstrlenA( g_osi.szCSDVersion ) ) ) ? 1 : 0; } HWND hwndImeDef = _ImmGetDefaultIMEWnd(g_hwndCurr); if ( hwndImeDef && bRestore && iShowStatusWindow ) SendMessageA(hwndImeDef, WM_IME_CONTROL, IMC_OPENSTATUSWINDOW, 0); HRESULT hr; hr = CoInitialize(NULL); if (SUCCEEDED(hr)) { ITfLangBarMgr* plbm = NULL; hr = CoCreateInstance(CLSID_TF_LangBarMgr, NULL, CLSCTX_INPROC_SERVER, __uuidof(ITfLangBarMgr), (void**)&plbm); if (SUCCEEDED(hr) && plbm) { DWORD dwCur; ULONG uRc; if (SUCCEEDED(hr)) { if (bRestore) { if (g_dwPrevFloat) hr = plbm->ShowFloating(g_dwPrevFloat); } else { hr = plbm->GetShowFloatingStatus(&dwCur); if (SUCCEEDED(hr)) g_dwPrevFloat = dwCur; if ( !( g_dwPrevFloat & TF_SFT_DESKBAND ) ) { hr = plbm->ShowFloating(TF_SFT_HIDDEN); } } } uRc = plbm->Release(); } CoUninitialize(); } if ( hwndImeDef && !bRestore ) { // The following OPENSTATUSWINDOW is required to hide ATOK16 toolbar (FS9:#7546) SendMessageA(hwndImeDef, WM_IME_CONTROL, IMC_OPENSTATUSWINDOW, 0); SendMessageA(hwndImeDef, WM_IME_CONTROL, IMC_CLOSESTATUSWINDOW, 0); } } bool ImeUi_IsSendingKeyMessage() { return bIsSendingKeyMessage; } static void OnInputLangChangeWorker() { if ( !g_bUILessMode ) { g_iCandListIndexBase = ( g_hklCurrent == _CHT_HKL_DAYI ) ? 0 : 1; } SetImeApi(); } static void OnInputLangChange() { UINT uLang = GETPRIMLANG(); CheckToggleState(); OnInputLangChangeWorker(); if (uLang != GETPRIMLANG()) { // Korean IME always uses level 3 support. // Other languages use the level that is specified by ImeUi_SetSupportLevel() SetSupportLevel( ( GETPRIMLANG() == LANG_KOREAN ) ? 3 : g_dwIMELevelSaved ); } HWND hwndImeDef = _ImmGetDefaultIMEWnd(g_hwndCurr); if ( hwndImeDef ) { // Fix for Zooty #3995: prevent CHT IME toobar from showing up SendMessageA(hwndImeDef, WM_IME_CONTROL, IMC_OPENSTATUSWINDOW, 0); SendMessageA(hwndImeDef, WM_IME_CONTROL, IMC_CLOSESTATUSWINDOW, 0); } } static void SetImeApi() { _GetReadingString = NULL; _ShowReadingWindow = NULL; if(g_bUILessMode) return; char szImeFile[MAX_PATH + 1]; HKL kl = g_hklCurrent; if ( _ImmGetIMEFileNameA( kl, szImeFile, sizeof( szImeFile ) - 1 ) <= 0 ) return; HMODULE hIme = LoadLibraryA( szImeFile ); if ( !hIme ) return; _GetReadingString = (UINT (WINAPI*)(HIMC, UINT, LPWSTR, PINT, BOOL*, PUINT)) (GetProcAddress(hIme, "GetReadingString")); _ShowReadingWindow =(BOOL (WINAPI*)(HIMC himc, BOOL)) (GetProcAddress(hIme, "ShowReadingWindow")); if ( _ShowReadingWindow ) { HIMC himc; if ( NULL != ( himc = _ImmGetContext( g_hwndCurr ) ) ) { _ShowReadingWindow( himc, false ); _ImmReleaseContext( g_hwndCurr, himc ); } } } static void CheckInputLocale() { static HKL hklPrev = 0; g_hklCurrent = GetKeyboardLayout( 0 ); if ( hklPrev == g_hklCurrent ) { return; } hklPrev = g_hklCurrent; switch ( GETPRIMLANG() ) { // Simplified Chinese case LANG_CHINESE: g_bVerticalCand = true; switch ( GETSUBLANG() ) { case SUBLANG_CHINESE_SIMPLIFIED: g_pszIndicatior = g_aszIndicator[INDICATOR_CHS]; //g_bVerticalCand = GetImeId() == 0; g_bVerticalCand = false; break; case SUBLANG_CHINESE_TRADITIONAL: g_pszIndicatior = g_aszIndicator[INDICATOR_CHT]; break; default: // unsupported sub-language g_pszIndicatior = g_aszIndicator[INDICATOR_NON_IME]; break; } break; // Korean case LANG_KOREAN: g_pszIndicatior = g_aszIndicator[INDICATOR_KOREAN]; g_bVerticalCand = false; break; // Japanese case LANG_JAPANESE: g_pszIndicatior = g_aszIndicator[INDICATOR_JAPANESE]; g_bVerticalCand = true; break; default: g_pszIndicatior = g_aszIndicator[INDICATOR_NON_IME]; } char szCodePage[8]; int iRc = GetLocaleInfoA( MAKELCID( GETLANG(), SORT_DEFAULT ), LOCALE_IDEFAULTANSICODEPAGE, szCodePage, COUNTOF( szCodePage ) ); iRc; g_uCodePage = _strtoul( szCodePage, NULL, 0 ); for ( int i = 0; i < 256; i++ ) { LeadByteTable[i] = (BYTE)IsDBCSLeadByteEx( g_uCodePage, (BYTE)i ); } } void ImeUi_SetWindow(HWND hwnd) { g_hwndCurr = hwnd; g_disableCicero.DisableCiceroOnThisWnd( hwnd ); } UINT ImeUi_GetInputCodePage() { return g_uCodePage; } DWORD ImeUi_GetFlags() { return g_dwImeUiFlags; } void ImeUi_SetFlags( DWORD dwFlags, bool bSet ) { if ( bSet ) { g_dwImeUiFlags |= dwFlags; } else { g_dwImeUiFlags &= ~dwFlags; } } /////////////////////////////////////////////////////////////////////////////// // // CTsfUiLessMode methods // /////////////////////////////////////////////////////////////////////////////// // // SetupSinks() // Set up sinks. A sink is used to receive a Text Service Framework event. // CUIElementSink implements multiple sink interfaces to receive few different TSF events. // BOOL CTsfUiLessMode::SetupSinks() { // ITfThreadMgrEx is available on Vista or later. HRESULT hr; hr = CoCreateInstance(CLSID_TF_ThreadMgr, NULL, CLSCTX_INPROC_SERVER, __uuidof(ITfThreadMgrEx), (void**)&m_tm); if (hr != S_OK) { return FALSE; } // ready to start interacting TfClientId cid; // not used if (FAILED(m_tm->ActivateEx(&cid, TF_TMAE_UIELEMENTENABLEDONLY))) { return FALSE; } // Setup sinks BOOL bRc = FALSE; m_TsfSink = new CUIElementSink(); if (m_TsfSink) { ITfSource *srcTm; if (SUCCEEDED(hr = m_tm->QueryInterface(__uuidof(ITfSource), (void **)&srcTm))) { // Sink for reading window change if (SUCCEEDED(hr = srcTm->AdviseSink(__uuidof(ITfUIElementSink), (ITfUIElementSink*)m_TsfSink, &m_dwUIElementSinkCookie))) { // Sink for input locale change if (SUCCEEDED(hr = srcTm->AdviseSink(__uuidof(ITfInputProcessorProfileActivationSink), (ITfInputProcessorProfileActivationSink*)m_TsfSink, &m_dwAlpnSinkCookie))) { if (SetupCompartmentSinks()) // Setup compartment sinks for the first time { bRc = TRUE; } } } srcTm->Release(); } } return bRc; } void CTsfUiLessMode::ReleaseSinks() { HRESULT hr; ITfSource *source; // Remove all sinks if ( m_tm && SUCCEEDED(m_tm->QueryInterface(__uuidof(ITfSource), (void **)&source))) { hr = source->UnadviseSink(m_dwUIElementSinkCookie); hr = source->UnadviseSink(m_dwAlpnSinkCookie); source->Release(); SetupCompartmentSinks(TRUE); // Remove all compartment sinks m_tm->Deactivate(); SAFE_RELEASE(m_tm); SAFE_RELEASE(m_TsfSink); } } CTsfUiLessMode::CUIElementSink::CUIElementSink() { _cRef = 1; } CTsfUiLessMode::CUIElementSink::~CUIElementSink() { } STDAPI CTsfUiLessMode::CUIElementSink::QueryInterface(REFIID riid, void **ppvObj) { if (ppvObj == NULL) return E_INVALIDARG; *ppvObj = NULL; if (IsEqualIID(riid, IID_IUnknown)) { *ppvObj = reinterpret_cast
(this); } else if (IsEqualIID(riid, __uuidof(ITfUIElementSink))) { *ppvObj = (ITfUIElementSink *)this; } else if (IsEqualIID(riid, __uuidof(ITfInputProcessorProfileActivationSink))) { *ppvObj = (ITfInputProcessorProfileActivationSink*)this; } else if (IsEqualIID(riid, __uuidof(ITfCompartmentEventSink))) { *ppvObj = (ITfCompartmentEventSink*)this; } if (*ppvObj) { AddRef(); return S_OK; } return E_NOINTERFACE; } STDAPI_(ULONG) CTsfUiLessMode::CUIElementSink::AddRef() { return ++_cRef; } STDAPI_(ULONG) CTsfUiLessMode::CUIElementSink::Release() { LONG cr = --_cRef; if (_cRef == 0) { delete this; } return cr; } STDAPI CTsfUiLessMode::CUIElementSink::BeginUIElement(DWORD dwUIElementId, BOOL *pbShow) { ITfUIElement *pElement = GetUIElement(dwUIElementId); if (!pElement) return E_INVALIDARG; ITfReadingInformationUIElement *preading = NULL; ITfCandidateListUIElement *pcandidate = NULL; *pbShow = FALSE; if (!g_bCandList && SUCCEEDED(pElement->QueryInterface(__uuidof(ITfReadingInformationUIElement), (void **)&preading))) { MakeReadingInformationString(preading); preading->Release(); } else if (SUCCEEDED(pElement->QueryInterface(__uuidof(ITfCandidateListUIElement), (void **)&pcandidate))) { m_nCandidateRefCount++; MakeCandidateStrings(pcandidate); pcandidate->Release(); } pElement->Release(); return S_OK; } STDAPI CTsfUiLessMode::CUIElementSink::UpdateUIElement(DWORD dwUIElementId) { ITfUIElement *pElement = GetUIElement(dwUIElementId); if (!pElement) return E_INVALIDARG; ITfReadingInformationUIElement *preading = NULL; ITfCandidateListUIElement *pcandidate = NULL; if (!g_bCandList && SUCCEEDED(pElement->QueryInterface(__uuidof(ITfReadingInformationUIElement), (void **)&preading))) { MakeReadingInformationString(preading); preading->Release(); } else if (SUCCEEDED(pElement->QueryInterface(__uuidof(ITfCandidateListUIElement), (void **)&pcandidate))) { MakeCandidateStrings(pcandidate); pcandidate->Release(); } pElement->Release(); return S_OK; } STDAPI CTsfUiLessMode::CUIElementSink::EndUIElement(DWORD dwUIElementId) { ITfUIElement *pElement = GetUIElement(dwUIElementId); if (!pElement) return E_INVALIDARG; ITfReadingInformationUIElement *preading = NULL; if (!g_bCandList && SUCCEEDED(pElement->QueryInterface(__uuidof(ITfReadingInformationUIElement), (void **)&preading))) { g_dwCount = 0; preading->Release(); } ITfCandidateListUIElement *pcandidate = NULL; if (SUCCEEDED(pElement->QueryInterface(__uuidof(ITfCandidateListUIElement), (void **)&pcandidate))) { m_nCandidateRefCount--; if (m_nCandidateRefCount == 0) CloseCandidateList(); pcandidate->Release(); } pElement->Release(); return S_OK; } void CTsfUiLessMode::UpdateImeState(BOOL bResetCompartmentEventSink) { ITfCompartmentMgr* pcm; ITfCompartment* pTfOpenMode = NULL; ITfCompartment* pTfConvMode = NULL; if ( GetCompartments( &pcm, &pTfOpenMode, &pTfConvMode ) ) { VARIANT valOpenMode; VARIANT valConvMode; pTfOpenMode->GetValue( &valOpenMode ); pTfConvMode->GetValue( &valConvMode ); if ( valOpenMode.vt == VT_I4 ) { if ( g_bChineseIME ) { g_dwState = valOpenMode.lVal != 0 && valConvMode.lVal != 0 ? IMEUI_STATE_ON : IMEUI_STATE_ENGLISH; } else { g_dwState = valOpenMode.lVal != 0 ? IMEUI_STATE_ON : IMEUI_STATE_OFF; } } VariantClear( &valOpenMode ); VariantClear( &valConvMode ); if ( bResetCompartmentEventSink ) { SetupCompartmentSinks( FALSE, pTfOpenMode, pTfConvMode ); // Reset compartment sinks } pTfOpenMode->Release(); pTfConvMode->Release(); pcm->Release(); } } STDAPI CTsfUiLessMode::CUIElementSink::OnActivated(DWORD dwProfileType, LANGID langid, REFCLSID clsid, REFGUID catid, REFGUID guidProfile, HKL hkl, DWORD dwFlags) { static GUID TF_PROFILE_DAYI = { 0x037B2C25, 0x480C, 0x4D7F, 0xB0, 0x27, 0xD6, 0xCA, 0x6B, 0x69, 0x78, 0x8A }; g_iCandListIndexBase = IsEqualGUID( TF_PROFILE_DAYI, guidProfile ) ? 0 : 1; if ( IsEqualIID( catid, GUID_TFCAT_TIP_KEYBOARD ) && ( dwFlags & TF_IPSINK_FLAG_ACTIVE ) ) { g_bChineseIME = ( dwProfileType & TF_PROFILETYPE_INPUTPROCESSOR ) && langid == LANG_CHT; if ( dwProfileType & TF_PROFILETYPE_INPUTPROCESSOR ) { UpdateImeState(TRUE); } else g_dwState = IMEUI_STATE_OFF; OnInputLangChange(); } return S_OK; } STDAPI CTsfUiLessMode::CUIElementSink::OnChange(REFGUID rguid) { UpdateImeState(); return S_OK; } void CTsfUiLessMode::MakeReadingInformationString(ITfReadingInformationUIElement* preading) { UINT cchMax; UINT uErrorIndex = 0; BOOL fVertical; DWORD dwFlags; preading->GetUpdatedFlags(&dwFlags); preading->GetMaxReadingStringLength(&cchMax); preading->GetErrorIndex(&uErrorIndex); // errorIndex is zero-based preading->IsVerticalOrderPreferred(&fVertical); g_iReadingError = (int)uErrorIndex; g_bHorizontalReading = !fVertical; g_bReadingWindow = true; g_uCandPageSize = MAX_CANDLIST; g_dwSelection = g_iReadingError ? g_iReadingError - 1 : (DWORD)-1; g_iReadingError--; // g_iReadingError is used only in horizontal window, and has to be -1 if there's no error. #ifndef UNICODE if ( g_iReadingError > 0 ) { // convert g_iReadingError to byte based LPCSTR pszNext = g_szReadingString; for (int i = 0; i < g_iReadingError && pszNext && *pszNext; ++i) { pszNext = CharNext(pszNext); } if (pszNext) // should be non-NULL, but just in case { g_iReadingError = pszNext - g_szReadingString; } } #endif BSTR bstr; if (SUCCEEDED(preading->GetString(&bstr))) { if (bstr) { #ifndef UNICODE char szStr[COUNTOF(g_szReadingString)*2]; szStr[0] = 0; int iRc = WideCharToMultiByte(CP_ACP, 0, bstr, -1, szStr, sizeof(szStr), NULL, NULL); if (iRc >= sizeof(szStr)) { szStr[sizeof(szStr)-1] = 0; } StringCchCopy( g_szReadingString, COUNTOF(g_szReadingString), szStr ); #else StringCchCopy( g_szReadingString, COUNTOF(g_szReadingString), bstr ); #endif g_dwCount = cchMax; LPCTSTR pszSource = g_szReadingString; if ( fVertical ) { // for vertical reading window, copy each character to g_szCandidate array. for ( UINT i = 0; i < cchMax; i++ ) { LPTSTR pszDest = g_szCandidate[i]; if ( *pszSource ) { LPTSTR pszNextSrc = CharNext(pszSource); SIZE_T size = (LPSTR)pszNextSrc - (LPSTR)pszSource; CopyMemory( pszDest, pszSource, size ); pszSource = pszNextSrc; pszDest += size; } *pszDest = 0; } } else { g_szCandidate[0][0] = TEXT(' '); // hack to make rendering happen } SysFreeString(bstr); } } } void CTsfUiLessMode::MakeCandidateStrings(ITfCandidateListUIElement* pcandidate) { UINT uIndex = 0; UINT uCount = 0; UINT uCurrentPage = 0; UINT *IndexList = NULL; UINT uPageCnt = 0; DWORD dwPageStart = 0; DWORD dwPageSize = 0; BSTR bstr; pcandidate->GetSelection(&uIndex); pcandidate->GetCount(&uCount); pcandidate->GetCurrentPage(&uCurrentPage); g_dwSelection = (DWORD)uIndex; g_dwCount = (DWORD)uCount; g_bCandList = true; g_bReadingWindow = false; pcandidate->GetPageIndex(NULL, 0, &uPageCnt); if(uPageCnt > 0) { IndexList = (UINT *)ImeUiCallback_Malloc(sizeof(UINT)*uPageCnt); if(IndexList) { pcandidate->GetPageIndex(IndexList, uPageCnt, &uPageCnt); dwPageStart = IndexList[uCurrentPage]; dwPageSize = (uCurrentPage < uPageCnt-1) ? min(uCount, IndexList[uCurrentPage+1]) - dwPageStart: uCount - dwPageStart; } } g_uCandPageSize = min(dwPageSize, MAX_CANDLIST); g_dwSelection = g_dwSelection - dwPageStart; memset(&g_szCandidate, 0, sizeof(g_szCandidate)); for (UINT i = dwPageStart, j = 0; (DWORD)i < g_dwCount && j < g_uCandPageSize; i++, j++) { if (SUCCEEDED(pcandidate->GetString( i, &bstr ))) { if(bstr) { #ifndef UNICODE char szStr[COUNTOF(g_szCandidate[0])*2]; szStr[0] = 0; int iRc = WideCharToMultiByte(CP_ACP, 0, bstr, -1, szStr, sizeof(szStr), NULL, NULL); if (iRc >= sizeof(szStr)) { szStr[sizeof(szStr)-1] = 0; } ComposeCandidateLine( j, szStr ); #else ComposeCandidateLine( j, bstr ); #endif SysFreeString(bstr); } } } if (GETPRIMLANG() == LANG_KOREAN) { g_dwSelection = (DWORD)-1; } if(IndexList) { ImeUiCallback_Free(IndexList); } } ITfUIElement* CTsfUiLessMode::GetUIElement(DWORD dwUIElementId) { ITfUIElementMgr *puiem; ITfUIElement *pElement = NULL; if (SUCCEEDED(m_tm->QueryInterface(__uuidof(ITfUIElementMgr), (void **)&puiem))) { puiem->GetUIElement(dwUIElementId, &pElement); puiem->Release(); } return pElement; } BOOL CTsfUiLessMode::CurrentInputLocaleIsIme() { BOOL ret = FALSE; HRESULT hr; ITfInputProcessorProfiles *pProfiles; hr = CoCreateInstance(CLSID_TF_InputProcessorProfiles, NULL, CLSCTX_INPROC_SERVER, __uuidof(ITfInputProcessorProfiles), (LPVOID*)&pProfiles); if (SUCCEEDED(hr)) { ITfInputProcessorProfileMgr *pProfileMgr; hr = pProfiles->QueryInterface(__uuidof(ITfInputProcessorProfileMgr), (LPVOID*)&pProfileMgr); if (SUCCEEDED(hr)) { TF_INPUTPROCESSORPROFILE tip; hr = pProfileMgr->GetActiveProfile( GUID_TFCAT_TIP_KEYBOARD, &tip ); if (SUCCEEDED(hr)) { ret = ( tip.dwProfileType & TF_PROFILETYPE_INPUTPROCESSOR ) != 0; } pProfileMgr->Release(); } pProfiles->Release(); } return ret; } // Sets up or removes sink for UI element. // UI element sink should be removed when IME is disabled, // otherwise the sink can be triggered when a game has multiple instances of IME UI library. void CTsfUiLessMode::EnableUiUpdates(bool bEnable) { if ( m_tm == NULL || ( bEnable && m_dwUIElementSinkCookie != TF_INVALID_COOKIE ) || ( !bEnable && m_dwUIElementSinkCookie == TF_INVALID_COOKIE ) ) { return; } ITfSource *srcTm = NULL; HRESULT hr = E_FAIL; if (SUCCEEDED(hr = m_tm->QueryInterface(__uuidof(ITfSource), (void **)&srcTm))) { if ( bEnable ) { hr = srcTm->AdviseSink(__uuidof(ITfUIElementSink), (ITfUIElementSink*)m_TsfSink, &m_dwUIElementSinkCookie); } else { hr = srcTm->UnadviseSink(m_dwUIElementSinkCookie); m_dwUIElementSinkCookie = TF_INVALID_COOKIE; } srcTm->Release(); } } // Returns open mode compartments and compartment manager. // Function fails if it fails to acquire any of the objects to be returned. BOOL CTsfUiLessMode::GetCompartments( ITfCompartmentMgr** ppcm, ITfCompartment** ppTfOpenMode, ITfCompartment** ppTfConvMode ) { ITfCompartmentMgr* pcm = NULL; ITfCompartment* pTfOpenMode = NULL; ITfCompartment* pTfConvMode = NULL; static GUID _GUID_COMPARTMENT_KEYBOARD_INPUTMODE_CONVERSION = { 0xCCF05DD8, 0x4A87, 0x11D7, 0xA6, 0xE2, 0x00, 0x06, 0x5B, 0x84, 0x43, 0x5C }; HRESULT hr; if (SUCCEEDED(hr = m_tm->QueryInterface( IID_ITfCompartmentMgr, (void**)&pcm ))) { if (SUCCEEDED(hr = pcm->GetCompartment( GUID_COMPARTMENT_KEYBOARD_OPENCLOSE, &pTfOpenMode ))) { if (SUCCEEDED(hr = pcm->GetCompartment( _GUID_COMPARTMENT_KEYBOARD_INPUTMODE_CONVERSION, &pTfConvMode ))) { *ppcm = pcm; *ppTfOpenMode = pTfOpenMode; *ppTfConvMode = pTfConvMode; return TRUE; } pTfOpenMode->Release(); } pcm->Release(); } return FALSE; } // There are three ways to call this function: // SetupCompartmentSinks() : initialization // SetupCompartmentSinks(FALSE, openmode, convmode) : Resetting sinks. This is necessary as DaYi and Array IME resets compartment on switching input locale // SetupCompartmentSinks(TRUE) : clean up sinks BOOL CTsfUiLessMode::SetupCompartmentSinks( BOOL bRemoveOnly, ITfCompartment* pTfOpenMode, ITfCompartment* pTfConvMode ) { bool bLocalCompartments = false; ITfCompartmentMgr* pcm = NULL; BOOL bRc = FALSE; HRESULT hr = E_FAIL; if ( !pTfOpenMode && !pTfConvMode ) { bLocalCompartments = true; GetCompartments( &pcm, &pTfOpenMode, &pTfConvMode ); } if ( !( pTfOpenMode && pTfConvMode ) ) { // Invalid parameters or GetCompartments() has failed. return FALSE; } ITfSource *srcOpenMode = NULL; if (SUCCEEDED(hr = pTfOpenMode->QueryInterface( IID_ITfSource, (void**)&srcOpenMode ))) { // Remove existing sink for open mode if ( m_dwOpenModeSinkCookie != TF_INVALID_COOKIE ) { srcOpenMode->UnadviseSink( m_dwOpenModeSinkCookie ); m_dwOpenModeSinkCookie = TF_INVALID_COOKIE; } // Setup sink for open mode (toggle state) change if ( bRemoveOnly || SUCCEEDED(hr = srcOpenMode->AdviseSink( IID_ITfCompartmentEventSink, (ITfCompartmentEventSink*)m_TsfSink, &m_dwOpenModeSinkCookie ))) { ITfSource *srcConvMode = NULL; if (SUCCEEDED(hr = pTfConvMode->QueryInterface( IID_ITfSource, (void**)&srcConvMode ))) { // Remove existing sink for open mode if ( m_dwConvModeSinkCookie != TF_INVALID_COOKIE ) { srcConvMode->UnadviseSink( m_dwConvModeSinkCookie ); m_dwConvModeSinkCookie = TF_INVALID_COOKIE; } // Setup sink for open mode (toggle state) change if ( bRemoveOnly || SUCCEEDED(hr = srcConvMode->AdviseSink( IID_ITfCompartmentEventSink, (ITfCompartmentEventSink*)m_TsfSink, &m_dwConvModeSinkCookie ))) { bRc = TRUE; } srcConvMode->Release(); } } srcOpenMode->Release(); } if ( bLocalCompartments ) { pTfOpenMode->Release(); pTfConvMode->Release(); pcm->Release(); } return bRc; } WORD ImeUi_GetPrimaryLanguage() { return GETPRIMLANG(); }; DWORD ImeUi_GetImeId(UINT uIndex) { return GetImeId(uIndex); }; WORD ImeUi_GetLanguage() { return GETLANG(); }; PTSTR ImeUi_GetIndicatior() { return g_pszIndicatior ; }; bool ImeUi_IsShowReadingWindow() { return g_bReadingWindow; }; bool ImeUi_IsShowCandListWindow() { return g_bCandList; }; bool ImeUi_IsVerticalCand() { return g_bVerticalCand; }; bool ImeUi_IsHorizontalReading() { return g_bHorizontalReading; }; TCHAR* ImeUi_GetCandidate(UINT idx) { if ( idx < MAX_CANDLIST) return g_szCandidate[idx]; else return g_szCandidate[0]; } DWORD ImeUi_GetCandidateSelection() { return g_dwSelection; } DWORD ImeUi_GetCandidateCount() { return g_dwCount; } TCHAR* ImeUi_GetCompositionString() { return g_szCompositionString; } BYTE* ImeUi_GetCompStringAttr() { return g_szCompAttrString; } DWORD ImeUi_GetImeCursorChars() { return g_IMECursorChars; }
ImeUi.cpp
网页地址
文件地址
上一页
20/29
下一页
下载
( 103 KB )
Comments
Total ratings:
0
Average rating:
无评论
of 10
Would you like to comment?
Join now
, or
Logon
if you are already a member.