Index: chrome/browser/renderer_host/render_widget_host_view_gtk.cc |
=================================================================== |
--- chrome/browser/renderer_host/render_widget_host_view_gtk.cc (revision 18994) |
+++ chrome/browser/renderer_host/render_widget_host_view_gtk.cc (working copy) |
@@ -71,6 +71,17 @@ |
g_signal_connect(widget, "scroll-event", |
G_CALLBACK(MouseScrollEvent), host_view); |
+ // Create a GtkIMContext instance and attach its signal handlers. |
+ host_view->im_context_ = gtk_im_multicontext_new(); |
+ g_signal_connect(host_view->im_context_, "preedit_start", |
+ G_CALLBACK(InputMethodPreeditStart), host_view); |
+ g_signal_connect(host_view->im_context_, "preedit_end", |
+ G_CALLBACK(InputMethodPreeditEnd), host_view); |
+ g_signal_connect(host_view->im_context_, "preedit_changed", |
+ G_CALLBACK(InputMethodPreeditChanged), host_view); |
+ g_signal_connect(host_view->im_context_, "commit", |
+ G_CALLBACK(InputMethodCommit), host_view); |
+ |
GtkTargetList* target_list = gtk_target_list_new(NULL, 0); |
gtk_target_list_add_text_targets(target_list, 0); |
gint num_targets = 0; |
@@ -109,6 +120,19 @@ |
NativeWebKeyboardEvent wke(event); |
host_view->GetRenderWidgetHost()->ForwardKeyboardEvent(wke); |
} |
+ |
+ // Dispatch this event to the GtkIMContext object. |
+ // It sends a "commit" signal when it has a character to be inserted |
+ // even when we use a US keyboard so that we can send a Char event |
+ // (or an IME event) to the renderer in our "commit"-signal handler. |
+ // We should send a KeyDown (or a KeyUp) event before dispatching this |
+ // event to the GtkIMContext object (and send a Char event) so that WebKit |
+ // can dispatch the JavaScript events in the following order: onkeydown(), |
+ // onkeypress(), and onkeyup(). (Many JavaScript pages assume this.) |
+ // TODO(hbono): we should not dispatch a key event when the input focus |
+ // is in a password input? |
+ gtk_im_context_filter_keypress(host_view->im_context_, event); |
+ |
// We return TRUE because we did handle the event. If it turns out webkit |
// can't handle the event, we'll deal with it in |
// RenderView::UnhandledKeyboardEvent(). |
@@ -215,6 +239,68 @@ |
return FALSE; |
} |
+ static void InputMethodCommit(GtkIMContext* im_context, |
+ gchar* text, |
+ RenderWidgetHostViewGtk* host_view) { |
+ std::wstring im_text = UTF8ToWide(text); |
+ if (!host_view->im_is_composing_cjk_text_ && im_text.length() == 1) { |
+ // Send a Char event when we input a composed character without IMEs so |
+ // that this event is to be dispatched to onkeypress() handlers, |
+ // autofill, etc. |
+ ForwardCharEvent(host_view, im_text[0]); |
+ } else { |
+ // Send an IME event. |
+ // Unlike a Char event, an IME event is NOT dispatched to onkeypress() |
+ // handlers or autofill. |
+ host_view->GetRenderWidgetHost()->ImeConfirmComposition(im_text); |
+ } |
+ } |
+ |
+ static void InputMethodPreeditStart(GtkIMContext* im_context, |
+ RenderWidgetHostViewGtk* host_view) { |
+ // Start monitoring IME events of the renderer. |
+ // TODO(hbono): a renderer sends these IME events not only for sending the |
+ // caret position, but also for enabling/disabling IMEs. If we need to |
+ // enable/disable IMEs, we should move this code to a better place. |
+ // (This signal handler is called only when an IME is enabled. So, once |
+ // we disable an IME, we cannot receive any IME events from the renderer, |
+ // i.e. we cannot re-enable the IME any longer.) |
+ host_view->GetRenderWidgetHost()->ImeSetInputMode(true); |
+ host_view->im_is_composing_cjk_text_ = true; |
+ } |
+ |
+ static void InputMethodPreeditEnd(GtkIMContext* im_context, |
+ RenderWidgetHostViewGtk* host_view) { |
+ // End monitoring IME events. |
+ host_view->GetRenderWidgetHost()->ImeSetInputMode(false); |
+ host_view->im_is_composing_cjk_text_ = false; |
+ } |
+ |
+ static void InputMethodPreeditChanged(GtkIMContext* im_context, |
+ RenderWidgetHostViewGtk* host_view) { |
+ // Send an IME event to update the composition node of the renderer. |
+ // TODO(hbono): an IME intercepts all key events while composing a text, |
+ // i.e. we cannot receive any GDK_KEY_PRESS (or GDK_KEY_UP) events. |
+ // Should we send pseudo KeyDown (and KeyUp) events to emulate Windows? |
+ gchar* preedit_text = NULL; |
+ gint cursor_position = 0; |
+ gtk_im_context_get_preedit_string(im_context, &preedit_text, NULL, |
+ &cursor_position); |
+ host_view->GetRenderWidgetHost()->ImeSetComposition( |
+ UTF8ToWide(preedit_text), cursor_position, -1, -1); |
+ g_free(preedit_text); |
+ } |
+ |
+ static void ForwardCharEvent(RenderWidgetHostViewGtk* host_view, |
+ wchar_t im_character) { |
+ if (!im_character) |
+ return; |
+ |
+ NativeWebKeyboardEvent char_event(im_character, |
+ base::Time::Now().ToDoubleT()); |
+ host_view->GetRenderWidgetHost()->ForwardKeyboardEvent(char_event); |
+ } |
+ |
DISALLOW_IMPLICIT_CONSTRUCTORS(RenderWidgetHostViewGtkWidget); |
}; |
@@ -232,11 +318,15 @@ |
is_showing_context_menu_(false), |
parent_host_view_(NULL), |
parent_(NULL), |
- is_popup_first_mouse_release_(true) { |
+ is_popup_first_mouse_release_(true), |
+ im_context_(NULL), |
+ im_is_composing_cjk_text_(false) { |
host_->set_view(this); |
} |
RenderWidgetHostViewGtk::~RenderWidgetHostViewGtk() { |
+ if (im_context_) |
+ g_object_unref(im_context_); |
view_.Destroy(); |
} |
@@ -396,7 +486,32 @@ |
void RenderWidgetHostViewGtk::IMEUpdateStatus(int control, |
const gfx::Rect& caret_rect) { |
- NOTIMPLEMENTED(); |
+ // The renderer has updated its IME status. |
+ // Control the GtkIMContext object according to this status. |
+ if (!im_context_) |
+ return; |
+ |
+ if (control == IME_DISABLE) { |
+ // TODO(hbono): this code just resets the GtkIMContext object and |
+ // detaches it from this window. Should we prevent sending key events to |
+ // the GtkIMContext object (or unref it) when we disable IMEs? |
+ gtk_im_context_reset(im_context_); |
+ gtk_im_context_set_client_window(im_context_, NULL); |
+ gtk_im_context_set_cursor_location(im_context_, NULL); |
+ } else { |
+ // TODO(hbono): we should finish (not reset) an ongoing composition |
+ // when |control| is IME_COMPLETE_COMPOSITION. |
+ |
+ // Attach the GtkIMContext object to this window. |
+ gtk_im_context_set_client_window(im_context_, view_.get()->window); |
+ |
+ // Updates the position of the IME candidate window. |
+ // The position sent from the renderer is a relative one, so we need to |
+ // attach the GtkIMContext object to this window before changing the |
+ // position. |
+ GdkRectangle cursor_rect(caret_rect.ToGdkRectangle()); |
+ gtk_im_context_set_cursor_location(im_context_, &cursor_rect); |
+ } |
} |
void RenderWidgetHostViewGtk::DidPaintRect(const gfx::Rect& rect) { |