#include "pch.hpp"

//
//===========================================================================//
// File:	 RenderIME.cpp													 //
// Project:  GameOS					                                         //
// Contents: Render and manipulate the IME									 //
//---------------------------------------------------------------------------//
// Date      Who    Modification                                             //
// 12/12/99  JTR    Original code: Kazuyuki S. Adapted to GameOS.			 //
// 04/14/00  JTR    Major Modifications.									 //
// 04/18/00  KS     Corrections and Cleanup.							     //
// 05/08/00  KS     Switched to LeadByteTable							     //
// 05/09/00  KS     Corrections and Cleanup.							     //
// 06/16/00 05:22 by yutaka.
//---------------------------------------------------------------------------//
// Copyright (C) 2000 Microsoft Corp.						                 //
// All Rights reserved worldwide                                             //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               //
//===========================================================================//
//

#include "LocalizationManager.hpp"
#include <mbstring.h>
#include <mbctype.h>
#include "font3d.hpp"

extern DWORD gLanguageDLL;
//
// From WinProc.cpp
//
extern float FontSize;
extern HGOSFONT3D FontHandle;
extern DWORD FontColor;
extern HFONT oldFont;
extern HDC DesktopDC;
//
// IME Variables
//
#define MAX_CANDLIST 9
#define INTERNAL_DEPENDENCY			// Comment this out if your not using the
									// GameOS library.
#define CODEPAGE_CHS		936
#define CODEPAGE_CHT		950
#define CODEPAGE_KOR		949
#define CODEPAGE_JPN		932

#define _TAIWAN_HKL			((HKL)0xE0080404)
#define _TAIWAN_HKL2		((HKL)0xE0090404) // New Chang Jie
#define _TAIWAN_IMEFILENAME	"TINTLGNT.IME"
#define _TAIWAN_IMEFILENAME2	"CINTLGNT.IME" // New Chang Jie
#define _TAIWAN_IMEFILENAME3	"MSTCIPHA.IME" // Phonetic 5.1
#define _TAIWAN_IMEVERSION42 (0x00040002) // New(Phonetic/ChanJie)IME98  : 4.2.x.x // Win98
#define _TAIWAN_IMEVERSION43 (0x00040003) // New(Phonetic/ChanJie)IME98a : 4.3.x.x // Win2k
#define _TAIWAN_IMEVERSION44 (0x00040004) // New ChanJie IME98b          : 4.4.x.x // WinXP
#define _TAIWAN_IMEVERSION50 (0x00050000) // New(Phonetic/ChanJie)IME5.0 : 5.0.x.x // WinME
#define _TAIWAN_IMEVERSION51 (0x00050001) // New(Phonetic/ChanJie)IME5.1 : 5.1.x.x // IME2002(w/OfficeXP)
#define _TAIWAN_IMEVERSION52 (0x00050002) // New(Phonetic/ChanJie)IME5.2 : 5.2.x.x // IME2002a(w/WinXP)

#ifndef INTERNAL_DEPENDENCY
struct gos_CaretInfo
{
	DWORD		fontX;
	DWORD		fontY;
	float		fontSize;
	HGOSFONT3D	fontHandle;
	DWORD		fontColorComp;
	DWORD		fontColorCand;
	bool		fontBold;
	bool		fontItalic;
	bool		fontProportional;
	RECT		margins ;
} ;
#endif

gosIME_Appearance gSkinIME;

struct _SkinCompStr
{
	DWORD	colorInput;
	DWORD	colorTargetConv;
	DWORD	colorConverted;
	DWORD	colorTargetNotConv;
	DWORD	colorInputErr;
};

_SkinCompStr gSkinCompStr;

// ...from Win98DDK version of IMM.H
typedef struct tagINPUTCONTEXT {                        
    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];                   
} INPUTCONTEXT, *PINPUTCONTEXT, NEAR *NPINPUTCONTEXT, FAR *LPINPUTCONTEXT;  

extern HWND hWindow;
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][256];
static DWORD g_dwSelection, g_dwCount;
DWORD g_bDisableImeCompletely = false;
static DWORD g_dwIMELevel;
static char g_szMultiLineCompString[256*2];
static bool g_bReadingWindow = false;
static bool g_bHorizontalReading = false;
static bool g_bVerticalCand = true;
static UINT g_uCodePage = 0;
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 char g_szImeFileName[MAX_PATH];
static char g_szReadingString[32];	// Used only in case of horizontal reading window
static int g_szReadingError;	// Used only in case of horizontal reading window

// Global Variables
gos_CaretInfo g_CaretInfo;
HFONT g_hIMEFont = NULL;
bool g_bComposingIME = false;
DWORD swirl = 0;
double lastSwirl;
unsigned char g_SymbolIME[3];
const char* c_szCandListFormat = "%d %s";
const char* c_szCandListFormatHorizontal = "%d%s ";

extern float FontMinX, FontMaxX;
extern float FontMinY, FontMaxY;
extern bool FontBold;
extern bool FontItalic;
extern bool FontProportional;
extern bool InsideBeginScene;

extern HIMC (WINAPI* _ImmGetContext)(HWND hW);
extern BOOL (WINAPI* _ImmReleaseContext)( HWND hW, HIMC hImc );
extern HIMC (WINAPI* _ImmAssociateContext)( HWND hW, HIMC himc );
extern LONG (WINAPI* _ImmGetCompositionString)( HIMC himc, DWORD dwIndex, LPVOID lpBuf, DWORD dwBufLen );
extern BOOL (WINAPI* _ImmSetCompositionString)( HIMC hIMC, DWORD dwIndex, LPVOID lpBuf, DWORD dwBufLen, LPVOID lpRead, DWORD dwReadLen );
extern BOOL (WINAPI* _ImmGetOpenStatus)( HIMC himc );
extern BOOL (WINAPI* _ImmSetOpenStatus)( HIMC himc, BOOL flag );
extern BOOL (WINAPI* _ImmGetConversionStatus)( HIMC himc, LPDWORD lpfdwConversion, LPDWORD lpfdwSentence );
extern DWORD (WINAPI* _ImmGetCandidateList)( HIMC himc, DWORD deIndex, LPCANDIDATELIST lpCandList, DWORD dwBufLen );
extern LPINPUTCONTEXT (WINAPI* _ImmLockIMC)(HIMC hIMC);
extern BOOL (WINAPI* _ImmUnlockIMC)(HIMC hIMC);
extern LPVOID (WINAPI* _ImmLockIMCC)(HIMCC hIMCC);
extern BOOL (WINAPI* _ImmUnlockIMCC)(HIMCC hIMCC);
extern HWND (WINAPI* _ImmGetDefaultIMEWnd)(HWND hW);
extern HWND (WINAPI* _ImmGetIMEFileName)(HKL hKL, LPTSTR lpszFileName, UINT uBufLen);
extern UINT (WINAPI* _ImmGetVirtualKey)( HWND hW );
extern BOOL (WINAPI* _ImmNotifyIME)(HIMC, DWORD dwAction, DWORD dwIndex, DWORD dwValue);
extern BOOL (WINAPI* _ImmSetConversionStatus)(HIMC hIMC, DWORD fdwConversion, DWORD fdwSentence);
extern BOOL (WINAPI* _ImmSimulateHotKey)(HWND hWnd, DWORD dwHotKeyID);
extern BOOL (WINAPI* _ImmIsIME)(HKL hKL);

#ifdef INTERNAL_DEPENDENCY
extern void GDIGetChrSize( DWORD * w, DWORD * h, int index);
extern void GDICreateFont( char * string = NULL);
extern HDC gdiDC;
extern BYTE LeadByteTable[256];
#define _isleadbyte(b) (LeadByteTable[(unsigned char)(b)])
#else
#define _isleadbyte(b) isleadbyte((unsigned char)(b))
#endif
void ShowIMEActive(bool flag);

DWORD GetIMEInfo();
static void IMEDrawRect( int x1, int y1, int x2, int y2, DWORD Color );
static void GetReadingString(HWND hWnd);
static DWORD GetTaiwanIMEVersion();
static void CheckChineseIMEStatus();
static void SetReadingWindowOrientation(DWORD dwVer);

//
//	local helper functions
//

// returns number of characters from number of bytes
static int mbGetCharCount(LPCSTR pszString, int iBytes)
{
	int iCount = 0;
	for (int i = 0; pszString[i] && i < iBytes; i++) {
		iCount++;
		if (pszString[i + 1] && _isleadbyte(pszString[i])) // jcem - check last-byte + '\0' char 
			i++;
	}
	if (i != iBytes)
		iCount = -iCount;	// indicate error - iBytes specifies wrong boundary (i.e. the last byte is leadbyte)
	return iCount;
}

