summaryrefslogtreecommitdiffstats
path: root/chrome/browser/ime_input.cc
diff options
context:
space:
mode:
authorinitial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98>2008-07-26 23:55:29 +0000
committerinitial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98>2008-07-26 23:55:29 +0000
commit09911bf300f1a419907a9412154760efd0b7abc3 (patch)
treef131325fb4e2ad12c6d3504ab75b16dd92facfed /chrome/browser/ime_input.cc
parent586acc5fe142f498261f52c66862fa417c3d52d2 (diff)
downloadchromium_src-09911bf300f1a419907a9412154760efd0b7abc3.zip
chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.gz
chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.bz2
Add chrome to the repository.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@15 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/ime_input.cc')
-rw-r--r--chrome/browser/ime_input.cc345
1 files changed, 345 insertions, 0 deletions
diff --git a/chrome/browser/ime_input.cc b/chrome/browser/ime_input.cc
new file mode 100644
index 0000000..ca32b1c
--- /dev/null
+++ b/chrome/browser/ime_input.cc
@@ -0,0 +1,345 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "chrome/browser/ime_input.h"
+
+#include "base/scoped_ptr.h"
+#include "base/string_util.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")
+
+///////////////////////////////////////////////////////////////////////////////
+// ImeInput
+
+ImeInput::ImeInput()
+ : ime_status_(false),
+ input_language_id_(LANG_USER_DEFAULT),
+ is_composing_(false),
+ system_caret_(false),
+ caret_x_(-1),
+ caret_y_(-1) {
+}
+
+ImeInput::~ImeInput() {
+}
+
+bool ImeInput::SetInputLanguage() {
+ // Retrieve the current keyboard layout from Windows and determine whether
+ // or not the current input context has IMEs.
+ // Also save its input language for language-specific operations required
+ // while composing a text.
+ HKL keyboard_layout = ::GetKeyboardLayout(0);
+ input_language_id_ = reinterpret_cast<LANGID>(keyboard_layout);
+ ime_status_ = (::ImmIsIME(keyboard_layout) == TRUE) ? true : false;
+ 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
+ // to ::ImmSetCandidateWindow(), i.e. they do not move their candidate
+ // window to the position given as its parameters, and use the position
+ // of the current system caret instead, i.e. it uses ::GetCaretPos() to
+ // retrieve the position of their IME candidate window.
+ // Therefore, we create a temporary system caret for Chinese IMEs and use
+ // it during this input context.
+ if (PRIMARYLANGID(input_language_id_) == LANG_CHINESE) {
+ if (!system_caret_) {
+ if (::CreateCaret(window_handle, NULL, 1, 1)) {
+ system_caret_ = true;
+ }
+ }
+ }
+ // Restore the positions of the IME windows.
+ UpdateImeWindow(window_handle);
+}
+
+void 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.
+ // NOTE(hbono): We can NEVER let WTL call ::DefWindowProc() when we update
+ // the styles of IME windows because the 'lparam' variable is a local one
+ // and all its updates disappear in returning from this function, i.e. WTL
+ // does not call ::DefWindowProc() with our updated 'lparam' value but call
+ // the function with its original value and over-writes our window styles.
+ *handled = TRUE;
+ lparam &= ~ISC_SHOWUICOMPOSITIONWINDOW;
+ ::DefWindowProc(window_handle, message, wparam, lparam);
+}
+
+void ImeInput::DestroyImeWindow(HWND window_handle) {
+ // Destroy the system caret if we have created for this IME input context.
+ if (system_caret_) {
+ ::DestroyCaret();
+ system_caret_ = false;
+ }
+}
+
+void ImeInput::MoveImeWindow(HWND window_handle, HIMC imm_context) {
+ int x = caret_x_;
+ int y = caret_y_;
+ const int kCaretMargin = 1;
+ // As written in a comment in ImeInput::CreateImeWindow(),
+ // Chinese IMEs ignore function calls to ::ImmSetCandidateWindow()
+ // when a user disables TSF (Text Service Framework) and CUAS (Cicero
+ // Unaware Application Support).
+ // On the other hand, when a user enables TSF and CUAS, Chinese IMEs
+ // ignore the position of the current system caret and uses the
+ // parameters given to ::ImmSetCandidateWindow() with its 'dwStyle'
+ // parameter CFS_CANDIDATEPOS.
+ // Therefore, we do not only call ::ImmSetCandidateWindow() but also
+ // set the positions of the temporary system caret if it exists.
+ CANDIDATEFORM candidate_position = {0, CFS_CANDIDATEPOS, {x, y},
+ {0, 0, 0, 0}};
+ ::ImmSetCandidateWindow(imm_context, &candidate_position);
+ if (system_caret_) {
+ ::SetCaretPos(x, y);
+ }
+ if (PRIMARYLANGID(input_language_id_) == LANG_KOREAN) {
+ // Chinese IMEs and Japanese IMEs require the upper-left corner of
+ // the caret to move the position of their candidate windows.
+ // On the other hand, Korean IMEs require the lower-left corner of the
+ // caret to move their candidate windows.
+ y += kCaretMargin;
+ }
+ // Japanese IMEs and Korean IMEs also use the rectangle given to
+ // ::ImmSetCandidateWindow() with its 'dwStyle' parameter CFS_EXCLUDE
+ // to move their candidate windows when a user disables TSF and CUAS.
+ // Therefore, we also set this parameter here.
+ CANDIDATEFORM exclude_rectangle = {0, CFS_EXCLUDE, {x, y},
+ {x, y, x, y + kCaretMargin}};
+ ::ImmSetCandidateWindow(imm_context, &exclude_rectangle);
+}
+
+void ImeInput::UpdateImeWindow(HWND window_handle) {
+ // Just move the IME window attached to the given window.
+ if (caret_x_ >= 0 && caret_y_ >= 0) {
+ HIMC imm_context = ::ImmGetContext(window_handle);
+ if (imm_context) {
+ MoveImeWindow(window_handle, imm_context);
+ ::ImmReleaseContext(window_handle, imm_context);
+ }
+ }
+}
+
+void ImeInput::CleanupComposition(HWND window_handle) {
+ // Notify the IMM attached to the given window to complete the ongoing
+ // composition, (this case happens when the given window is de-activated
+ // while composing a text and re-activated), and reset the omposition status.
+ if (is_composing_) {
+ HIMC imm_context = ::ImmGetContext(window_handle);
+ if (imm_context) {
+ ::ImmNotifyIME(imm_context, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
+ ::ImmReleaseContext(window_handle, imm_context);
+ }
+ ResetComposition(window_handle);
+ }
+}
+
+void ImeInput::ResetComposition(HWND window_handle) {
+ // Currently, just reset the composition status.
+ is_composing_ = false;
+}
+
+void ImeInput::CompleteComposition(HWND window_handle, HIMC imm_context) {
+ // We have to confirm there is an ongoing composition before completing it.
+ // This is for preventing some IMEs from getting confused while completing an
+ // ongoing composition even if they do not have any ongoing compositions.)
+ if (is_composing_) {
+ ::ImmNotifyIME(imm_context, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
+ ResetComposition(window_handle);
+ }
+}
+
+void ImeInput::GetCaret(HIMC imm_context, LPARAM lparam,
+ ImeComposition* composition) {
+ // This operation is optional and language-dependent because the caret
+ // style is depended on the language, e.g.:
+ // * Korean IMEs: the caret is a blinking block,
+ // (It contains only one hangul character);
+ // * Chinese IMEs: the caret is a blinking line,
+ // (i.e. they do not need to retrieve the target selection);
+ // * Japanese IMEs: the caret is a selection (or undelined) block,
+ // (which can contain one or more Japanese characters).
+ int target_start = -1;
+ int target_end = -1;
+ switch (PRIMARYLANGID(input_language_id_)) {
+ case LANG_KOREAN:
+ if (lparam & CS_NOMOVECARET) {
+ target_start = 0;
+ target_end = 1;
+ }
+ break;
+ case LANG_CHINESE:
+ break;
+ case LANG_JAPANESE:
+ // For Japanese IMEs, the robustest way to retrieve the caret
+ // is scanning the attribute of the latest composition string and
+ // retrieving the begining and the end of the target clause, i.e.
+ // a clause being converted.
+ size_t composition_length = composition->ime_string.length();
+ composition->cursor_position = static_cast<int>(composition_length);
+ if (lparam & GCS_COMPATTR) {
+ int attribute_size = ::ImmGetCompositionString(imm_context,
+ GCS_COMPATTR,
+ NULL, 0);
+ if (attribute_size > 0) {
+ scoped_array<char> attribute_data(new char[attribute_size]);
+ if (attribute_data.get()) {
+ ::ImmGetCompositionString(imm_context, GCS_COMPATTR,
+ attribute_data.get(), attribute_size);
+ int i = 0;
+ for (; i < attribute_size; i++) {
+ if (IsTargetAttribute(attribute_data[i])) break;
+ }
+ target_start = i;
+ for (; i < attribute_size; i++) {
+ if (!IsTargetAttribute(attribute_data[i])) break;
+ }
+ target_end = i;
+ }
+ }
+ }
+ break;
+ }
+ composition->target_start = target_start;
+ composition->target_end = target_end;
+}
+
+bool ImeInput::GetString(HIMC imm_context, WPARAM lparam, int type,
+ ImeComposition* composition) {
+ bool result = 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);
+ if (string_data) {
+ // Fill the given ImeComposition object.
+ ::ImmGetCompositionString(imm_context, type,
+ string_data, string_size);
+ composition->string_type = type;
+ result = true;
+ }
+ }
+ }
+ return result;
+}
+
+bool ImeInput::GetResult(HWND window_handle, LPARAM lparam,
+ ImeComposition* composition) {
+ bool result = 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->cursor_position = -1;
+ composition->target_start = -1;
+ composition->target_end = -1;
+ ::ImmReleaseContext(window_handle, imm_context);
+ }
+ return result;
+}
+
+bool ImeInput::GetComposition(HWND window_handle, LPARAM lparam,
+ ImeComposition* composition) {
+ bool result = 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);
+
+ // Retrieve the cursor position in the IME composition.
+ int cursor_position = ::ImmGetCompositionString(imm_context,
+ GCS_CURSORPOS, NULL, 0);
+ composition->cursor_position = cursor_position;
+ composition->target_start = -1;
+ composition->target_end = -1;
+
+ // Retrieve the target selection and Update the ImeComposition
+ // object.
+ GetCaret(imm_context, lparam, composition);
+
+ // Mark that there is an ongoing composition.
+ is_composing_ = true;
+
+ ::ImmReleaseContext(window_handle, imm_context);
+ }
+ return result;
+}
+
+void ImeInput::DisableIME(HWND window_handle) {
+ // A renderer process have moved its input focus to a password input
+ // when there is an ongoing composition, e.g. a user has clicked a
+ // mouse button and selected a password input while composing a text.
+ // For this case, we have to complete the ongoing composition and
+ // clean up the resources attached to this object BEFORE DISABLING THE IME.
+ CleanupComposition(window_handle);
+ ::ImmAssociateContextEx(window_handle, NULL, 0);
+}
+
+void ImeInput::EnableIME(HWND window_handle, int x, int y, bool complete) {
+ // Load the default IME context.
+ // NOTE(hbono)
+ // IMM ignores this call if the IME context is loaded. Therefore, we do
+ // not have to check whether or not the IME context is loaded.
+ ::ImmAssociateContextEx(window_handle, NULL, IACE_DEFAULT);
+ // Complete the ongoing composition and move the IME windows.
+ HIMC imm_context = ::ImmGetContext(window_handle);
+ if (imm_context) {
+ if (complete) {
+ // A renderer process have moved its input focus to another edit
+ // control when there is an ongoing composition, e.g. a user has
+ // clicked a mouse button and selected another edit control while
+ // composing a text.
+ // For this case, we have to complete the ongoing composition and
+ // hide the IME windows BEFORE MOVING THEM.
+ CompleteComposition(window_handle, imm_context);
+ }
+ // Save the caret position, and Update the position of the IME window.
+ // This update is used for moving an IME window when a renderer process
+ // resize/moves the input caret.
+ if (x >= 0 && y >= 0) {
+ caret_x_ = x;
+ caret_y_ = y;
+ MoveImeWindow(window_handle, imm_context);
+ }
+ ::ImmReleaseContext(window_handle, imm_context);
+ }
+}