Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(112)

Side by Side Diff: chrome/browser/renderer_host/render_widget_host_view_gtk.cc

Issue 147011: Integrating GtkIMContext into the RenderWidgetHostViewGtk class (Take 2).... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 11 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/renderer_host/render_widget_host_view_gtk.h" 5 #include "chrome/browser/renderer_host/render_widget_host_view_gtk.h"
6 6
7 #include <gtk/gtk.h> 7 #include <gtk/gtk.h>
8 #include <gdk/gdk.h> 8 #include <gdk/gdk.h>
9 #include <gdk/gdkkeysyms.h> 9 #include <gdk/gdkkeysyms.h>
10 #include <gdk/gdkx.h> 10 #include <gdk/gdkx.h>
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
64 G_CALLBACK(OnFocusOut), host_view); 64 G_CALLBACK(OnFocusOut), host_view);
65 g_signal_connect(widget, "button-press-event", 65 g_signal_connect(widget, "button-press-event",
66 G_CALLBACK(ButtonPressReleaseEvent), host_view); 66 G_CALLBACK(ButtonPressReleaseEvent), host_view);
67 g_signal_connect(widget, "button-release-event", 67 g_signal_connect(widget, "button-release-event",
68 G_CALLBACK(ButtonPressReleaseEvent), host_view); 68 G_CALLBACK(ButtonPressReleaseEvent), host_view);
69 g_signal_connect(widget, "motion-notify-event", 69 g_signal_connect(widget, "motion-notify-event",
70 G_CALLBACK(MouseMoveEvent), host_view); 70 G_CALLBACK(MouseMoveEvent), host_view);
71 g_signal_connect(widget, "scroll-event", 71 g_signal_connect(widget, "scroll-event",
72 G_CALLBACK(MouseScrollEvent), host_view); 72 G_CALLBACK(MouseScrollEvent), host_view);
73 73
74 // Create a GtkIMContext instance and attach its signal handlers.
75 host_view->im_context_ = gtk_im_multicontext_new();
76 g_signal_connect(host_view->im_context_, "preedit_start",
77 G_CALLBACK(InputMethodPreeditStart), host_view);
78 g_signal_connect(host_view->im_context_, "preedit_end",
79 G_CALLBACK(InputMethodPreeditEnd), host_view);
80 g_signal_connect(host_view->im_context_, "preedit_changed",
81 G_CALLBACK(InputMethodPreeditChanged), host_view);
82 g_signal_connect(host_view->im_context_, "commit",
83 G_CALLBACK(InputMethodCommit), host_view);
84
74 GtkTargetList* target_list = gtk_target_list_new(NULL, 0); 85 GtkTargetList* target_list = gtk_target_list_new(NULL, 0);
75 gtk_target_list_add_text_targets(target_list, 0); 86 gtk_target_list_add_text_targets(target_list, 0);
76 gint num_targets = 0; 87 gint num_targets = 0;
77 GtkTargetEntry* targets = gtk_target_table_new_from_list(target_list, 88 GtkTargetEntry* targets = gtk_target_table_new_from_list(target_list,
78 &num_targets); 89 &num_targets);
79 gtk_selection_clear_targets(widget, GDK_SELECTION_PRIMARY); 90 gtk_selection_clear_targets(widget, GDK_SELECTION_PRIMARY);
80 gtk_selection_add_targets(widget, GDK_SELECTION_PRIMARY, targets, 91 gtk_selection_add_targets(widget, GDK_SELECTION_PRIMARY, targets,
81 num_targets); 92 num_targets);
82 gtk_target_list_unref(target_list); 93 gtk_target_list_unref(target_list);
83 gtk_target_table_free(targets, num_targets); 94 gtk_target_table_free(targets, num_targets);
(...skipping 18 matching lines...) Expand all
102 RenderWidgetHostViewGtk* host_view) { 113 RenderWidgetHostViewGtk* host_view) {
103 if (host_view->parent_ && host_view->activatable() && 114 if (host_view->parent_ && host_view->activatable() &&
104 GDK_Escape == event->keyval) { 115 GDK_Escape == event->keyval) {
105 // Force popups to close on Esc just in case the renderer is hung. This 116 // Force popups to close on Esc just in case the renderer is hung. This
106 // allows us to release our keyboard grab. 117 // allows us to release our keyboard grab.
107 host_view->host_->Shutdown(); 118 host_view->host_->Shutdown();
108 } else { 119 } else {
109 NativeWebKeyboardEvent wke(event); 120 NativeWebKeyboardEvent wke(event);
110 host_view->GetRenderWidgetHost()->ForwardKeyboardEvent(wke); 121 host_view->GetRenderWidgetHost()->ForwardKeyboardEvent(wke);
111 } 122 }
123
124 // Dispatch this event to the GtkIMContext object.
125 // It sends a "commit" signal when it has a character to be inserted
126 // even when we use a US keyboard so that we can send a Char event
127 // (or an IME event) to the renderer in our "commit"-signal handler.
128 // We should send a KeyDown (or a KeyUp) event before dispatching this
129 // event to the GtkIMContext object (and send a Char event) so that WebKit
130 // can dispatch the JavaScript events in the following order: onkeydown(),
131 // onkeypress(), and onkeyup(). (Many JavaScript pages assume this.)
132 // TODO(hbono): we should not dispatch a key event when the input focus
133 // is in a password input?
134 if (!gtk_im_context_filter_keypress(host_view->im_context_, event)) {
135 // The GtkIMContext object cannot handle this key event.
136 // This case is caused by two reasons:
137 // 1. The given key event is a control-key event, (e.g. return, page up,
138 // page down, tab, arrows, etc.) or;
139 // 2. The given key event is not a control-key event but printable
140 // characters aren't assigned to the event, (e.g. alt+d, etc.)
141 // Create a Char event manually from this key event and send it to the
142 // renderer only when this event is a control-key event because
143 // control-key events should be processed by WebKit.
144 // TODO(hbono): Windows Chrome sends a Char event with its isSystemKey
145 // value true for the above case 2. We should emulate this behavior?
146 if (event->type == GDK_KEY_PRESS &&
147 !gdk_keyval_to_unicode(event->keyval)) {
148 NativeWebKeyboardEvent wke(event);
149 wke.type = WebKit::WebInputEvent::Char;
150 host_view->GetRenderWidgetHost()->ForwardKeyboardEvent(wke);
151 }
152 }
153
112 // We return TRUE because we did handle the event. If it turns out webkit 154 // We return TRUE because we did handle the event. If it turns out webkit
113 // can't handle the event, we'll deal with it in 155 // can't handle the event, we'll deal with it in
114 // RenderView::UnhandledKeyboardEvent(). 156 // RenderView::UnhandledKeyboardEvent().
115 return TRUE; 157 return TRUE;
116 } 158 }
117 159
118 static gboolean OnFocusIn(GtkWidget* widget, GdkEventFocus* focus, 160 static gboolean OnFocusIn(GtkWidget* widget, GdkEventFocus* focus,
119 RenderWidgetHostViewGtk* host_view) { 161 RenderWidgetHostViewGtk* host_view) {
120 int x, y; 162 int x, y;
121 gtk_widget_get_pointer(widget, &x, &y); 163 gtk_widget_get_pointer(widget, &x, &y);
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after
208 return FALSE; 250 return FALSE;
209 } 251 }
210 252
211 static gboolean MouseScrollEvent(GtkWidget* widget, GdkEventScroll* event, 253 static gboolean MouseScrollEvent(GtkWidget* widget, GdkEventScroll* event,
212 RenderWidgetHostViewGtk* host_view) { 254 RenderWidgetHostViewGtk* host_view) {
213 host_view->GetRenderWidgetHost()->ForwardWheelEvent( 255 host_view->GetRenderWidgetHost()->ForwardWheelEvent(
214 WebInputEventFactory::mouseWheelEvent(event)); 256 WebInputEventFactory::mouseWheelEvent(event));
215 return FALSE; 257 return FALSE;
216 } 258 }
217 259
260 static void InputMethodCommit(GtkIMContext* im_context,
261 gchar* text,
262 RenderWidgetHostViewGtk* host_view) {
263 std::wstring im_text = UTF8ToWide(text);
264 if (!host_view->im_is_composing_cjk_text_ && im_text.length() == 1) {
265 // Send a Char event when we input a composed character without IMEs so
266 // that this event is to be dispatched to onkeypress() handlers,
267 // autofill, etc.
268 ForwardCharEvent(host_view, im_text[0]);
269 } else {
270 // Send an IME event.
271 // Unlike a Char event, an IME event is NOT dispatched to onkeypress()
272 // handlers or autofill.
273 host_view->GetRenderWidgetHost()->ImeConfirmComposition(im_text);
274 }
275 }
276
277 static void InputMethodPreeditStart(GtkIMContext* im_context,
278 RenderWidgetHostViewGtk* host_view) {
279 // Start monitoring IME events of the renderer.
280 // TODO(hbono): a renderer sends these IME events not only for sending the
281 // caret position, but also for enabling/disabling IMEs. If we need to
282 // enable/disable IMEs, we should move this code to a better place.
283 // (This signal handler is called only when an IME is enabled. So, once
284 // we disable an IME, we cannot receive any IME events from the renderer,
285 // i.e. we cannot re-enable the IME any longer.)
286 host_view->GetRenderWidgetHost()->ImeSetInputMode(true);
287 host_view->im_is_composing_cjk_text_ = true;
288 }
289
290 static void InputMethodPreeditEnd(GtkIMContext* im_context,
291 RenderWidgetHostViewGtk* host_view) {
292 // End monitoring IME events.
293 host_view->GetRenderWidgetHost()->ImeSetInputMode(false);
294 host_view->im_is_composing_cjk_text_ = false;
295 }
296
297 static void InputMethodPreeditChanged(GtkIMContext* im_context,
298 RenderWidgetHostViewGtk* host_view) {
299 // Send an IME event to update the composition node of the renderer.
300 // TODO(hbono): an IME intercepts all key events while composing a text,
301 // i.e. we cannot receive any GDK_KEY_PRESS (or GDK_KEY_UP) events.
302 // Should we send pseudo KeyDown (and KeyUp) events to emulate Windows?
303 gchar* preedit_text = NULL;
304 gint cursor_position = 0;
305 gtk_im_context_get_preedit_string(im_context, &preedit_text, NULL,
306 &cursor_position);
307 host_view->GetRenderWidgetHost()->ImeSetComposition(
308 UTF8ToWide(preedit_text), cursor_position, -1, -1);
309 g_free(preedit_text);
310 }
311
312 static void ForwardCharEvent(RenderWidgetHostViewGtk* host_view,
313 wchar_t im_character) {
314 if (!im_character)
315 return;
316
317 NativeWebKeyboardEvent char_event(im_character,
318 base::Time::Now().ToDoubleT());
319 host_view->GetRenderWidgetHost()->ForwardKeyboardEvent(char_event);
320 }
321
218 DISALLOW_IMPLICIT_CONSTRUCTORS(RenderWidgetHostViewGtkWidget); 322 DISALLOW_IMPLICIT_CONSTRUCTORS(RenderWidgetHostViewGtkWidget);
219 }; 323 };
220 324
221 // static 325 // static
222 RenderWidgetHostView* RenderWidgetHostView::CreateViewForWidget( 326 RenderWidgetHostView* RenderWidgetHostView::CreateViewForWidget(
223 RenderWidgetHost* widget) { 327 RenderWidgetHost* widget) {
224 return new RenderWidgetHostViewGtk(widget); 328 return new RenderWidgetHostViewGtk(widget);
225 } 329 }
226 330
227 RenderWidgetHostViewGtk::RenderWidgetHostViewGtk(RenderWidgetHost* widget_host) 331 RenderWidgetHostViewGtk::RenderWidgetHostViewGtk(RenderWidgetHost* widget_host)
228 : host_(widget_host), 332 : host_(widget_host),
229 about_to_validate_and_paint_(false), 333 about_to_validate_and_paint_(false),
230 is_hidden_(false), 334 is_hidden_(false),
231 is_loading_(false), 335 is_loading_(false),
232 is_showing_context_menu_(false), 336 is_showing_context_menu_(false),
233 parent_host_view_(NULL), 337 parent_host_view_(NULL),
234 parent_(NULL), 338 parent_(NULL),
235 is_popup_first_mouse_release_(true) { 339 is_popup_first_mouse_release_(true),
340 im_context_(NULL),
341 im_is_composing_cjk_text_(false) {
236 host_->set_view(this); 342 host_->set_view(this);
237 } 343 }
238 344
239 RenderWidgetHostViewGtk::~RenderWidgetHostViewGtk() { 345 RenderWidgetHostViewGtk::~RenderWidgetHostViewGtk() {
346 if (im_context_)
347 g_object_unref(im_context_);
240 view_.Destroy(); 348 view_.Destroy();
241 } 349 }
242 350
243 void RenderWidgetHostViewGtk::InitAsChild() { 351 void RenderWidgetHostViewGtk::InitAsChild() {
244 view_.Own(RenderWidgetHostViewGtkWidget::CreateNewWidget(this)); 352 view_.Own(RenderWidgetHostViewGtkWidget::CreateNewWidget(this));
245 gtk_widget_show(view_.get()); 353 gtk_widget_show(view_.get());
246 } 354 }
247 355
248 void RenderWidgetHostViewGtk::InitAsPopup( 356 void RenderWidgetHostViewGtk::InitAsPopup(
249 RenderWidgetHostView* parent_host_view, const gfx::Rect& pos) { 357 RenderWidgetHostView* parent_host_view, const gfx::Rect& pos) {
(...skipping 139 matching lines...) Expand 10 before | Expand all | Expand 10 after
389 497
390 void RenderWidgetHostViewGtk::SetIsLoading(bool is_loading) { 498 void RenderWidgetHostViewGtk::SetIsLoading(bool is_loading) {
391 is_loading_ = is_loading; 499 is_loading_ = is_loading;
392 // Only call ShowCurrentCursor() when it will actually change the cursor. 500 // Only call ShowCurrentCursor() when it will actually change the cursor.
393 if (current_cursor_.GetCursorType() == GDK_LAST_CURSOR) 501 if (current_cursor_.GetCursorType() == GDK_LAST_CURSOR)
394 ShowCurrentCursor(); 502 ShowCurrentCursor();
395 } 503 }
396 504
397 void RenderWidgetHostViewGtk::IMEUpdateStatus(int control, 505 void RenderWidgetHostViewGtk::IMEUpdateStatus(int control,
398 const gfx::Rect& caret_rect) { 506 const gfx::Rect& caret_rect) {
399 NOTIMPLEMENTED(); 507 // The renderer has updated its IME status.
508 // Control the GtkIMContext object according to this status.
509 if (!im_context_)
510 return;
511
512 if (control == IME_DISABLE) {
513 // TODO(hbono): this code just resets the GtkIMContext object and
514 // detaches it from this window. Should we prevent sending key events to
515 // the GtkIMContext object (or unref it) when we disable IMEs?
516 gtk_im_context_reset(im_context_);
517 gtk_im_context_set_client_window(im_context_, NULL);
518 gtk_im_context_set_cursor_location(im_context_, NULL);
519 } else {
520 // TODO(hbono): we should finish (not reset) an ongoing composition
521 // when |control| is IME_COMPLETE_COMPOSITION.
522
523 // Attach the GtkIMContext object to this window.
524 gtk_im_context_set_client_window(im_context_, view_.get()->window);
525
526 // Updates the position of the IME candidate window.
527 // The position sent from the renderer is a relative one, so we need to
528 // attach the GtkIMContext object to this window before changing the
529 // position.
530 GdkRectangle cursor_rect(caret_rect.ToGdkRectangle());
531 gtk_im_context_set_cursor_location(im_context_, &cursor_rect);
532 }
400 } 533 }
401 534
402 void RenderWidgetHostViewGtk::DidPaintRect(const gfx::Rect& rect) { 535 void RenderWidgetHostViewGtk::DidPaintRect(const gfx::Rect& rect) {
403 if (is_hidden_) 536 if (is_hidden_)
404 return; 537 return;
405 538
406 if (about_to_validate_and_paint_) 539 if (about_to_validate_and_paint_)
407 invalid_rect_ = invalid_rect_.Union(rect); 540 invalid_rect_ = invalid_rect_.Union(rect);
408 else 541 else
409 Paint(rect); 542 Paint(rect);
(...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after
546 void RenderWidgetHostViewGtk::ReceivedSelectionText(GtkClipboard* clipboard, 679 void RenderWidgetHostViewGtk::ReceivedSelectionText(GtkClipboard* clipboard,
547 const gchar* text, gpointer userdata) { 680 const gchar* text, gpointer userdata) {
548 // If there's nothing to paste (|text| is NULL), do nothing. 681 // If there's nothing to paste (|text| is NULL), do nothing.
549 if (!text) 682 if (!text)
550 return; 683 return;
551 RenderWidgetHostViewGtk* host_view = 684 RenderWidgetHostViewGtk* host_view =
552 reinterpret_cast<RenderWidgetHostViewGtk*>(userdata); 685 reinterpret_cast<RenderWidgetHostViewGtk*>(userdata);
553 host_view->host_->Send(new ViewMsg_InsertText(host_view->host_->routing_id(), 686 host_view->host_->Send(new ViewMsg_InsertText(host_view->host_->routing_id(),
554 UTF8ToUTF16(text))); 687 UTF8ToUTF16(text)));
555 } 688 }
OLDNEW
« no previous file with comments | « chrome/browser/renderer_host/render_widget_host_view_gtk.h ('k') | chrome/browser/tab_contents/tab_contents_view_gtk.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698