static void SendControlKeys(UINT vk, UINT num)
{
	if (num == 0)
		return;
	for (int i = 0; i < num ; i++) {
		SendMessage(hWindow, WM_KEYDOWN, vk, 0);
	}
	SendMessage(hWindow, WM_KEYUP, vk, 0);
	// WM_CHAR is sent here to post VK without character information.
	// This is necessary to make GosScript GetCtrlKey() work correctly
	SendMessage(hWindow, WM_CHAR, 0, 0);
}

// move caret to the end of composition string
static void IMEMoveCaretToTheEnd(HWND hwnd)
{
	int mblen = _mbstrlen(g_szCompositionString);
	SendControlKeys(VK_RIGHT, mblen - g_IMECursorChars);
}

// send key messages to erase composition string.
static void IMECancelCompString(HWND hwnd, bool bUseBackSpace = true, int iNewStrLen = 0)
{
	if (g_dwIMELevel != 3)
		return;
	int mblen = _mbstrlen(g_szCompositionString);
	int i;
	IMEMoveCaretToTheEnd(hwnd);

	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 < mblen)
	{
		for (i = 0; i < mblen - iNewStrLen; i++)
		{
			SendMessage(hwnd, WM_KEYDOWN, VK_BACK, 0);
			SendMessage(hwnd, WM_CHAR, 8, 0);	//Backspace character
		}
		SendMessage(hwnd, WM_KEYUP, VK_BACK, 0);
	}
	else
		iNewStrLen = mblen;

	SendControlKeys(VK_LEFT, iNewStrLen);
}

// initialize composition string data.
static void IMEInitCompStringData(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)
		IMEDrawRect(x, y + 1, x + 2, y + height - 1, g_CaretInfo.fontColorComp);
}

static int ReadResourceInt(UINT id)
{
	return (int)strtoul( gos_GetResourceString( gLanguageDLL, id ), NULL, 0 );
}

static void myDirtyRectangeRestoreArea(DWORD l, DWORD t, DWORD r, DWORD b)
{
	if (r > Environment.screenWidth)
		r = Environment.screenWidth;
	if (b > Environment.screenHeight)
		b = Environment.screenHeight;
	gosDirtyRectangeRestoreArea(l, t, r, b);
}

//
//	RenderIME()
//	Draws all IME UI
//
void RenderIME()
{	
	if (!g_bImeEnabled || !g_CaretInfo.fontHandle)
		return;

	if (g_CaretInfo.fontHandle->MagicNumber != 0x4587abba)
	{
		// Font isn't valid. The app should have turned off IME.
#ifdef LAB_ONLY
		PAUSE(("Invalid font. IME is now disabled."));
#endif
		gos_EnableIME(false);
		return;
	}

	// don't corrupt current renderstate settings with 
	// out own!
	bool bPushRenderStates = false;
	// Push render state only when InsideBeginScene is true,
	// or assertion fails in gos_PushRenderStates()
	if (InsideBeginScene) {
		bPushRenderStates = true;
		gos_PushRenderStates();
	}
	gos_SetupViewport( 0,1.0,0,0,0.0,0.0,1.0,1.0 );
	ShowIMEActive(g_bComposingIME);

	static int bCand_Index = -1;
	static int bCand_Line = -1;

	if (bCand_Index == -1)
	{
		DWORD dwSize;
		bCand_Index = 1;
		bCand_Line = 1;
		dwSize = sizeof(int);
		gos_LoadDataFromRegistry("Cand_Index", &bCand_Index, &dwSize);
		dwSize = sizeof(int);
		gos_LoadDataFromRegistry("Cand_Line", &bCand_Line, &dwSize);
	}

	// Process timer for caret blink
	UINT uCurrentTime = (UINT)(gos_GetElapsedTime(1) * 1000.0f);
	if (uCurrentTime - g_uCaretBlinkLast > g_uCaretBlinkTime)
	{
		g_uCaretBlinkLast = uCurrentTime;
		g_bCaretDraw = !g_bCaretDraw;
	}

	int left=0;
	int top=0;
	int right=0;
	int bottom=0;	
	int i = 0;

	gos_TextSetAttributes(g_CaretInfo.fontHandle, g_CaretInfo.fontColorComp, -g_CaretInfo.fontSize/1.5f,
		false, g_CaretInfo.fontProportional, g_CaretInfo.fontBold, g_CaretInfo.fontItalic, 0, true);	// hardcoded proportional, non-bold, non-italic

	DWORD uDummy;
	SIZE largest = {0,0};

	int len = strlen(g_szCompositionString);

	DWORD cType = 0;

	gos_TextSetRegion(0,0, 10000, 10000);

	DWORD bgX = g_CaretInfo.fontX;
	DWORD bgY = g_CaretInfo.fontY;
	DWORD candX = 0;
	DWORD candY = 0;
	char* pszMlcs = g_szMultiLineCompString;

	DWORD wCompChar = 0, hCompChar;
	gos_TextStringLength(&uDummy, &hCompChar, " ");
	if (g_dwIMELevel == 3 && g_IMECursorBytes && g_szCompositionString[0])
	{
		// shift starting point of drawing composition string according to the current caret position.
		char temp[512];
		temp[0] = 0;
		strncpy( temp, g_szCompositionString, g_IMECursorBytes );
		temp[g_IMECursorBytes] = 0;
		gos_TextStringLength(&wCompChar, &hCompChar, temp);
		bgX -= wCompChar;
	}
#ifdef INTERNAL_DEPENDENCY
	RECT rcCompDirty = {10000, 10000, 0, 0};
	bool bUpdateCompDirtyRect = false;
#endif

	//
	// Draw the background colors for IME text nuggets
	//
	bool saveCandPos = false;

	if (g_uCodePage != CODEPAGE_KOR || g_bCaretDraw)	// Korean uses composition string as blinking block caret
    for (i = 0; i < len ; i += cType)
	{
		DWORD bgColor = 0x00000000;
#if 0 //def INTERNAL_DEPENDENCY :removed due to display problem in cand list: kazuyuks 000816
		cType = 1 + (g_szCompositionString[i + 1] && LeadByteTable[(unsigned char)g_szCompositionString[i]]); // jcem - check last-byte + '\0' char 
		if (cType == 1)
			GDIGetChrSize(&wCompChar, &hCompChar, g_szCompositionString[i]);
		else
			GDIGetChrSize(&wCompChar, &hCompChar, ((((unsigned char *)(g_szCompositionString+i))[0])<<8) + (((unsigned char *)(g_szCompositionString+i))[1]));
#else
		cType = 1 + ((g_szCompositionString[i + 1] && _isleadbyte(g_szCompositionString[i])) ? 1 : 0); // jcem - check last-byte + '\0' char 
		char szChar[3];
		szChar[0] = g_szCompositionString[i];
		szChar[1] = szChar[2] = 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];
		gos_TextStringLength(&wCompChar, &hCompChar, szChar);
#endif

		switch(g_szCompAttrString[i])
		{
		case ATTR_INPUT:
			bgColor = gSkinCompStr.colorInput;
			break;
		case ATTR_TARGET_CONVERTED:
			bgColor = gSkinCompStr.colorTargetConv;	
			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(( "Attributes on IME characters are wrong" ));
			break;
		}

		if (g_dwIMELevel == 3)
		{
			if (bgX >= g_CaretInfo.margins.left && bgX <= g_CaretInfo.margins.right)
			{
				bUpdateCompDirtyRect = true;
				IMEDrawRect(bgX, bgY, bgX + wCompChar, bgY + hCompChar, bgColor);
			}
		}
		else if (g_dwIMELevel == 2)
		{
			// make sure at least 6 bytes (possible space, NUL for current line, possible DBCS, 2 more NUL) 
			// are available in multiline composition string buffer
			bool bWrite = (pszMlcs - g_szMultiLineCompString <
					sizeof(g_szMultiLineCompString) / sizeof(g_szMultiLineCompString[0]) - 6);

			if (bgX + wCompChar >= g_CaretInfo.margins.right) {
				bgX = g_CaretInfo.margins.left;
				bgY = bgY + hCompChar;
				if (bWrite)
				{
					if (pszMlcs == g_szMultiLineCompString || pszMlcs[-1] == 0)
						*pszMlcs++ = ' ';	// to avoid zero length line
					*pszMlcs++ = 0;
				}
			}
			bUpdateCompDirtyRect = true;
			IMEDrawRect(bgX, bgY, bgX + wCompChar, bgY + hCompChar, bgColor);
			if (bWrite) {
				*pszMlcs++ = g_szCompositionString[i];
				if (cType == 2)
					*pszMlcs++ = g_szCompositionString[i+1];
			}
			if (i == g_IMECursorBytes)
				DrawCaret(bgX, bgY, hCompChar);
		}
		if (saveCandPos && candX == 0)
		{
			saveCandPos = false;
			candX = bgX;
			candY = bgY;
		}
#ifdef INTERNAL_DEPENDENCY
		if (Environment.DirtyRectangle && bUpdateCompDirtyRect)
		{
			bUpdateCompDirtyRect = false;
			if (rcCompDirty.left > bgX)
				rcCompDirty.left = bgX;
			if (rcCompDirty.top > bgY)
				rcCompDirty.top = bgY;
			if (rcCompDirty.right < bgX + wCompChar)
				rcCompDirty.right = bgX + wCompChar;
			if (rcCompDirty.bottom < bgY + hCompChar)
				rcCompDirty.bottom = bgY + hCompChar;
		}
#endif
		bgX += wCompChar;
	}

