| 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 20 matching lines...) Expand all Loading... |
| 31 GtkIMContextWrapper::GtkIMContextWrapper(RenderWidgetHostViewGtk* host_view) | 31 GtkIMContextWrapper::GtkIMContextWrapper(RenderWidgetHostViewGtk* host_view) |
| 32 : host_view_(host_view), | 32 : host_view_(host_view), |
| 33 context_(gtk_im_multicontext_new()), | 33 context_(gtk_im_multicontext_new()), |
| 34 context_simple_(gtk_im_context_simple_new()), | 34 context_simple_(gtk_im_context_simple_new()), |
| 35 is_focused_(false), | 35 is_focused_(false), |
| 36 is_composing_text_(false), | 36 is_composing_text_(false), |
| 37 is_enabled_(false), | 37 is_enabled_(false), |
| 38 is_in_key_event_handler_(false), | 38 is_in_key_event_handler_(false), |
| 39 preedit_selection_start_(0), | 39 preedit_selection_start_(0), |
| 40 preedit_selection_end_(0), | 40 preedit_selection_end_(0), |
| 41 is_preedit_changed_(false) { | 41 is_preedit_changed_(false), |
| 42 suppress_next_commit_(false) { |
| 42 DCHECK(context_); | 43 DCHECK(context_); |
| 43 DCHECK(context_simple_); | 44 DCHECK(context_simple_); |
| 44 | 45 |
| 45 // context_ and context_simple_ share the same callback handlers. | 46 // context_ and context_simple_ share the same callback handlers. |
| 46 // All data come from them are treated equally. | 47 // All data come from them are treated equally. |
| 47 // context_ is for full input method support. | 48 // context_ is for full input method support. |
| 48 // context_simple_ is for supporting dead/compose keys when input method is | 49 // context_simple_ is for supporting dead/compose keys when input method is |
| 49 // disabled by webkit, eg. in password input box. | 50 // disabled by webkit, eg. in password input box. |
| 50 g_signal_connect(context_, "preedit_start", | 51 g_signal_connect(context_, "preedit_start", |
| 51 G_CALLBACK(HandlePreeditStartThunk), this); | 52 G_CALLBACK(HandlePreeditStartThunk), this); |
| (...skipping 26 matching lines...) Expand all Loading... |
| 78 } | 79 } |
| 79 | 80 |
| 80 GtkIMContextWrapper::~GtkIMContextWrapper() { | 81 GtkIMContextWrapper::~GtkIMContextWrapper() { |
| 81 if (context_) | 82 if (context_) |
| 82 g_object_unref(context_); | 83 g_object_unref(context_); |
| 83 if (context_simple_) | 84 if (context_simple_) |
| 84 g_object_unref(context_simple_); | 85 g_object_unref(context_simple_); |
| 85 } | 86 } |
| 86 | 87 |
| 87 void GtkIMContextWrapper::ProcessKeyEvent(GdkEventKey* event) { | 88 void GtkIMContextWrapper::ProcessKeyEvent(GdkEventKey* event) { |
| 89 suppress_next_commit_ = false; |
| 90 |
| 88 // Indicates preedit-changed and commit signal handlers that we are | 91 // Indicates preedit-changed and commit signal handlers that we are |
| 89 // processing a key event. | 92 // processing a key event. |
| 90 is_in_key_event_handler_ = true; | 93 is_in_key_event_handler_ = true; |
| 91 // Reset this flag so that we can know if preedit is changed after | 94 // Reset this flag so that we can know if preedit is changed after |
| 92 // processing this key event. | 95 // processing this key event. |
| 93 is_preedit_changed_ = false; | 96 is_preedit_changed_ = false; |
| 94 // Clear it so that we can know if something needs committing after | 97 // Clear it so that we can know if something needs committing after |
| 95 // processing this key event. | 98 // processing this key event. |
| 96 commit_text_.clear(); | 99 commit_text_.clear(); |
| 97 | 100 |
| (...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 167 | 170 |
| 168 // Send unfiltered keydown and keyup events after sending IME result. | 171 // Send unfiltered keydown and keyup events after sending IME result. |
| 169 if (event->type == GDK_KEY_PRESS && !filtered) | 172 if (event->type == GDK_KEY_PRESS && !filtered) |
| 170 ProcessUnfilteredKeyPressEvent(&wke); | 173 ProcessUnfilteredKeyPressEvent(&wke); |
| 171 else if (event->type == GDK_KEY_RELEASE) | 174 else if (event->type == GDK_KEY_RELEASE) |
| 172 host_view_->ForwardKeyboardEvent(wke); | 175 host_view_->ForwardKeyboardEvent(wke); |
| 173 } | 176 } |
| 174 | 177 |
| 175 void GtkIMContextWrapper::UpdateInputMethodState(WebKit::WebTextInputType type, | 178 void GtkIMContextWrapper::UpdateInputMethodState(WebKit::WebTextInputType type, |
| 176 const gfx::Rect& caret_rect) { | 179 const gfx::Rect& caret_rect) { |
| 180 suppress_next_commit_ = false; |
| 181 |
| 177 // The renderer has updated its IME status. | 182 // The renderer has updated its IME status. |
| 178 // Control the GtkIMContext object according to this status. | 183 // Control the GtkIMContext object according to this status. |
| 179 if (!context_ || !is_focused_) | 184 if (!context_ || !is_focused_) |
| 180 return; | 185 return; |
| 181 | 186 |
| 182 DCHECK(!is_in_key_event_handler_); | 187 DCHECK(!is_in_key_event_handler_); |
| 183 | 188 |
| 184 bool is_enabled = (type == WebKit::WebTextInputTypeText); | 189 bool is_enabled = (type == WebKit::WebTextInputTypeText); |
| 185 if (is_enabled_ != is_enabled) { | 190 if (is_enabled_ != is_enabled) { |
| 186 is_enabled_ = is_enabled; | 191 is_enabled_ = is_enabled; |
| (...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 274 #endif | 279 #endif |
| 275 | 280 |
| 276 void GtkIMContextWrapper::CancelComposition() { | 281 void GtkIMContextWrapper::CancelComposition() { |
| 277 if (!is_enabled_) | 282 if (!is_enabled_) |
| 278 return; | 283 return; |
| 279 | 284 |
| 280 DCHECK(!is_in_key_event_handler_); | 285 DCHECK(!is_in_key_event_handler_); |
| 281 | 286 |
| 282 // To prevent any text from being committed when resetting the |context_|; | 287 // To prevent any text from being committed when resetting the |context_|; |
| 283 is_in_key_event_handler_ = true; | 288 is_in_key_event_handler_ = true; |
| 289 suppress_next_commit_ = true; |
| 284 | 290 |
| 285 gtk_im_context_reset(context_); | 291 gtk_im_context_reset(context_); |
| 286 gtk_im_context_reset(context_simple_); | 292 gtk_im_context_reset(context_simple_); |
| 287 | 293 |
| 288 if (is_focused_) { | 294 if (is_focused_) { |
| 289 // Some input methods may not honour the reset call. Focusing out/in the | 295 // Some input methods may not honour the reset call. Focusing out/in the |
| 290 // |context_| to make sure it gets reset correctly. | 296 // |context_| to make sure it gets reset correctly. |
| 291 gtk_im_context_focus_out(context_); | 297 gtk_im_context_focus_out(context_); |
| 292 gtk_im_context_focus_in(context_); | 298 gtk_im_context_focus_in(context_); |
| 293 } | 299 } |
| (...skipping 129 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 423 if (is_composing_text_) { | 429 if (is_composing_text_) { |
| 424 if (host_view_->GetRenderWidgetHost()) | 430 if (host_view_->GetRenderWidgetHost()) |
| 425 host_view_->GetRenderWidgetHost()->ImeConfirmComposition(); | 431 host_view_->GetRenderWidgetHost()->ImeConfirmComposition(); |
| 426 | 432 |
| 427 // Reset the input method. | 433 // Reset the input method. |
| 428 CancelComposition(); | 434 CancelComposition(); |
| 429 } | 435 } |
| 430 } | 436 } |
| 431 | 437 |
| 432 void GtkIMContextWrapper::HandleCommit(const string16& text) { | 438 void GtkIMContextWrapper::HandleCommit(const string16& text) { |
| 439 if (suppress_next_commit_) { |
| 440 suppress_next_commit_ = false; |
| 441 return; |
| 442 } |
| 443 |
| 433 // Append the text to the buffer, because commit signal might be fired | 444 // Append the text to the buffer, because commit signal might be fired |
| 434 // multiple times when processing a key event. | 445 // multiple times when processing a key event. |
| 435 commit_text_.append(text); | 446 commit_text_.append(text); |
| 436 // Nothing needs to do, if it's currently in ProcessKeyEvent() | 447 // Nothing needs to do, if it's currently in ProcessKeyEvent() |
| 437 // handler, which will send commit text to webkit later. Otherwise, | 448 // handler, which will send commit text to webkit later. Otherwise, |
| 438 // we need send it here. | 449 // we need send it here. |
| 439 // It's possible that commit signal is fired without a key event, for | 450 // It's possible that commit signal is fired without a key event, for |
| 440 // example when user input via a voice or handwriting recognition software. | 451 // example when user input via a voice or handwriting recognition software. |
| 441 // In this case, the text must be committed directly. | 452 // In this case, the text must be committed directly. |
| 442 if (!is_in_key_event_handler_ && host_view_->GetRenderWidgetHost()) | 453 if (!is_in_key_event_handler_ && host_view_->GetRenderWidgetHost()) |
| 443 host_view_->GetRenderWidgetHost()->ImeConfirmComposition(text); | 454 host_view_->GetRenderWidgetHost()->ImeConfirmComposition(text); |
| 444 } | 455 } |
| 445 | 456 |
| 446 void GtkIMContextWrapper::HandlePreeditStart() { | 457 void GtkIMContextWrapper::HandlePreeditStart() { |
| 447 is_composing_text_ = true; | 458 is_composing_text_ = true; |
| 448 } | 459 } |
| 449 | 460 |
| 450 void GtkIMContextWrapper::HandlePreeditChanged(const gchar* text, | 461 void GtkIMContextWrapper::HandlePreeditChanged(const gchar* text, |
| 451 PangoAttrList* attrs, | 462 PangoAttrList* attrs, |
| 452 int cursor_position) { | 463 int cursor_position) { |
| 464 suppress_next_commit_ = false; |
| 453 // Don't set is_preedit_changed_ to false if there is no change, because | 465 // Don't set is_preedit_changed_ to false if there is no change, because |
| 454 // this handler might be called multiple times with the same data. | 466 // this handler might be called multiple times with the same data. |
| 455 is_preedit_changed_ = true; | 467 is_preedit_changed_ = true; |
| 456 preedit_text_.clear(); | 468 preedit_text_.clear(); |
| 457 preedit_underlines_.clear(); | 469 preedit_underlines_.clear(); |
| 458 preedit_selection_start_ = 0; | 470 preedit_selection_start_ = 0; |
| 459 preedit_selection_end_ = 0; | 471 preedit_selection_end_ = 0; |
| 460 | 472 |
| 461 ExtractCompositionInfo(text, attrs, cursor_position, &preedit_text_, | 473 ExtractCompositionInfo(text, attrs, cursor_position, &preedit_text_, |
| 462 &preedit_underlines_, &preedit_selection_start_, | 474 &preedit_underlines_, &preedit_selection_start_, |
| (...skipping 169 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 632 } while (pango_attr_iterator_next(iter)); | 644 } while (pango_attr_iterator_next(iter)); |
| 633 pango_attr_iterator_destroy(iter); | 645 pango_attr_iterator_destroy(iter); |
| 634 } | 646 } |
| 635 | 647 |
| 636 // Use a black thin underline by default. | 648 // Use a black thin underline by default. |
| 637 if (underlines->empty()) { | 649 if (underlines->empty()) { |
| 638 underlines->push_back( | 650 underlines->push_back( |
| 639 WebKit::WebCompositionUnderline(0, length, SK_ColorBLACK, false)); | 651 WebKit::WebCompositionUnderline(0, length, SK_ColorBLACK, false)); |
| 640 } | 652 } |
| 641 } | 653 } |
| OLD | NEW |