<commit_before>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/renderer_host/gtk_im_context_wrapper.h"

#include <gdk/gdk.h>
#include <gdk/gdkkeysyms.h>
#include <gtk/gtk.h>

#include "app/l10n_util.h"
#include "base/logging.h"
#include "base/string_util.h"
#include "chrome/app/chrome_dll_resource.h"
#include "chrome/browser/gtk/gtk_util.h"
#include "chrome/browser/gtk/menu_gtk.h"
#include "chrome/browser/renderer_host/render_widget_host.h"
#include "chrome/browser/renderer_host/render_widget_host_view_gtk.h"
#include "chrome/common/native_web_keyboard_event.h"
#include "chrome/common/render_messages.h"
#include "gfx/rect.h"
#include "grit/generated_resources.h"

GtkIMContextWrapper::GtkIMContextWrapper(RenderWidgetHostViewGtk* host_view)
    : host_view_(host_view),
      context_(gtk_im_multicontext_new()),
      context_simple_(gtk_im_context_simple_new()),
      is_focused_(false),
      is_composing_text_(false),
      is_enabled_(false),
      is_in_key_event_handler_(false),
      preedit_cursor_position_(0),
      is_preedit_changed_(false) {
  DCHECK(context_);
  DCHECK(context_simple_);

  // context_ and context_simple_ share the same callback handlers.
  // All data come from them are treated equally.
  // context_ is for full input method support.
  // context_simple_ is for supporting dead/compose keys when input method is
  // disabled by webkit, eg. in password input box.
  g_signal_connect(context_, "preedit_start",
                   G_CALLBACK(HandlePreeditStartThunk), this);
  g_signal_connect(context_, "preedit_end",
                   G_CALLBACK(HandlePreeditEndThunk), this);
  g_signal_connect(context_, "preedit_changed",
                   G_CALLBACK(HandlePreeditChangedThunk), this);
  g_signal_connect(context_, "commit",
                   G_CALLBACK(HandleCommitThunk), this);

  g_signal_connect(context_simple_, "preedit_start",
                   G_CALLBACK(HandlePreeditStartThunk), this);
  g_signal_connect(context_simple_, "preedit_end",
                   G_CALLBACK(HandlePreeditEndThunk), this);
  g_signal_connect(context_simple_, "preedit_changed",
                   G_CALLBACK(HandlePreeditChangedThunk), this);
  g_signal_connect(context_simple_, "commit",
                   G_CALLBACK(HandleCommitThunk), this);

  GtkWidget* widget = host_view->native_view();
  DCHECK(widget);

  g_signal_connect(widget, "realize",
                   G_CALLBACK(HandleHostViewRealizeThunk), this);
  g_signal_connect(widget, "unrealize",
                   G_CALLBACK(HandleHostViewUnrealizeThunk), this);

  // Set client window if the widget is already realized.
  HandleHostViewRealize(widget);
}

GtkIMContextWrapper::~GtkIMContextWrapper() {
  if (context_)
    g_object_unref(context_);
  if (context_simple_)
    g_object_unref(context_simple_);
}

void GtkIMContextWrapper::ProcessKeyEvent(GdkEventKey* event) {
  // Indicates preedit-changed and commit signal handlers that we are
  // processing a key event.
  is_in_key_event_handler_ = true;
  // Reset this flag so that we can know if preedit is changed after
  // processing this key event.
  is_preedit_changed_ = false;
  // Clear it so that we can know if something needs committing after
  // processing this key event.
  commit_text_.clear();

  // According to Document Object Model (DOM) Level 3 Events Specification
  // Appendix A: Keyboard events and key identifiers
  // http://www.w3.org/TR/DOM-Level-3-Events/keyset.html:
  // The event sequence would be:
  // 1. keydown
  // 2. textInput
  // 3. keyup
  //
  // So keydown must be sent to webkit before sending input method result,
  // while keyup must be sent afterwards.
  // However on Windows, if a keydown event has been processed by IME, its
  // virtual keycode will be changed to VK_PROCESSKEY(0xE5) before being sent
  // to application.
  // To emulate the windows behavior as much as possible, we need to send the
  // key event to the GtkIMContext object first, and decide whether or not to
  // send the original key event to webkit according to the result from IME.
  //
  // If IME is enabled by WebKit, this event will be dispatched to context_
  // to get full IME support. Otherwise it'll be dispatched to
  // context_simple_, so that dead/compose keys can still work.
  //
  // 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.)
  gboolean filtered = false;
  if (is_enabled_) {
    filtered = gtk_im_context_filter_keypress(context_, event);
  } else {
    filtered = gtk_im_context_filter_keypress(context_simple_, event);
  }

  NativeWebKeyboardEvent wke(event);

  // Send filtered keydown event before sending IME result.
  if (event->type == GDK_KEY_PRESS && filtered)
    ProcessFilteredKeyPressEvent(&wke);

  // Send IME results. In most cases, it's only available if the key event
  // is filtered by IME. But in rare cases, an unfiltered key event may also
  // generate IME results.
  // Any IME results generated by a unfiltered key down event must be sent
  // before the key down event, to avoid some tricky issues. For example,
  // when using latin-post input method, pressing 'a' then Backspace, may
  // generate following events in sequence:
  //  1. keydown 'a' (filtered)
  //  2. preedit changed to "a"
  //  3. keyup 'a' (unfiltered)
  //  4. keydown Backspace (unfiltered)
  //  5. commit "a"
  //  6. preedit end
  //  7. keyup Backspace (unfiltered)
  //
  // In this case, the input box will be in a strange state if keydown
  // Backspace is sent to webkit before commit "a" and preedit end.
  ProcessInputMethodResult(event, filtered);

  // Send unfiltered keydown and keyup events after sending IME result.
  if (event->type == GDK_KEY_PRESS && !filtered)
    ProcessUnfilteredKeyPressEvent(&wke);
  else if (event->type == GDK_KEY_RELEASE)
    host_view_->ForwardKeyboardEvent(wke);

  // End of key event processing.
  is_in_key_event_handler_ = false;
}