#ifdef INTERNAL_DEPENDENCY
	if (Environment.DirtyRectangle &&
		rcCompDirty.left < rcCompDirty.right && rcCompDirty.top < rcCompDirty.bottom)
		myDirtyRectangeRestoreArea( 
			rcCompDirty.left,
			rcCompDirty.top,
			rcCompDirty.right + 2,	// 2 for caret's width
			rcCompDirty.bottom );
#endif

	if (g_dwIMELevel == 2)
	{
		// in case the caret in composition string is at the end of it, draw it here
		if (len != 0 && i == g_IMECursorBytes)
			DrawCaret(bgX, bgY, hCompChar);

		// Draw composition string.
		gosASSERT(pszMlcs - g_szMultiLineCompString <=
					sizeof(g_szMultiLineCompString) / sizeof(g_szMultiLineCompString[0]) - 2);
		*pszMlcs++ = 0;
		*pszMlcs++ = 0;
		DWORD x, y;
		x = g_CaretInfo.fontX;
		y = g_CaretInfo.fontY;
		pszMlcs = g_szMultiLineCompString;
		while (*pszMlcs && 
			pszMlcs - g_szMultiLineCompString < sizeof(g_szMultiLineCompString) / sizeof(g_szMultiLineCompString[0]))
		{
			gos_TextSetPosition(x, y);
			gos_TextDraw(pszMlcs);
			pszMlcs += strlen(pszMlcs) + 1;
			x = g_CaretInfo.margins.left;
			y += hCompChar;
		}
	}

	if (g_dwCount && g_szCandidate[0][0] != 0)
	{
		// If position of candidate list is not initialized yet, set it here.
		if (candX == 0)
		{
			candX = g_CaretInfo.fontX;
			candY = g_CaretInfo.fontY;
		}

		// Find out the largest candidate string
		largest.cx = largest.cy = 0;
		LPCSTR pcszFormat = g_bVerticalCand ? c_szCandListFormat : c_szCandListFormatHorizontal;
		if (g_bReadingWindow && g_bHorizontalReading)
			gos_TextStringLength((DWORD*)&largest.cx, (DWORD*)&largest.cy, g_szReadingString);
		else {
			for (int i = 0; g_szCandidate[i][0] && i < MAX_CANDLIST; i++)
			{
				DWORD tx, ty;
				if (bCand_Index && !g_bReadingWindow)
				{
					char szTemp[256];
					sprintf(szTemp, pcszFormat, i+1, g_szCandidate[i]);
					gos_TextStringLength(&tx, &ty, szTemp);
				}
				else
					gos_TextStringLength(&tx, &ty, g_szCandidate[i]);
				if ((signed)tx > largest.cx)
					largest.cx = tx;
				if ((signed)ty > largest.cy)
					largest.cy = ty;
			}
		}

		DWORD uDigitWidth, uSpaceWidth;
#if 0 //def INTERNAL_DEPENDENCY	:removed due to display problem in cand list: kazuyuks 000816
		GDIGetChrSize(&uDigitWidth, &uDummy, '0');
		GDIGetChrSize(&uSpaceWidth, &uDummy, ' ');
#else
		gos_TextStringLength(&uDigitWidth, &uDummy, "0");
		gos_TextStringLength(&uSpaceWidth, &uDummy, " ");
#endif
		DWORD dwMerginX = (uSpaceWidth + 1) / 2;

		// Show candidate list above composition string if there isn't enough room below.
		DWORD slotsUsed;
		if (g_bReadingWindow && g_dwCount < MAX_CANDLIST)
			slotsUsed = g_dwCount;
		else
			slotsUsed = MAX_CANDLIST;
		DWORD dwCandHeight;
		if (g_bVerticalCand && !(g_bReadingWindow && g_bHorizontalReading))
			dwCandHeight = slotsUsed * largest.cy + 2;
		else
			dwCandHeight = largest.cy + 2;
		if ( candY + hCompChar + dwCandHeight > Environment.screenHeight)
			candY -= dwCandHeight;
		else
			candY += hCompChar;
		if (candY < 0)
			candY = 0;

		// Move candidate list horizontally to keep it inside of screen
		DWORD dwCandWidth;
		if (g_bReadingWindow && g_bHorizontalReading)
			dwCandWidth = largest.cx + 2 + dwMerginX * 2;
		else if (g_bVerticalCand)
			dwCandWidth = largest.cx + 2 + dwMerginX * 2;
		else
			dwCandWidth = slotsUsed * (largest.cx + 1) + 1;
		if (candX + dwCandWidth > Environment.screenWidth)
			candX = Environment.screenWidth - dwCandWidth;
		if (candX < 0)
			candX = 0;
	
		// Draw frame and background of candidate list
		int saveRight;

		left = candX;
		top = candY;
		right = candX + dwCandWidth;
		bottom = candY + dwCandHeight;

		IMEDrawRect(left, top, right, bottom, gSkinIME.candColorBorder);
#ifdef INTERNAL_DEPENDENCY
		if (Environment.DirtyRectangle)
			myDirtyRectangeRestoreArea( left, top, right, bottom );
#endif
		left++;
		top++;
		right--;
		bottom--;

		if (g_bVerticalCand) {
			saveRight = right;
			// assumption : all digits have same width
			if (bCand_Index && !g_bReadingWindow && bCand_Line) {
				right = left + dwMerginX + uDigitWidth + uSpaceWidth / 2 - 1;
				IMEDrawRect(left, top, right, bottom, gSkinIME.candColorBase);
				left = right + 1;
				right = saveRight;
				IMEDrawRect(left, top, right, bottom, gSkinIME.candColorBase);
			}
			else
				IMEDrawRect(left,top, right, bottom, gSkinIME.candColorBase);
		}
		else
		{
			for (i = 0; i < slotsUsed; i++)
			{
				IMEDrawRect(left, top, left + largest.cx, bottom, gSkinIME.candColorBase);
				left += largest.cx + 1;
			}
		}

		candX++;
		candY++;
		if (g_bReadingWindow && g_bHorizontalReading) {
			int iStart = -1, iEnd = -1, iDummy;
			candX += dwMerginX;

			// draw background of error character if it exists
			char szTemp[sizeof(g_szReadingString)];
			if (g_szReadingError >= 0) {
				strcpy(szTemp, g_szReadingString);
				char* psz = (char*)_mbsinc((unsigned char*)szTemp + g_szReadingError);
				*psz = 0;
				gos_TextStringLength((DWORD*)&iEnd, (DWORD*)&iDummy, szTemp);
				char cSave = szTemp[g_szReadingError];
				szTemp[g_szReadingError] = 0;
				gos_TextStringLength((DWORD*)&iStart, (DWORD*)&iDummy, szTemp);
				szTemp[g_szReadingError] = cSave;
				IMEDrawRect(candX + iStart, candY, candX + iEnd, candY + largest.cy, gSkinIME.candColorBorder);
			}

			gos_TextSetPosition(candX , candY);
			FontColor = g_CaretInfo.fontColorCand;
			gos_TextDraw(g_szReadingString);

			// draw error character if it exists
			if (iStart >= 0) {
				gos_TextSetPosition(candX + iStart, candY);
				FontColor = 0xff000000 + (~((0x00ffffff) & g_CaretInfo.fontColorCand));
				gos_TextDraw(szTemp + g_szReadingError);
			}
		}
		else {
			//display candidate
			for (i = 0; i < MAX_CANDLIST && i < g_dwCount; i++)
			{
				if (g_dwSelection == (DWORD)i)
				{
					FontColor = 0xff000000 + (~((0x00ffffff) & g_CaretInfo.fontColorCand));
					IMEDrawRect(candX, candY + i * largest.cy, candX - 1 + dwCandWidth, candY + (i + 1) * largest.cy, gSkinIME.candColorBorder);
				}
				else
					FontColor = g_CaretInfo.fontColorCand;
				if (g_szCandidate[i][0] != 0)
				{
					if (g_bVerticalCand)
						gos_TextSetPosition(dwMerginX + candX , candY + i * largest.cy);
					else
						gos_TextSetPosition(uSpaceWidth / 2 + candX + i * (largest.cx + 1), candY);
					if (bCand_Index && !g_bReadingWindow) {
						char szTemp[256];
						sprintf(szTemp, pcszFormat, i+1, g_szCandidate[i]);
						gos_TextDraw(szTemp);
					}
					else
						gos_TextDraw(g_szCandidate[i]);
				}
			}
		}
	}
