aboutsummaryrefslogtreecommitdiff
path: root/samples/DX_APIUsage/DXUT/Optional/ImeUi.cpp
diff options
context:
space:
mode:
authorDave Clark <[email protected]>2018-02-28 17:22:22 -0500
committerDave Clark <[email protected]>2018-02-28 17:22:22 -0500
commit25528fd230f5f4298c35123a833cdb112675808e (patch)
treef5aca3f5ee5a7734df41e7b974a04c37ddff528e /samples/DX_APIUsage/DXUT/Optional/ImeUi.cpp
parentPush GfeSDK #173 (diff)
downloadgfesdk-25528fd230f5f4298c35123a833cdb112675808e.tar.xz
gfesdk-25528fd230f5f4298c35123a833cdb112675808e.zip
Push SDK # 1.1.186
Documentation updates.
Diffstat (limited to 'samples/DX_APIUsage/DXUT/Optional/ImeUi.cpp')
-rw-r--r--samples/DX_APIUsage/DXUT/Optional/ImeUi.cpp3662
1 files changed, 3662 insertions, 0 deletions
diff --git a/samples/DX_APIUsage/DXUT/Optional/ImeUi.cpp b/samples/DX_APIUsage/DXUT/Optional/ImeUi.cpp
new file mode 100644
index 0000000..f7a0cb2
--- /dev/null
+++ b/samples/DX_APIUsage/DXUT/Optional/ImeUi.cpp
@@ -0,0 +1,3662 @@
+//--------------------------------------------------------------------------------------
+// File: ImeUi.cpp
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+//--------------------------------------------------------------------------------------
+#include "dxut.h"
+#include "ImeUi.h"
+#include <math.h>
+#include <msctf.h>
+#include <malloc.h>
+#include <strsafe.h>
+
+// 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<IUnknown*>( 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;
+}
+