void GtkIMContextWrapper::UpdateStatus(int control,
                                       const gfx::Rect& caret_rect) {
  // The renderer has updated its IME status.
  // Control the GtkIMContext object according to this status.
  if (!context_ || !is_focused_)
    return;

  DCHECK(!is_in_key_event_handler_);

  // TODO(<EMAIL>): Following code causes a side effect:
  // When trying to move cursor from one text input box to another while
  // composition text is still not confirmed, following CompleteComposition()
  // calls will prevent the cursor from moving outside the first input box.
  if (control == IME_DISABLE) {
    if (is_enabled_) {
      CompleteComposition();
      gtk_im_context_reset(context_simple_);
      gtk_im_context_focus_out(context_);
      is_enabled_ = false;
    }
  } else {<commit_msg>When we get an IME_CANCEL_COMPOSITION event from the renderer, clear the IME state in GTK+.<commit_after>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/renderer_host/gtk_im_context_wrapper.h"

#include <gdk/gdk.h>
#include <gdk/gdkkeysyms.h>
#include <gtk/gtk.h>

#include "app/l10n_util.h"
#include "base/logging.h"
#include "base/string_util.h"
#include "chrome/app/chrome_dll_resource.h"
#include "chrome/browser/gtk/gtk_util.h"
#include "chrome/browser/gtk/menu_gtk.h"
#include "chrome/browser/renderer_host/render_widget_host.h"
#include "chrome/browser/renderer_host/render_widget_host_view_gtk.h"
#include "chrome/common/native_web_keyboard_event.h"
#include "chrome/common/render_messages.h"
#include "gfx/rect.h"
#include "grit/generated_resources.h"

GtkIMContextWrapper::GtkIMContextWrapper(RenderWidgetHostViewGtk* host_view)
    : host_view_(host_view),
      context_(gtk_im_multicontext_new()),
      context_simple_(gtk_im_context_simple_new()),
      is_focused_(false),
      is_composing_text_(false),
      is_enabled_(false),
      is_in_key_event_handler_(false),
      preedit_cursor_position_(0),
      is_preedit_changed_(false) {
  DCHECK(context_);
  DCHECK(context_simple_);

  // context_ and context_simple_ share the same callback handlers.
  // All data come from them are treated equally.
  // context_ is for full input method support.
  // context_simple_ is for supporting dead/compose keys when input method is
  // disabled by webkit, eg. in password input box.
  g_signal_connect(context_, "preedit_start",
                   G_CALLBACK(HandlePreeditStartThunk), this);
  g_signal_connect(context_, "preedit_end",
                   G_CALLBACK(HandlePreeditEndThunk), this);
  g_signal_connect(context_, "preedit_changed",
                   G_CALLBACK(HandlePreeditChangedThunk), this);
  g_signal_connect(context_, "commit",
                   G_CALLBACK(HandleCommitThunk), this);

  g_signal_connect(context_simple_, "preedit_start",
                   G_CALLBACK(HandlePreeditStartThunk), this);
  g_signal_connect(context_simple_, "preedit_end",
                   G_CALLBACK(HandlePreeditEndThunk), this);
  g_signal_connect(context_simple_, "preedit_changed",
                   G_CALLBACK(HandlePreeditChangedThunk), this);
  g_signal_connect(context_simple_, "commit",
                   G_CALLBACK(HandleCommitThunk), this);

  GtkWidget* widget = host_view->native_view();
  DCHECK(widget);

  g_signal_connect(widget, "realize",
                   G_CALLBACK(HandleHostViewRealizeThunk), this);
  g_signal_connect(widget, "unrealize",
                   G_CALLBACK(HandleHostViewUnrealizeThunk), this);

  // Set client window if the widget is already realized.
  HandleHostViewRealize(widget);
}

GtkIMContextWrapper::~GtkIMContextWrapper() {
  if (context_)
    g_object_unref(context_);
  if (context_simple_)
    g_object_unref(context_simple_);
}

void GtkIMContextWrapper::ProcessKeyEvent(GdkEventKey* event) {
  // Indicates preedit-changed and commit signal handlers that we are
  // processing a key event.
  is_in_key_event_handler_ = true;
  // Reset this flag so that we can know if preedit is changed after
  // processing this key event.
  is_preedit_changed_ = false;
  // Clear it so that we can know if something needs committing after
  // processing this key event.
  commit_text_.clear();

  // According to Document Object Model (DOM) Level 3 Events Specification
  // Appendix A: Keyboard events and key identifiers
  // http://www.w3.org/TR/DOM-Level-3-Events/keyset.html:
  // The event sequence would be:
  // 1. keydown
  // 2. textInput
  // 3. keyup
  //
  // So keydown must be sent to webkit before sending input method result,
  // while keyup must be sent afterwards.
  // However on Windows, if a keydown event has been processed by IME, its
  // virtual keycode will be changed to VK_PROCESSKEY(0xE5) before being sent
  // to application.
  // To emulate the windows behavior as much as possible, we need to send the
  // key event to the GtkIMContext object first, and decide whether or not to
  // send the original key event to webkit according to the result from IME.
  //
  // If IME is enabled by WebKit, this event will be dispatched to context_
  // to get full IME support. Otherwise it'll be dispatched to
  // context_simple_, so that dead/compose keys can still work.
  //
  // 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.)
  gboolean filtered = false;
  if (is_enabled_) {
    filtered = gtk_im_context_filter_keypress(context_, event);
  } else {
    filtered = gtk_im_context_filter_keypress(context_simple_, event);
  }

  NativeWebKeyboardEvent wke(event);

  // Send filtered keydown event before sending IME result.
  if (event->type == GDK_KEY_PRESS && filtered)
    ProcessFilteredKeyPressEvent(&wke);

  // Send IME results. In most cases, it's only available if the key event
  // is filtered by IME. But in rare cases, an unfiltered key event may also
  // generate IME results.
  // Any IME results generated by a unfiltered key down event must be sent
  // before the key down event, to avoid some tricky issues. For example,
  // when using latin-post input method, pressing 'a' then Backspace, may
  // generate following events in sequence:
  //  1. keydown 'a' (filtered)
  //  2. preedit changed to "a"
  //  3. keyup 'a' (unfiltered)
  //  4. keydown Backspace (unfiltered)
  //  5. commit "a"
  //  6. preedit end
  //  7. keyup Backspace (unfiltered)
  //
  // In this case, the input box will be in a strange state if keydown
  // Backspace is sent to webkit before commit "a" and preedit end.
  ProcessInputMethodResult(event, filtered);

  // Send unfiltered keydown and keyup events after sending IME result.
  if (event->type == GDK_KEY_PRESS && !filtered)
    ProcessUnfilteredKeyPressEvent(&wke);
  else if (event->type == GDK_KEY_RELEASE)
    host_view_->ForwardKeyboardEvent(wke);

  // End of key event processing.
  is_in_key_event_handler_ = false;
}

void GtkIMContextWrapper::UpdateStatus(int control,
                                       const gfx::Rect& caret_rect) {
  // The renderer has updated its IME status.
  // Control the GtkIMContext object according to this status.
  if (!context_ || !is_focused_)
    return;

  DCHECK(!is_in_key_event_handler_);

  // TODO(<EMAIL>): Following code causes a side effect:
  // When trying to move cursor from one text input box to another while
  // composition text is still not confirmed, following CompleteComposition()
  // calls will prevent the cursor from moving outside the first input box.
  if (control == IME_DISABLE) {
    if (is_enabled_) {
      CompleteComposition();
      gtk_im_context_reset(context_simple_);
      gtk_im_context_focus_out(context_);
      is_enabled_ = false;
    }
  } else if (control == IME_CANCEL_COMPOSITION) {
    preedit_text_.clear();
    preedit_cursor_position_ = 0;
    gtk_im_context_reset(context_simple_);
    gtk_im_context_reset(context_);
  } else {
{'fraction_non_alphanumeric': 0.09041554959785522, 'fraction_numerical': 0.0030160857908847183, 'mean_word_length': 3.516041162227603, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 21, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3134880', 'n_tokens_mistral': 4627, 'n_tokens_neox': 4493, 'n_words': 1443}