#ifdef INTERNAL_DEPENDENCY
	if (Environment.DirtyRectangle && g_szCompositionString[0])
		myDirtyRectangeRestoreArea( g_CaretInfo.margins.left, g_CaretInfo.margins.top, g_CaretInfo.margins.right, g_CaretInfo.margins.bottom );
#endif

	if (bPushRenderStates)
		gos_PopRenderStates();
	//
	// We removed the following gos_PositionIME() call, since this requires GOS app 
	// to call gos_PositionIME() every frame. kazuyuks 000816
	//
	//gos_PositionIME( g_CaretInfo.fontX, g_CaretInfo.fontY, 0xffffffff, 0xff000000, 0xa0, 24, 0 );
}

//
// Draw a square region using quads in a given color
//
static void IMEDrawRect( int x1, int y1, int x2, int y2, DWORD Color )
{
	if (x1 < 0) x1 = 0;
	if (x2 < 0) x2 = 0;
	if (y1 < 0) y1 = 0;
	if (y2 < 0) y2 = 0;
	if (x1 > Environment.screenWidth) x1 = Environment.screenWidth;
	if (x2 > Environment.screenWidth) x2 = Environment.screenWidth;
	if (y1 > Environment.screenHeight) y1 = Environment.screenHeight;
	if (y2 > Environment.screenHeight) y2 = Environment.screenHeight;
	if (x1 == x2 || y1 == y2) return;
	
	gos_VERTEX pVertices[4];

	memset( pVertices, 0, sizeof(pVertices) );
	pVertices[0].rhw	= 1.0;
	pVertices[1].rhw	= 1.0;
	pVertices[2].rhw	= 1.0;
	pVertices[3].rhw	= 1.0;

	pVertices[0].x		= (float)x1;
	pVertices[0].y		= (float)y1;
	pVertices[0].argb	= Color;

	pVertices[1].x		= (float)x1;
	pVertices[1].y		= (float)y2;
	pVertices[1].argb	= Color;

	pVertices[2].x		= (float)x2;
	pVertices[2].y		= (float)y2;
	pVertices[2].argb	= Color;

	pVertices[3].x		= (float)x2;
	pVertices[3].y		= (float)y1;
	pVertices[3].argb	= Color;

	gos_SetRenderState( gos_State_Texture, 0 );
	gos_SetRenderState( gos_State_Filter, gos_FilterNone );
	gos_SetRenderState( gos_State_ZCompare, 0 );
	gos_SetRenderState( gos_State_ZWrite, 0 );
	gos_SetRenderState( gos_State_AlphaTest, 0 );
	gos_SetRenderState( gos_State_Perspective, 0 );
	gos_SetRenderState( gos_State_Specular, 0 );
	gos_SetRenderState( gos_State_Dither, 0 );
	gos_SetRenderState( gos_State_Clipping, 0 );
	gos_SetRenderState( gos_State_TextureMapBlend, gos_BlendModulate );
	gos_SetRenderState( gos_State_ShadeMode, gos_ShadeFlat );
	gos_SetRenderState( gos_State_MonoEnable, 0 );
	if( (Color&0xff000000)==0xff000000 )
		gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_OneZero );
	else
		gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_AlphaInvAlpha );

	gos_DrawQuads( pVertices, 4 );
}

//
//	ProcessIMEMessages()
//	Processes IME related messages and acquire information
//
LPARAM ProcessIMEMessages( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, bool * trapped )
{
	HIMC himc;
	int len;
	int i;

	if (g_bDisableImeCompletely) 
	{
		*trapped = false;
		return 0;
	}

	*trapped = true;
	
	switch( uMsg )
	{
//
//	IME Handling
//
	case WM_INPUTLANGCHANGE:
		CheckChineseIMEStatus();
		*trapped = false;
		break;

    case WM_IME_CONTROL:
		*trapped = false;
        break;

    case WM_IME_SETCONTEXT:
		//
		// We don't want anything to display, so we have to clear this
		//
        lParam = 0;
		*trapped = false;
        break;

    case WM_IME_STARTCOMPOSITION:
		IMEInitCompStringData();
		*trapped = true;
		break;

    case WM_IME_COMPOSITIONFULL:
		*trapped = false;
        break;		

    case WM_IME_COMPOSITION:
		{
			LONG lRet;
			char szCompStr[sizeof(g_szCompositionString)/sizeof(g_szCompositionString[0])];

			if (NULL == (himc = _ImmGetContext(hWnd)))
				return 0;

				// ResultStr must be processed before composition string.
		        if ( lParam & GCS_RESULTSTR )
				{
					lRet = _ImmGetCompositionString( himc, GCS_RESULTSTR, szCompStr, 255 );
					szCompStr[lRet] = 0;
					IMECancelCompString(hWindow, false, _mbstrlen(szCompStr));

					strcpy(g_szCompositionString, szCompStr);
	
					for (int i = 0; i < lRet ; i++)
					{
						SendMessage(hWindow, WM_CHAR, (unsigned char)g_szCompositionString[i], 0);
					}
					IMEInitCompStringData();
				}
		        //
		        // Reads in the composition string.
		        //
		        if ( lParam & GCS_COMPSTR )
		        {
					//////////////////////////////////////////////////////
					// Retrieve the latest user-selected IME candidates
					lRet = _ImmGetCompositionString( himc, GCS_COMPSTR, szCompStr, 255 );
					szCompStr[lRet] = 0;
					//
					// Remove the whole of the string
					//
					IMECancelCompString(hWindow, false, _mbstrlen(szCompStr));

					strcpy(g_szCompositionString, szCompStr);
					lRet = _ImmGetCompositionString( himc, GCS_COMPATTR, g_szCompAttrString, 255 );
					g_szCompAttrString[lRet] = 0;

					if ((g_uCodePage == CODEPAGE_CHT && !GetTaiwanIMEVersion()) // for older Taiwan IME
						|| g_uCodePage == CODEPAGE_CHS)
					{
						int i, chars = strlen(g_szCompositionString) / 2;
						if (chars)
						{
							g_dwCount = 4;
							g_dwSelection = -1;	// don't select any candidate

							for (i = 3; i >= 0; i--)
							{
								if (i > chars - 1)
									g_szCandidate[i][0] = 0;
								else
								{
									g_szCandidate[i][0] = g_szCompositionString[i*2];
									g_szCandidate[i][1] = g_szCompositionString[i*2+1];
									g_szCandidate[i][2] = 0;
								}
							}
							memset(g_szCompositionString, 0, 8);
							g_bReadingWindow = true;
							SetReadingWindowOrientation(0);
							if (g_bHorizontalReading) {
								g_szReadingError = -1;
								g_szReadingString[0] = 0;
								for (int i = 0; i < g_dwCount; i++) {
									if (g_dwSelection == (DWORD)i)
										g_szReadingError = strlen(g_szReadingString);
									strcat(g_szReadingString, g_szCandidate[i]);
								}
							}
						}
						else
							g_dwCount = 0;
					}

					// get caret position in composition string
					g_IMECursorBytes = _ImmGetCompositionString(himc, GCS_CURSORPOS, NULL, 0);
					g_IMECursorChars = mbGetCharCount(g_szCompositionString, g_IMECursorBytes);

					if (g_dwIMELevel == 3)
					{
						// send composition string via WM_CHAR
						len = strlen(g_szCompositionString);
						for (i = 0; i < len ; i++)
							SendMessage(hWindow, WM_CHAR, (unsigned char)g_szCompositionString[i], 0);

						// move caret to appropreate location
						len = _mbstrlen(g_szCompositionString + g_IMECursorBytes);
						SendControlKeys(VK_LEFT, len);
					}
				}
				_ImmReleaseContext(hWnd, himc);
		}
		//
		// DO NOT REMOVE THIS RETURN
		// The GDI disable  available via WM_IME_SETCONTEXT does not work. The only way to 
		// hide the GDI representation is to have this return here.
		return 0;

    case WM_IME_ENDCOMPOSITION:
		IMECancelCompString(hWindow);
		IMEInitCompStringData();
		*trapped = false;
        break;

    case WM_IME_NOTIFY:
        switch (wParam)
        {
			case IMN_SETCONVERSIONMODE:
				CheckChineseIMEStatus();
				*trapped = false;
				break;

			case IMN_SETOPENSTATUS:
				{
					if (NULL == (himc = _ImmGetContext(hWnd)))
					{
						g_bComposingIME = false;
						return 0;
					}
					BOOL  fOpen;
					DWORD dwConvMode, dwSentMode;
					fOpen = _ImmGetOpenStatus(himc);
					g_bComposingIME = fOpen ? true : false;
					SPEW(("jreicher", "composing = %d", g_bComposingIME));
					if (fOpen)
					    _ImmGetConversionStatus(himc, &dwConvMode, &dwSentMode);
					else
					    dwConvMode = 0;
					_ImmReleaseContext(hWnd, himc);
				}
				*trapped = false;
			    break;
			
			case IMN_OPENCANDIDATE:
			case IMN_CHANGECANDIDATE:
				{
					if (NULL == (himc = _ImmGetContext(hWnd)))
						return 0;
					
					LPCANDIDATELIST lpCandList;
					DWORD dwIndex, dwBufLen;
				    DWORD temp;
				    LPTSTR lpStr;

					g_bReadingWindow = false;
					dwIndex = 0;
					dwBufLen = _ImmGetCandidateList( himc, dwIndex, NULL, 0 );
					if ( dwBufLen )
					{
					    lpCandList = (LPCANDIDATELIST)LocalAlloc(LPTR, dwBufLen);
					    _ImmGetCandidateList( himc, dwIndex, lpCandList, dwBufLen );
					    g_dwSelection = lpCandList->dwSelection;
					    g_dwCount = lpCandList->dwCount;

						int what_page = g_dwSelection / lpCandList->dwPageSize;
						g_dwSelection = g_dwSelection - (what_page * lpCandList->dwPageSize);

					    memset(&g_szCandidate, 0, sizeof(g_szCandidate));
					    for (int i = (what_page * lpCandList->dwPageSize), j = 0; i < lpCandList->dwCount && j < MAX_CANDLIST; i++, j++)
					    {
					        temp = (DWORD)lpCandList + lpCandList->dwOffset[i];
					        lpStr = (LPTSTR)temp;
					        _mbscpy(((unsigned char *) g_szCandidate[j]), (const unsigned char *) lpStr);
					    }
					    LocalFree( (HANDLE)lpCandList );
						_ImmReleaseContext(hWnd, himc);

						// don't display selection in candidate list in case of Korean and old Taiwan IME.
						if (g_uCodePage == CODEPAGE_KOR ||
							g_uCodePage == CODEPAGE_CHT && GetTaiwanIMEVersion() == 0
							)
							g_dwSelection = -1;
					}
					return 0;
				}
			
			case IMN_CLOSECANDIDATE:
				if (!g_bReadingWindow)	// fix for Ent Gen #120.
				{
					g_dwCount = 0;
					memset(&g_szCandidate, 0, sizeof(g_szCandidate));
				}
			    return 0;

			// Jun.16,2000 05:21 by yutaka.
			case IMN_PRIVATE:
				GetReadingString(hWnd);
				{
				DWORD dwVer = GetTaiwanIMEVersion();
				// Trap some messages to hide reading window
				if (!(
					((dwVer == _TAIWAN_IMEVERSION42 || dwVer == _TAIWAN_IMEVERSION43 || dwVer == _TAIWAN_IMEVERSION44)
						&& ((lParam == 1) || (lParam == 2))) ||
					((dwVer >= _TAIWAN_IMEVERSION50 || dwVer <= _TAIWAN_IMEVERSION52)
						&& ((lParam == 16) || (lParam == 17)))
				))
					*trapped = false;	// This is necessary to on/off Taiwan IME by shift key - 000619 kazuyuks
				}
				break;

			default:
				*trapped = true;
				break;
		}
		break;

	default:
	{
		*trapped = false;
	}
	}
	return 0;
}

