172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved. 2c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Use of this source code is governed by a BSD-style license that can be 3c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// found in the LICENSE file. 4c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 5c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/renderer_host/gtk_im_context_wrapper.h" 6c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 7c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <gdk/gdk.h> 8c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <gdk/gdkkeysyms.h> 9c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <gtk/gtk.h> 1072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 11c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <algorithm> 12c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 13c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/logging.h" 14c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/string_util.h" 15c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/third_party/icu/icu_utf.h" 163345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "base/utf_string_conversions.h" 17513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch#include "chrome/app/chrome_command_ids.h" 1872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "chrome/browser/ui/gtk/gtk_util.h" 19c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/renderer_host/render_widget_host_view_gtk.h" 20c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/common/render_messages.h" 21dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "content/browser/renderer_host/render_widget_host.h" 22ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "content/common/native_web_keyboard_event.h" 23c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "grit/generated_resources.h" 24c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "third_party/skia/include/core/SkColor.h" 25ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "third_party/WebKit/Source/WebKit/chromium/public/WebCompositionUnderline.h" 26ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "ui/base/gtk/gtk_im_context_util.h" 2772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/base/l10n/l10n_util.h" 2872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/gfx/gtk_util.h" 2972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/gfx/rect.h" 30c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 31dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#if !defined(TOOLKIT_VIEWS) 32dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "chrome/browser/ui/gtk/menu_gtk.h" 33dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#endif 34dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen 3521d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsennamespace { 3672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// Copied from third_party/WebKit/Source/WebCore/page/EventHandler.cpp 3721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen// 3821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen// Match key code of composition keydown event on windows. 3921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen// IE sends VK_PROCESSKEY which has value 229; 4021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen// 4121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen// Please refer to following documents for detals: 4221d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen// - Virtual-Key Codes 4321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen// http://msdn.microsoft.com/en-us/library/ms645540(VS.85).aspx 4421d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen// - How the IME System Works 4521d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen// http://msdn.microsoft.com/en-us/library/cc194848.aspx 4621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen// - ImmGetVirtualKey Function 4721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen// http://msdn.microsoft.com/en-us/library/dd318570(VS.85).aspx 4821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsenconst int kCompositionEventKeyCode = 229; 4921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen} // namespace 5021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen 51ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// ui::CompositionUnderline should be identical to 52ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// WebKit::WebCompositionUnderline, so that we can do reinterpret_cast safely. 53ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// TODO(suzhe): remove it after migrating all code in chrome to use 54ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// ui::CompositionUnderline. 55ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian MonsenCOMPILE_ASSERT(sizeof(ui::CompositionUnderline) == 56ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen sizeof(WebKit::WebCompositionUnderline), 57ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen ui_CompositionUnderline__WebKit_WebCompositionUnderline_diff); 58ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 59c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochGtkIMContextWrapper::GtkIMContextWrapper(RenderWidgetHostViewGtk* host_view) 60c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch : host_view_(host_view), 61c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch context_(gtk_im_multicontext_new()), 62c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch context_simple_(gtk_im_context_simple_new()), 63c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch is_focused_(false), 64c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch is_composing_text_(false), 65c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch is_enabled_(false), 66c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch is_in_key_event_handler_(false), 67ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen is_composition_changed_(false), 6872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen suppress_next_commit_(false), 6972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen last_key_code_(0), 7072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen last_key_was_up_(false), 7172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen last_key_filtered_no_result_(false) { 72c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK(context_); 73c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK(context_simple_); 74c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 75c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // context_ and context_simple_ share the same callback handlers. 76c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // All data come from them are treated equally. 77c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // context_ is for full input method support. 78c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // context_simple_ is for supporting dead/compose keys when input method is 79c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // disabled by webkit, eg. in password input box. 80c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch g_signal_connect(context_, "preedit_start", 81c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch G_CALLBACK(HandlePreeditStartThunk), this); 82c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch g_signal_connect(context_, "preedit_end", 83c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch G_CALLBACK(HandlePreeditEndThunk), this); 84c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch g_signal_connect(context_, "preedit_changed", 85c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch G_CALLBACK(HandlePreeditChangedThunk), this); 86c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch g_signal_connect(context_, "commit", 87c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch G_CALLBACK(HandleCommitThunk), this); 88c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 89c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch g_signal_connect(context_simple_, "preedit_start", 90c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch G_CALLBACK(HandlePreeditStartThunk), this); 91c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch g_signal_connect(context_simple_, "preedit_end", 92c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch G_CALLBACK(HandlePreeditEndThunk), this); 93c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch g_signal_connect(context_simple_, "preedit_changed", 94c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch G_CALLBACK(HandlePreeditChangedThunk), this); 95c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch g_signal_connect(context_simple_, "commit", 96c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch G_CALLBACK(HandleCommitThunk), this); 97c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 98c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch GtkWidget* widget = host_view->native_view(); 99c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK(widget); 100c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 101c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch g_signal_connect(widget, "realize", 102c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch G_CALLBACK(HandleHostViewRealizeThunk), this); 103c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch g_signal_connect(widget, "unrealize", 104c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch G_CALLBACK(HandleHostViewUnrealizeThunk), this); 105c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 106c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Set client window if the widget is already realized. 107c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch HandleHostViewRealize(widget); 108c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 109c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 110c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochGtkIMContextWrapper::~GtkIMContextWrapper() { 111c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (context_) 112c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch g_object_unref(context_); 113c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (context_simple_) 114c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch g_object_unref(context_simple_); 115c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 116c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 117c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid GtkIMContextWrapper::ProcessKeyEvent(GdkEventKey* event) { 1183345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick suppress_next_commit_ = false; 1193345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 120c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Indicates preedit-changed and commit signal handlers that we are 121c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // processing a key event. 122c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch is_in_key_event_handler_ = true; 123c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Reset this flag so that we can know if preedit is changed after 124c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // processing this key event. 125ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen is_composition_changed_ = false; 126c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Clear it so that we can know if something needs committing after 127c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // processing this key event. 128c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch commit_text_.clear(); 129c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 130c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // According to Document Object Model (DOM) Level 3 Events Specification 131c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Appendix A: Keyboard events and key identifiers 132c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // http://www.w3.org/TR/DOM-Level-3-Events/keyset.html: 133c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // The event sequence would be: 134c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // 1. keydown 135c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // 2. textInput 136c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // 3. keyup 137c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // 138c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // So keydown must be sent to webkit before sending input method result, 139c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // while keyup must be sent afterwards. 140c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // However on Windows, if a keydown event has been processed by IME, its 141c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // virtual keycode will be changed to VK_PROCESSKEY(0xE5) before being sent 142c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // to application. 143c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // To emulate the windows behavior as much as possible, we need to send the 144c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // key event to the GtkIMContext object first, and decide whether or not to 145c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // send the original key event to webkit according to the result from IME. 146c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // 147c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // If IME is enabled by WebKit, this event will be dispatched to context_ 148c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // to get full IME support. Otherwise it'll be dispatched to 149c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // context_simple_, so that dead/compose keys can still work. 150c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // 151c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // It sends a "commit" signal when it has a character to be inserted 152c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // even when we use a US keyboard so that we can send a Char event 153c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // (or an IME event) to the renderer in our "commit"-signal handler. 154c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // We should send a KeyDown (or a KeyUp) event before dispatching this 155c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // event to the GtkIMContext object (and send a Char event) so that WebKit 156c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // can dispatch the JavaScript events in the following order: onkeydown(), 157c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // onkeypress(), and onkeyup(). (Many JavaScript pages assume this.) 158c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch gboolean filtered = false; 159c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (is_enabled_) { 160c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch filtered = gtk_im_context_filter_keypress(context_, event); 161c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } else { 162c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch filtered = gtk_im_context_filter_keypress(context_simple_, event); 163c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 164c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 165c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Reset this flag here, as it's only used in input method callbacks. 166c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch is_in_key_event_handler_ = false; 167c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 168c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NativeWebKeyboardEvent wke(event); 169c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 1703345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // If the key event was handled by the input method, then we need to prevent 1713345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // RenderView::UnhandledKeyboardEvent() from processing it. 1723345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // Otherwise unexpected result may occur. For example if it's a 1733345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // Backspace key event, the browser may go back to previous page. 17472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen // We just send all keyup events to the browser to avoid breaking the 17572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen // browser's MENU key function, which is actually the only keyup event 17672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen // handled in the browser. 17772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (filtered && event->type == GDK_KEY_PRESS) 1783345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick wke.skip_in_browser = true; 1793345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 18072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen const int key_code = wke.windowsKeyCode; 18172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen const bool has_result = HasInputMethodResult(); 18272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 183c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Send filtered keydown event before sending IME result. 18472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen // In order to workaround http://crosbug.com/6582, we only send a filtered 18572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen // keydown event if it generated any input method result. 18672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (event->type == GDK_KEY_PRESS && filtered && has_result) 187c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ProcessFilteredKeyPressEvent(&wke); 188c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 189c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Send IME results. In most cases, it's only available if the key event 190c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // is filtered by IME. But in rare cases, an unfiltered key event may also 191c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // generate IME results. 192c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Any IME results generated by a unfiltered key down event must be sent 193c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // before the key down event, to avoid some tricky issues. For example, 194c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // when using latin-post input method, pressing 'a' then Backspace, may 195c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // generate following events in sequence: 196c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // 1. keydown 'a' (filtered) 197c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // 2. preedit changed to "a" 198c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // 3. keyup 'a' (unfiltered) 199c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // 4. keydown Backspace (unfiltered) 200c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // 5. commit "a" 201c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // 6. preedit end 202c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // 7. keyup Backspace (unfiltered) 203c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // 204c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // In this case, the input box will be in a strange state if keydown 205c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Backspace is sent to webkit before commit "a" and preedit end. 20672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (has_result) 20772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ProcessInputMethodResult(event, filtered); 208c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 209c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Send unfiltered keydown and keyup events after sending IME result. 21072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (event->type == GDK_KEY_PRESS && !filtered) { 211c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ProcessUnfilteredKeyPressEvent(&wke); 21272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen } else if (event->type == GDK_KEY_RELEASE) { 21372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen // In order to workaround http://crosbug.com/6582, we need to suppress 21472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen // the keyup event if corresponding keydown event was suppressed, or 21572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen // the last key event was a keyup event with the same keycode. 21672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen const bool suppress = (last_key_code_ == key_code) && 21772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen (last_key_was_up_ || last_key_filtered_no_result_); 21872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 21972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (!suppress) 22072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen host_view_->ForwardKeyboardEvent(wke); 22172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen } 22272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 22372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen last_key_code_ = key_code; 22472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen last_key_was_up_ = (event->type == GDK_KEY_RELEASE); 22572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen last_key_filtered_no_result_ = (filtered && !has_result); 226c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 227c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 228c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid GtkIMContextWrapper::UpdateInputMethodState(WebKit::WebTextInputType type, 229c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const gfx::Rect& caret_rect) { 2303345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick suppress_next_commit_ = false; 2313345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 232c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // The renderer has updated its IME status. 233c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Control the GtkIMContext object according to this status. 234c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!context_ || !is_focused_) 235c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 236c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 237c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK(!is_in_key_event_handler_); 238c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 239c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch bool is_enabled = (type == WebKit::WebTextInputTypeText); 240c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (is_enabled_ != is_enabled) { 241c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch is_enabled_ = is_enabled; 242c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (is_enabled) 243c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch gtk_im_context_focus_in(context_); 244c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch else 245c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch gtk_im_context_focus_out(context_); 246c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 247c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 248c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (is_enabled) { 249c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Updates the position of the IME candidate window. 250c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // The position sent from the renderer is a relative one, so we need to 251c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // attach the GtkIMContext object to this window before changing the 252c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // position. 253c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch GdkRectangle cursor_rect(caret_rect.ToGdkRectangle()); 254c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch gtk_im_context_set_cursor_location(context_, &cursor_rect); 255c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 256c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 257c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 258c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid GtkIMContextWrapper::OnFocusIn() { 259c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (is_focused_) 260c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 261c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 262c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Tracks the focused state so that we can give focus to the 263c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // GtkIMContext object correctly later when IME is enabled by WebKit. 264c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch is_focused_ = true; 265c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 26672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen last_key_code_ = 0; 26772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen last_key_was_up_ = false; 26872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen last_key_filtered_no_result_ = false; 26972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 270c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Notify the GtkIMContext object of this focus-in event only if IME is 271c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // enabled by WebKit. 272c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (is_enabled_) 273c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch gtk_im_context_focus_in(context_); 274c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 275c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // context_simple_ is always enabled. 276c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Actually it doesn't care focus state at all. 277c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch gtk_im_context_focus_in(context_simple_); 278c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 279c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Enables RenderWidget's IME related events, so that we can be notified 280c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // when WebKit wants to enable or disable IME. 281c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (host_view_->GetRenderWidgetHost()) 282c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch host_view_->GetRenderWidgetHost()->SetInputMethodActive(true); 283c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 284c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 285c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid GtkIMContextWrapper::OnFocusOut() { 286c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!is_focused_) 287c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 288c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 289c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Tracks the focused state so that we won't give focus to the 290c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // GtkIMContext object unexpectly. 291c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch is_focused_ = false; 292c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 293c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Notify the GtkIMContext object of this focus-out event only if IME is 294c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // enabled by WebKit. 295c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (is_enabled_) { 296c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // To reset the GtkIMContext object and prevent data loss. 297c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ConfirmComposition(); 298c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch gtk_im_context_focus_out(context_); 299c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 300c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 301c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // To make sure it'll be in correct state when focused in again. 302c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch gtk_im_context_reset(context_simple_); 303c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch gtk_im_context_focus_out(context_simple_); 304c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 305c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch is_composing_text_ = false; 306c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 307c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Disable RenderWidget's IME related events to save bandwidth. 308c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (host_view_->GetRenderWidgetHost()) 309c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch host_view_->GetRenderWidgetHost()->SetInputMethodActive(false); 310c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 311c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 3123345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#if !defined(TOOLKIT_VIEWS) 3133345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick// Not defined for views because the views context menu doesn't 3143345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick// implement input methods yet. 315c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid GtkIMContextWrapper::AppendInputMethodsContextMenu(MenuGtk* menu) { 316c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch gboolean show_input_method_menu = TRUE; 317c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 318c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch g_object_get(gtk_widget_get_settings(GTK_WIDGET(host_view_->native_view())), 319c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch "gtk-show-input-method-menu", &show_input_method_menu, NULL); 320c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!show_input_method_menu) 321c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 322c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 323513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch std::string label = gfx::ConvertAcceleratorsFromWindowsStyle( 324c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch l10n_util::GetStringUTF8(IDS_CONTENT_CONTEXT_INPUT_METHODS_MENU)); 325c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch GtkWidget* menuitem = gtk_menu_item_new_with_mnemonic(label.c_str()); 326c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch GtkWidget* submenu = gtk_menu_new(); 327c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu); 328c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch gtk_im_multicontext_append_menuitems(GTK_IM_MULTICONTEXT(context_), 329c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch GTK_MENU_SHELL(submenu)); 330c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch menu->AppendSeparator(); 331c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch menu->AppendMenuItem(IDC_INPUT_METHODS_MENU, menuitem); 332c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 3333345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#endif 334c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 335c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid GtkIMContextWrapper::CancelComposition() { 336c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!is_enabled_) 337c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 338c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 339c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK(!is_in_key_event_handler_); 340c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 341c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // To prevent any text from being committed when resetting the |context_|; 342c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch is_in_key_event_handler_ = true; 3433345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick suppress_next_commit_ = true; 344c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 345c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch gtk_im_context_reset(context_); 346c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch gtk_im_context_reset(context_simple_); 347c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 348c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (is_focused_) { 349c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Some input methods may not honour the reset call. Focusing out/in the 350c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // |context_| to make sure it gets reset correctly. 351c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch gtk_im_context_focus_out(context_); 352c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch gtk_im_context_focus_in(context_); 353c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 354c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 355c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch is_composing_text_ = false; 356ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen composition_.Clear(); 357c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch commit_text_.clear(); 358c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 359c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch is_in_key_event_handler_ = false; 360c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 361c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 36272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenbool GtkIMContextWrapper::NeedCommitByForwardingCharEvent() const { 363c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // If there is no composition text and has only one character to be 364c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // committed, then the character will be send to webkit as a Char event 365c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // instead of a confirmed composition text. 366c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // It should be fine to handle BMP character only, as non-BMP characters 367c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // can always be committed as confirmed composition text. 368c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return !is_composing_text_ && commit_text_.length() == 1; 369c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 370c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 37172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenbool GtkIMContextWrapper::HasInputMethodResult() const { 372ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen return commit_text_.length() || is_composition_changed_; 37372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen} 37472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 375c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid GtkIMContextWrapper::ProcessFilteredKeyPressEvent( 376c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NativeWebKeyboardEvent* wke) { 377c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // If IME has filtered this event, then replace virtual key code with 378c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // VK_PROCESSKEY. See comment in ProcessKeyEvent() for details. 379c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // It's only required for keydown events. 380c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // To emulate windows behavior, when input method is enabled, if the commit 381c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // text can be emulated by a Char event, then don't do this replacement. 382c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!NeedCommitByForwardingCharEvent()) { 383c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch wke->windowsKeyCode = kCompositionEventKeyCode; 384c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // keyidentifier must be updated accordingly, otherwise this key event may 385c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // still be processed by webkit. 386c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch wke->setKeyIdentifierFromWindowsKeyCode(); 387c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 388c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch host_view_->ForwardKeyboardEvent(*wke); 389c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 390c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 391c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid GtkIMContextWrapper::ProcessUnfilteredKeyPressEvent( 392c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NativeWebKeyboardEvent* wke) { 393c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Send keydown event as it, because it's not filtered by IME. 394c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch host_view_->ForwardKeyboardEvent(*wke); 395c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 396c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // IME is disabled by WebKit or the GtkIMContext object cannot handle 397c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // this key event. 398c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // This case is caused by two reasons: 399c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // 1. The given key event is a control-key event, (e.g. return, page up, 400c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // page down, tab, arrows, etc.) or; 401c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // 2. The given key event is not a control-key event but printable 402c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // characters aren't assigned to the event, (e.g. alt+d, etc.) 403c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Create a Char event manually from this key event and send it to the 404c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // renderer when this Char event contains a printable character which 405c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // should be processed by WebKit. 406c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // isSystemKey will be set to true if this key event has Alt modifier, 407c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // see WebInputEventFactory::keyboardEvent() for details. 408c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (wke->text[0]) { 409c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch wke->type = WebKit::WebInputEvent::Char; 410c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch wke->skip_in_browser = true; 411c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch host_view_->ForwardKeyboardEvent(*wke); 412c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 413c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 414c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 415c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid GtkIMContextWrapper::ProcessInputMethodResult(const GdkEventKey* event, 416c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch bool filtered) { 417c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch RenderWidgetHost* host = host_view_->GetRenderWidgetHost(); 418c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!host) 419c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 420c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 421c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch bool committed = false; 422c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // We do commit before preedit change, so that we can optimize some 423c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // unnecessary preedit changes. 424c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (commit_text_.length()) { 425c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (filtered && NeedCommitByForwardingCharEvent()) { 426c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Send a Char event when we input a composed character without IMEs 427c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // so that this event is to be dispatched to onkeypress() handlers, 428c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // autofill, etc. 429c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Only commit text generated by a filtered key down event can be sent 430c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // as a Char event, because a unfiltered key down event will probably 431c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // generate another Char event. 432c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // TODO(james.su@gmail.com): Is it necessary to support non BMP chars 433c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // here? 434c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NativeWebKeyboardEvent char_event(commit_text_[0], 435c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch event->state, 436c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch base::Time::Now().ToDoubleT()); 437c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch char_event.skip_in_browser = true; 438c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch host_view_->ForwardKeyboardEvent(char_event); 439c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } else { 440c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch committed = true; 441c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Send an IME event. 442c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Unlike a Char event, an IME event is NOT dispatched to onkeypress() 443c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // handlers or autofill. 444c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch host->ImeConfirmComposition(commit_text_); 445c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Set this flag to false, as this composition session has been 446c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // finished. 447c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch is_composing_text_ = false; 448c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 449c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 450c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 451c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Send preedit text only if it's changed. 452c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // If a text has been committed, then we don't need to send the empty 453c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // preedit text again. 454ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (is_composition_changed_) { 455ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (composition_.text.length()) { 456c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Another composition session has been started. 457c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch is_composing_text_ = true; 458ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // TODO(suzhe): convert both renderer_host and renderer to use 459ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // ui::CompositionText. 460ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen const std::vector<WebKit::WebCompositionUnderline>& underlines = 461ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen reinterpret_cast<const std::vector<WebKit::WebCompositionUnderline>&>( 462ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen composition_.underlines); 463ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen host->ImeSetComposition(composition_.text, underlines, 464ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen composition_.selection.start(), 465ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen composition_.selection.end()); 466c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } else if (!committed) { 467c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch host->ImeCancelComposition(); 468c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 469c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 470c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 471c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 472c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid GtkIMContextWrapper::ConfirmComposition() { 473c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!is_enabled_) 474c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 475c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 476c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK(!is_in_key_event_handler_); 477c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 478c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (is_composing_text_) { 479c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (host_view_->GetRenderWidgetHost()) 480c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch host_view_->GetRenderWidgetHost()->ImeConfirmComposition(); 481c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 482c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Reset the input method. 483c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch CancelComposition(); 484c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 485c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 486c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 487c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid GtkIMContextWrapper::HandleCommit(const string16& text) { 4883345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick if (suppress_next_commit_) { 4893345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick suppress_next_commit_ = false; 4903345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick return; 4913345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick } 4923345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 493c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Append the text to the buffer, because commit signal might be fired 494c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // multiple times when processing a key event. 495c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch commit_text_.append(text); 496c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Nothing needs to do, if it's currently in ProcessKeyEvent() 497c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // handler, which will send commit text to webkit later. Otherwise, 498c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // we need send it here. 499c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // It's possible that commit signal is fired without a key event, for 500c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // example when user input via a voice or handwriting recognition software. 501c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // In this case, the text must be committed directly. 50221d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen if (!is_in_key_event_handler_ && host_view_->GetRenderWidgetHost()) { 50321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen // Workaround http://crbug.com/45478 by sending fake key down/up events. 50421d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen SendFakeCompositionKeyEvent(WebKit::WebInputEvent::RawKeyDown); 505c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch host_view_->GetRenderWidgetHost()->ImeConfirmComposition(text); 50621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen SendFakeCompositionKeyEvent(WebKit::WebInputEvent::KeyUp); 50721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen } 508c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 509c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 510c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid GtkIMContextWrapper::HandlePreeditStart() { 511201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch // Ignore preedit related signals triggered by CancelComposition() method. 512201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch if (suppress_next_commit_) 513201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch return; 514c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch is_composing_text_ = true; 515c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 516c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 517c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid GtkIMContextWrapper::HandlePreeditChanged(const gchar* text, 518c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch PangoAttrList* attrs, 519c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch int cursor_position) { 520201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch // Ignore preedit related signals triggered by CancelComposition() method. 521201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch if (suppress_next_commit_) 522201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch return; 523201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch 524ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Don't set is_composition_changed_ to false if there is no change, because 525c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // this handler might be called multiple times with the same data. 526ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen is_composition_changed_ = true; 527ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen composition_.Clear(); 528c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 529ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen ui::ExtractCompositionTextFromGtkPreedit(text, attrs, cursor_position, 530ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen &composition_); 531ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 532ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // TODO(suzhe): due to a bug of webkit, we currently can't use selection range 533ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // with composition string. See: https://bugs.webkit.org/show_bug.cgi?id=40805 534ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen composition_.selection = ui::Range(cursor_position); 535c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 536c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // In case we are using a buggy input method which doesn't fire 537c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // "preedit_start" signal. 538ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (composition_.text.length()) 539c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch is_composing_text_ = true; 540c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 541c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Nothing needs to do, if it's currently in ProcessKeyEvent() 542c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // handler, which will send preedit text to webkit later. 543c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Otherwise, we need send it here if it's been changed. 544201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch if (!is_in_key_event_handler_ && is_composing_text_ && 545201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch host_view_->GetRenderWidgetHost()) { 54621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen // Workaround http://crbug.com/45478 by sending fake key down/up events. 54721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen SendFakeCompositionKeyEvent(WebKit::WebInputEvent::RawKeyDown); 548ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // TODO(suzhe): convert both renderer_host and renderer to use 549ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // ui::CompositionText. 550ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen const std::vector<WebKit::WebCompositionUnderline>& underlines = 551ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen reinterpret_cast<const std::vector<WebKit::WebCompositionUnderline>&>( 552ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen composition_.underlines); 553c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch host_view_->GetRenderWidgetHost()->ImeSetComposition( 554ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen composition_.text, underlines, composition_.selection.start(), 555ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen composition_.selection.end()); 55621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen SendFakeCompositionKeyEvent(WebKit::WebInputEvent::KeyUp); 557c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 558c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 559c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 560c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid GtkIMContextWrapper::HandlePreeditEnd() { 561ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (composition_.text.length()) { 562c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // The composition session has been finished. 563ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen composition_.Clear(); 564ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen is_composition_changed_ = true; 565c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 566c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // If there is still a preedit text when firing "preedit-end" signal, 567c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // we need inform webkit to clear it. 568c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // It's only necessary when it's not in ProcessKeyEvent (). 569c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!is_in_key_event_handler_ && host_view_->GetRenderWidgetHost()) 570c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch host_view_->GetRenderWidgetHost()->ImeCancelComposition(); 571c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 572c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 573c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Don't set is_composing_text_ to false here, because "preedit_end" 574c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // signal may be fired before "commit" signal. 575c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 576c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 577c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid GtkIMContextWrapper::HandleHostViewRealize(GtkWidget* widget) { 578c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // We should only set im context's client window once, because when setting 579c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // client window.im context may destroy and recreate its internal states and 580c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // objects. 581c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (widget->window) { 582c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch gtk_im_context_set_client_window(context_, widget->window); 583c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch gtk_im_context_set_client_window(context_simple_, widget->window); 584c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 585c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 586c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 587c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid GtkIMContextWrapper::HandleHostViewUnrealize() { 588c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch gtk_im_context_set_client_window(context_, NULL); 589c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch gtk_im_context_set_client_window(context_simple_, NULL); 590c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 591c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 59221d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsenvoid GtkIMContextWrapper::SendFakeCompositionKeyEvent( 59321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen WebKit::WebInputEvent::Type type) { 59421d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen NativeWebKeyboardEvent fake_event; 59521d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen fake_event.windowsKeyCode = kCompositionEventKeyCode; 59621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen fake_event.skip_in_browser = true; 59721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen fake_event.type = type; 59821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen host_view_->ForwardKeyboardEvent(fake_event); 59921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen} 60021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen 601c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid GtkIMContextWrapper::HandleCommitThunk( 602c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch GtkIMContext* context, gchar* text, GtkIMContextWrapper* self) { 603c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch self->HandleCommit(UTF8ToUTF16(text)); 604c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 605c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 606c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid GtkIMContextWrapper::HandlePreeditStartThunk( 607c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch GtkIMContext* context, GtkIMContextWrapper* self) { 608c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch self->HandlePreeditStart(); 609c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 610c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 611c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid GtkIMContextWrapper::HandlePreeditChangedThunk( 612c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch GtkIMContext* context, GtkIMContextWrapper* self) { 613c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch gchar* text = NULL; 614c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch PangoAttrList* attrs = NULL; 615c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch gint cursor_position = 0; 616c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch gtk_im_context_get_preedit_string(context, &text, &attrs, &cursor_position); 617c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch self->HandlePreeditChanged(text, attrs, cursor_position); 618c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch g_free(text); 619c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch pango_attr_list_unref(attrs); 620c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 621c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 622c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid GtkIMContextWrapper::HandlePreeditEndThunk( 623c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch GtkIMContext* context, GtkIMContextWrapper* self) { 624c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch self->HandlePreeditEnd(); 625c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 626c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 627c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid GtkIMContextWrapper::HandleHostViewRealizeThunk( 628c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch GtkWidget* widget, GtkIMContextWrapper* self) { 629c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch self->HandleHostViewRealize(widget); 630c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 631c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 632c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid GtkIMContextWrapper::HandleHostViewUnrealizeThunk( 633c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch GtkWidget* widget, GtkIMContextWrapper* self) { 634c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch self->HandleHostViewUnrealize(); 635c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 636