Esempio n. 1
0
void CIME::CheckInputLocale()
{
	static HKL hklPrev = 0;
	s_hklCurrent = GetKeyboardLayout(0);
	if (hklPrev == s_hklCurrent)
		return;

	hklPrev = s_hklCurrent;
	switch (GetPrimaryLanguage())
	{
		// Simplified Chinese
	case LANG_CHINESE:
		s_bVerticalCand = true;
		switch (GetSubLanguage())
		{
		case SUBLANG_CHINESE_SIMPLIFIED:
			s_wszCurrIndicator = s_aszIndicator[INDICATOR_CHS];
			s_bVerticalCand = GetImeId() == 0;
			break;
		case SUBLANG_CHINESE_TRADITIONAL:
			s_wszCurrIndicator = s_aszIndicator[INDICATOR_CHT];
			break;
		default:    // unsupported sub-language
			s_wszCurrIndicator = s_aszIndicator[INDICATOR_NON_IME];
			break;
		}
		break;
		// Korean
	case LANG_KOREAN:
		s_wszCurrIndicator = s_aszIndicator[INDICATOR_KOREAN];
		s_bVerticalCand = false;
		break;
		// Japanese
	case LANG_JAPANESE:
		s_wszCurrIndicator = s_aszIndicator[INDICATOR_JAPANESE];
		s_bVerticalCand = true;
		break;
	default:
		// A non-IME language.  Obtain the language abbreviation
		// and store it for rendering the indicator later.
		s_wszCurrIndicator = s_aszIndicator[INDICATOR_NON_IME];
	}

	// If non-IME, use the language abbreviation.
	if(s_wszCurrIndicator == s_aszIndicator[INDICATOR_NON_IME])
	{
		WCHAR wszLang[5];
		GetLocaleInfoW(MAKELCID(LOWORD(s_hklCurrent), SORT_DEFAULT), LOCALE_SABBREVLANGNAME, wszLang, 5);
		s_wszCurrIndicator[0] = wszLang[0];
		s_wszCurrIndicator[1] = towlower(wszLang[1]);
	}
}
Esempio n. 2
0
void CIME::CheckToggleState()
{
	CheckInputLocale();
	bool bIme = _ImmIsIME(s_hklCurrent) != 0;
	s_bChineseIME = (GetPrimaryLanguage() == LANG_CHINESE) && bIme;

	HIMC hImc;
	if(NULL != (hImc = ImmGetContext(UIGetHWND())))
	{
		if(s_bChineseIME)
		{
			DWORD dwConvMode, dwSentMode;
			_ImmGetConversionStatus(hImc, &dwConvMode, &dwSentMode);
			s_ImeState = (dwConvMode & IME_CMODE_NATIVE) ? IMEUI_STATE_ON : IMEUI_STATE_ENGLISH;
		}
		else
		{
			s_ImeState = (bIme && _ImmGetOpenStatus(hImc) != 0) ? IMEUI_STATE_ON : IMEUI_STATE_OFF;
		}
		_ImmReleaseContext(UIGetHWND(), hImc);
	}
	else
		s_ImeState = IMEUI_STATE_OFF;
}
Esempio n. 3
0
//--------------------------------------------------------------------------------------
DXUTAPI void CDXUTIMEEditBox::RenderComposition()
{
	s_CompString.SetText(ImeUi_GetCompositionString());

	RECT rcCaret =
	{
		0, 0, 0, 0
	};
	int nX, nXFirst;
	m_Buffer.CPtoX(m_nCaret, FALSE, &nX);
	m_Buffer.CPtoX(m_nFirstVisible, FALSE, &nXFirst);
	CDXUTElement* pElement = m_Elements[1];

	// Get the required width
	RECT rc =
	{
		m_rcText.left + nX - nXFirst, m_rcText.top,
		m_rcText.left + nX - nXFirst, m_rcText.bottom
	};
	m_pDialog->CalcTextRect(s_CompString.GetBuffer(), pElement, &rc);

	// If the composition string is too long to fit within
	// the text area, move it to below the current line.
	// This matches the behavior of the default IME.
	if (rc.right > m_rcText.right)
		OffsetRect(&rc, m_rcText.left - rc.left, rc.bottom - rc.top);

	// Save the rectangle position for processing highlighted text.
	RECT rcFirst = rc;

	// Update s_ptCompString for RenderCandidateReadingWindow().
	s_ptCompString.x = rc.left; s_ptCompString.y = rc.top;

	DWORD TextColor = m_CompColor;
	// Render the window and string.
	// If the string is too long, we must wrap the line.
	pElement->FontColor.SetCurrent(TextColor);
	const WCHAR* pwszComp = s_CompString.GetBuffer();
	int nCharLeft = s_CompString.GetTextSize();
	for (;;)
	{
		// Find the last character that can be drawn on the same line.
		int nLastInLine;
		int bTrail;
		s_CompString.XtoCP(m_rcText.right - rc.left, &nLastInLine, &bTrail);
		int nNumCharToDraw = std::min(nCharLeft, nLastInLine);
		m_pDialog->CalcTextRect(pwszComp, pElement, &rc, nNumCharToDraw);

		// Draw the background
		// For Korean IME, blink the composition window background as if it
		// is a cursor.
		if (GetPrimaryLanguage() == LANG_KOREAN)
		{
			if (m_bCaretOn)
			{
				m_pDialog->DrawRect(&rc, m_CompWinColor);
			}
			else
			{
				// Not drawing composition string background. We
				// use the editbox's text color for composition
				// string text.
				TextColor = m_Elements[0]->FontColor.States[DXUT_STATE_NORMAL];
			}
		}
		else
		{
			// Non-Korean IME. Always draw composition background.
			m_pDialog->DrawRect(&rc, m_CompWinColor);
		}

		// Draw the text
		pElement->FontColor.SetCurrent(TextColor);
		m_pDialog->DrawText(pwszComp, pElement, &rc, false);

		// Advance pointer and counter
		nCharLeft -= nNumCharToDraw;
		pwszComp += nNumCharToDraw;
		if (nCharLeft <= 0)
			break;

		// Advance rectangle coordinates to beginning of next line
		OffsetRect(&rc, m_rcText.left - rc.left, rc.bottom - rc.top);
	}

	// Load the rect for the first line again.
	rc = rcFirst;

	// Inspect each character in the comp string.
	// For target-converted and target-non-converted characters,
	// we display a different background color so they appear highlighted.
	int nCharFirst = 0;
	nXFirst = 0;
	s_nFirstTargetConv = -1;
	BYTE* pAttr;
	const WCHAR* pcComp;
	for (pcComp = s_CompString.GetBuffer(), pAttr = ImeUi_GetCompStringAttr();
		*pcComp != L'\0'; ++pcComp, ++pAttr)
	{
		DWORD bkColor;

		// Render a different background for this character
		int nXLeft, nXRight;
		s_CompString.CPtoX(int(pcComp - s_CompString.GetBuffer()), FALSE, &nXLeft);
		s_CompString.CPtoX(int(pcComp - s_CompString.GetBuffer()), TRUE, &nXRight);

		// Check if this character is off the right edge and should
		// be wrapped to the next line.
		if (nXRight - nXFirst > m_rcText.right - rc.left)
		{
			// Advance rectangle coordinates to beginning of next line
			OffsetRect(&rc, m_rcText.left - rc.left, rc.bottom - rc.top);

			// Update the line's first character information
			nCharFirst = int(pcComp - s_CompString.GetBuffer());
			s_CompString.CPtoX(nCharFirst, FALSE, &nXFirst);
		}

		// If the caret is on this character, save the coordinates
		// for drawing the caret later.
		if (ImeUi_GetImeCursorChars() == (DWORD)(pcComp - s_CompString.GetBuffer()))
		{
			rcCaret = rc;
			rcCaret.left += nXLeft - nXFirst - 1;
			rcCaret.right = rcCaret.left + 2;
		}

		// Set up color based on the character attribute
		if (*pAttr == ATTR_TARGET_CONVERTED)
		{
			pElement->FontColor.SetCurrent(m_CompTargetColor);
			bkColor = m_CompTargetBkColor;
		}
		else if (*pAttr == ATTR_TARGET_NOTCONVERTED)
		{
			pElement->FontColor.SetCurrent(m_CompTargetNonColor);
			bkColor = m_CompTargetNonBkColor;
		}
		else
		{
			continue;
		}

		RECT rcTarget =
		{
			rc.left + nXLeft - nXFirst, rc.top, rc.left + nXRight - nXFirst, rc.bottom
		};
		m_pDialog->DrawRect(&rcTarget, bkColor);
		m_pDialog->DrawText(pcComp, pElement, &rcTarget, false, 1);

		// Record the first target converted character's index
		if (-1 == s_nFirstTargetConv)
			s_nFirstTargetConv = int(pAttr - ImeUi_GetCompStringAttr());
	}

	// Render the composition caret
	if (m_bCaretOn)
	{
		// If the caret is at the very end, its position would not have
		// been computed in the above loop.  We compute it here.
		if (ImeUi_GetImeCursorChars() == (DWORD)s_CompString.GetTextSize())
		{
			s_CompString.CPtoX(ImeUi_GetImeCursorChars(), FALSE, &nX);
			rcCaret = rc;
			rcCaret.left += nX - nXFirst - 1;
			rcCaret.right = rcCaret.left + 2;
		}

		m_pDialog->DrawRect(&rcCaret, m_CompCaretColor);
	}
}
Esempio n. 4
0
//--------------------------------------------------------------------------------------
_Use_decl_annotations_
DXUTAPI void CDXUTIMEEditBox::RenderCandidateReadingWindow(bool bReading)
{
	RECT rc;
	UINT nNumEntries = bReading ? 4 : MAX_CANDLIST;
	int nX, nXFirst, nXComp;
	m_Buffer.CPtoX(m_nCaret, FALSE, &nX);
	m_Buffer.CPtoX(m_nFirstVisible, FALSE, &nXFirst);

	DWORD TextColor, TextBkColor, SelTextColor, SelBkColor;
	if (bReading)
	{
		TextColor = m_ReadingColor;
		TextBkColor = m_ReadingWinColor;
		SelTextColor = m_ReadingSelColor;
		SelBkColor = m_ReadingSelBkColor;
	}
	else
	{
		TextColor = m_CandidateColor;
		TextBkColor = m_CandidateWinColor;
		SelTextColor = m_CandidateSelColor;
		SelBkColor = m_CandidateSelBkColor;
	}

	// For Japanese IME, align the window with the first target converted character.
	// For all other IMEs, align with the caret.  This is because the caret
	// does not move for Japanese IME.
	if (GetLanguage() == MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL) && !GetImeId())
		nXComp = 0;
	else if (GetPrimaryLanguage() == LANG_JAPANESE)
		s_CompString.CPtoX(s_nFirstTargetConv, FALSE, &nXComp);
	else
		s_CompString.CPtoX(ImeUi_GetImeCursorChars(), FALSE, &nXComp);

	// Compute the size of the candidate window
	int nWidthRequired = 0;
	int nHeightRequired = 0;
	int nSingleLineHeight = 0;

	if ((ImeUi_IsVerticalCand() && !bReading) ||
		(!ImeUi_IsHorizontalReading() && bReading))
	{
		// Vertical window
		for (UINT i = 0; i < nNumEntries; ++i)
		{
			if (*(ImeUi_GetCandidate(i)) == L'\0')
				break;
			SetRect(&rc, 0, 0, 0, 0);
			m_pDialog->CalcTextRect(ImeUi_GetCandidate(i), m_Elements[1], &rc);
			nWidthRequired = std::max<int>(nWidthRequired, rc.right - rc.left);
			nSingleLineHeight = std::max<int>(nSingleLineHeight, rc.bottom - rc.top);
		}
		nHeightRequired = nSingleLineHeight * nNumEntries;
	}
	else
	{
		// Horizontal window
		SetRect(&rc, 0, 0, 0, 0);
		if (bReading)
			m_pDialog->CalcTextRect(s_wszReadingString, m_Elements[1], &rc);
		else
		{
			WCHAR wszCand[256] = L"";

			s_CandList.nFirstSelected = 0;
			s_CandList.nHoriSelectedLen = 0;
			for (UINT i = 0; i < MAX_CANDLIST; ++i)
			{
				if (*ImeUi_GetCandidate(i) == L'\0')
					break;

				WCHAR wszEntry[32];
				swprintf_s(wszEntry, 32, L"%s ", ImeUi_GetCandidate(i));
				// If this is the selected entry, mark its char position.
				if (ImeUi_GetCandidateSelection() == i)
				{
					s_CandList.nFirstSelected = (int)wcslen(wszCand);
					s_CandList.nHoriSelectedLen = (int)wcslen(wszEntry) - 1;  // Minus space
				}
				wcscat_s(wszCand, 256, wszEntry);
			}
			wszCand[wcslen(wszCand) - 1] = L'\0';  // Remove the last space
			s_CandList.HoriCand.SetText(wszCand);

			m_pDialog->CalcTextRect(s_CandList.HoriCand.GetBuffer(), m_Elements[1], &rc);
		}
		nWidthRequired = rc.right - rc.left;
		nSingleLineHeight = nHeightRequired = rc.bottom - rc.top;
	}

	// Now that we have the dimension, calculate the location for the candidate window.
	// We attempt to fit the window in this order:
	// bottom, top, right, left.

	bool bHasPosition = false;

	// Bottom
	SetRect(&rc, s_ptCompString.x + nXComp, s_ptCompString.y + m_rcText.bottom - m_rcText.top,
		s_ptCompString.x + nXComp + nWidthRequired, s_ptCompString.y + m_rcText.bottom - m_rcText.top +
		nHeightRequired);
	// if the right edge is cut off, move it left.
	if (rc.right > m_pDialog->GetWidth())
	{
		rc.left -= rc.right - m_pDialog->GetWidth();
		rc.right = m_pDialog->GetWidth();
	}
	if (rc.bottom <= m_pDialog->GetHeight())
		bHasPosition = true;

	// Top
	if (!bHasPosition)
	{
		SetRect(&rc, s_ptCompString.x + nXComp, s_ptCompString.y - nHeightRequired,
			s_ptCompString.x + nXComp + nWidthRequired, s_ptCompString.y);
		// if the right edge is cut off, move it left.
		if (rc.right > m_pDialog->GetWidth())
		{
			rc.left -= rc.right - m_pDialog->GetWidth();
			rc.right = m_pDialog->GetWidth();
		}
		if (rc.top >= 0)
			bHasPosition = true;
	}

	// Right
	if (!bHasPosition)
	{
		int nXCompTrail;
		s_CompString.CPtoX(ImeUi_GetImeCursorChars(), TRUE, &nXCompTrail);
		SetRect(&rc, s_ptCompString.x + nXCompTrail, 0,
			s_ptCompString.x + nXCompTrail + nWidthRequired, nHeightRequired);
		if (rc.right <= m_pDialog->GetWidth())
			bHasPosition = true;
	}

	// Left
	if (!bHasPosition)
	{
		SetRect(&rc, s_ptCompString.x + nXComp - nWidthRequired, 0,
			s_ptCompString.x + nXComp, nHeightRequired);
		if (rc.right >= 0)
			bHasPosition = true;
	}

	if (!bHasPosition)
	{
		// The dialog is too small for the candidate window.
		// Fall back to render at 0, 0.  Some part of the window
		// will be cut off.
		rc.left = 0;
		rc.right = nWidthRequired;
	}

	// If we are rendering the candidate window, save the position
	// so that mouse clicks are checked properly.
	if (!bReading)
		s_CandList.rcCandidate = rc;

	// Render the elements
	m_pDialog->DrawRect(&rc, TextBkColor);
	if ((ImeUi_IsVerticalCand() && !bReading) ||
		(!ImeUi_IsHorizontalReading() && bReading))
	{
		// Vertical candidate window
		for (UINT i = 0; i < nNumEntries; ++i)
		{
			// Here we are rendering one line at a time
			rc.bottom = rc.top + nSingleLineHeight;
			// Use a different color for the selected string
			if (ImeUi_GetCandidateSelection() == i)
			{
				m_pDialog->DrawRect(&rc, SelBkColor);
				m_Elements[1]->FontColor.SetCurrent(SelTextColor);
			}
			else
				m_Elements[1]->FontColor.SetCurrent(TextColor);

			m_pDialog->DrawText(ImeUi_GetCandidate(i), m_Elements[1], &rc);

			rc.top += nSingleLineHeight;
		}
	}
	else
	{
		// Horizontal candidate window
		m_Elements[1]->FontColor.SetCurrent(TextColor);
		if (bReading)
			m_pDialog->DrawText(s_wszReadingString, m_Elements[1], &rc);
		else
			m_pDialog->DrawText(s_CandList.HoriCand.GetBuffer(), m_Elements[1], &rc);

		// Render the selected entry differently
		if (!bReading)
		{
			int nXLeft, nXRight;
			s_CandList.HoriCand.CPtoX(s_CandList.nFirstSelected, FALSE, &nXLeft);
			s_CandList.HoriCand.CPtoX(s_CandList.nFirstSelected + s_CandList.nHoriSelectedLen, FALSE, &nXRight);

			rc.right = rc.left + nXRight;
			rc.left += nXLeft;
			m_pDialog->DrawRect(&rc, SelBkColor);
			m_Elements[1]->FontColor.SetCurrent(SelTextColor);
			m_pDialog->DrawText(s_CandList.HoriCand.GetBuffer() + s_CandList.nFirstSelected,
				m_Elements[1], &rc, false);
		}
	}
}
Esempio n. 5
0
//--------------------------------------------------------------------------------------
_Use_decl_annotations_
DXUTAPI bool CDXUTIMEEditBox::HandleMouse(UINT uMsg, const POINT& pt, WPARAM wParam, LPARAM lParam)
{
	if (!m_bEnabled || !m_bVisible)
		return false;

	switch (uMsg)
	{
	case WM_LBUTTONDOWN:
	case WM_LBUTTONDBLCLK:
	{
							 DXUTFontNode* pFont = m_pDialog->GetFont(m_Elements[9]->iFont);

							 // Check if this click is on top of the composition string
							 int nCompStrWidth;
							 s_CompString.CPtoX(s_CompString.GetTextSize(), FALSE, &nCompStrWidth);

							 if (s_ptCompString.x <= pt.x &&
								 s_ptCompString.y <= pt.y &&
								 s_ptCompString.x + nCompStrWidth > pt.x &&
								 s_ptCompString.y + pFont->nHeight > pt.y)
							 {
								 int nCharBodyHit, nCharHit;
								 int nTrail;

								 // Determine the character clicked on.
								 s_CompString.XtoCP(pt.x - s_ptCompString.x, &nCharBodyHit, &nTrail);
								 if (nTrail && nCharBodyHit < s_CompString.GetTextSize())
									 nCharHit = nCharBodyHit + 1;
								 else
									 nCharHit = nCharBodyHit;

								 switch (GetPrimaryLanguage())
								 {
								 case LANG_JAPANESE:
									 // For Japanese, there are two cases.  If s_nFirstTargetConv is
									 // -1, the comp string hasn't been converted yet, and we use
									 // s_nCompCaret.  For any other value of s_nFirstTargetConv,
									 // the string has been converted, so we use clause information.

									 if (s_nFirstTargetConv != -1)
									 {
										 int nClauseClicked = 0;
										 while ((int)s_adwCompStringClause[nClauseClicked + 1] <= nCharBodyHit)
											 ++nClauseClicked;

										 int nClauseSelected = 0;
										 while ((int)s_adwCompStringClause[nClauseSelected + 1] <= s_nFirstTargetConv)
											 ++nClauseSelected;

										 BYTE nVirtKey = nClauseClicked > nClauseSelected ? VK_RIGHT : VK_LEFT;
										 int nSendCount = abs(nClauseClicked - nClauseSelected);
										 while (nSendCount-- > 0)
											 SendKey(nVirtKey);

										 return true;
									 }

									 // Not converted case. Fall thru to Chinese case.

								 case LANG_CHINESE:
								 {
													  // For Chinese, use s_nCompCaret.
													  BYTE nVirtKey = nCharHit > (int)ImeUi_GetImeCursorChars() ? VK_RIGHT : VK_LEFT;
													  int nSendCount = abs(nCharHit - (int)ImeUi_GetImeCursorChars());
													  while (nSendCount-- > 0)
														  SendKey(nVirtKey);
													  break;
								 }
								 }

								 return true;
							 }

							 // Check if the click is on top of the candidate window
							 if (ImeUi_IsShowCandListWindow() && PtInRect(&s_CandList.rcCandidate, pt))
							 {
								 if (ImeUi_IsVerticalCand())
								 {
									 // Vertical candidate window

									 // Compute the row the click is on
									 int nRow = (pt.y - s_CandList.rcCandidate.top) / pFont->nHeight;

									 if (nRow < (int)ImeUi_GetCandidateCount())
									 {
										 // nRow is a valid entry.
										 // Now emulate keystrokes to select the candidate at this row.
										 switch (GetPrimaryLanguage())
										 {
										 case LANG_CHINESE:
										 case LANG_KOREAN:
											 // For Chinese and Korean, simply send the number keystroke.
											 SendKey((BYTE)('0' + nRow + 1));
											 break;

										 case LANG_JAPANESE:
											 // For Japanese, move the selection to the target row,
											 // then send Right, then send Left.

											 BYTE nVirtKey;
											 if (nRow > (int)ImeUi_GetCandidateSelection())
												 nVirtKey = VK_DOWN;
											 else
												 nVirtKey = VK_UP;
											 int nNumToHit = abs(int(nRow - ImeUi_GetCandidateSelection()));
											 for (int nStrike = 0; nStrike < nNumToHit; ++nStrike)
												 SendKey(nVirtKey);

											 // Do this to close the candidate window without ending composition.
											 SendKey(VK_RIGHT);
											 SendKey(VK_LEFT);

											 break;
										 }
									 }
								 }
								 else
								 {
									 // Horizontal candidate window

									 // Determine which the character the click has hit.
									 int nCharHit;
									 int nTrail;
									 s_CandList.HoriCand.XtoCP(pt.x - s_CandList.rcCandidate.left, &nCharHit, &nTrail);

									 // Determine which candidate string the character belongs to.
									 int nCandidate = ImeUi_GetCandidateCount() - 1;

									 int nEntryStart = 0;
									 for (UINT i = 0; i < ImeUi_GetCandidateCount(); ++i)
									 {
										 if (nCharHit >= nEntryStart)
										 {
											 // Haven't found it.
											 nEntryStart += (int)wcslen(ImeUi_GetCandidate(i)) + 1;  // plus space separator
										 }
										 else
										 {
											 // Found it.  This entry starts at the right side of the click point,
											 // so the char belongs to the previous entry.
											 nCandidate = i - 1;
											 break;
										 }
									 }

									 // Now emulate keystrokes to select the candidate entry.
									 switch (GetPrimaryLanguage())
									 {
									 case LANG_CHINESE:
									 case LANG_KOREAN:
										 // For Chinese and Korean, simply send the number keystroke.
										 SendKey((BYTE)('0' + nCandidate + 1));
										 break;
									 }
								 }

								 return true;
							 }
	}
	}

	// If we didn't care for the msg, let the parent process it.
	return CDXUTEditBox::HandleMouse(uMsg, pt, wParam, lParam);
}
Esempio n. 6
0
bool CIME::StaticMsgProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	HIMC hImc;

	if(!s_bEnableImeSystem)
		return false;

