OLD | NEW |
---|---|
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 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/gtk_im_context_wrapper.h" | 5 #include "chrome/browser/renderer_host/gtk_im_context_wrapper.h" |
6 | 6 |
7 #include <gdk/gdk.h> | 7 #include <gdk/gdk.h> |
8 #include <gdk/gdkkeysyms.h> | 8 #include <gdk/gdkkeysyms.h> |
9 #include <gtk/gtk.h> | 9 #include <gtk/gtk.h> |
10 #include <algorithm> | 10 #include <algorithm> |
(...skipping 190 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
201 // enabled by WebKit. | 201 // enabled by WebKit. |
202 if (is_enabled_) | 202 if (is_enabled_) |
203 gtk_im_context_focus_in(context_); | 203 gtk_im_context_focus_in(context_); |
204 | 204 |
205 // context_simple_ is always enabled. | 205 // context_simple_ is always enabled. |
206 // Actually it doesn't care focus state at all. | 206 // Actually it doesn't care focus state at all. |
207 gtk_im_context_focus_in(context_simple_); | 207 gtk_im_context_focus_in(context_simple_); |
208 | 208 |
209 // Enables RenderWidget's IME related events, so that we can be notified | 209 // Enables RenderWidget's IME related events, so that we can be notified |
210 // when WebKit wants to enable or disable IME. | 210 // when WebKit wants to enable or disable IME. |
211 host_view_->GetRenderWidgetHost()->SetInputMethodActive(true); | 211 if (host_view_->GetRenderWidgetHost()) |
satorux1
2010/06/24 04:19:36
host_view_ is guaranteed not to be NULL, right?
James Su
2010/06/24 04:21:46
Yes. GtkIMContextWrapper object will be destroyed
| |
212 host_view_->GetRenderWidgetHost()->SetInputMethodActive(true); | |
212 } | 213 } |
213 | 214 |
214 void GtkIMContextWrapper::OnFocusOut() { | 215 void GtkIMContextWrapper::OnFocusOut() { |
215 if (!is_focused_) | 216 if (!is_focused_) |
216 return; | 217 return; |
217 | 218 |
218 // Tracks the focused state so that we won't give focus to the | 219 // Tracks the focused state so that we won't give focus to the |
219 // GtkIMContext object unexpectly. | 220 // GtkIMContext object unexpectly. |
220 is_focused_ = false; | 221 is_focused_ = false; |
221 | 222 |
222 // Notify the GtkIMContext object of this focus-out event only if IME is | 223 // Notify the GtkIMContext object of this focus-out event only if IME is |
223 // enabled by WebKit. | 224 // enabled by WebKit. |
224 if (is_enabled_) { | 225 if (is_enabled_) { |
225 // To reset the GtkIMContext object and prevent data loss. | 226 // To reset the GtkIMContext object and prevent data loss. |
226 ConfirmComposition(); | 227 ConfirmComposition(); |
227 gtk_im_context_focus_out(context_); | 228 gtk_im_context_focus_out(context_); |
228 } | 229 } |
229 | 230 |
230 // To make sure it'll be in correct state when focused in again. | 231 // To make sure it'll be in correct state when focused in again. |
231 gtk_im_context_reset(context_simple_); | 232 gtk_im_context_reset(context_simple_); |
232 gtk_im_context_focus_out(context_simple_); | 233 gtk_im_context_focus_out(context_simple_); |
233 | 234 |
234 is_composing_text_ = false; | 235 is_composing_text_ = false; |
235 | 236 |
236 // Disable RenderWidget's IME related events to save bandwidth. | 237 // Disable RenderWidget's IME related events to save bandwidth. |
237 host_view_->GetRenderWidgetHost()->SetInputMethodActive(false); | 238 if (host_view_->GetRenderWidgetHost()) |
239 host_view_->GetRenderWidgetHost()->SetInputMethodActive(false); | |
238 } | 240 } |
239 | 241 |
240 void GtkIMContextWrapper::AppendInputMethodsContextMenu(MenuGtk* menu) { | 242 void GtkIMContextWrapper::AppendInputMethodsContextMenu(MenuGtk* menu) { |
241 std::string label = gtk_util::ConvertAcceleratorsFromWindowsStyle( | 243 std::string label = gtk_util::ConvertAcceleratorsFromWindowsStyle( |
242 l10n_util::GetStringUTF8(IDS_CONTENT_CONTEXT_INPUT_METHODS_MENU)); | 244 l10n_util::GetStringUTF8(IDS_CONTENT_CONTEXT_INPUT_METHODS_MENU)); |
243 GtkWidget* menuitem = gtk_menu_item_new_with_mnemonic(label.c_str()); | 245 GtkWidget* menuitem = gtk_menu_item_new_with_mnemonic(label.c_str()); |
244 GtkWidget* submenu = gtk_menu_new(); | 246 GtkWidget* submenu = gtk_menu_new(); |
245 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu); | 247 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu); |
246 gtk_im_multicontext_append_menuitems(GTK_IM_MULTICONTEXT(context_), | 248 gtk_im_multicontext_append_menuitems(GTK_IM_MULTICONTEXT(context_), |
247 GTK_MENU_SHELL(submenu)); | 249 GTK_MENU_SHELL(submenu)); |
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
337 if (wke->text[0]) { | 339 if (wke->text[0]) { |
338 wke->type = WebKit::WebInputEvent::Char; | 340 wke->type = WebKit::WebInputEvent::Char; |
339 wke->skip_in_browser = true; | 341 wke->skip_in_browser = true; |
340 host_view_->ForwardKeyboardEvent(*wke); | 342 host_view_->ForwardKeyboardEvent(*wke); |
341 } | 343 } |
342 } | 344 } |
343 | 345 |
344 void GtkIMContextWrapper::ProcessInputMethodResult(const GdkEventKey* event, | 346 void GtkIMContextWrapper::ProcessInputMethodResult(const GdkEventKey* event, |
345 bool filtered) { | 347 bool filtered) { |
346 RenderWidgetHost* host = host_view_->GetRenderWidgetHost(); | 348 RenderWidgetHost* host = host_view_->GetRenderWidgetHost(); |
349 if (!host) | |
350 return; | |
351 | |
347 bool committed = false; | 352 bool committed = false; |
348 // We do commit before preedit change, so that we can optimize some | 353 // We do commit before preedit change, so that we can optimize some |
349 // unnecessary preedit changes. | 354 // unnecessary preedit changes. |
350 if (commit_text_.length()) { | 355 if (commit_text_.length()) { |
351 if (filtered && NeedCommitByForwardingCharEvent()) { | 356 if (filtered && NeedCommitByForwardingCharEvent()) { |
352 // Send a Char event when we input a composed character without IMEs | 357 // Send a Char event when we input a composed character without IMEs |
353 // so that this event is to be dispatched to onkeypress() handlers, | 358 // so that this event is to be dispatched to onkeypress() handlers, |
354 // autofill, etc. | 359 // autofill, etc. |
355 // Only commit text generated by a filtered key down event can be sent | 360 // Only commit text generated by a filtered key down event can be sent |
356 // as a Char event, because a unfiltered key down event will probably | 361 // as a Char event, because a unfiltered key down event will probably |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
389 } | 394 } |
390 } | 395 } |
391 | 396 |
392 void GtkIMContextWrapper::ConfirmComposition() { | 397 void GtkIMContextWrapper::ConfirmComposition() { |
393 if (!is_enabled_) | 398 if (!is_enabled_) |
394 return; | 399 return; |
395 | 400 |
396 DCHECK(!is_in_key_event_handler_); | 401 DCHECK(!is_in_key_event_handler_); |
397 | 402 |
398 if (is_composing_text_) { | 403 if (is_composing_text_) { |
399 host_view_->GetRenderWidgetHost()->ImeConfirmComposition(); | 404 if (host_view_->GetRenderWidgetHost()) |
405 host_view_->GetRenderWidgetHost()->ImeConfirmComposition(); | |
400 | 406 |
401 // Reset the input method. | 407 // Reset the input method. |
402 CancelComposition(); | 408 CancelComposition(); |
403 } | 409 } |
404 } | 410 } |
405 | 411 |
406 void GtkIMContextWrapper::HandleCommit(const string16& text) { | 412 void GtkIMContextWrapper::HandleCommit(const string16& text) { |
407 // Append the text to the buffer, because commit signal might be fired | 413 // Append the text to the buffer, because commit signal might be fired |
408 // multiple times when processing a key event. | 414 // multiple times when processing a key event. |
409 commit_text_.append(text); | 415 commit_text_.append(text); |
410 // Nothing needs to do, if it's currently in ProcessKeyEvent() | 416 // Nothing needs to do, if it's currently in ProcessKeyEvent() |
411 // handler, which will send commit text to webkit later. Otherwise, | 417 // handler, which will send commit text to webkit later. Otherwise, |
412 // we need send it here. | 418 // we need send it here. |
413 // It's possible that commit signal is fired without a key event, for | 419 // It's possible that commit signal is fired without a key event, for |
414 // example when user input via a voice or handwriting recognition software. | 420 // example when user input via a voice or handwriting recognition software. |
415 // In this case, the text must be committed directly. | 421 // In this case, the text must be committed directly. |
416 if (!is_in_key_event_handler_) | 422 if (!is_in_key_event_handler_ && host_view_->GetRenderWidgetHost()) |
417 host_view_->GetRenderWidgetHost()->ImeConfirmComposition(text); | 423 host_view_->GetRenderWidgetHost()->ImeConfirmComposition(text); |
418 } | 424 } |
419 | 425 |
420 void GtkIMContextWrapper::HandlePreeditStart() { | 426 void GtkIMContextWrapper::HandlePreeditStart() { |
421 is_composing_text_ = true; | 427 is_composing_text_ = true; |
422 } | 428 } |
423 | 429 |
424 void GtkIMContextWrapper::HandlePreeditChanged(const gchar* text, | 430 void GtkIMContextWrapper::HandlePreeditChanged(const gchar* text, |
425 PangoAttrList* attrs, | 431 PangoAttrList* attrs, |
426 int cursor_position) { | 432 int cursor_position) { |
(...skipping 10 matching lines...) Expand all Loading... | |
437 &preedit_selection_end_); | 443 &preedit_selection_end_); |
438 | 444 |
439 // In case we are using a buggy input method which doesn't fire | 445 // In case we are using a buggy input method which doesn't fire |
440 // "preedit_start" signal. | 446 // "preedit_start" signal. |
441 if (preedit_text_.length()) | 447 if (preedit_text_.length()) |
442 is_composing_text_ = true; | 448 is_composing_text_ = true; |
443 | 449 |
444 // Nothing needs to do, if it's currently in ProcessKeyEvent() | 450 // Nothing needs to do, if it's currently in ProcessKeyEvent() |
445 // handler, which will send preedit text to webkit later. | 451 // handler, which will send preedit text to webkit later. |
446 // Otherwise, we need send it here if it's been changed. | 452 // Otherwise, we need send it here if it's been changed. |
447 if (!is_in_key_event_handler_) { | 453 if (!is_in_key_event_handler_ && host_view_->GetRenderWidgetHost()) { |
448 host_view_->GetRenderWidgetHost()->ImeSetComposition( | 454 host_view_->GetRenderWidgetHost()->ImeSetComposition( |
449 preedit_text_, preedit_underlines_, preedit_selection_start_, | 455 preedit_text_, preedit_underlines_, preedit_selection_start_, |
450 preedit_selection_end_); | 456 preedit_selection_end_); |
451 } | 457 } |
452 } | 458 } |
453 | 459 |
454 void GtkIMContextWrapper::HandlePreeditEnd() { | 460 void GtkIMContextWrapper::HandlePreeditEnd() { |
455 bool changed = false; | |
456 if (preedit_text_.length()) { | 461 if (preedit_text_.length()) { |
457 // The composition session has been finished. | 462 // The composition session has been finished. |
458 preedit_text_.clear(); | 463 preedit_text_.clear(); |
459 preedit_underlines_.clear(); | 464 preedit_underlines_.clear(); |
460 is_preedit_changed_ = true; | 465 is_preedit_changed_ = true; |
461 changed = true; | 466 |
467 // If there is still a preedit text when firing "preedit-end" signal, | |
468 // we need inform webkit to clear it. | |
469 // It's only necessary when it's not in ProcessKeyEvent (). | |
470 if (!is_in_key_event_handler_ && host_view_->GetRenderWidgetHost()) | |
471 host_view_->GetRenderWidgetHost()->ImeCancelComposition(); | |
462 } | 472 } |
463 | 473 |
464 // If there is still a preedit text when firing "preedit-end" signal, | |
465 // we need inform webkit to clear it. | |
466 // It's only necessary when it's not in ProcessKeyEvent (). | |
467 if (!is_in_key_event_handler_ && changed) | |
468 host_view_->GetRenderWidgetHost()->ImeCancelComposition(); | |
469 | |
470 // Don't set is_composing_text_ to false here, because "preedit_end" | 474 // Don't set is_composing_text_ to false here, because "preedit_end" |
471 // signal may be fired before "commit" signal. | 475 // signal may be fired before "commit" signal. |
472 } | 476 } |
473 | 477 |
474 void GtkIMContextWrapper::HandleHostViewRealize(GtkWidget* widget) { | 478 void GtkIMContextWrapper::HandleHostViewRealize(GtkWidget* widget) { |
475 // We should only set im context's client window once, because when setting | 479 // We should only set im context's client window once, because when setting |
476 // client window.im context may destroy and recreate its internal states and | 480 // client window.im context may destroy and recreate its internal states and |
477 // objects. | 481 // objects. |
478 if (widget->window) { | 482 if (widget->window) { |
479 gtk_im_context_set_client_window(context_, widget->window); | 483 gtk_im_context_set_client_window(context_, widget->window); |
(...skipping 128 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
608 } while (pango_attr_iterator_next(iter)); | 612 } while (pango_attr_iterator_next(iter)); |
609 pango_attr_iterator_destroy(iter); | 613 pango_attr_iterator_destroy(iter); |
610 } | 614 } |
611 | 615 |
612 // Use a black thin underline by default. | 616 // Use a black thin underline by default. |
613 if (underlines->empty()) { | 617 if (underlines->empty()) { |
614 underlines->push_back( | 618 underlines->push_back( |
615 WebKit::WebCompositionUnderline(0, length, SK_ColorBLACK, false)); | 619 WebKit::WebCompositionUnderline(0, length, SK_ColorBLACK, false)); |
616 } | 620 } |
617 } | 621 } |
OLD | NEW |