client/EterLib/IME.cpp

2303 lines
62 KiB
C++
Raw Normal View History

#include "StdAfx.h"
#include "IME.h"
#include "TextTag.h"
#include "../eterBase/Utils.h"
#include "msctf.h"
#include <oleauto.h>
#define COUNTOF(a) ( sizeof( a ) / sizeof( ( a )[0] ) )
int CIME::ms_compLen;
int CIME::ms_curpos;
int CIME::ms_lastpos;
wchar_t CIME::m_wText[IMESTR_MAXLEN];
#define MAKEIMEVERSION(major, minor) ((DWORD)(((BYTE)(major) << 24) | ((BYTE)(minor) << 16)))
#define IMEID_VER(dwId) ((dwId) & 0xffff0000)
#define IMEID_LANG(dwId) ((dwId) & 0x0000ffff)
#define GETLANG() LOWORD(CIME::ms_hklCurrent)
#define GETPRIMLANG() ((WORD)PRIMARYLANGID(GETLANG()))
#define GETSUBLANG() SUBLANGID(GETLANG())
#define LANG_CHT MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL)
#define LANG_CHS MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED)
// Chinese Traditional
#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_IMEFILENAME1 "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/Whistler)
#define IMEID_CHT_VER60 (LANG_CHT | MAKEIMEVERSION(6, 0)) // New(Phonetic/ChanJie)IME6.0 : 6.0.x.x // IME XP(w/WinXP SP1)
#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
// Chinese Simplized
#define _CHS_HKL ((HKL)0xE00E0804) // MSPY
#define _CHS_HKL_QQPINYIN ((HKL)0xE0210804) // QQ PinYin
#define _CHS_HKL_SOGOU ((HKL)0xE0220804) // Sougou PinYin
#define _CHS_HKL_GOOGLEPINYIN ((HKL)0xE0230804) // Google PinYin
#define CHS_IMEFILENAME1 "PINTLGNT.IME" // MSPY1.5/2/3
#define CHS_IMEFILENAME2 "MSSCIPYA.IME" // MSPY3 for OfficeXP
#define CHS_IMEFILENAME_QQPINYIN "QQPINYIN.IME" // QQ PinYin
#define CHS_IMEFILENAME_SOGOUPY "SOGOUPY.IME" // Sougou PinYin
#define CHS_IMEFILENAME_GOOGLEPINYIN2 "GOOGLEPINYIN2.IME" // Google PinYin 2
#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
enum { INDICATOR_NON_IME, INDICATOR_CHS, INDICATOR_CHT, INDICATOR_KOREAN, INDICATOR_JAPANESE };
enum { IMEUI_STATE_OFF, IMEUI_STATE_ON, IMEUI_STATE_ENGLISH };
#define LCID_INVARIANT MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT)
wchar_t s_aszIndicator[5][3] =
{
L"En",
L"\x7B80",
L"\x7E41",
L"\xAC00",
L"\x3042",
};
INPUTCONTEXT* (WINAPI * CIME::_ImmLockIMC)( HIMC );
BOOL (WINAPI * CIME::_ImmUnlockIMC)( HIMC );
LPVOID (WINAPI * CIME::_ImmLockIMCC)( HIMCC );
BOOL (WINAPI * CIME::_ImmUnlockIMCC)( HIMCC );
UINT (WINAPI * CIME::_GetReadingString)( HIMC, UINT, LPWSTR, PINT, BOOL*, PUINT );
BOOL (WINAPI * CIME::_ShowReadingWindow)( HIMC, BOOL );
bool CIME::ms_bInitialized = false;
bool CIME::ms_bDisableIMECompletely = false;
bool CIME::ms_bImeEnabled = false;
bool CIME::ms_bUILessMode = false;
bool CIME::ms_bCaptureInput = false;
bool CIME::ms_bChineseIME = false;
bool CIME::ms_bUseIMMCandidate = false;
HWND CIME::ms_hWnd;
HKL CIME::ms_hklCurrent;
char CIME::ms_szKeyboardLayout[KL_NAMELENGTH+1];
OSVERSIONINFOA CIME::ms_stOSVI;
HINSTANCE CIME::ms_hImm32Dll;
HINSTANCE CIME::ms_hCurrentImeDll;
DWORD CIME::ms_dwImeState;
DWORD CIME::ms_adwId[2] = { 0, 0 };
// IME Level
DWORD CIME::ms_dwIMELevel;
DWORD CIME::ms_dwIMELevelSaved;
// Candidate List
bool CIME::ms_bCandidateList;
DWORD CIME::ms_dwCandidateCount;
bool CIME::ms_bVerticalCandidate;
int CIME::ms_iCandListIndexBase;
WCHAR CIME::ms_wszCandidate[CIME::MAX_CANDLIST][MAX_CANDIDATE_LENGTH];
DWORD CIME::ms_dwCandidateSelection;
DWORD CIME::ms_dwCandidatePageSize;
// Reading Window
bool CIME::ms_bReadingInformation;
int CIME::ms_iReadingError = 0;
bool CIME::ms_bHorizontalReading;
std::vector<wchar_t> CIME::ms_wstrReading;
// Indicator
wchar_t* CIME::ms_wszCurrentIndicator;
IIMEEventSink* CIME::ms_pEvent;
int CIME::ms_ulbegin;
int CIME::ms_ulend;
UINT CIME::ms_uOutputCodePage = 0;
UINT CIME::ms_uInputCodePage = 0;
extern DWORD gs_codePage=0;
extern DWORD GetDefaultCodePage();
extern int ReadToken(const char* token);
extern const char* FindToken(const char* begin, const char* end);
///////////////////////////////////////////////////////////////////////////////
//
// 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;
// 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;
/*---------------------------------------------------------------------------*/ /* Public */
CIME::CIME()
{
ms_hWnd = NULL;
ms_bCandidateList = false;
ms_bReadingInformation = false;
Clear();
m_max = 0;
m_userMax = 0;
m_bOnlyNumberMode = FALSE;
m_hOrgIMC = NULL;
m_bEnablePaste = false;
m_bUseDefaultIME = false;
}
CIME::~CIME()
{
SAFE_FREE_LIBRARY(ms_hCurrentImeDll);
SAFE_FREE_LIBRARY(ms_hImm32Dll);
}
bool CIME::Initialize(HWND hWnd)
{
if(ms_bInitialized)
return true;
ms_hWnd = hWnd;
g_disableCicero.Initialize();
ms_stOSVI.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
GetVersionExA(&ms_stOSVI);
bool bUnicodeImm = false;
// IMM in NT or Win98 supports Unicode
if ( ms_stOSVI.dwPlatformId == VER_PLATFORM_WIN32_NT ||
( ms_stOSVI.dwMajorVersion > 4 ) ||
( ms_stOSVI.dwMajorVersion == 4 ) && ( ms_stOSVI.dwMinorVersion > 0 ) ) {
bUnicodeImm = true;
}
// Load ImmLock/ImmUnlock Function Proc
CHAR szPath[MAX_PATH+1];
ms_bDisableIMECompletely = false;
if(GetSystemDirectoryA(szPath, MAX_PATH+1)) {
strcat(szPath, "\\imm32.dll");
ms_hImm32Dll = LoadLibraryA(szPath);
if(ms_hImm32Dll)
{
_ImmLockIMC = (INPUTCONTEXT*(WINAPI *)(HIMC)) GetProcAddress(ms_hImm32Dll, "ImmLockIMC");
_ImmUnlockIMC = (BOOL(WINAPI *)(HIMC)) GetProcAddress(ms_hImm32Dll, "ImmUnlockIMC");
_ImmLockIMCC = (LPVOID(WINAPI *)(HIMCC)) GetProcAddress(ms_hImm32Dll, "ImmLockIMCC");
_ImmUnlockIMCC = (BOOL(WINAPI *)(HIMCC)) GetProcAddress(ms_hImm32Dll, "ImmUnlockIMCC");
BOOL (WINAPI* _ImmDisableTextFrameService)(DWORD) = (BOOL (WINAPI*)(DWORD))GetProcAddress(ms_hImm32Dll, "ImmDisableTextFrameService");
if ( _ImmDisableTextFrameService )
_ImmDisableTextFrameService( (DWORD)-1 );
} else {
ms_bDisableIMECompletely = true;
}
}
ms_bInitialized = true;
m_hOrgIMC = ImmGetContext( ms_hWnd );
ImmReleaseContext( ms_hWnd, m_hOrgIMC );
CheckInputLocale();
ChangeInputLanguageWorker();
SetSupportLevel(2);
ms_bUILessMode = CTsfUiLessMode::SetupSinks() != FALSE;
CheckToggleState();
if ( ms_bUILessMode )
{
ms_bChineseIME = ( GETPRIMLANG() == LANG_CHINESE ) && CTsfUiLessMode::CurrentInputLocaleIsIme();
CTsfUiLessMode::UpdateImeState();
}
return true;
}
void CIME::Uninitialize()
{
if ( !ms_bInitialized )
return;
CTsfUiLessMode::ReleaseSinks();
if ( ms_hWnd )
ImmAssociateContext(ms_hWnd, m_hOrgIMC);
ms_hWnd = NULL;
m_hOrgIMC = NULL;
SAFE_FREE_LIBRARY(ms_hCurrentImeDll);
SAFE_FREE_LIBRARY(ms_hImm32Dll);
g_disableCicero.Uninitialize();
ms_bInitialized = false;
}
void CIME::UseDefaultIME()
{
m_bUseDefaultIME = true;
}
bool CIME::IsIMEEnabled()
{
return ms_bImeEnabled;
}
void CIME::EnableIME(bool bEnable)
{
if (!ms_bInitialized || !ms_hWnd)
return;
if (ms_bDisableIMECompletely)
bEnable = false;
ImmAssociateContext(ms_hWnd, bEnable ? m_hOrgIMC : NULL);
ms_bImeEnabled = bEnable;
if (bEnable)
CheckToggleState();
CTsfUiLessMode::EnableUiUpdates(bEnable);
}
void CIME::DisableIME()
{
EnableIME(false);
}
void CIME::EnableCaptureInput()
{
ms_bCaptureInput = true;
}
void CIME::DisableCaptureInput()
{
ms_bCaptureInput = false;
}
bool CIME::IsCaptureEnabled()
{
return ms_bCaptureInput;
}
void CIME::Clear()
{
ms_lastpos = 0;
ms_curpos = 0;
ms_compLen = 0;
ms_ulbegin = 0;
ms_ulend = 0;
}
int CIME::GetReading(std::string & rstrText)
{
char reading[IMEREADING_MAXLEN];
if(ms_wstrReading.size() == 0)
return 0;
int readingLen = WideCharToMultiByte(ms_uOutputCodePage, 0, &ms_wstrReading[0], ms_wstrReading.size(), reading, sizeof(reading), NULL, NULL);
rstrText.append(GetCodePageText());
rstrText.append(reading, reading + readingLen);
return rstrText.size();
}
int CIME::GetReadingError()
{
return ms_iReadingError;
}
void CIME::SetMax(int iMax)
{
m_max = iMax;
}
void CIME::SetUserMax(int iMax)
{
m_userMax = iMax;
}
void CIME::SetText(const char* szText, int len)
{
ms_compLen = 0;
ms_ulbegin = 0;
ms_ulend = 0;
const char* begin = szText;
const char* end = begin + len;
const char* iter = FindToken(begin, end);
int m_wTextLen = sizeof(m_wText)/sizeof(wchar_t);
ms_lastpos = MultiByteToWideChar(ms_uInputCodePage, 0, begin, iter-begin, m_wText, m_wTextLen);
if (iter < end)
ms_lastpos += MultiByteToWideChar(ReadToken(iter), 0, (iter+5), end-(iter+5), m_wText+ms_lastpos, m_wTextLen-ms_lastpos);
ms_curpos = min(ms_curpos, ms_lastpos);
}
int CIME::GetText(std::string & rstrText, bool addCodePage)
{
int outCodePage = ms_uOutputCodePage;
int dataCodePage;
switch (outCodePage)
{
//case 1256: // ARABIC
case 1268: // VIETNAM
dataCodePage = CP_UTF8;
break;
default:
dataCodePage = outCodePage;
}
int len = 0;
char text[IMESTR_MAXLEN];
len += WideCharToMultiByte(dataCodePage, 0, m_wText, ms_curpos, text, sizeof(text)-len, NULL, NULL);
len += WideCharToMultiByte(dataCodePage, 0, m_wszComposition, ms_compLen, text+len, sizeof(text)-len, NULL, NULL);
len += WideCharToMultiByte(dataCodePage, 0, m_wText+ms_curpos, ms_lastpos-ms_curpos, text+len, sizeof(text)-len, NULL, NULL);
int i;
for(i=0; i<len; ++i)
if((BYTE)text[i] > 0x7F) break;
if(i == len)
{
rstrText.append(text, text+len);
}
else
{
rstrText.append(text, text+i);
//if (addCodePage)
// rstrText.append(GetCodePageText());
rstrText.append(text+i, text+len);
}
return rstrText.size();
}
const char* CIME::GetCodePageText()
{
static char szCodePage[16];
const int defCodePage = GetDefaultCodePage();
const int outCodePage = ms_uOutputCodePage;
if (outCodePage != defCodePage)
{
sprintf(szCodePage, "@%04d", outCodePage);
}
else
{
szCodePage[0] = 0;
}
return szCodePage;
}
int CIME::GetCodePage()
{
return ms_uOutputCodePage;
}
int CIME::GetCandidatePageCount()
{
return ms_dwCandidatePageSize;
}
int CIME::GetCandidateCount()
{
return ms_dwCandidateCount;
}
int CIME::GetCandidate(DWORD index, std::string & rstrText)
{
if(index >= MAX_CANDLIST)
return 0;
LPCWSTR wszText = ms_wszCandidate[index];
if(wszText == NULL)
return 0;
int wTextLen = wcslen(wszText);
if(wTextLen == 0)
return 0;
char text[IMESTR_MAXLEN];
int len = ::WideCharToMultiByte(CP_UTF8, 0, wszText, wTextLen, text, sizeof(text), 0, 0);
rstrText.append("@9999");
rstrText.append(text, text+len);
return wTextLen;
}
int CIME::GetCandidateSelection()
{
return ms_dwCandidateSelection;
}
void CIME::SetInputMode(DWORD dwMode)
{
HIMC hImc = ImmGetContext(ms_hWnd);
ImmSetConversionStatus(hImc, dwMode, IME_SMODE_AUTOMATIC);
ImmReleaseContext(ms_hWnd, hImc);
}
DWORD CIME::GetInputMode()
{
DWORD dwCMode, dwSMode;
HIMC hImc = ImmGetContext(ms_hWnd);
ImmGetConversionStatus(hImc, &dwCMode, &dwSMode);
ImmReleaseContext(ms_hWnd, hImc);
return dwCMode;
}
void CIME::SetNumberMode()
{
m_bOnlyNumberMode = TRUE;
}
void CIME::SetStringMode()
{
m_bOnlyNumberMode = FALSE;
}
void CIME::AddExceptKey(wchar_t key)
{
m_exceptKey.push_back(key);
}
void CIME::ClearExceptKey()
{
m_exceptKey.clear();
}
bool CIME::__IsWritable(wchar_t key)
{
if ( m_exceptKey.end() == std::find(m_exceptKey.begin(),m_exceptKey.end(),key) )
return true;
else
return false;
}
void CIME::EnablePaste(bool bFlag)
{
m_bEnablePaste = bFlag;
}
void CIME::PasteTextFromClipBoard()
{
if (!m_bEnablePaste)
return;
if (!OpenClipboard(NULL))
return;
HANDLE handle = GetClipboardData(CF_TEXT);
char * buffer = (char*)GlobalLock(handle);
std::string strClipboard = buffer;
GlobalUnlock(handle);
CloseClipboard();
if (strClipboard.empty())
return;
const char* begin = strClipboard.c_str();
const char* end = begin + strClipboard.length();
wchar_t m_wText[IMESTR_MAXLEN];
int wstrLen = MultiByteToWideChar(ms_uInputCodePage, 0, begin, end-begin, m_wText, IMESTR_MAXLEN);
InsertString(m_wText, wstrLen);
if(ms_pEvent)
ms_pEvent->OnUpdate();
}
void CIME::FinalizeString(bool bSend)
{
HIMC himc;
static bool s_bProcessing = false; // to avoid infinite recursion
if ( !ms_bInitialized || s_bProcessing || NULL == ( himc = ImmGetContext( ms_hWnd ) ) )
return;
s_bProcessing = true;
if (ms_dwIMELevel == 2 && bSend)
{
//// Send composition string to app.
//LONG lRet = lstrlenW( m_wszComposition );
////assert( lRet >= 2);
//// In case of CHT IME, don't send the trailing double byte space, if it exists.
//if ( GETLANG() == LANG_CHT && (lRet >= 1)
// && m_wszComposition[lRet - 1] == 0x3000 )
//{
// lRet--;
//}
//SendCompString();
}
//InitCompStringData();
// clear composition string in IME
ImmNotifyIME(himc, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
if (ms_bUILessMode)
{
// For some reason ImmNotifyIME doesn't work on DaYi and Array CHT IMEs. Cancel composition string by setting zero-length string.
ImmSetCompositionStringW(himc, SCS_SETSTR, L"", sizeof(wchar_t), L"", sizeof(wchar_t));
}
// 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(ms_hWnd, 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();
s_bProcessing = false;
}
int CIME::GetCompLen()
{
return ms_compLen;
}
int CIME::GetULBegin()
{
return ms_ulbegin;
}
int CIME::GetULEnd()
{
return ms_ulend;
}
void CIME::CloseCandidateList()
{
ms_bCandidateList = false;
ms_dwCandidateCount = 0;
memset(&ms_wszCandidate, 0, sizeof(ms_wszCandidate));
if(ms_pEvent)
ms_pEvent->OnCloseCandidateList();
}
void CIME::CloseReadingInformation()
{
CIME::ms_bReadingInformation = false;
if(CIME::ms_pEvent)
CIME::ms_pEvent->OnCloseReadingWnd();
}
void CIME::ChangeInputLanguage()
{
UINT uLanguage = (UINT) GETLANG();
CheckToggleState();
ChangeInputLanguageWorker();
if (uLanguage != GETLANG())
{
// Korean IME always uses level 3 support.
// Other languages use the level that is specified by ImeUi_SetSupportLevel()
SetSupportLevel( ( GETPRIMLANG() == LANG_KOREAN ) ? 3 : ms_dwIMELevelSaved );
}
if(ms_pEvent)
ms_pEvent->OnChangeCodePage();
//HWND hwndImeDef = ImmGetDefaultIMEWnd(ms_hWnd);
//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);
//}
}
void CIME::ChangeInputLanguageWorker()
{
if ( !ms_bUILessMode )
ms_iCandListIndexBase = ( ms_hklCurrent == _CHT_HKL_DAYI ) ? 0 : 1;
SetupImeApi();
}
void CIME::SetSupportLevel( DWORD dwImeLevel )
{
if ( dwImeLevel < 2 || 3 < dwImeLevel )
return;
if ( GETPRIMLANG() == LANG_KOREAN )
{
dwImeLevel = 3;
}
ms_dwIMELevel = dwImeLevel;
// cancel current composition string.
FinalizeString();
//SetCompStringColor();
}
/*---------------------------------------------------------------------------*/ /* Protected */
void CIME::IncCurPos()
{
if (ms_curpos < ms_lastpos)
{
int pos = FindColorTagEndPosition(m_wText + ms_curpos, ms_lastpos - ms_curpos);
if (pos > 0)
ms_curpos = min(ms_lastpos, max(0, ms_curpos + (pos + 1)));
else
++ms_curpos;
//++ms_curpos;
}
}
void CIME::DecCurPos()
{
if (ms_curpos > 0)
{
int pos = FindColorTagStartPosition(m_wText + ms_curpos - 1, ms_curpos);
if (pos > 0)
ms_curpos = min(ms_lastpos, max(0, ms_curpos - (pos + 1)));
else
--ms_curpos;
//--ms_curpos;
}
}
int CIME::GetCurPos()
{
int pos = GetTextTagOutputLen(m_wText, ms_curpos);
return pos;
//return ms_curpos;
}
void CIME::SetCurPos(int offset)
{
if (offset < 0 || offset > ms_lastpos)
{
ms_curpos = ms_lastpos;
return;
}
else
{
// offset<65><74> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20>ؽ<EFBFBD>Ʈ<EFBFBD><C6AE> <20><>ġ<EFBFBD><C4A1> <20>´<EFBFBD>. <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>ؾ<EFBFBD><D8BE><EFBFBD>.
//ms_curpos = min(ms_lastpos, offset);
ms_curpos = min(ms_lastpos, GetTextTagInternalPosFromRenderPos(m_wText, ms_lastpos, offset));
}
}
void CIME::DelCurPos()
{
if (ms_curpos < ms_lastpos)
{
int eraseCount = FindColorTagEndPosition(m_wText + ms_curpos, ms_lastpos - ms_curpos) + 1;
wcscpy(m_wText + ms_curpos, m_wText + ms_curpos + eraseCount);
ms_lastpos -= eraseCount;
ms_curpos = min(ms_lastpos, ms_curpos);
}
}
void CIME::PasteString(const char * str)
{
const char * begin = str;
const char * end = str + strlen(str);
wchar_t m_wText[IMESTR_MAXLEN];
int wstrLen = MultiByteToWideChar(ms_uInputCodePage, 0, begin, end - begin, m_wText, IMESTR_MAXLEN);
InsertString(m_wText, wstrLen);
if(ms_pEvent)
ms_pEvent->OnUpdate();
}
/*---------------------------------------------------------------------------*/ /* Private */
void CIME::InsertString(wchar_t* wString, int iSize)
{
if (IsMax(wString, iSize))
return;
if (ms_curpos < ms_lastpos)
memmove(m_wText+ms_curpos+iSize, m_wText+ms_curpos, sizeof(wchar_t)*(ms_lastpos-ms_curpos));
memcpy(m_wText+ms_curpos, wString, sizeof(wchar_t)*iSize);
ms_curpos += iSize;
ms_lastpos += iSize;
}
void CIME::OnChar(wchar_t c)
{
if (m_bOnlyNumberMode)
if (!iswdigit(c))
return;
if (!__IsWritable(c))
return;
InsertString(&c, 1);
}
UINT CIME::GetCodePageFromLang(LANGID langid)
{
unsigned pri_langid = PRIMARYLANGID(langid);
switch (pri_langid)
{
case LANG_JAPANESE:
//setlocale(LC_ALL, ".932");
return 932;
case LANG_KOREAN:
//setlocale(LC_ALL, ".949");
return 949;
case LANG_CHINESE:
{
switch (SUBLANGID(langid))
{
case SUBLANG_CHINESE_SIMPLIFIED:
case SUBLANG_CHINESE_SINGAPORE:
//setlocale(LC_ALL, ".936");
return 936;
case SUBLANG_CHINESE_TRADITIONAL:
case SUBLANG_CHINESE_MACAU:
case SUBLANG_CHINESE_HONGKONG:
//setlocale(LC_ALL, ".950");
return 950;
}
}
//setlocale(LC_ALL, ".936");
return 936;
case LANG_ARABIC:
return 1256;
case LANG_GREEK:
//setlocale(LC_ALL, ".1253");
return 1253;
case LANG_TURKISH:
//setlocale(LC_ALL, ".1254");
return 1254;
case LANG_HEBREW:
//setlocale(LC_ALL, ".1255");
return 1255;
case LANG_ESTONIAN:
case LANG_LATVIAN:
case LANG_LITHUANIAN:
//setlocale(LC_ALL, ".1257");
return 1257;
case LANG_VIETNAMESE:
return 1258;
case LANG_THAI:
//setlocale(LC_ALL, ".874");
return 874;
case LANG_CZECH:
case LANG_HUNGARIAN:
case LANG_POLISH:
case LANG_CROATIAN:
case LANG_MACEDONIAN:
case LANG_ROMANIAN:
case LANG_SLOVAK:
case LANG_SLOVENIAN:
//setlocale(LC_ALL, ".1250");
return 1250;
case LANG_RUSSIAN:
case LANG_BELARUSIAN:
case LANG_BULGARIAN:
case LANG_UKRAINIAN:
return 1251;
case LANG_GERMAN:
//_wsetlocale(LC_ALL, ".1252");
return 1252;
default:
//TraceError("UNKNOWN IME[%d]\n", langid);
//setlocale(LC_ALL, ".949");
return 1252;
}
}
void CIME::CompositionProcess(HIMC hImc)
{
ms_compLen = ImmGetCompositionStringW(hImc, GCS_COMPSTR, m_wszComposition, sizeof(m_wszComposition))/sizeof(wchar_t);
//OutputDebugStringW( L"Composition: " );
//OutputDebugStringW( m_wszComposition );
//for( int i=0; i < (int) ms_compLen * 2; i++ ) {
// LPBYTE pbyData = (LPBYTE) m_wszComposition;
// pbyData += i;
// WCHAR tszName[32];
// swprintf_s( tszName, L"%02X ", (unsigned int) *pbyData );
// OutputDebugStringW( tszName );
//}
//OutputDebugStringW( L"\n" );
if (IsMax(m_wszComposition, ms_compLen))
{
ImmNotifyIME(hImc, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
ms_compLen = 0;
}
}
void CIME::CompositionProcessBuilding(HIMC hImc)
{
int textLen = WideCharToMultiByte(ms_uOutputCodePage, 0, m_wText, ms_lastpos, 0, 0, NULL, NULL);
if (textLen >= m_max)
{
ImmNotifyIME(hImc, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
ms_compLen = 0;
return;
}
ms_compLen = ImmGetCompositionStringW(hImc, GCS_COMPSTR, m_wszComposition, sizeof(m_wszComposition))/sizeof(wchar_t);
//OutputDebugStringW( L"Composition: " );
//OutputDebugStringW( m_wszComposition );
//for( int i=0; i < (int) ms_compLen * 2; i++ ) {
// LPBYTE pbyData = (LPBYTE) m_wszComposition;
// pbyData += i;
// WCHAR tszName[32];
// swprintf_s( tszName, L"%02X ", (unsigned int) *pbyData );
// OutputDebugStringW( tszName );
//}
//OutputDebugStringW( L"\n" );
}
void CIME::ResultProcess(HIMC hImc)
{
wchar_t temp[IMESTR_MAXLEN];
int len = ImmGetCompositionStringW(hImc, GCS_RESULTSTR, temp, sizeof(temp))/sizeof(wchar_t);
if (len <= 0)
return;
InsertString(temp, len);
}
void CIME::AttributeProcess(HIMC hImc)
{
BYTE attribute[IMESTR_MAXLEN];
LONG attributeLen = ImmGetCompositionStringW(hImc, GCS_COMPATTR, &attribute, sizeof(attribute)) / sizeof(BYTE);
int start,end;
for(start=0; start<attributeLen; ++start) if(attribute[start]==ATTR_TARGET_CONVERTED || attribute[start]==ATTR_TARGET_NOTCONVERTED) break;
for(end=start; end<attributeLen; ++end) if(attribute[end]!=attribute[start]) break;
ms_ulbegin = start;
ms_ulend = end;
}
void CIME::CandidateProcess(HIMC hImc)
{
std::vector<BYTE> abyCandidate;
DWORD dwCandidateLen = ImmGetCandidateListW(hImc, 0, NULL, 0);
abyCandidate.resize(dwCandidateLen);
if(dwCandidateLen > 0) {
ms_bCandidateList = true;
CANDIDATELIST* lpCandidateList = (CANDIDATELIST*)(&abyCandidate[0]);
dwCandidateLen = ImmGetCandidateListW(hImc, 0, lpCandidateList, dwCandidateLen);
ms_dwCandidateSelection = lpCandidateList->dwSelection;
ms_dwCandidateCount = lpCandidateList->dwCount;
int iStartOfPage = 0;
if( GETLANG() == LANG_CHS ) {
// 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 < ms_dwCandidateCount; i++)
{
UINT uLen = lstrlenW((LPWSTR)((DWORD)lpCandidateList + lpCandidateList->dwOffset[i])) + (3 - sizeof(WCHAR));
if (uLen + cChars > maxCandChar)
{
if (i > ms_dwCandidateSelection)
{
break;
}
iStartOfPage = i;
cChars = uLen;
}
else
{
cChars += uLen;
}
}
ms_dwCandidatePageSize = i - iStartOfPage;
} else {
ms_dwCandidatePageSize = MIN( lpCandidateList->dwPageSize, MAX_CANDLIST );
iStartOfPage = ms_bUILessMode ? lpCandidateList->dwPageStart : (ms_dwCandidateSelection / (MAX_CANDLIST - 1)) * (MAX_CANDLIST - 1);
}
ms_dwCandidateSelection = ( GETLANG() == LANG_CHS && !GetImeId() ) ? (DWORD)-1 : ms_dwCandidateSelection - iStartOfPage;
//printf( "SEL: %d, START: %d, PAGED: %d\n", ms_dwCandidateSelection, iStartOfPage, ms_dwCandidatePageSize );
memset(&ms_wszCandidate, 0, sizeof(ms_wszCandidate));
for(UINT i = iStartOfPage, j = 0; (DWORD)i < lpCandidateList->dwCount && j < ms_dwCandidatePageSize; i++, j++) {
wcscpy( ms_wszCandidate[j], (LPWSTR)( (DWORD)lpCandidateList + lpCandidateList->dwOffset[i] ) );
}
// don't display selection in candidate list in case of Korean and old Chinese IME.
if ( GETPRIMLANG() == LANG_KOREAN || GETLANG() == LANG_CHT && !GetImeId() )
ms_dwCandidateSelection = (DWORD) -1;
}
}
void CIME::ReadingProcess(HIMC hImc)
{
if (!ms_adwId[0])
{
return;
}
DWORD dwErr = 0;
if (_GetReadingString)
{
UINT uMaxUiLen;
BOOL bVertical;
// Obtain the reading string size
int wstrLen = _GetReadingString(hImc, 0, NULL, (PINT)&dwErr, &bVertical, &uMaxUiLen);
if(wstrLen == 0) {
ms_wstrReading.resize(0);
} else {
wchar_t *wstr = (wchar_t*)alloca(sizeof(wchar_t) * wstrLen);
_GetReadingString(hImc, wstrLen, wstr, (PINT)&dwErr, &bVertical, &uMaxUiLen);
ms_wstrReading.assign(wstr, wstr+wstrLen);
}
ms_bHorizontalReading = (bVertical == 0);
} else {
// IMEs that doesn't implement Reading String API
wchar_t* temp = NULL;
DWORD tempLen = 0;
bool bUnicodeIme = false;
INPUTCONTEXT *lpIC = _ImmLockIMC(hImc);
if (lpIC == NULL)
{
temp = NULL;
tempLen = 0;
}
else
{
LPBYTE p = 0;
switch(ms_adwId[0])
{
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(lpIC->hPrivate) + 24);
if (!p) break;
tempLen = *(DWORD *)(p + 7 * 4 + 32 * 4);
dwErr = *(DWORD *)(p + 8 * 4 + 32 * 4);
temp = (wchar_t *)(p + 56);
bUnicodeIme = true;
break;
case IMEID_CHT_VER50: // 5.0.x.x // WinME
p = *(LPBYTE *)((LPBYTE)_ImmLockIMCC(lpIC->hPrivate) + 3 * 4);
if(!p) break;
p = *(LPBYTE *)((LPBYTE)p + 1*4 + 5*4 + 4*2);
if(!p) break;
tempLen = *(DWORD *)(p + 1*4 + (16*2+2*4) + 5*4 + 16);
dwErr = *(DWORD *)(p + 1*4 + (16*2+2*4) + 5*4 + 16 + 1*4);
temp = (wchar_t *)(p + 1*4 + (16*2+2*4) + 5*4);
bUnicodeIme = 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(lpIC->hPrivate) + 4);
if(!p) break;
p = *(LPBYTE *)((LPBYTE)p + 1*4 + 5*4);
if(!p) break;
tempLen = *(DWORD *)(p + 1*4 + (16*2+2*4) + 5*4 + 16 * 2);
dwErr = *(DWORD *)(p + 1*4 + (16*2+2*4) + 5*4 + 16 * 2 + 1*4);
temp = (wchar_t *) (p + 1*4 + (16*2+2*4) + 5*4);
bUnicodeIme = true;
break;
// the code tested only with Win 98 SE (MSPY 1.5/ ver 4.1.0.21)
case IMEID_CHS_VER41:
{
int nOffset;
nOffset = (ms_adwId[1] >= 0x00000002) ? 8 : 7;
p = *(LPBYTE *)((LPBYTE)_ImmLockIMCC(lpIC->hPrivate) + nOffset * 4);
if(!p) break;
tempLen = *(DWORD *)(p + 7*4 + 16*2*4);
dwErr = *(DWORD *)(p + 8*4 + 16*2*4);
dwErr = min(dwErr, tempLen);
temp = (wchar_t *)(p + 6*4 + 16*2*1);
bUnicodeIme = true;
}
break;
case IMEID_CHS_VER42: // 4.2.x.x // SCIME98 or MSPY2 (w/Office2k, Win2k, WinME, etc)
{
OSVERSIONINFOA osi;
osi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
GetVersionExA(&osi);
int nTcharSize = (osi.dwPlatformId == VER_PLATFORM_WIN32_NT) ? sizeof(wchar_t) : sizeof(char);
p = *(LPBYTE *)((LPBYTE)_ImmLockIMCC(lpIC->hPrivate) + 1*4 + 1*4 + 6*4);
if(!p) break;
tempLen = *(DWORD *)(p + 1*4 + (16*2+2*4) + 5*4 + 16 * nTcharSize);
dwErr = *(DWORD *)(p + 1*4 + (16*2+2*4) + 5*4 + 16 * nTcharSize + 1*4);
temp = (wchar_t *) (p + 1*4 + (16*2+2*4) + 5*4);
bUnicodeIme = (osi.dwPlatformId == VER_PLATFORM_WIN32_NT) ? true : false;
}
break;
default:
temp = NULL;
tempLen = 0;
break;
}
}
if(tempLen == 0) {
ms_wstrReading.resize(0);
} else {
if(bUnicodeIme) {
ms_wstrReading.assign(temp, temp+tempLen);
} else {
int wstrLen = MultiByteToWideChar(ms_uInputCodePage, 0, (char*)temp, tempLen, NULL, 0);
wchar_t* wstr = (wchar_t*)alloca(sizeof(wchar_t)*wstrLen);
MultiByteToWideChar(ms_uInputCodePage, 0, (char*)temp, tempLen, wstr, wstrLen);
ms_wstrReading.assign(wstr, wstr+wstrLen);
}
}
_ImmUnlockIMCC(lpIC->hPrivate);
_ImmUnlockIMC(hImc);
ms_bHorizontalReading = GetReadingWindowOrientation();
}
if (ms_wstrReading.size()) {
ms_bReadingInformation = true;
if(ms_pEvent)
ms_pEvent->OnOpenReadingWnd();
} else {
CloseReadingInformation();
}
}
bool CIME::IsMax(const wchar_t* wInput, int len)
{
if (ms_lastpos + len > IMESTR_MAXLEN)
return true;
int textLen = WideCharToMultiByte(ms_uOutputCodePage, 0, m_wText, ms_lastpos, 0, 0, NULL, NULL);
int inputLen = WideCharToMultiByte(ms_uOutputCodePage, 0, wInput, len, 0, 0, NULL, NULL);
//return textLen + inputLen > m_max;
if (textLen + inputLen > m_max)
return true;
else if (m_userMax != 0 && m_max != m_userMax)
{
std::wstring str = GetTextTagOutputString(m_wText, ms_lastpos);
std::wstring input = GetTextTagOutputString(wInput, len);
int textLen = WideCharToMultiByte(ms_uOutputCodePage, 0, str.c_str(), str.length(), 0, 0, NULL, NULL);
int inputLen = WideCharToMultiByte(ms_uOutputCodePage, 0, input.c_str(), input.length(), 0, 0, NULL, NULL);
return textLen + inputLen > m_userMax;
}
return false;
}
DWORD CIME::GetImeId( UINT uIndex )
{
static HKL hklPrev = 0;
char szTmp[1024];
if (uIndex >= COUNTOF(ms_adwId))
return 0;
HKL hkl = ms_hklCurrent;
if(hklPrev == hkl)
return ms_adwId[uIndex];
hklPrev = hkl;
DWORD dwLang = ((DWORD)hkl & 0xffff);
if ( ms_bUILessMode && GETLANG() == LANG_CHT ) {
// In case of Vista, artifitial value is returned so that it's not considered as older IME.
ms_adwId[0] = IMEID_CHT_VER_VISTA;
ms_adwId[1] = 0;
return ms_adwId[0];
}
if (!((ms_hklCurrent == _CHT_HKL_NEW_PHONETIC) || (ms_hklCurrent == _CHT_HKL_NEW_CHANG_JIE) || (ms_hklCurrent == _CHT_HKL_NEW_QUICK) || (ms_hklCurrent == _CHT_HKL_HK_CANTONESE) || (ms_hklCurrent == _CHS_HKL))) {
ms_adwId[0] = ms_adwId[1] = 0;
return 0;
}
if (!ImmGetIMEFileNameA(ms_hklCurrent, szTmp, (sizeof(szTmp) / sizeof(szTmp[0])) - 1)) {
ms_adwId[0] = ms_adwId[1] = 0;
return 0;
}
if (!_GetReadingString)
{
if ((CompareStringA(LCID_INVARIANT, NORM_IGNORECASE, szTmp, -1, CHT_IMEFILENAME1, -1) != CSTR_EQUAL) &&
(CompareStringA(LCID_INVARIANT, NORM_IGNORECASE, szTmp, -1, CHT_IMEFILENAME2, -1) != CSTR_EQUAL) &&
(CompareStringA(LCID_INVARIANT, NORM_IGNORECASE, szTmp, -1, CHT_IMEFILENAME3, -1) != CSTR_EQUAL) &&
(CompareStringA(LCID_INVARIANT, NORM_IGNORECASE, szTmp, -1, CHS_IMEFILENAME1, -1) != CSTR_EQUAL) &&
(CompareStringA(LCID_INVARIANT, NORM_IGNORECASE, szTmp, -1, CHS_IMEFILENAME2, -1) != CSTR_EQUAL))
{
ms_adwId[0] = ms_adwId[1] = 0;
return 0;
}
}
DWORD dwVerHandle;
DWORD dwVerSize = GetFileVersionInfoSize(szTmp, &dwVerHandle);
LANGID langId = LOWORD(ms_hklCurrent);
if (dwVerSize)
{
LPVOID lpVerBuffer = alloca(dwVerSize);
if (GetFileVersionInfo(szTmp, dwVerHandle, dwVerSize, lpVerBuffer))
{
LPVOID lpVerData;
UINT cbVerData;
if(VerQueryValue(lpVerBuffer, "\\", &lpVerData, &cbVerData))
{
DWORD dwVer = ((VS_FIXEDFILEINFO*) lpVerData)->dwFileVersionMS;
dwVer = (dwVer & 0x00ff0000) << 8 | (dwVer & 0x000000ff) << 16;
if (_GetReadingString
||
(langId == 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)))
||
(langId == LANG_CHS &&
(dwVer == MAKEIMEVERSION(4, 1) ||
dwVer == MAKEIMEVERSION(4, 2) ||
dwVer == MAKEIMEVERSION(5, 3))))
{
ms_adwId[0] = dwVer | langId;
ms_adwId[1] = ((VS_FIXEDFILEINFO*)lpVerData)->dwFileVersionLS;
return ms_adwId[uIndex];
}
}
}
}
ms_adwId[0] = ms_adwId[1] = 0;
return ms_adwId[0];
}
bool CIME::GetReadingWindowOrientation()
{
bool bHorizontalReading = (ms_hklCurrent == _CHS_HKL) || (ms_hklCurrent == _CHT_HKL_NEW_CHANG_JIE) || (ms_adwId[0] == 0);
if(!bHorizontalReading && (GETLANG() == LANG_CHT))
{
char szRegPath[MAX_PATH];
HKEY hKey;
DWORD dwVer = ms_adwId[0] & 0xFFFF0000;
strcpy(szRegPath, "software\\microsoft\\windows\\currentversion\\");
strcat(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)
)
{
bHorizontalReading = true;
}
}
RegCloseKey(hKey);
}
}
return bHorizontalReading;
}
void CIME::SetupImeApi()
{
char szImeFile[MAX_PATH + 1];
_GetReadingString = NULL;
_ShowReadingWindow = NULL;
ms_bUseIMMCandidate = false;
if(ImmGetIMEFileNameA(ms_hklCurrent, szImeFile, COUNTOF(szImeFile) - 1) == 0)
return;
if(stricmp(szImeFile, CHS_IMEFILENAME_QQPINYIN) == 0 || stricmp(szImeFile, CHS_IMEFILENAME_SOGOUPY) == 0 || stricmp(szImeFile, CHS_IMEFILENAME_GOOGLEPINYIN2) == 0)
ms_bUseIMMCandidate = true;
if (ms_bUILessMode)
return;
SAFE_FREE_LIBRARY(ms_hCurrentImeDll);
ms_hCurrentImeDll = LoadLibraryA(szImeFile);
if (ms_hCurrentImeDll) {
_GetReadingString = (UINT (WINAPI*)(HIMC, UINT, LPWSTR, PINT, BOOL*, PUINT)) (GetProcAddress(ms_hCurrentImeDll, "GetReadingString"));
_ShowReadingWindow =(BOOL (WINAPI*)(HIMC, BOOL)) (GetProcAddress(ms_hCurrentImeDll, "ShowReadingWindow"));
if(_ShowReadingWindow) {
HIMC hImc = ImmGetContext(ms_hWnd);
if(hImc) {
_ShowReadingWindow(hImc, false);
ImmReleaseContext(ms_hWnd, hImc);
}
}
}
}
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;
}
void CIME::CheckInputLocale()
{
static UINT s_uPrevCodePage = 0xFFFF;
static HKL s_hklPrev = NULL;
ms_hklCurrent = GetKeyboardLayout( 0 );
if ( s_hklPrev == ms_hklCurrent )
return;
s_hklPrev = ms_hklCurrent;
char szCodePage[8];
int iRc = GetLocaleInfoA( MAKELCID( GETLANG(), SORT_DEFAULT ), LOCALE_IDEFAULTANSICODEPAGE, szCodePage, COUNTOF( szCodePage ) ); iRc;
ms_uInputCodePage = _strtoul( szCodePage, NULL, 0 );
if ( s_uPrevCodePage == ms_uInputCodePage )
return;
s_uPrevCodePage = ms_uInputCodePage;
GetKeyboardLayoutName(ms_szKeyboardLayout);
switch (GETPRIMLANG())
{
case LANG_KOREAN:
ms_bVerticalCandidate = false;
ms_wszCurrentIndicator = s_aszIndicator[INDICATOR_KOREAN];
break;
case LANG_JAPANESE:
ms_bVerticalCandidate = true;
ms_wszCurrentIndicator = s_aszIndicator[INDICATOR_JAPANESE];
break;
case LANG_CHINESE:
ms_bVerticalCandidate = true;
switch(GETSUBLANG())
{
case SUBLANG_CHINESE_SIMPLIFIED:
case SUBLANG_CHINESE_SINGAPORE:
//ms_bVerticalCandidate = (GetImeId() == 0);
ms_bVerticalCandidate = false;
ms_wszCurrentIndicator = s_aszIndicator[INDICATOR_CHS];
break;
case SUBLANG_CHINESE_TRADITIONAL:
case SUBLANG_CHINESE_HONGKONG:
case SUBLANG_CHINESE_MACAU:
ms_wszCurrentIndicator = s_aszIndicator[INDICATOR_CHT];
break;
default: // unsupported sub-language
ms_wszCurrentIndicator = s_aszIndicator[INDICATOR_NON_IME];
break;
}
break;
default:
ms_wszCurrentIndicator = s_aszIndicator[INDICATOR_NON_IME];
break;
}
if (ms_wszCurrentIndicator == s_aszIndicator[INDICATOR_NON_IME])
{
char szLang[10];
GetLocaleInfoA(MAKELCID(GETLANG(), SORT_DEFAULT), LOCALE_SABBREVLANGNAME, szLang, sizeof(szLang));
ms_wszCurrentIndicator[0] = szLang[0];
ms_wszCurrentIndicator[1] = towlower(szLang[1]);
}
// <20>ƶ<EFBFBD><C6B6><EFBFBD><EEBFA1> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20>ڵ<EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20>ٲ<EFBFBD><D9B2><EFBFBD> <20>ʴ´<CAB4>
// <20><><EFBFBD><20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20>ʴ´<CAB4>.
if(ms_uOutputCodePage != 1256) {
ms_uOutputCodePage = ms_uInputCodePage;
Clear();
}
//for ( int i = 0; i < 256; i++ )
//{
// LeadByteTable[i] = (BYTE)IsDBCSLeadByteEx( g_uCodePage, (BYTE)i );
//}
}
void CIME::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 ( ms_bUILessMode )
return;
/* Check Toggle State */
bool bIme = ImmIsIME( ms_hklCurrent ) != 0
&& ( ( 0xF0000000 & (DWORD)ms_hklCurrent ) == 0xE0000000 ); // Hack to detect IME correctly. When IME is running as TIP, ImmIsIME() returns true for CHT US keyboard.
ms_bChineseIME = ( GETPRIMLANG() == LANG_CHINESE ) && bIme;
HIMC himc;
if (NULL != (himc = ImmGetContext(ms_hWnd))) {
if (ms_bChineseIME) {
DWORD dwConvMode, dwSentMode;
ImmGetConversionStatus(himc, &dwConvMode, &dwSentMode);
ms_dwImeState = ( dwConvMode & IME_CMODE_NATIVE ) ? IMEUI_STATE_ON : IMEUI_STATE_ENGLISH;
}
else
{
ms_dwImeState = ( bIme && ImmGetOpenStatus( himc ) != 0 ) ? IMEUI_STATE_ON : IMEUI_STATE_OFF;
}
ImmReleaseContext(ms_hWnd, himc);
}
else
ms_dwImeState = IMEUI_STATE_OFF;
}
///////////////////////////////////////////////////////////////////////////////
//
// 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;
//BSTR bstrDesc;
//OutputDebugStringW(L"BEGINUI: ");
//pElement->GetDescription(&bstrDesc);
//OutputDebugStringW(bstrDesc);
//OutputDebugStringW(L"\n");
if (SUCCEEDED(pElement->QueryInterface(__uuidof(ITfReadingInformationUIElement), (void **)&preading)))
{
MakeReadingInformationString(preading);
if(CIME::ms_pEvent)
CIME::ms_pEvent->OnOpenReadingWnd();
preading->Release();
}
else if (SUCCEEDED(pElement->QueryInterface(__uuidof(ITfCandidateListUIElement), (void **)&pcandidate)))
{
m_nCandidateRefCount++;
MakeCandidateStrings(pcandidate);
if(CIME::ms_pEvent)
CIME::ms_pEvent->OnOpenCandidateList();
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;
//BSTR bstrDesc;
//pElement->GetDescription(&bstrDesc);
//OutputDebugStringW(L"UPDATEUI: ");
//OutputDebugStringW(bstrDesc);
//OutputDebugStringW(L"\n");
if (SUCCEEDED(pElement->QueryInterface(__uuidof(ITfReadingInformationUIElement), (void **)&preading)))
{
MakeReadingInformationString(preading);
if(CIME::ms_pEvent)
CIME::ms_pEvent->OnOpenReadingWnd();
preading->Release();
}
else if (SUCCEEDED(pElement->QueryInterface(__uuidof(ITfCandidateListUIElement), (void **)&pcandidate)))
{
MakeCandidateStrings(pcandidate);
if(CIME::ms_pEvent)
CIME::ms_pEvent->OnOpenCandidateList();
pcandidate->Release();
}
pElement->Release();
return S_OK;
}
STDAPI CTsfUiLessMode::CUIElementSink::EndUIElement(DWORD dwUIElementId)
{
ITfUIElement *pElement = GetUIElement(dwUIElementId);
if (!pElement)
return E_INVALIDARG;
//BSTR bstrDesc;
//OutputDebugStringW(L"ENDUI: ");
//pElement->GetDescription(&bstrDesc);
//OutputDebugStringW(bstrDesc);
//OutputDebugStringW(L"\n");
ITfReadingInformationUIElement *preading = NULL;
if (SUCCEEDED(pElement->QueryInterface(__uuidof(ITfReadingInformationUIElement), (void **)&preading)))
{
CIME::CloseReadingInformation();
preading->Release();
}
ITfCandidateListUIElement *pcandidate = NULL;
if (SUCCEEDED(pElement->QueryInterface(__uuidof(ITfCandidateListUIElement), (void **)&pcandidate)))
{
m_nCandidateRefCount--;
if (m_nCandidateRefCount == 0)
CIME::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 ( CIME::ms_bChineseIME )
{
CIME::ms_dwImeState = valOpenMode.lVal != 0 && valConvMode.lVal != 0 ? IMEUI_STATE_ON : IMEUI_STATE_ENGLISH;
}
else
{
CIME::ms_dwImeState = 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 };
CIME::ms_iCandListIndexBase = IsEqualGUID( TF_PROFILE_DAYI, guidProfile ) ? 0 : 1;
if ( IsEqualIID( catid, GUID_TFCAT_TIP_KEYBOARD ) && ( dwFlags & TF_IPSINK_FLAG_ACTIVE ) )
{
CIME::ms_bChineseIME = ( dwProfileType & TF_PROFILETYPE_INPUTPROCESSOR ) && langid == LANG_CHT;
if ( dwProfileType & TF_PROFILETYPE_INPUTPROCESSOR )
{
UpdateImeState(TRUE);
}
else
CIME::ms_dwImeState = IMEUI_STATE_OFF;
CIME::ChangeInputLanguage();
}
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;
CIME::ms_wstrReading.resize(0);
preading->GetUpdatedFlags(&dwFlags);
preading->GetMaxReadingStringLength(&cchMax);
preading->GetErrorIndex(&uErrorIndex); // errorIndex is zero-based
preading->IsVerticalOrderPreferred(&fVertical);
CIME::ms_iReadingError = (int)uErrorIndex;
CIME::ms_bHorizontalReading = !fVertical;
CIME::ms_bReadingInformation = true;
BSTR bstr;
if (SUCCEEDED(preading->GetString(&bstr)))
{
if (bstr)
{
CIME::ms_wstrReading.assign( (wchar_t *) bstr, (wchar_t *) bstr+lstrlenW(bstr) );
LPCWSTR pszSource = &(CIME::ms_wstrReading[0]);
if ( fVertical )
{
CIME::ms_dwCandidatePageSize = CIME::MAX_CANDLIST;
// ms_iReadingError is used only in horizontal window, and has to be -1 if there's no error.
CIME::ms_dwCandidateSelection = CIME::ms_iReadingError ? CIME::ms_iReadingError - 1 : (DWORD)-1;
CIME::ms_dwCandidateCount = cchMax;
// for vertical reading window, copy each character to g_szCandidate array.
for ( UINT i = 0; i < cchMax; i++ )
{
LPWSTR pszDest = CIME::ms_wszCandidate[i];
if ( *pszSource )
{
LPWSTR pszNextSrc = CharNextW(pszSource);
SIZE_T size = (LPSTR)pszNextSrc - (LPSTR)pszSource;
CopyMemory( pszDest, pszSource, size );
pszSource = pszNextSrc;
pszDest += size;
}
*pszDest = 0;
}
}
//else
//{
// CIME::ms_wszCandidate[0][0] = L' '; // 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);
CIME::ms_dwCandidateSelection = (DWORD)uIndex;
CIME::ms_dwCandidateCount = (DWORD)uCount;
CIME::ms_bCandidateList = true;
pcandidate->GetPageIndex(NULL, 0, &uPageCnt);
if(uPageCnt > 0)
{
IndexList = (UINT *) 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;
}
}
CIME::ms_dwCandidatePageSize = min(dwPageSize, CIME::MAX_CANDLIST);
CIME::ms_dwCandidateSelection = CIME::ms_dwCandidateSelection - dwPageStart;
memset(&CIME::ms_wszCandidate, 0, sizeof(CIME::ms_wszCandidate));
for (UINT i = dwPageStart, j = 0; (DWORD)i < CIME::ms_dwCandidateCount && j < CIME::ms_dwCandidatePageSize; i++, j++)
{
if (SUCCEEDED(pcandidate->GetString( i, &bstr )))
{
if(bstr)
{
wcscpy( CIME::ms_wszCandidate[j], bstr );
SysFreeString(bstr);
}
}
}
//OutputDebugStringW( L"\n" );
if (GETPRIMLANG() == LANG_KOREAN)
{
CIME::ms_dwCandidateSelection = (DWORD)-1;
}
if(IndexList)
{
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;
}
/* IME Message Handler */
LRESULT CIME::WMInputLanguage(HWND hWnd, UINT /*uiMsg*/, WPARAM /*wParam*/, LPARAM lParam)
{
ChangeInputLanguage();
return 0;
}
LRESULT CIME::WMStartComposition(HWND /*hWnd*/, UINT /*uiMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/)
{
return 1L;
}
LRESULT CIME::WMComposition(HWND hWnd, UINT /*uiMsg*/, WPARAM /*wParam*/, LPARAM lParam)
{
LRESULT result = 0;
if(ms_bCaptureInput == false)
return 0;
HIMC hImc = ImmGetContext(hWnd);
if(hImc == NULL)
return 0;
if(lParam&GCS_RESULTSTR)
ResultProcess(hImc);
if(lParam&GCS_COMPATTR)
AttributeProcess(hImc);
if(lParam&GCS_COMPSTR)
{
if (ms_uOutputCodePage == 950) // <20><20><><EFBFBD><EFBFBD> <20>Է<EFBFBD> ó<><C3B3>
{
if (lParam&GCS_COMPATTR)
CompositionProcessBuilding(hImc);
else
CompositionProcess(hImc);
}
else
{
CompositionProcess(hImc);
}
}
ImmReleaseContext(hWnd, hImc);
if(ms_pEvent)
ms_pEvent->OnUpdate();
return (result);
}
LRESULT CIME::WMEndComposition(HWND /*hWnd*/, UINT /*uiMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/)
{
ms_compLen = 0;
ms_ulbegin = 0;
ms_ulend = 0;
if(ms_pEvent)
ms_pEvent->OnUpdate();
return 0L;
}
LRESULT CIME::WMNotify(HWND hWnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
LRESULT result = 0;
if(ms_bCaptureInput == false)
return 0;
switch (wParam) {
case IMN_OPENCANDIDATE:
case IMN_CHANGECANDIDATE: {
if (ms_bUILessMode && !ms_bUseIMMCandidate)
break;
HIMC hImc = ImmGetContext(hWnd);
if (hImc == NULL)
break;
CandidateProcess(hImc);
if (!m_bUseDefaultIME) {
if(ms_pEvent)
ms_pEvent->OnOpenCandidateList();
} else
result = ::DefWindowProc(hWnd, uiMsg, wParam, lParam);
ImmReleaseContext(hWnd, hImc);
break;
}
case IMN_CLOSECANDIDATE:
if (ms_bUILessMode && !ms_bUseIMMCandidate)
break;
if (!m_bUseDefaultIME)
CloseCandidateList();
else
result = DefWindowProc(hWnd, uiMsg, wParam, lParam);
break;
case IMN_SETCONVERSIONMODE:
case IMN_SETOPENSTATUS:
if (ms_bUILessMode)
break;
CheckToggleState();
break;
case IMN_PRIVATE: {
if (ms_bUILessMode)
break;
HIMC hImc = ImmGetContext(hWnd);
if (hImc == NULL)
break;
ReadingProcess(hImc);
// Trap some messages to hide reading window
switch(ms_adwId[0])
{
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))
return 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))
return true;
break;
}
ImmReleaseContext(hWnd, hImc);
break;
}
}
if(ms_pEvent)
ms_pEvent->OnUpdate();
return result;
}
LRESULT CIME::WMChar(HWND /*hWnd*/, UINT /*uiMsg*/, WPARAM wParam, LPARAM lParam)
{
unsigned char c = unsigned char(wParam & 0xff);
switch (c)
{
case 8:
if(ms_bCaptureInput == false)
return 0;
if (ms_curpos > 0)
{
DecCurPos();
DelCurPos();
}
if(ms_pEvent)
ms_pEvent->OnUpdate();
return 0;
break;
default:
if(ms_pEvent) {
if (ms_pEvent->OnWM_CHAR(wParam, lParam))
break;
}
if(ms_bCaptureInput == false)
return 0;
wchar_t w[10];
MultiByteToWideChar(ms_uInputCodePage, 0, (char*)&c, 1, w, 1);
OnChar(w[0]);
if (w[0] == L'|')
OnChar(w[0]);
if(ms_pEvent)
ms_pEvent->OnUpdate();
break;
}
return 0;
}