Chromium Code Reviews| 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 |