| Index: ui/base/win/ime_input.cc
|
| diff --git a/chrome/browser/ime_input.cc b/ui/base/win/ime_input.cc
|
| similarity index 61%
|
| rename from chrome/browser/ime_input.cc
|
| rename to ui/base/win/ime_input.cc
|
| index edb49f8cc7163ceaeb70d35a2639ab9752f49de6..a520f03ae09fa96c33ba46319dc46bd93b7f2060 100644
|
| --- a/chrome/browser/ime_input.cc
|
| +++ b/ui/base/win/ime_input.cc
|
| @@ -1,19 +1,26 @@
|
| -// Copyright (c) 2010 The Chromium Authors. All rights reserved.
|
| +// Copyright (c) 2011 The Chromium Authors. All rights reserved.
|
| // Use of this source code is governed by a BSD-style license that can be
|
| // found in the LICENSE file.
|
|
|
| -#include "chrome/browser/ime_input.h"
|
| +#include "ui/base/win/ime_input.h"
|
|
|
| #include "base/basictypes.h"
|
| #include "base/scoped_ptr.h"
|
| +#include "base/string16.h"
|
| #include "base/string_util.h"
|
| +#include "base/utf_string_conversions.h"
|
| #include "third_party/skia/include/core/SkColor.h"
|
| +#include "ui/base/ime/composition_text.h"
|
|
|
| // "imm32.lib" is required by IMM32 APIs used in this file.
|
| // NOTE(hbono): To comply with a comment from Darin, I have added
|
| // this #pragma directive instead of adding "imm32.lib" to a project file.
|
| #pragma comment(lib, "imm32.lib")
|
|
|
| +// Following code requires wchar_t to be same as char16. It should always be
|
| +// true on Windows.
|
| +COMPILE_ASSERT(sizeof(wchar_t) == sizeof(char16), wchar_t__char16_diff);
|
| +
|
| ///////////////////////////////////////////////////////////////////////////////
|
| // ImeInput
|
|
|
| @@ -62,11 +69,10 @@ void GetCompositionTargetRange(HIMC imm_context, int* target_start,
|
|
|
| // Helper function for ImeInput::GetCompositionInfo() method, to get underlines
|
| // information of the current composition string.
|
| -void GetCompositionUnderlines(
|
| - HIMC imm_context,
|
| - int target_start,
|
| - int target_end,
|
| - std::vector<WebKit::WebCompositionUnderline>* underlines) {
|
| +void GetCompositionUnderlines(HIMC imm_context,
|
| + int target_start,
|
| + int target_end,
|
| + ui::CompositionUnderlines* underlines) {
|
| int clause_size = ::ImmGetCompositionString(imm_context, GCS_COMPCLAUSE,
|
| NULL, 0);
|
| int clause_length = clause_size / sizeof(uint32);
|
| @@ -76,15 +82,15 @@ void GetCompositionUnderlines(
|
| ::ImmGetCompositionString(imm_context, GCS_COMPCLAUSE,
|
| clause_data.get(), clause_size);
|
| for (int i = 0; i < clause_length - 1; ++i) {
|
| - WebKit::WebCompositionUnderline underline;
|
| - underline.startOffset = clause_data[i];
|
| - underline.endOffset = clause_data[i+1];
|
| + ui::CompositionUnderline underline;
|
| + underline.start_offset = clause_data[i];
|
| + underline.end_offset = clause_data[i+1];
|
| underline.color = SK_ColorBLACK;
|
| underline.thick = false;
|
|
|
| // Use thick underline for the target clause.
|
| - if (underline.startOffset >= static_cast<unsigned>(target_start) &&
|
| - underline.endOffset <= static_cast<unsigned>(target_end)) {
|
| + if (underline.start_offset >= static_cast<unsigned>(target_start) &&
|
| + underline.end_offset <= static_cast<unsigned>(target_end)) {
|
| underline.thick = true;
|
| }
|
| underlines->push_back(underline);
|
| @@ -93,8 +99,25 @@ void GetCompositionUnderlines(
|
| }
|
| }
|
|
|
| +// Checks if a given primary language ID is a RTL language.
|
| +bool IsRTLPrimaryLangID(LANGID lang) {
|
| + switch (lang) {
|
| + case LANG_ARABIC:
|
| + case LANG_HEBREW:
|
| + case LANG_PERSIAN:
|
| + case LANG_SYRIAC:
|
| + case LANG_UIGHUR:
|
| + case LANG_URDU:
|
| + return true;
|
| + default:
|
| + return false;
|
| + }
|
| +}
|
| +
|
| } // namespace
|
|
|
| +namespace ui {
|
| +
|
| ImeInput::ImeInput()
|
| : ime_status_(false),
|
| input_language_id_(LANG_USER_DEFAULT),
|
| @@ -117,7 +140,6 @@ bool ImeInput::SetInputLanguage() {
|
| return ime_status_;
|
| }
|
|
|
| -
|
| void ImeInput::CreateImeWindow(HWND window_handle) {
|
| // When a user disables TSF (Text Service Framework) and CUAS (Cicero
|
| // Unaware Application Support), Chinese IMEs somehow ignore function calls
|
| @@ -141,9 +163,9 @@ void ImeInput::CreateImeWindow(HWND window_handle) {
|
| UpdateImeWindow(window_handle);
|
| }
|
|
|
| -void ImeInput::SetImeWindowStyle(HWND window_handle, UINT message,
|
| - WPARAM wparam, LPARAM lparam,
|
| - BOOL* handled) {
|
| +LRESULT ImeInput::SetImeWindowStyle(HWND window_handle, UINT message,
|
| + WPARAM wparam, LPARAM lparam,
|
| + BOOL* handled) {
|
| // To prevent the IMM (Input Method Manager) from displaying the IME
|
| // composition window, Update the styles of the IME windows and EXPLICITLY
|
| // call ::DefWindowProc() here.
|
| @@ -154,7 +176,7 @@ void ImeInput::SetImeWindowStyle(HWND window_handle, UINT message,
|
| // the function with its original value and over-writes our window styles.
|
| *handled = TRUE;
|
| lparam &= ~ISC_SHOWUICOMPOSITIONWINDOW;
|
| - ::DefWindowProc(window_handle, message, wparam, lparam);
|
| + return ::DefWindowProc(window_handle, message, wparam, lparam);
|
| }
|
|
|
| void ImeInput::DestroyImeWindow(HWND window_handle) {
|
| @@ -249,12 +271,18 @@ void ImeInput::CompleteComposition(HWND window_handle, HIMC imm_context) {
|
| }
|
|
|
| void ImeInput::GetCompositionInfo(HIMC imm_context, LPARAM lparam,
|
| - ImeComposition* composition) {
|
| + CompositionText* composition) {
|
| // We only care about GCS_COMPATTR, GCS_COMPCLAUSE and GCS_CURSORPOS, and
|
| // convert them into underlines and selection range respectively.
|
| composition->underlines.clear();
|
|
|
| - int length = static_cast<int>(composition->ime_string.length());
|
| + int length = static_cast<int>(composition->text.length());
|
| +
|
| + // Find out the range selected by the user.
|
| + int target_start = length;
|
| + int target_end = length;
|
| + if (lparam & GCS_COMPATTR)
|
| + GetCompositionTargetRange(imm_context, &target_start, &target_end);
|
|
|
| // Retrieve the selection range information. If CS_NOMOVECARET is specified,
|
| // that means the cursor should not be moved, then we just place the caret at
|
| @@ -263,20 +291,23 @@ void ImeInput::GetCompositionInfo(HIMC imm_context, LPARAM lparam,
|
| // TODO(suzhe): due to a bug of webkit, we currently can't use selection range
|
| // with composition string. See: https://bugs.webkit.org/show_bug.cgi?id=40805
|
| if (lparam & CS_NOMOVECARET) {
|
| - composition->selection_start = composition->selection_end = 0;
|
| + composition->selection = ui::Range(0);
|
| } else if (lparam & GCS_CURSORPOS) {
|
| - composition->selection_start = composition->selection_end =
|
| - ::ImmGetCompositionString(imm_context, GCS_CURSORPOS, NULL, 0);
|
| + // If cursor position is same as target_start or target_end, then selects
|
| + // the target range instead. We always use cursor position as selection end,
|
| + // so that if the client doesn't support drawing selection with composition,
|
| + // it can always retrieve the correct cursor position.
|
| + int cursor = ::ImmGetCompositionString(imm_context, GCS_CURSORPOS, NULL, 0);
|
| + if (cursor == target_start)
|
| + composition->selection = ui::Range(target_end, cursor);
|
| + else if (cursor == target_end)
|
| + composition->selection = ui::Range(target_start, cursor);
|
| + else
|
| + composition->selection = ui::Range(cursor);
|
| } else {
|
| - composition->selection_start = composition->selection_end = length;
|
| + composition->selection = ui::Range(target_start, target_end);
|
| }
|
|
|
| - // Find out the range selected by the user.
|
| - int target_start = 0;
|
| - int target_end = 0;
|
| - if (lparam & GCS_COMPATTR)
|
| - GetCompositionTargetRange(imm_context, &target_start, &target_end);
|
| -
|
| // Retrieve the clause segmentations and convert them to underlines.
|
| if (lparam & GCS_COMPCLAUSE) {
|
| GetCompositionUnderlines(imm_context, target_start, target_end,
|
| @@ -285,23 +316,23 @@ void ImeInput::GetCompositionInfo(HIMC imm_context, LPARAM lparam,
|
|
|
| // Set default underlines in case there is no clause information.
|
| if (!composition->underlines.size()) {
|
| - WebKit::WebCompositionUnderline underline;
|
| + CompositionUnderline underline;
|
| underline.color = SK_ColorBLACK;
|
| if (target_start > 0) {
|
| - underline.startOffset = 0;
|
| - underline.endOffset = target_start;
|
| + underline.start_offset = 0;
|
| + underline.end_offset = target_start;
|
| underline.thick = false;
|
| composition->underlines.push_back(underline);
|
| }
|
| if (target_end > target_start) {
|
| - underline.startOffset = target_start;
|
| - underline.endOffset = target_end;
|
| + underline.start_offset = target_start;
|
| + underline.end_offset = target_end;
|
| underline.thick = true;
|
| composition->underlines.push_back(underline);
|
| }
|
| if (target_end < length) {
|
| - underline.startOffset = target_end;
|
| - underline.endOffset = length;
|
| + underline.start_offset = target_end;
|
| + underline.end_offset = length;
|
| underline.thick = false;
|
| composition->underlines.push_back(underline);
|
| }
|
| @@ -309,72 +340,66 @@ void ImeInput::GetCompositionInfo(HIMC imm_context, LPARAM lparam,
|
| }
|
|
|
| bool ImeInput::GetString(HIMC imm_context, WPARAM lparam, int type,
|
| - ImeComposition* composition) {
|
| - bool result = false;
|
| + string16* result) {
|
| + bool ret = false;
|
| if (lparam & type) {
|
| int string_size = ::ImmGetCompositionString(imm_context, type, NULL, 0);
|
| if (string_size > 0) {
|
| int string_length = string_size / sizeof(wchar_t);
|
| - wchar_t *string_data = WriteInto(&composition->ime_string,
|
| - string_length + 1);
|
| + wchar_t *string_data = WriteInto(result, string_length + 1);
|
| if (string_data) {
|
| - // Fill the given ImeComposition object.
|
| - ::ImmGetCompositionString(imm_context, type,
|
| - string_data, string_size);
|
| - composition->string_type = type;
|
| - result = true;
|
| + // Fill the given result object.
|
| + ::ImmGetCompositionString(imm_context, type, string_data, string_size);
|
| + ret = true;
|
| }
|
| }
|
| }
|
| - return result;
|
| + return ret;
|
| }
|
|
|
| -bool ImeInput::GetResult(HWND window_handle, LPARAM lparam,
|
| - ImeComposition* composition) {
|
| - bool result = false;
|
| +bool ImeInput::GetResult(HWND window_handle, LPARAM lparam, string16* result) {
|
| + bool ret = false;
|
| HIMC imm_context = ::ImmGetContext(window_handle);
|
| if (imm_context) {
|
| - // Copy the result string to the ImeComposition object.
|
| - result = GetString(imm_context, lparam, GCS_RESULTSTR, composition);
|
| - // Reset all the other parameters because a result string does not
|
| - // have composition attributes.
|
| - composition->selection_start = 0;
|
| - composition->selection_end = 0;
|
| + ret = GetString(imm_context, lparam, GCS_RESULTSTR, result);
|
| ::ImmReleaseContext(window_handle, imm_context);
|
| }
|
| - return result;
|
| + return ret;
|
| }
|
|
|
| bool ImeInput::GetComposition(HWND window_handle, LPARAM lparam,
|
| - ImeComposition* composition) {
|
| - bool result = false;
|
| + CompositionText* composition) {
|
| + bool ret = false;
|
| HIMC imm_context = ::ImmGetContext(window_handle);
|
| if (imm_context) {
|
| - // Copy the composition string to the ImeComposition object.
|
| - result = GetString(imm_context, lparam, GCS_COMPSTR, composition);
|
| -
|
| - // This is a dirty workaround for facebook. Facebook deletes the placeholder
|
| - // character (U+3000) used by Traditional-Chinese IMEs at the beginning of
|
| - // composition text. This prevents WebKit from replacing this placeholder
|
| - // character with a Traditional-Chinese character, i.e. we cannot input any
|
| - // characters in a comment box of facebook with Traditional-Chinese IMEs.
|
| - // As a workaround, we replace U+3000 at the beginning of composition text
|
| - // with U+FF3F, a placeholder character used by Japanese IMEs.
|
| - if (input_language_id_ == MAKELANGID(LANG_CHINESE,
|
| - SUBLANG_CHINESE_TRADITIONAL) &&
|
| - composition->ime_string[0] == 0x3000) {
|
| - composition->ime_string[0] = 0xFF3F;
|
| - }
|
| + // Copy the composition string to the CompositionText object.
|
| + ret = GetString(imm_context, lparam, GCS_COMPSTR, &composition->text);
|
| +
|
| + if (ret) {
|
| + // This is a dirty workaround for facebook. Facebook deletes the
|
| + // placeholder character (U+3000) used by Traditional-Chinese IMEs at the
|
| + // beginning of composition text. This prevents WebKit from replacing this
|
| + // placeholder character with a Traditional-Chinese character, i.e. we
|
| + // cannot input any characters in a comment box of facebook with
|
| + // Traditional-Chinese IMEs. As a workaround, we replace U+3000 at the
|
| + // beginning of composition text with U+FF3F, a placeholder character used
|
| + // by Japanese IMEs.
|
| + if (input_language_id_ == MAKELANGID(LANG_CHINESE,
|
| + SUBLANG_CHINESE_TRADITIONAL) &&
|
| + composition->text[0] == 0x3000) {
|
| + composition->text[0] = 0xFF3F;
|
| + }
|
|
|
| - // Retrieve the composition underlines and selection range information.
|
| - GetCompositionInfo(imm_context, lparam, composition);
|
| + // Retrieve the composition underlines and selection range information.
|
| + GetCompositionInfo(imm_context, lparam, composition);
|
|
|
| - // Mark that there is an ongoing composition.
|
| - is_composing_ = true;
|
| + // Mark that there is an ongoing composition.
|
| + is_composing_ = true;
|
| + }
|
|
|
| ::ImmReleaseContext(window_handle, imm_context);
|
| }
|
| - return result;
|
| + return ret;
|
| }
|
|
|
| void ImeInput::DisableIME(HWND window_handle) {
|
| @@ -421,3 +446,116 @@ void ImeInput::UpdateCaretRect(HWND window_handle,
|
| }
|
| }
|
| }
|
| +
|
| +std::string ImeInput::GetInputLanguageName() const {
|
| + const LCID locale_id = MAKELCID(input_language_id_, SORT_DEFAULT);
|
| + // max size for LOCALE_SISO639LANGNAME and LOCALE_SISO3166CTRYNAME is 9.
|
| + wchar_t buffer[9];
|
| +
|
| + // Get language id.
|
| + int length = ::GetLocaleInfo(locale_id, LOCALE_SISO639LANGNAME, &buffer[0],
|
| + arraysize(buffer));
|
| + if (length <= 1)
|
| + return std::string();
|
| +
|
| + std::string language;
|
| + WideToUTF8(buffer, length - 1, &language);
|
| + if (SUBLANGID(input_language_id_) == SUBLANG_NEUTRAL)
|
| + return language;
|
| +
|
| + // Get region id.
|
| + length = ::GetLocaleInfo(locale_id, LOCALE_SISO3166CTRYNAME, &buffer[0],
|
| + arraysize(buffer));
|
| + if (length <= 1)
|
| + return language;
|
| +
|
| + std::string region;
|
| + WideToUTF8(buffer, length - 1, ®ion);
|
| + return language.append(1, '-').append(region);
|
| +}
|
| +
|
| +base::i18n::TextDirection ImeInput::GetTextDirection() const {
|
| + return IsRTLPrimaryLangID(PRIMARYLANGID(input_language_id_)) ?
|
| + base::i18n::RIGHT_TO_LEFT : base::i18n::LEFT_TO_RIGHT;
|
| +}
|
| +
|
| +// static
|
| +bool ImeInput::IsRTLKeyboardLayoutInstalled() {
|
| + static enum {
|
| + RTL_KEYBOARD_LAYOUT_NOT_INITIALIZED,
|
| + RTL_KEYBOARD_LAYOUT_INSTALLED,
|
| + RTL_KEYBOARD_LAYOUT_NOT_INSTALLED,
|
| + RTL_KEYBOARD_LAYOUT_ERROR,
|
| + } layout = RTL_KEYBOARD_LAYOUT_NOT_INITIALIZED;
|
| +
|
| + // Cache the result value.
|
| + if (layout != RTL_KEYBOARD_LAYOUT_NOT_INITIALIZED)
|
| + return layout == RTL_KEYBOARD_LAYOUT_INSTALLED;
|
| +
|
| + // Retrieve the number of layouts installed in this system.
|
| + int size = GetKeyboardLayoutList(0, NULL);
|
| + if (size <= 0) {
|
| + layout = RTL_KEYBOARD_LAYOUT_ERROR;
|
| + return false;
|
| + }
|
| +
|
| + // Retrieve the keyboard layouts in an array and check if there is an RTL
|
| + // layout in it.
|
| + scoped_array<HKL> layouts(new HKL[size]);
|
| + ::GetKeyboardLayoutList(size, layouts.get());
|
| + for (int i = 0; i < size; ++i) {
|
| + if (IsRTLPrimaryLangID(PRIMARYLANGID(layouts[i]))) {
|
| + layout = RTL_KEYBOARD_LAYOUT_INSTALLED;
|
| + return true;
|
| + }
|
| + }
|
| +
|
| + layout = RTL_KEYBOARD_LAYOUT_NOT_INSTALLED;
|
| + return false;
|
| +}
|
| +
|
| +bool ImeInput::IsCtrlShiftPressed(base::i18n::TextDirection* direction) {
|
| + uint8_t keystate[256];
|
| + if (!::GetKeyboardState(&keystate[0]))
|
| + return false;
|
| +
|
| + // To check if a user is pressing only a control key and a right-shift key
|
| + // (or a left-shift key), we use the steps below:
|
| + // 1. Check if a user is pressing a control key and a right-shift key (or
|
| + // a left-shift key).
|
| + // 2. If the condition 1 is true, we should check if there are any other
|
| + // keys pressed at the same time.
|
| + // To ignore the keys checked in 1, we set their status to 0 before
|
| + // checking the key status.
|
| + const int kKeyDownMask = 0x80;
|
| + if ((keystate[VK_CONTROL] & kKeyDownMask) == 0)
|
| + return false;
|
| +
|
| + if (keystate[VK_RSHIFT] & kKeyDownMask) {
|
| + keystate[VK_RSHIFT] = 0;
|
| + *direction = base::i18n::RIGHT_TO_LEFT;
|
| + } else if (keystate[VK_LSHIFT] & kKeyDownMask) {
|
| + keystate[VK_LSHIFT] = 0;
|
| + *direction = base::i18n::LEFT_TO_RIGHT;
|
| + } else {
|
| + return false;
|
| + }
|
| +
|
| + // Scan the key status to find pressed keys. We should adandon changing the
|
| + // text direction when there are other pressed keys.
|
| + // This code is executed only when a user is pressing a control key and a
|
| + // right-shift key (or a left-shift key), i.e. we should ignore the status of
|
| + // the keys: VK_SHIFT, VK_CONTROL, VK_RCONTROL, and VK_LCONTROL.
|
| + // So, we reset their status to 0 and ignore them.
|
| + keystate[VK_SHIFT] = 0;
|
| + keystate[VK_CONTROL] = 0;
|
| + keystate[VK_RCONTROL] = 0;
|
| + keystate[VK_LCONTROL] = 0;
|
| + for (int i = 0; i <= VK_PACKET; ++i) {
|
| + if (keystate[i] & kKeyDownMask)
|
| + return false;
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +} // namespace ui
|
|
|