void _stdcall gos_PositionIME(DWORD x, DWORD y)
{
	g_CaretInfo.fontX = x;
	g_CaretInfo.fontY = y;
	g_CaretInfo.fontSize = FontSize;
	g_CaretInfo.fontHandle = FontHandle;
	g_CaretInfo.margins.left = float2long(FontMinX);
	g_CaretInfo.margins.right = float2long(FontMaxX);
	g_CaretInfo.margins.top = float2long(FontMinY);
	g_CaretInfo.margins.bottom = float2long(FontMaxY);
	g_CaretInfo.fontBold = FontBold;
	g_CaretInfo.fontItalic = FontItalic;
	g_CaretInfo.fontProportional = FontProportional;
	if (0 == gSkinIME.candColorText)
		g_CaretInfo.fontColorCand = FontColor;
	else
		g_CaretInfo.fontColorCand = gSkinIME.candColorText;
	if (0 == gSkinIME.compColorText)
		g_CaretInfo.fontColorComp = FontColor;
	else
		g_CaretInfo.fontColorComp = gSkinIME.compColorText;
}

void _stdcall gos_ToggleIME(bool onOff)
{
	HIMC himc;
	if (onOff)
		gos_EnableIME(onOff);
	if (NULL != (himc = _ImmGetContext(hWindow))) 
	{
		SPEW(("jreicher", "imeon = %d", onOff));
		if (g_bDisableImeCompletely)
			onOff = false;

		switch (g_uCodePage) {
		case CODEPAGE_CHS:
		case CODEPAGE_CHT:
		{
		 	// toggle Chinese IME
			extern void Update();

			// Fix for MW4Patch2 #275
			static bool s_bInit = false;
			if (!s_bInit) {
				s_bInit = true;
				_ImmSimulateHotKey(hWindow, IME_THOTKEY_IME_NONIME_TOGGLE);
				Update();
				_ImmSimulateHotKey(hWindow, IME_THOTKEY_IME_NONIME_TOGGLE);
				Update();
			}
			// End; Fix for MW4Patch2 #275

			if ((onOff && !g_bChineseIME) || (!onOff && g_bChineseIME))
			{
				_ImmSimulateHotKey(hWindow, IME_THOTKEY_IME_NONIME_TOGGLE);
				Update();
			}
			_ImmSetOpenStatus(himc, onOff);
			break;
		}
		case CODEPAGE_KOR:
		 	// toggle Korean IME
			if ((onOff && !g_bComposingIME) || (!onOff && g_bComposingIME))
				_ImmSimulateHotKey(hWindow, IME_KHOTKEY_ENGLISH);
			break;
		case CODEPAGE_JPN:
			_ImmSetOpenStatus(himc, onOff);
			break;
		}
		g_bComposingIME = _ImmGetOpenStatus(himc) ? true : false;
		_ImmReleaseContext(hWindow, himc);
	}
}

// called by gos_GetMachineInformation()
DWORD GetIMEInfo()
{
	HIMC himc;

	DWORD dwRc = 0;
	if (NULL != (himc = _ImmGetContext(hWindow))) 
	{
		// ImmGetOpenStatus() doesn't return correct open status for CHT IME.
		if (g_uCodePage == CODEPAGE_CHS || g_uCodePage == CODEPAGE_CHT)
			return g_bComposingIME;
		dwRc = _ImmGetOpenStatus(himc) ? 0x1: 0x0;
		_ImmReleaseContext(hWindow, himc);
	}
	return dwRc;
}

void _stdcall gos_EnableIME( bool onOff )
{
	if (!hWindow)
		return;
	if (g_bDisableImeCompletely)
		onOff = false;

	SPEW(("jreicher", "IME enabled: %d", onOff));

	HIMC himcDbg;
	himcDbg = _ImmAssociateContext( hWindow, onOff ? g_himcOrg : NULL );
	g_bImeEnabled = onOff;
}

