<commit_before>// Copyright (c) 2012 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/chromeos/input_method/input_method_engine_ibus.h"

#define XK_MISCELLANY
#include <X11/keysymdef.h>
#include <map>

#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/string_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/chromeos/input_method/input_method_configuration.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/ibus/ibus_client.h"
#include "chromeos/dbus/ibus/ibus_component.h"
#include "chromeos/dbus/ibus/ibus_engine_factory_service.h"
#include "chromeos/dbus/ibus/ibus_engine_service.h"
#include "chromeos/dbus/ibus/ibus_lookup_table.h"
#include "chromeos/dbus/ibus/ibus_property.h"
#include "chromeos/dbus/ibus/ibus_text.h"
#include "chromeos/ime/component_extension_ime_manager.h"
#include "chromeos/ime/extension_ime_util.h"
#include "chromeos/ime/ibus_keymap.h"
#include "chromeos/ime/input_method_manager.h"
#include "dbus/object_path.h"

namespace chromeos {
const char* kErrorNotActive = "IME is not active";
const char* kErrorWrongContext = "Context is not active";
const char* kCandidateNotFound = "Candidate not found";
const char* kEngineBusPrefix = "org.freedesktop.IBus.";
const char* kObjectPathPrefix = "/org/freedesktop/IBus/Engine/";

namespace {
const uint32 kIBusAltKeyMask = 1 << 3;
const uint32 kIBusCtrlKeyMask = 1 << 2;
const uint32 kIBusShiftKeyMask = 1 << 0;
const uint32 kIBusKeyReleaseMask = 1 << 30;
}

InputMethodEngineIBus::InputMethodEngineIBus()
    : focused_(false),
      active_(false),
      context_id_(0),
      next_context_id_(1),
      current_object_path_(0),
      aux_text_(new IBusText()),
      aux_text_visible_(false),
      observer_(NULL),
      preedit_text_(new IBusText()),
      preedit_cursor_(0),
      component_(new IBusComponent()),
      table_(new IBusLookupTable()),
      window_visible_(false),
      weak_ptr_factory_(this) {
}

InputMethodEngineIBus::~InputMethodEngineIBus() {
  input_method::GetInputMethodManager()->RemoveInputMethodExtension(ibus_id_);

  // Do not unset engine before removing input method extension, above function
  // may call reset function of engine object.
  // TODO(nona): Call Reset manually here and remove relevant code from
  //             InputMethodManager once ibus-daemon is gone. (crbug.com/158273)
  GetCurrentService()->UnsetEngine(this);
}

void InputMethodEngineIBus::Initialize(
    InputMethodEngine::Observer* observer,
    const char* engine_name,
    const char* extension_id,
    const char* engine_id,
    const char* description,
    const char* language,
    const std::vector<std::string>& layouts,
    std::string* error) {
  DCHECK(observer) << "Observer must not be null.";

  observer_ = observer;
  engine_id_ = engine_id;

  input_method::InputMethodManager* manager =
      input_method::GetInputMethodManager();
  ComponentExtensionIMEManager* comp_ext_ime_manager
      = manager->GetComponentExtensionIMEManager();

  if (comp_ext_ime_manager->IsInitialized() &&
      comp_ext_ime_manager->IsWhitelistedExtension(extension_id)) {
    ibus_id_ = comp_ext_ime_manager->GetId(extension_id, engine_id);
  } else {
    ibus_id_ = extension_ime_util::GetInputMethodID(extension_id, engine_id);
  }

  component_.reset(new IBusComponent());
  component_->set_name(std::string(kEngineBusPrefix) + std::string(engine_id));
  component_->set_description(description);
  component_->set_author(engine_name);

  chromeos::IBusComponent::EngineDescription engine_desc;
  engine_desc.engine_id = ibus_id_;
  engine_desc.display_name = description;
  engine_desc.description = description;
  engine_desc.language_code = language;
  engine_desc.author = ibus_id_;

  component_->mutable_engine_description()->push_back(engine_desc);
  manager->AddInputMethodExtension(ibus_id_, engine_name, layouts, language,
                                   this);
  // If connection is avaiable, register component. If there are no connection
  // to ibus-daemon, OnConnected callback will register component instead.
  if (IsConnected())
    RegisterComponent();
}

bool InputMethodEngineIBus::SetComposition(
    int context_id,
    const char* text,
    int selection_start,
    int selection_end,
    int cursor,
    const std::vector<SegmentInfo>& segments,
    std::string* error) {
  if (!active_) {
    *error = kErrorNotActive;
    return false;
  }
  if (context_id != context_id_ || context_id_ == -1) {
    *error = kErrorWrongContext;
    return false;
  }

  preedit_cursor_ = cursor;
  preedit_text_.reset(new IBusText());
  preedit_text_->set_text(text);

  // TODO: Add support for displaying selected text in the composition string.
  for (std::vector<SegmentInfo>::const_iterator segment = segments.begin();
       segment != segments.end(); ++segment) {
    IBusText::UnderlineAttribute underline;

    switch (segment->style) {
      case SEGMENT_STYLE_UNDERLINE:
        underline.type = IBusText::IBUS_TEXT_UNDERLINE_SINGLE;
        break;
      case SEGMENT_STYLE_DOUBLE_UNDERLINE:
        underline.type = IBusText::IBUS_TEXT_UNDERLINE_DOUBLE;
        break;<commit_msg>Support selection of composition text.<commit_after>// Copyright (c) 2012 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/chromeos/input_method/input_method_engine_ibus.h"

#define XK_MISCELLANY
#include <X11/keysymdef.h>
#include <map>

#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/string_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/chromeos/input_method/input_method_configuration.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/ibus/ibus_client.h"
#include "chromeos/dbus/ibus/ibus_component.h"
#include "chromeos/dbus/ibus/ibus_engine_factory_service.h"
#include "chromeos/dbus/ibus/ibus_engine_service.h"
#include "chromeos/dbus/ibus/ibus_lookup_table.h"
#include "chromeos/dbus/ibus/ibus_property.h"
#include "chromeos/dbus/ibus/ibus_text.h"
#include "chromeos/ime/component_extension_ime_manager.h"
#include "chromeos/ime/extension_ime_util.h"
#include "chromeos/ime/ibus_keymap.h"
#include "chromeos/ime/input_method_manager.h"
#include "dbus/object_path.h"

namespace chromeos {
const char* kErrorNotActive = "IME is not active";
const char* kErrorWrongContext = "Context is not active";
const char* kCandidateNotFound = "Candidate not found";
const char* kEngineBusPrefix = "org.freedesktop.IBus.";
const char* kObjectPathPrefix = "/org/freedesktop/IBus/Engine/";

namespace {
const uint32 kIBusAltKeyMask = 1 << 3;
const uint32 kIBusCtrlKeyMask = 1 << 2;
const uint32 kIBusShiftKeyMask = 1 << 0;
const uint32 kIBusKeyReleaseMask = 1 << 30;
}

InputMethodEngineIBus::InputMethodEngineIBus()
    : focused_(false),
      active_(false),
      context_id_(0),
      next_context_id_(1),
      current_object_path_(0),
      aux_text_(new IBusText()),
      aux_text_visible_(false),
      observer_(NULL),
      preedit_text_(new IBusText()),
      preedit_cursor_(0),
      component_(new IBusComponent()),
      table_(new IBusLookupTable()),
      window_visible_(false),
      weak_ptr_factory_(this) {
}

InputMethodEngineIBus::~InputMethodEngineIBus() {
  input_method::GetInputMethodManager()->RemoveInputMethodExtension(ibus_id_);

  // Do not unset engine before removing input method extension, above function
  // may call reset function of engine object.
  // TODO(nona): Call Reset manually here and remove relevant code from
  //             InputMethodManager once ibus-daemon is gone. (crbug.com/158273)
  GetCurrentService()->UnsetEngine(this);
}

void InputMethodEngineIBus::Initialize(
    InputMethodEngine::Observer* observer,
    const char* engine_name,
    const char* extension_id,
    const char* engine_id,
    const char* description,
    const char* language,
    const std::vector<std::string>& layouts,
    std::string* error) {
  DCHECK(observer) << "Observer must not be null.";

  observer_ = observer;
  engine_id_ = engine_id;

  input_method::InputMethodManager* manager =
      input_method::GetInputMethodManager();
  ComponentExtensionIMEManager* comp_ext_ime_manager
      = manager->GetComponentExtensionIMEManager();

  if (comp_ext_ime_manager->IsInitialized() &&
      comp_ext_ime_manager->IsWhitelistedExtension(extension_id)) {
    ibus_id_ = comp_ext_ime_manager->GetId(extension_id, engine_id);
  } else {
    ibus_id_ = extension_ime_util::GetInputMethodID(extension_id, engine_id);
  }

  component_.reset(new IBusComponent());
  component_->set_name(std::string(kEngineBusPrefix) + std::string(engine_id));
  component_->set_description(description);
  component_->set_author(engine_name);

  chromeos::IBusComponent::EngineDescription engine_desc;
  engine_desc.engine_id = ibus_id_;
  engine_desc.display_name = description;
  engine_desc.description = description;
  engine_desc.language_code = language;
  engine_desc.author = ibus_id_;

  component_->mutable_engine_description()->push_back(engine_desc);
  manager->AddInputMethodExtension(ibus_id_, engine_name, layouts, language,
                                   this);
  // If connection is avaiable, register component. If there are no connection
  // to ibus-daemon, OnConnected callback will register component instead.
  if (IsConnected())
    RegisterComponent();
}

bool InputMethodEngineIBus::SetComposition(
    int context_id,
    const char* text,
    int selection_start,
    int selection_end,
    int cursor,
    const std::vector<SegmentInfo>& segments,
    std::string* error) {
  if (!active_) {
    *error = kErrorNotActive;
    return false;
  }
  if (context_id != context_id_ || context_id_ == -1) {
    *error = kErrorWrongContext;
    return false;
  }

  preedit_cursor_ = cursor;
  preedit_text_.reset(new IBusText());
  preedit_text_->set_text(text);

  preedit_text_->mutable_selection_attributes()->clear();
  IBusText::SelectionAttribute selection;
  selection.start_index = selection_start;
  selection.end_index = selection_end;
  preedit_text_->mutable_selection_attributes()->push_back(selection);

  // TODO: Add support for displaying selected text in the composition string.
  for (std::vector<SegmentInfo>::const_iterator segment = segments.begin();
       segment != segments.end(); ++segment) {
    IBusText::UnderlineAttribute underline;

    switch (segment->style) {
      case SEGMENT_STYLE_UNDERLINE:
        underline.type = IBusText::IBUS_TEXT_UNDERLINE_SINGLE;
        break;
      case SEGMENT_STYLE_DOUBLE_UNDERLINE:
        underline.type = IBusText::IBUS_TEXT_UNDERLINE_DOUBLE;
        break;
{'fraction_non_alphanumeric': 0.1080440921927667, 'fraction_numerical': 0.006194770884576843, 'mean_word_length': 4.738630423418714, 'pattern_counts': {'":': 0, '<': 33, '<?xml version=': 0, '>': 45, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, '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': '1462614', 'n_tokens_mistral': 3538, 'n_tokens_neox': 3431, 'n_words': 707}