#if defined(DEBUG) || defined(_DEBUG)
	m_bIMEStaticMsgProcCalled = true;
#endif

	switch(uMsg)
	{
	case WM_ACTIVATEAPP:
		if(wParam)
		{
			// Populate s_Locale with the list of keyboard layouts.
			UINT cKL = GetKeyboardLayoutList(0, NULL);
			s_Locale.clear();
			HKL *phKL = new HKL[cKL];
			if(phKL)
			{
				GetKeyboardLayoutList(cKL, phKL);
				for(UINT i = 0; i < cKL; ++i)
				{
					CInputLocale Locale;

					// Filter out East Asian languages that are not IME.
					if((PRIMARYLANGID(LOWORD(phKL[i])) == LANG_CHINESE ||
						PRIMARYLANGID(LOWORD(phKL[i])) == LANG_JAPANESE ||
						PRIMARYLANGID(LOWORD(phKL[i])) == LANG_KOREAN) &&
						!_ImmIsIME(phKL[i]))
						continue;

					// If this language is already in the list, don't add it again.
					bool bBreak = false;
					for(size_t e = 0; e < s_Locale.size(); ++e)
						if(LOWORD(s_Locale[e].m_hKL) ==
							LOWORD(phKL[i]))
						{
							bBreak = true;
							break;
						}
						if(bBreak)
							break;

						Locale.m_hKL = phKL[i];
						WCHAR wszDesc[128] = L"";
						switch(PRIMARYLANGID(LOWORD(phKL[i])))
						{
							// Simplified Chinese
						case LANG_CHINESE:
							switch(SUBLANGID(LOWORD(phKL[i])))
							{
							case SUBLANG_CHINESE_SIMPLIFIED:
								Locale.m_wstrLangAbb = s_aszIndicator[INDICATOR_CHS];
								break;
							case SUBLANG_CHINESE_TRADITIONAL:
								Locale.m_wstrLangAbb = s_aszIndicator[INDICATOR_CHT];
								break;
							default:    // unsupported sub-language
								GetLocaleInfoW(MAKELCID(LOWORD(phKL[i]), SORT_DEFAULT), LOCALE_SABBREVLANGNAME, wszDesc, 128);
								Locale.m_wstrLangAbb = wszDesc[0];
								Locale.m_wstrLangAbb += towlower(wszDesc[1]);
								break;
							}
							break;
							// Korean
						case LANG_KOREAN:
							Locale.m_wstrLangAbb = s_aszIndicator[INDICATOR_KOREAN];
							break;
							// Japanese
						case LANG_JAPANESE:
							Locale.m_wstrLangAbb = s_aszIndicator[INDICATOR_JAPANESE];
							break;         
						default:
							// A non-IME language.  Obtain the language abbreviation
							// and store it for rendering the indicator later.
							GetLocaleInfoW(MAKELCID(LOWORD(phKL[i]), SORT_DEFAULT), LOCALE_SABBREVLANGNAME, wszDesc, 128);
							Locale.m_wstrLangAbb = wszDesc[0];
							Locale.m_wstrLangAbb += towlower(wszDesc[1]);
							break;
						}

						GetLocaleInfoW(MAKELCID(LOWORD(phKL[i]), SORT_DEFAULT), LOCALE_SLANGUAGE, wszDesc, 128);
						Locale.m_wstrLang = wszDesc;

						s_Locale.push_back(Locale);
				}
				delete[] phKL;
			}
		}
		break;

	case WM_INPUTLANGCHANGE:
		UITRACE(L"WM_INPUTLANGCHANGE\n");
		{
			UINT uLang = GetPrimaryLanguage();
			CheckToggleState();
			if (uLang != GetPrimaryLanguage())
			{
				// Korean IME always inserts on keystroke.  Other IMEs do not.
				s_bInsertOnType = (GetPrimaryLanguage() == LANG_KOREAN);
			}

			// IME changed.  Setup the new IME.
			SetupImeApi();
			if(_ShowReadingWindow)
			{
				if (NULL != (hImc = ImmGetContext(UIGetHWND())))
				{
					_ShowReadingWindow(hImc, false);
					_ImmReleaseContext(UIGetHWND(), hImc);
				}
			}
		}
		return true;

	case WM_IME_SETCONTEXT:
		UITRACE(L"WM_IME_SETCONTEXT\n");
		//
		// We don't want anything to display, so we have to clear this
		//
		lParam = 0;
		return false;

		// Handle WM_IME_STARTCOMPOSITION here since
		// we do not want the default IME handler to see
		// this when our fullscreen app is running.
	case WM_IME_STARTCOMPOSITION:
		UITRACE(L"WM_IME_STARTCOMPOSITION\n");
		ResetCompositionString();
		// Since the composition string has its own caret, we don't render
		// the edit control's own caret to avoid double carets on screen.
///ghghgh		s_bHideCaret = true;
		return true;

	case WM_IME_COMPOSITION:
		UITRACE(L"WM_IME_COMPOSITION\n");
		return false;
	}

	return false;
}