void ShowIMEActive(bool flag)
{
	// draw a fan in greyscale if not active
	// draw it in swirling rainbow if active
	
	gos_SetRenderState( gos_State_Texture, 0 );
	gos_SetRenderState( gos_State_Filter, gos_FilterNone );
	gos_SetRenderState( gos_State_ZCompare, 0 );
	gos_SetRenderState( gos_State_ZWrite, 0 );
	gos_SetRenderState( gos_State_AlphaTest, 0 );
	gos_SetRenderState( gos_State_Perspective, 0 );
	gos_SetRenderState( gos_State_Specular, 0 );
	gos_SetRenderState( gos_State_Dither, 0 );
	gos_SetRenderState( gos_State_Clipping, 2 );
	gos_SetRenderState( gos_State_TextureMapBlend, gos_BlendModulate );
	gos_SetRenderState( gos_State_ShadeMode, gos_ShadeGouraud );
	gos_SetRenderState( gos_State_MonoEnable, 0 );
	gos_SetRenderState( gos_State_Fog, 0 );

	// if we want translucence
	gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_AlphaInvAlpha );

	gos_VERTEX PieData[17];	
	float SizeOfPie = (float) gSkinIME.symbolHeight;

	memset( &PieData[0], 0, sizeof(PieData) );

	switch(gSkinIME.symbolPlacement)
	{
	case 0:
		{
			if (SizeOfPie + g_CaretInfo.margins.right+4 > Environment.screenWidth)
			{
				PieData[0].x=(-SizeOfPie/2) + g_CaretInfo.margins.left-4;
				PieData[0].y= (float) g_CaretInfo.margins.bottom - (gSkinIME.symbolHeight/2);
			}
			else
			{
				PieData[0].x=-(SizeOfPie/2) + g_CaretInfo.margins.right+gSkinIME.symbolHeight+4;
				PieData[0].y= (float) g_CaretInfo.margins.bottom - (gSkinIME.symbolHeight/2);
			}
			break;
		}
	case 1: // upperleft
		PieData[0].x = 4+ (SizeOfPie/2);
		PieData[0].y = 4+ (SizeOfPie/2);
		break;
	case 2: // upperright
		PieData[0].x = Environment.screenWidth - (4+ (SizeOfPie/2));
		PieData[0].y = 4+ (SizeOfPie/2);
		break;
	case 3: // lowerright
		PieData[0].x = Environment.screenWidth - (4+ (SizeOfPie/2));
		PieData[0].y = Environment.screenHeight - (4+ (SizeOfPie/2));
		break;
	case 4: // lowerleft
		PieData[0].x = 4+ (SizeOfPie/2);
		PieData[0].y = Environment.screenHeight - (4+ (SizeOfPie/2));
		break;
	}
	PieData[0].rhw=1.0f;
	if (flag)
	{
		if (gos_GetElapsedTime() - lastSwirl > .25) 
		{
			swirl++;
			lastSwirl = gos_GetElapsedTime();
			if (swirl > 13)
				swirl = 0;
		}
	}
	else
		swirl = 0;
	for( int t1=1; t1<16; t1++ )
	{
		PieData[t1].x=(float)(PieData[0].x+SizeOfPie/2*sin( (2.0f*3.1415926f*(t1-1+(flag*swirl)))/14.0f ));
		PieData[t1].y=(float)(PieData[0].y-SizeOfPie/2*cos( (2.0f*3.1415926f*(t1-1+(flag*swirl)))/14.0f ));
		PieData[t1].rhw=1.0f;
	}
	PieData[t1].x=(float)(PieData[0].x+SizeOfPie/2*sin( 0 ));
	PieData[t1].y=(float)(PieData[0].y-SizeOfPie/2*cos( 0 ));
	PieData[t1].rhw=1.0f;

	if (!gSkinIME.symbolColor)
	{
		if (!flag)
		{
			PieData[0].argb=0xffffff +  (((DWORD)gSkinIME.symbolTranslucence)<<24);
			for( int t1=1; t1<16; t1++ )
			{
				PieData[t1].argb=0x404040 + (((DWORD)gSkinIME.symbolTranslucence)<<24);
			}
		}
		else
		{
		 	PieData[0].argb=0xffffff + (((DWORD)gSkinIME.symbolTranslucence)<<24);
	 		PieData[1].argb=0xff0000 + (((DWORD)gSkinIME.symbolTranslucence)<<24);
		 	PieData[2].argb=0xff3000 + (((DWORD)gSkinIME.symbolTranslucence)<<24);
		 	PieData[3].argb=0xff6000 + (((DWORD)gSkinIME.symbolTranslucence)<<24);
	 		PieData[4].argb=0xff9000 + (((DWORD)gSkinIME.symbolTranslucence)<<24);
		 	PieData[5].argb=0xffC000 + (((DWORD)gSkinIME.symbolTranslucence)<<24);
		 	PieData[6].argb=0xffff00 + (((DWORD)gSkinIME.symbolTranslucence)<<24);
	 		PieData[7].argb=0xC0ff00 + (((DWORD)gSkinIME.symbolTranslucence)<<24);
		 	PieData[8].argb=0x90ff00 + (((DWORD)gSkinIME.symbolTranslucence)<<24);
		 	PieData[9].argb=0x60ff00 + (((DWORD)gSkinIME.symbolTranslucence)<<24);
	 		PieData[10].argb=0x30c0ff + (((DWORD)gSkinIME.symbolTranslucence)<<24);
		 	PieData[11].argb=0x00a0ff + (((DWORD)gSkinIME.symbolTranslucence)<<24);
		 	PieData[12].argb=0x3090ff + (((DWORD)gSkinIME.symbolTranslucence)<<24);
	 		PieData[13].argb=0x6060ff + (((DWORD)gSkinIME.symbolTranslucence)<<24);
		 	PieData[14].argb=0x9030ff + (((DWORD)gSkinIME.symbolTranslucence)<<24);
		 	PieData[15].argb=0xc000ff + (((DWORD)gSkinIME.symbolTranslucence)<<24);
		}
	}
	else
	{
		PieData[0].argb=0xffffff +  (((DWORD)gSkinIME.symbolTranslucence)<<24);
		for( int t1=1; t1<16; t1++ )
		{
			PieData[t1].argb=gSkinIME.symbolColor + (((DWORD)gSkinIME.symbolTranslucence)<<24);
		}
	}
	gos_DrawFans( PieData, 16 );

	float fHeight = -gSkinIME.symbolHeight*0.625f;

	// fix for Ent Gen #120 - reduce the height of character when Korean IME is on
	if (g_uCodePage == CODEPAGE_KOR && flag)
		fHeight *= 0.8f;

	gos_TextSetAttributes(g_CaretInfo.fontHandle, (((DWORD)gSkinIME.symbolTranslucence)<<24) | gSkinIME.symbolColorText, fHeight, false, true, flag, false, 0, true); 

	gos_TextSetRegion( 0, 0, Environment.screenWidth, Environment.screenHeight );

	//
	// draw the proper symbol over the fan
	//
	DWORD  w, h;
	const char* cszSymbol = flag ? (const char*)g_SymbolIME : (const char*)"A";

	gos_TextStringLength(&w, &h, cszSymbol);
	gos_TextSetPosition(float2long(PieData[0].x)-w/2, float2long(PieData[0].y)-h/2);
	gos_TextDraw( cszSymbol );

#ifdef INTERNAL_DEPENDENCY
	if (Environment.DirtyRectangle)
		myDirtyRectangeRestoreArea( float2long(PieData[0].x)-(gSkinIME.symbolHeight/2), float2long(PieData[0].y)-(gSkinIME.symbolHeight/2), float2long(PieData[0].x)+(gSkinIME.symbolHeight/2), float2long(PieData[0].y)+(gSkinIME.symbolHeight/2) );
#endif
}

