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]); } }
//-------------------------------------------------------------------------------------- _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); } } }
// Obtain the reading string upon WM_IME_NOTIFY/INM_PRIVATE notification. void CIME::GetPrivateReadingString() { DWORD dwId = GetImeId(); if(!dwId) { s_bShowReadingWindow = false; return; } HIMC hImc; hImc = ImmGetContext(UIGetHWND()); if(!hImc) { s_bShowReadingWindow = false; return; } DWORD dwReadingStrLen = 0; DWORD dwErr = 0; WCHAR *pwszReadingStringBuffer = NULL; // Buffer for when the IME supports GetReadingString() WCHAR *wstr = 0; bool bUnicodeIme = false; // Whether the IME context component is Unicode. INPUTCONTEXT *lpIC = NULL; if(_GetReadingString) { UINT uMaxUiLen; BOOL bVertical; // Obtain the reading string size dwReadingStrLen = _GetReadingString(hImc, 0, NULL, (PINT)&dwErr, &bVertical, &uMaxUiLen); if(dwReadingStrLen) { wstr = pwszReadingStringBuffer = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) * dwReadingStrLen); if(!pwszReadingStringBuffer) { // Out of memory. Exit. _ImmReleaseContext(UIGetHWND(), hImc); return; } // Obtain the reading string dwReadingStrLen = _GetReadingString(hImc, dwReadingStrLen, wstr, (PINT)&dwErr, &bVertical, &uMaxUiLen); } s_bHorizontalReading = !bVertical; bUnicodeIme = true; } else { // IMEs that doesn't implement Reading String API lpIC = _ImmLockIMC(hImc); LPBYTE p = 0; switch(dwId) { case IMEID_CHT_VER42: // New(Phonetic/ChanJie)IME98 : 4.2.x.x // Win98 case IMEID_CHT_VER43: // New(Phonetic/ChanJie)IME98a : 4.3.x.x // WinMe, Win2k case IMEID_CHT_VER44: // New ChanJie IME98b : 4.4.x.x // WinXP p = *(LPBYTE *)((LPBYTE)_ImmLockIMCC(lpIC->hPrivate) + 24); if(!p) break; dwReadingStrLen = *(DWORD *)(p + 7 * 4 + 32 * 4); dwErr = *(DWORD *)(p + 8 * 4 + 32 * 4); wstr = (WCHAR *)(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; dwReadingStrLen = *(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); wstr = (WCHAR *)(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; dwReadingStrLen = *(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); wstr = (WCHAR *) (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 = (GetImeId(1) >= 0x00000002) ? 8 : 7; p = *(LPBYTE *)((LPBYTE)_ImmLockIMCC(lpIC->hPrivate) + nOffset * 4); if(!p) break; dwReadingStrLen = *(DWORD *)(p + 7*4 + 16*2*4); dwErr = *(DWORD *)(p + 8*4 + 16*2*4); dwErr = __min(dwErr, dwReadingStrLen); wstr = (WCHAR *)(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) { OSVERSIONINFOW osi; osi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOW); GetVersionExW(&osi); int nTcharSize = (osi.dwPlatformId == VER_PLATFORM_WIN32_NT) ? sizeof(WCHAR) : sizeof(char); p = *(LPBYTE *)((LPBYTE)_ImmLockIMCC(lpIC->hPrivate) + 1*4 + 1*4 + 6*4); if(!p) break; dwReadingStrLen = *(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); wstr = (WCHAR *) (p + 1*4 + (16*2+2*4) + 5*4); bUnicodeIme = (osi.dwPlatformId == VER_PLATFORM_WIN32_NT) ? true : false; } } // switch } // Copy the reading string to the candidate list first s_CandList.awszCandidate[0][0] = 0; s_CandList.awszCandidate[1][0] = 0; s_CandList.awszCandidate[2][0] = 0; s_CandList.awszCandidate[3][0] = 0; s_CandList.dwCount = dwReadingStrLen; s_CandList.dwSelection = (DWORD)-1; // do not select any char if(bUnicodeIme) { UINT i; for(i = 0; i < dwReadingStrLen; ++i) // dwlen > 0, if known IME { if(dwErr <= i && s_CandList.dwSelection == (DWORD)-1) { // select error char s_CandList.dwSelection = i; } s_CandList.awszCandidate[i][0] = wstr[i]; s_CandList.awszCandidate[i][1] = 0; } s_CandList.awszCandidate[i][0] = 0; } else { char *p = (char *)wstr; DWORD i, j; for(i = 0, j = 0; i < dwReadingStrLen; ++i, ++j) // dwlen > 0, if known IME { if(dwErr <= i && s_CandList.dwSelection == (DWORD)-1) { s_CandList.dwSelection = j; } // Obtain the current code page WCHAR wszCodePage[8]; UINT uCodePage = CP_ACP; // Default code page if(GetLocaleInfoW(MAKELCID(GetLanguage(), SORT_DEFAULT), LOCALE_IDEFAULTANSICODEPAGE, wszCodePage, sizeof(wszCodePage)/sizeof(wszCodePage[0]))) { uCodePage = wcstoul(wszCodePage, NULL, 0); } MultiByteToWideChar(uCodePage, 0, p + i, IsDBCSLeadByteEx(uCodePage, p[i]) ? 2 : 1, s_CandList.awszCandidate[j], 1); if(IsDBCSLeadByteEx(uCodePage, p[i])) ++i; } s_CandList.awszCandidate[j][0] = 0; s_CandList.dwCount = j; } if(!_GetReadingString) { _ImmUnlockIMCC(lpIC->hPrivate); _ImmUnlockIMC(hImc); GetReadingWindowOrientation(dwId); } _ImmReleaseContext(UIGetHWND(), hImc); if(pwszReadingStringBuffer) HeapFree(GetProcessHeap(), 0, pwszReadingStringBuffer); // Copy the string to the reading string buffer if(s_CandList.dwCount > 0) s_bShowReadingWindow = true; else s_bShowReadingWindow = false; if(s_bHorizontalReading) { s_CandList.nReadingError = -1; s_wstrReadingString.clear(); for(UINT i = 0; i < s_CandList.dwCount; ++i) { if(s_CandList.dwSelection == i) s_CandList.nReadingError = s_wstrReadingString.length(); s_wstrReadingString+=s_CandList.awszCandidate[i]; } } s_CandList.dwPageSize = MAX_CANDLIST; }