//-------------------------------------------------------------------------------------- // 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]; swprintf_s( 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]; wcscat_s( 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]; wcscat_s( 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\\" ); strcat_s( 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; }