void InitializeIME(HWND hWindow)
{
    static bool bInitialized = false;
    if (!bInitialized) {
        bInitialized = true;
        g_himcOrg = _ImmGetContext(hWindow);
        _ImmReleaseContext(hWindow, g_himcOrg);
    }

	g_uCaretBlinkTime = GetCaretBlinkTime();
 
	// Turn off IME toolbar
	HWND hwndImeDef = _ImmGetDefaultIMEWnd(hWindow);
	if (hwndImeDef)
		SendMessage(hwndImeDef, WM_IME_CONTROL, IMC_CLOSESTATUSWINDOW, 0);

	// 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.
	}

	// Check registry flag
	if (!g_bDisableImeCompletely) {
		DWORD disableIME = false;
		DWORD dwSize = sizeof(disableIME);
		gos_LoadDataFromRegistry("DisableIme", &disableIME, &dwSize, true);
		if (disableIME)
			g_bDisableImeCompletely = true;
	}

	// Load initial setting from language.dll
	gosASSERT(gLanguageDLL);
	gosIME_Appearance ia;

	ia.symbolColor				= (DWORD)ReadResourceInt(GOS_STR_SYMBOL_COLOR);
	ia.symbolColorText			= (DWORD)ReadResourceInt(GOS_STR_SYMBOL_COLOR_TEXT);
	ia.symbolHeight				= (BYTE)ReadResourceInt(GOS_STR_SYMBOL_SIZE);
	ia.symbolPlacement			= (BYTE)ReadResourceInt(GOS_STR_SYMBOL_PLACEMENT);
	ia.symbolTranslucence		= (BYTE)ReadResourceInt(GOS_STR_SYMBOL_TRANSLUCENCE);
	ia.candColorBase			= (DWORD)ReadResourceInt(GOS_STR_CAND_COLOR_BASE);
	ia.candColorBorder			= (DWORD)ReadResourceInt(GOS_STR_CAND_COLOR_FRAME);
	ia.candColorText			= (DWORD)ReadResourceInt(GOS_STR_CAND_COLOR_TEXT);
	ia.compColorInput			= (DWORD)ReadResourceInt(GOS_STR_COMP_COLOR_INPUT);
	ia.compColorTargetConv		= (DWORD)ReadResourceInt(GOS_STR_COMP_COLOR_TARGET_CONVERTED);
	ia.compColorConverted		= (DWORD)ReadResourceInt(GOS_STR_COMP_COLOR_CONVERTED);
	ia.compColorTargetNotConv	= (DWORD)ReadResourceInt(GOS_STR_COMP_COLOR_TARGET_NOTCONVERTED);
	ia.compColorInputErr		= (DWORD)ReadResourceInt(GOS_STR_COMP_COLOR_INPUT_ERROR);
	ia.compTranslucence			= (BYTE)ReadResourceInt(GOS_STR_COMP_TRANSLUCENCE);
	ia.compColorText			= (DWORD)ReadResourceInt(GOS_STR_COMP_COLOR_TEXT);

	gos_SetIMEAppearance( &ia );

	g_CaretInfo.fontX = 0;
	g_CaretInfo.fontY = 0;
	g_CaretInfo.fontSize = 12.0;
	g_CaretInfo.fontHandle = 0;
	g_CaretInfo.fontColorComp = 0;
	g_CaretInfo.fontColorCand = 0;
	g_CaretInfo.fontBold = 0;
	g_CaretInfo.fontItalic = 0;
	g_CaretInfo.fontProportional = 0;
	g_CaretInfo.margins.left = 0;
	g_CaretInfo.margins.right = Environment.screenWidth;
	g_CaretInfo.margins.top = 0;
	g_CaretInfo.margins.bottom = Environment.screenHeight;

	g_uCodePage = GetACP();
	switch (g_uCodePage)
	{
	// Simplified Chinese (TBD)
	case CODEPAGE_CHS:
		g_SymbolIME[0] = 0xa4;
		g_SymbolIME[1] = 0xa4;
		break;
	// Trad Chinese (Taiwan)
	case CODEPAGE_CHT:
		g_SymbolIME[0] = 0xa4;
		g_SymbolIME[1] = 0xa4;
		break;
	// Korean (TBD)
	case CODEPAGE_KOR:
		g_SymbolIME[0] = 0xb0;
		g_SymbolIME[1] = 0xa1;
		g_bVerticalCand = false;
		break;
 	// Japanese	& other	(0x411 = Japanese)
	case CODEPAGE_JPN:
		g_SymbolIME[0] = 0x82;
		g_SymbolIME[1] = 0xa0;
		break;							   
	default:
		g_SymbolIME[0] = 'A';
		g_SymbolIME[1] = 0;
	}
	g_SymbolIME[2] = 0x00;

	gos_SetIMELevel(3);
	gosASSERT(g_dwIMELevel == 2 || g_dwIMELevel == 3);
	
	HIMC himc;
	if (NULL != (himc = _ImmGetContext(hWindow))) {
		g_bComposingIME = himc && _ImmGetOpenStatus(himc) ? true : false;
		_ImmReleaseContext(hWindow, himc);
	}
	CheckChineseIMEStatus();
}

///////////////////////////////////
// Check Taiwan New Phonetic IME //
///////////////////////////////////

static DWORD GetTaiwanIMEVersion()
{
	static DWORD dwRet = 0;
	
	DWORD 	dwVerSize;
	DWORD 	dwVerHandle;
	LPVOID	lpVerBuffer;
	LPVOID	lpVerData;
	UINT	cbVerData;
	
	static HKL hklPrev = 0;

	HKL kl = GetKeyboardLayout(0);
	SPEW(("jreicher", "GetKeyboardLayout(0)=%08lX", kl ));
	if (hklPrev == kl)
		return dwRet;
	hklPrev = kl;

	if ( (kl != _TAIWAN_HKL) && (kl != _TAIWAN_HKL2) ){ // check non-Taiwan env
		goto error;
	}
	
	if ( _ImmGetIMEFileName( kl, g_szImeFileName, sizeof(g_szImeFileName)-1 ) <= 0 ){
		goto error;
	}
	SPEW(("jreicher", "ImmGetIMEFileName=[%s]", g_szImeFileName));

	if ( (strcmp( g_szImeFileName, _TAIWAN_IMEFILENAME) != 0) && (strcmp( g_szImeFileName, _TAIWAN_IMEFILENAME2) != 0)
		&& (strcmp( g_szImeFileName, _TAIWAN_IMEFILENAME3) != 0))
		goto error;
	
	dwVerSize = GetFileVersionInfoSize( g_szImeFileName, &dwVerHandle );
	if( dwVerSize )
	{
		lpVerBuffer = (LPVOID) gos_Malloc(dwVerSize);
		if( lpVerBuffer )
		{
			if( GetFileVersionInfo( g_szImeFileName, dwVerHandle, dwVerSize, lpVerBuffer ) )
			{
				if( VerQueryValue( lpVerBuffer, "\\", &lpVerData, &cbVerData ) )
				{
					#define pVerFixedInfo ((VS_FIXEDFILEINFO FAR*)lpVerData)
					SPEW(("jreicher", "FileVersion=[%d.%d.%d.%d]", HIWORD(pVerFixedInfo->dwFileVersionMS), LOWORD(pVerFixedInfo->dwFileVersionMS), HIWORD(pVerFixedInfo->dwFileVersionLS), LOWORD(pVerFixedInfo->dwFileVersionLS) ));
					DWORD dwVer = pVerFixedInfo->dwFileVersionMS;
					if ( dwVer == _TAIWAN_IMEVERSION42 || 
					     dwVer == _TAIWAN_IMEVERSION43 || 
					     dwVer == _TAIWAN_IMEVERSION44 || 
					     dwVer == _TAIWAN_IMEVERSION50 ||
					     dwVer == _TAIWAN_IMEVERSION51 ||
					     dwVer == _TAIWAN_IMEVERSION52
						 ){
						dwRet = dwVer;
						gos_Free(lpVerBuffer);
						return dwRet;
					}
					#undef pVerFixedInfo
				}
			}
		}
		gos_Free(lpVerBuffer);
	}

// The flow comes here in the following conditions
// - Non Taiwan IME input locale
// - Older Taiwan IME
// - Other error cases
error:
	dwRet = 0;
	return dwRet;
}

static void GetReadingString(HWND hWnd)
{
	DWORD dwTaiwanIMEVersion = GetTaiwanIMEVersion();
	if ( ! dwTaiwanIMEVersion )
		return;
	
	HIMC himc;
	
	himc = _ImmGetContext(hWnd);
	
	if (NULL != himc )
	{
		LPINPUTCONTEXT lpIMC = _ImmLockIMC(himc);
		
		// *** hacking code from Michael Yang ***
		
		LPBYTE p = 0;
		DWORD dwlen = 0;
		DWORD dwerr = 0;
		WCHAR *wstr = 0;
		bool unicode = FALSE;
		
		switch ( dwTaiwanIMEVersion ){
		
		case _TAIWAN_IMEVERSION42: // New(Phonetic/ChanJie)IME98  : 4.2.x.x // Win98
		case _TAIWAN_IMEVERSION43: // New(Phonetic/ChanJie)IME98a : 4.3.x.x // WinMe, Win2k
		case _TAIWAN_IMEVERSION44: // New ChanJie IME98b          : 4.4.x.x // WinXP
			
			p = *(LPBYTE *)((LPBYTE)_ImmLockIMCC(lpIMC->hPrivate) + 24);
			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 _TAIWAN_IMEVERSION50: // 5.0.x.x // WinME
			
			p = *(LPBYTE *)((LPBYTE)_ImmLockIMCC(lpIMC->hPrivate) + 3*4); // PCKeyCtrlManager
			p = *(LPBYTE *)((LPBYTE)p + 1*4 + 5*4 + 4*2 ); // = PCReading = &STypingInfo
			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 _TAIWAN_IMEVERSION51: // 5.1.x.x // IME2002(w/OfficeXP)
		case _TAIWAN_IMEVERSION52: // 5.2.x.x // (w/WinXP)

			p = *(LPBYTE *)((LPBYTE)_ImmLockIMCC(lpIMC->hPrivate) + 4);   // PCKeyCtrlManager
			p = *(LPBYTE *)((LPBYTE)p + 1*4 + 5*4);                       // = PCReading = &STypingInfo
			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;
		}
		
		SPEW(("jreicher", "READING Len=%d", dwlen));

		g_dwCount = dwlen;


		g_szCandidate[0][0] = 0;
		g_szCandidate[1][0] = 0;
		g_szCandidate[2][0] = 0;
		g_szCandidate[3][0] = 0;
		
		if ( unicode ){
			g_dwSelection = -1; // do not select any char
			for ( int i=0; i < dwlen ; i++ ) // dwlen > 0, if known IME : yutakah
			{
				if ( dwerr<=i && g_dwSelection==-1 ){ // select error char
					g_dwSelection = i;
				}

				char mbc[3];
				mbc[1] = 0;
				mbc[2] = 0;
				wctomb(mbc, wstr[i]);

				g_szCandidate[i][0] = mbc[0];
				g_szCandidate[i][1] = mbc[1];
				g_szCandidate[i][2] = 0;


				SPEW(( "jreicher", "  [%d]: UNICODE %04x MBCS %s", i, wstr[i], mbc ));
			}
			g_szCandidate[i][0] = 0;
		} else {
			TCHAR *p = (TCHAR *)wstr;
			g_dwSelection = -1;
			for ( int i=0, j=0; i < dwlen ; i++,j++ ) // dwlen > 0, if known IME : yutakah
			{
				if ( dwerr<=i && g_dwSelection==-1 ){
					g_dwSelection = j;
				}
				g_szCandidate[j][0] = p[i];
				g_szCandidate[j][1] = 0;
				g_szCandidate[j][2] = 0;
				if (p[i+1] && _isleadbyte(p[i])) // jcem - check last-byte + '\0' char 
				{
					i++;
					g_szCandidate[j][1] = p[i];
				}

				SPEW(( "jreicher", "  [%d]: MBCS %s", i, g_szCandidate[j] ));

			}
			g_szCandidate[j][0] = 0;
			g_dwCount = j;
		}
		
		_ImmUnlockIMCC( lpIMC->hPrivate );
		_ImmUnlockIMC( himc );
		_ImmReleaseContext(hWnd, himc);
		g_bReadingWindow = true;
		SetReadingWindowOrientation(dwTaiwanIMEVersion);
		if (g_bHorizontalReading) {
			g_szReadingError = -1;
			g_szReadingString[0] = 0;
			for (int i = 0; i < g_dwCount; i++) {
				if (g_dwSelection == (DWORD)i)
					g_szReadingError = strlen(g_szReadingString);
				strcat(g_szReadingString, g_szCandidate[i]);
			}
		}
	}
}


#define COUNTOF(a) (sizeof(a)/sizeof((a)[0]))

static struct {
	bool m_bCtrl;
	bool m_bShift;
	UINT m_uVk;
}
aHotKeys[] = {
	false,	false,	VK_APPS,
	true,	false,	'8',
	true,	false,	'Y',
	true,	false,	VK_DELETE,
	true,	false,	VK_F7,
	true,	false,	VK_F9,
	true,	false,	VK_F10,
	true,	false,	VK_F11,
	true,	false,	VK_F12,
	false,	false,	VK_F2,
	false,	false,	VK_F3,
	false,	false,	VK_F4,
	false,	false,	VK_F5,
	false,	false,	VK_F10,
	false,	true,	VK_F6,
	false,	true,	VK_F7,
	false,	true,	VK_F8,
	true,	true,	VK_F10,
	true,	false,	VK_SPACE,
	true,	false,	VK_BACK,
};

//
// 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 IgnoreImeHotKey(MSG* pmsg)
{
	if (!pmsg)
		return false;
	if (pmsg->wParam == VK_PROCESSKEY && (pmsg->message == WM_KEYDOWN || pmsg->message == WM_SYSKEYDOWN))
	{
		bool bCtrl, bShift;
		UINT uVkReal = _ImmGetVirtualKey(pmsg->hwnd);
		bCtrl = (GetKeyState(VK_CONTROL) & 0x8000) ? true : false;
		bShift = (GetKeyState(VK_SHIFT) & 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_uVk == uVkReal)
				return true;
		}
	}
	return false;
}

void _stdcall gos_FinalizeStringIME(void)
{
	if (g_szCompositionString[0] == 0)
		return;

	HIMC himc;
	if (NULL == (himc = _ImmGetContext(hWindow)))
		return;

	if (g_dwIMELevel == 2)
	{
		// Send composition string to app.
		LONG lRet = strlen(g_szCompositionString);
		for (int i = 0; i < lRet ; i++)
			SendMessage(hWindow, WM_CHAR, (unsigned char)g_szCompositionString[i], 0);
	}

	IMEInitCompStringData();
	// clear composition string in IME
	_ImmNotifyIME(himc, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
	_ImmReleaseContext(hWindow, himc);
	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;
}

void _stdcall gos_SetIMELevel(DWORD dwImeLevel)
{
	if (dwImeLevel < 2 || 3 < dwImeLevel)
		return;
	if (g_uCodePage == CODEPAGE_KOR)
		dwImeLevel = 3;
	g_dwIMELevel = dwImeLevel;
	// cancel current composition string.
	gos_FinalizeStringIME();
	SetCompStringColor();
}

void _stdcall gos_SetIMEAppearance( gosIME_Appearance* pia )
{
	if (NULL == pia)
		return;
	gSkinIME = *pia;
	gSkinIME.symbolColor			&= 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();
}

static void CheckChineseIMEStatus()
{
	if (!(g_uCodePage == CODEPAGE_CHT || g_uCodePage == CODEPAGE_CHS))
		return;
	HKL hkl = GetKeyboardLayout(0);
	g_bChineseIME = (PRIMARYLANGID(LOWORD(hkl)) == LANG_CHINESE) && _ImmIsIME(hkl);

	HIMC himc;
	if (NULL != (himc = _ImmGetContext(hWindow)))
	{
		DWORD dwConvMode, dwSentMode;
		_ImmGetConversionStatus(himc, &dwConvMode, &dwSentMode);
		_ImmReleaseContext(hWindow, himc);

		g_bComposingIME = g_bChineseIME && (dwConvMode & IME_CMODE_NATIVE);
	}
	else
		g_bComposingIME = false;
}

void gos_SetIMEInsertMode(bool bInsert)
{
	g_bInsertMode = bInsert;
}

// called by gos_GetMachineInformation()
DWORD GetIMECaretStatus()
{
	if (g_uCodePage == CODEPAGE_KOR && g_szCompositionString[0])
		return FALSE;
	return TRUE;
}

static void SetReadingWindowOrientation(DWORD dwVer)
{
	HKL kl = GetKeyboardLayout(0);
	g_bHorizontalReading = (kl == _TAIWAN_HKL2) || (dwVer == 0);
	if (!g_bHorizontalReading) {
		char szRegPath[MAX_PATH];
		HKEY hkey;
		wsprintf(szRegPath, "software\\microsoft\\windows\\currentversion\\%s",
			(dwVer >= _TAIWAN_IMEVERSION51) ? "MSTCIPH" : "TINTLGNT");
		LONG lRc = RegOpenKeyEx(HKEY_CURRENT_USER, szRegPath, 0, KEY_READ, &hkey);
		if (lRc == ERROR_SUCCESS) {
			DWORD dwSize = sizeof(DWORD), dwMapping, dwType;
			lRc = RegQueryValueEx(hkey, "keyboard mapping", NULL, &dwType, (PBYTE)&dwMapping, &dwSize);
			if (lRc == ERROR_SUCCESS) {
				if (
					(dwVer <= _TAIWAN_IMEVERSION50 && 
						((BYTE)dwMapping == 0x22 || (BYTE)dwMapping == 0x23)) ||
					((dwVer == _TAIWAN_IMEVERSION51 || _TAIWAN_IMEVERSION52) &&
						((BYTE)dwMapping >= 0x22 && (BYTE)dwMapping <= 0x24))) {
					g_bHorizontalReading = true;
				}
			}
		}
	}
}
