File fcitx-mozc-1.6.1187.102.3.patch of Package mozc
diff --git a/src/unix/fcitx/eim.cc b/src/unix/fcitx/eim.cc
new file mode 100644
index 0000000..eec6fde
--- /dev/null
+++ b/src/unix/fcitx/eim.cc
@@ -0,0 +1,201 @@
+// Copyright 2012~2013, Weng Xuetian <wengxt@gmail.com>
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// workaround
+#define _FCITX_LOG_H_
+
+#include <fcitx/instance.h>
+#include <fcitx/ime.h>
+#include <fcitx/hook.h>
+#include <fcitx/module.h>
+#include <fcitx-config/xdg.h>
+#include "fcitx_mozc.h"
+#include "mozc_connection.h"
+#include "mozc_response_parser.h"
+
+typedef struct _FcitxMozcState {
+ mozc::fcitx::FcitxMozc* mozc;
+} FcitxMozcState;
+
+
+static void* FcitxMozcCreate(FcitxInstance* instance);
+static void FcitxMozcDestroy(void *arg);
+static boolean FcitxMozcInit(void *arg); /**< FcitxMozcInit */
+static void FcitxMozcResetIM(void *arg); /**< FcitxMozcResetIM */
+static void FcitxMozcReset(void *arg); /**< FcitxMozcResetIM */
+static INPUT_RETURN_VALUE FcitxMozcDoInput(void *arg, FcitxKeySym, unsigned int); /**< FcitxMozcDoInput */
+static INPUT_RETURN_VALUE FcitxMozcDoReleaseInput(void *arg, FcitxKeySym, unsigned int); /**< FcitxMozcDoInput */
+static void FcitxMozcSave(void *arg); /**< FcitxMozcSave */
+static void FcitxMozcReloadConfig(void *arg); /**< FcitxMozcReloadConfig */
+
+extern "C" {
+
+FCITX_EXPORT_API
+FcitxIMClass ime = {
+ FcitxMozcCreate,
+ FcitxMozcDestroy
+};
+FCITX_EXPORT_API
+int ABI_VERSION = FCITX_ABI_VERSION;
+
+}
+
+static inline bool CheckLayout(FcitxInstance* instance)
+{
+ char *layout = NULL, *variant = NULL;
+ FcitxModuleFunctionArg args;
+ args.args[0] = &layout;
+ args.args[1] = &variant;
+ bool layout_is_jp = false;
+ FcitxModuleInvokeFunctionByName(instance, "fcitx-xkb", 1, args);
+ if (layout && strcmp(layout, "jp") == 0)
+ layout_is_jp = true;
+
+ fcitx_utils_free(layout);
+ fcitx_utils_free(variant);
+
+
+ return layout_is_jp;
+}
+
+static void* FcitxMozcCreate(FcitxInstance* instance)
+{
+ FcitxMozcState* mozcState = (FcitxMozcState*) fcitx_utils_malloc0(sizeof(FcitxMozcState));
+ bindtextdomain("fcitx-mozc", LOCALEDIR);
+
+ mozcState->mozc = new mozc::fcitx::FcitxMozc(
+ instance,
+ mozc::fcitx::MozcConnection::CreateMozcConnection(),
+ new mozc::fcitx::MozcResponseParser
+ );
+
+ mozcState->mozc->SetCompositionMode(mozc::commands::HIRAGANA);
+
+ FcitxIMEventHook hk;
+ hk.arg = mozcState;
+ hk.func = FcitxMozcReset;
+
+ FcitxInstanceRegisterResetInputHook(instance, hk);
+
+ FcitxIMIFace iface;
+ memset(&iface, 0, sizeof(FcitxIMIFace));
+ iface.Init = FcitxMozcInit;
+ iface.ResetIM = FcitxMozcResetIM;
+ iface.DoInput = FcitxMozcDoInput;
+ iface.DoReleaseInput = FcitxMozcDoReleaseInput;
+ iface.ReloadConfig = FcitxMozcReloadConfig;
+ iface.Save = FcitxMozcSave;
+
+
+ FcitxInstanceRegisterIMv2(
+ instance,
+ mozcState,
+ "mozc",
+ "Mozc",
+ mozcState->mozc->GetIconFile("mozc.png").c_str(),
+ iface,
+ 1,
+ "ja"
+ );
+
+ return mozcState;
+}
+
+static void FcitxMozcDestroy(void *arg)
+{
+ FcitxMozcState* mozcState = (FcitxMozcState*) arg;
+ delete mozcState->mozc;
+ free(mozcState);
+}
+
+INPUT_RETURN_VALUE FcitxMozcDoInput(void* arg, FcitxKeySym _sym, unsigned int _state)
+{
+ FcitxMozcState* mozcState = (FcitxMozcState*) arg;
+ FcitxInstance* instance = mozcState->mozc->GetInstance();
+ FcitxInputState* input = FcitxInstanceGetInputState(mozcState->mozc->GetInstance());
+ FCITX_UNUSED(_sym);
+ FCITX_UNUSED(_state);
+ FcitxKeySym sym = (FcitxKeySym) FcitxInputStateGetKeySym(input);
+ uint32 keycode = FcitxInputStateGetKeyCode(input);
+ uint32 state = FcitxInputStateGetKeyState(input);
+ bool result = mozcState->mozc->process_key_event(sym, keycode, state, CheckLayout(instance), false);
+ if (!result)
+ return IRV_TO_PROCESS;
+ else
+ return IRV_DISPLAY_CANDWORDS;
+}
+
+INPUT_RETURN_VALUE FcitxMozcDoReleaseInput(void* arg, FcitxKeySym _sym, unsigned int _state)
+{
+ FcitxMozcState* mozcState = (FcitxMozcState*) arg;
+ FcitxInstance* instance = mozcState->mozc->GetInstance();
+ FcitxInputState* input = FcitxInstanceGetInputState(mozcState->mozc->GetInstance());
+ FCITX_UNUSED(_sym);
+ FCITX_UNUSED(_state);
+ FcitxKeySym sym = (FcitxKeySym) FcitxInputStateGetKeySym(input);
+ uint32 keycode = FcitxInputStateGetKeyCode(input);
+ uint32 state = FcitxInputStateGetKeyState(input);
+ bool result = mozcState->mozc->process_key_event(sym, keycode, state, CheckLayout(instance), true);
+ if (!result)
+ return IRV_TO_PROCESS;
+ else
+ return IRV_DISPLAY_CANDWORDS;
+}
+
+
+
+boolean FcitxMozcInit(void* arg)
+{
+ FcitxMozcState* mozcState = (FcitxMozcState*) arg;
+ mozcState->mozc->init();
+ return true;
+}
+
+void FcitxMozcReloadConfig(void* arg)
+{
+
+}
+
+void FcitxMozcSave(void* arg)
+{
+ FCITX_UNUSED(arg);
+}
+
+void FcitxMozcResetIM(void* arg)
+{
+ FcitxMozcState* mozcState = (FcitxMozcState*) arg;
+ mozcState->mozc->resetim();
+}
+
+void FcitxMozcReset(void* arg)
+{
+ FcitxMozcState* mozcState = (FcitxMozcState*) arg;
+ mozcState->mozc->reset();
+
+}
diff --git a/src/unix/fcitx/fcitx-mozc.conf b/src/unix/fcitx/fcitx-mozc.conf
new file mode 100644
index 0000000..bcce806
--- /dev/null
+++ b/src/unix/fcitx/fcitx-mozc.conf
@@ -0,0 +1,10 @@
+[Addon]
+Name=fcitx-mozc
+GeneralName=Mozc
+Comment=Mozc support for Fcitx
+Category=InputMethod
+Enabled=True
+Library=fcitx-mozc.so
+Type=SharedLibrary
+SubConfig=
+IMRegisterMethod=ConfigFile
diff --git a/src/unix/fcitx/fcitx.gyp b/src/unix/fcitx/fcitx.gyp
new file mode 100644
index 0000000..c451ae1
--- /dev/null
+++ b/src/unix/fcitx/fcitx.gyp
@@ -0,0 +1,102 @@
+#
+# Copyright (c) 2010-2012 fcitx Project http://code.google.com/p/fcitx/
+#
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. Neither the name of authors nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+{
+ 'variables': {
+ 'relative_dir': 'unix/fcitx',
+ 'gen_out_dir': '<(SHARED_INTERMEDIATE_DIR)/<(relative_dir)',
+ 'pkg_config_libs': [
+ 'fcitx',
+ 'fcitx-config',
+ 'fcitx-utils',
+ ],
+ 'fcitx_dep_include_dirs': [
+ ],
+ 'fcitx_dependencies': [
+ '../../base/base.gyp:base',
+ '../../client/client.gyp:client',
+ '../../ipc/ipc.gyp:ipc',
+ '../../session/session_base.gyp:ime_switch_util',
+ '../../session/session_base.gyp:session_protocol',
+ ],
+ 'fcitx_defines': [
+ 'LOCALEDIR="<!@(fcitx4-config --prefix)/share/locale/"',
+ ]
+ },
+ 'targets': [
+ {
+ 'target_name': 'gen_fcitx_mozc_i18n',
+ 'type': 'none',
+ 'actions': [
+ {
+ 'action_name': 'gen_fcitx_mozc_i18n',
+ 'inputs': [
+ './gen_fcitx_mozc_i18n.sh'
+ ],
+ 'outputs': [
+ '<(gen_out_dir)/po/zh_CN.mo',
+ '<(gen_out_dir)/po/zh_TW.mo',
+ '<(gen_out_dir)/po/ja.mo',
+ ],
+ 'action': [
+ 'sh',
+ './gen_fcitx_mozc_i18n.sh',
+ '<(gen_out_dir)/po',
+ ],
+ }],
+ },
+ {
+ 'target_name': 'fcitx-mozc',
+ 'product_prefix': '',
+ 'type': 'loadable_module',
+ 'sources': [
+ 'fcitx_mozc.cc',
+ 'fcitx_key_translator.cc',
+ 'fcitx_key_event_handler.cc',
+ 'mozc_connection.cc',
+ 'mozc_response_parser.cc',
+ 'eim.cc',
+ ],
+ 'dependencies': [
+ '<@(fcitx_dependencies)',
+ 'gen_fcitx_mozc_i18n',
+ ],
+ 'cflags': [
+ '<!@(pkg-config --cflags <@(pkg_config_libs))',
+ ],
+ 'include_dirs': [
+ '<@(fcitx_dep_include_dirs)',
+ ],
+ 'defines': [
+ '<@(fcitx_defines)',
+ ],
+ },
+ ],
+}
diff --git a/src/unix/fcitx/fcitx_key_event_handler.cc b/src/unix/fcitx/fcitx_key_event_handler.cc
new file mode 100644
index 0000000..659e2c0
--- /dev/null
+++ b/src/unix/fcitx/fcitx_key_event_handler.cc
@@ -0,0 +1,245 @@
+// Copyright 2010-2012, Google Inc.
+// Copyright 2012~2013, Weng Xuetian <wengxt@gmail.com>
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// workaround
+#define _FCITX_LOG_H_
+
+#include "unix/fcitx/fcitx_key_event_handler.h"
+
+#include <map>
+
+#include "base/singleton.h"
+
+namespace mozc {
+namespace fcitx {
+
+namespace {
+// TODO(hsumita): Removes this class, and moves |data_| into member
+// variables of KeyEventhandler.
+class AdditionalModifiersData {
+ public:
+ AdditionalModifiersData() {
+ data_[commands::KeyEvent::LEFT_ALT] = commands::KeyEvent::ALT;
+ data_[commands::KeyEvent::RIGHT_ALT] = commands::KeyEvent::ALT;
+ data_[commands::KeyEvent::LEFT_CTRL] = commands::KeyEvent::CTRL;
+ data_[commands::KeyEvent::RIGHT_CTRL] = commands::KeyEvent::CTRL;
+ data_[commands::KeyEvent::LEFT_SHIFT] = commands::KeyEvent::SHIFT;
+ data_[commands::KeyEvent::RIGHT_SHIFT] = commands::KeyEvent::SHIFT;
+ }
+ const map<uint32, commands::KeyEvent::ModifierKey> &data() {
+ return data_;
+ }
+
+ private:
+ map<uint32, commands::KeyEvent::ModifierKey> data_;
+};
+
+// TODO(hsumita): Moves this function into member functions of
+// KeyEventHandler.
+void AddAdditionalModifiers(
+ set<commands::KeyEvent::ModifierKey> *modifier_keys_set) {
+ DCHECK(modifier_keys_set);
+
+ const map<uint32, commands::KeyEvent::ModifierKey> &data =
+ Singleton<AdditionalModifiersData>::get()->data();
+
+ // Adds MODIFIER if there are (LEFT|RIGHT)_MODIFIER like LEFT_SHIFT.
+ for (set<commands::KeyEvent::ModifierKey>::const_iterator it =
+ modifier_keys_set->begin(); it != modifier_keys_set->end(); ++it) {
+ map<uint32, commands::KeyEvent::ModifierKey>::const_iterator item =
+ data.find(*it);
+ if (item != data.end()) {
+ modifier_keys_set->insert(item->second);
+ }
+ }
+}
+
+bool IsModifierToBeSentOnKeyUp(const commands::KeyEvent &key_event) {
+ if (key_event.modifier_keys_size() == 0) {
+ return false;
+ }
+
+ if (key_event.modifier_keys_size() == 1 &&
+ key_event.modifier_keys(0) == commands::KeyEvent::CAPS) {
+ return false;
+ }
+
+ return true;
+}
+} // namespace
+
+KeyEventHandler::KeyEventHandler() : key_translator_(new KeyTranslator) {
+ Clear();
+}
+
+bool KeyEventHandler::GetKeyEvent(
+ FcitxKeySym keyval, uint32 keycode, uint32 modifiers,
+ config::Config::PreeditMethod preedit_method,
+ bool layout_is_jp, bool is_key_up, commands::KeyEvent *key) {
+ DCHECK(key);
+ key->Clear();
+
+ if (!key_translator_->Translate(
+ keyval, keycode, modifiers, preedit_method, layout_is_jp, key)) {
+ LOG(ERROR) << "Translate failed";
+ return false;
+ }
+
+ return ProcessModifiers(is_key_up, keyval, key);
+}
+
+void KeyEventHandler::Clear() {
+ is_non_modifier_key_pressed_ = false;
+ currently_pressed_modifiers_.clear();
+ modifiers_to_be_sent_.clear();
+}
+
+bool KeyEventHandler::ProcessModifiers(bool is_key_up, uint32 keyval,
+ commands::KeyEvent *key_event) {
+ // Manage modifier key event.
+ // Modifier key event is sent on key up if non-modifier key has not been
+ // pressed since key down of modifier keys and no modifier keys are pressed
+ // anymore.
+ // Following examples are expected behaviors.
+ //
+ // E.g.) Shift key is special. If Shift + printable key is pressed, key event
+ // does NOT have shift modifiers. It is handled by KeyTranslator class.
+ // <Event from ibus> <Event to server>
+ // Shift down | None
+ // "a" down | A
+ // "a" up | None
+ // Shift up | None
+ //
+ // E.g.) Usual key is sent on key down. Modifier keys are not sent if usual
+ // key is sent.
+ // <Event from ibus> <Event to server>
+ // Ctrl down | None
+ // "a" down | Ctrl+a
+ // "a" up | None
+ // Ctrl up | None
+ //
+ // E.g.) Modifier key is sent on key up.
+ // <Event from ibus> <Event to server>
+ // Shift down | None
+ // Shift up | Shift
+ //
+ // E.g.) Multiple modifier keys are sent on the last key up.
+ // <Event from ibus> <Event to server>
+ // Shift down | None
+ // Control down | None
+ // Shift up | None
+ // Control up | Control+Shift
+ //
+ // Essentialy we cannot handle modifier key evnet perfectly because
+ // - We cannot get current keyboard status with ibus. If some modifiers
+ // are pressed or released without focusing the target window, we
+ // cannot handle it.
+ // E.g.)
+ // <Event from ibus> <Event to server>
+ // Ctrl down | None
+ // (focuses out, Ctrl up, focuses in)
+ // Shift down | None
+ // Shift up | None (But we should send Shift key)
+ // To avoid a inconsistent state as much as possible, we clear states
+ // when key event without modifier keys is sent.
+
+ const bool is_modifier_only =
+ !(key_event->has_key_code() || key_event->has_special_key());
+
+ // We may get only up/down key event when a user moves a focus.
+ // This code handles such situation as much as possible.
+ // This code has a bug. If we send Shift + 'a', KeyTranslator removes a shift
+ // modifier and converts 'a' to 'A'. This codes does NOT consider these
+ // situation since we don't have enough data to handle it.
+ // TODO(hsumita): Moves the logic about a handling of Shift or Caps keys from
+ // KeyTranslator to MozcEngine.
+ if (key_event->modifier_keys_size() == 0) {
+ Clear();
+ }
+
+ if (!currently_pressed_modifiers_.empty() && !is_modifier_only) {
+ is_non_modifier_key_pressed_ = true;
+ }
+ if (is_non_modifier_key_pressed_) {
+ modifiers_to_be_sent_.clear();
+ }
+
+ if (is_key_up) {
+ currently_pressed_modifiers_.erase(keyval);
+ if (!is_modifier_only) {
+ return false;
+ }
+ if (!currently_pressed_modifiers_.empty() ||
+ modifiers_to_be_sent_.empty()) {
+ is_non_modifier_key_pressed_ = false;
+ return false;
+ }
+ if (is_non_modifier_key_pressed_) {
+ return false;
+ }
+ DCHECK(!is_non_modifier_key_pressed_);
+
+ // Modifier key event fires
+ key_event->mutable_modifier_keys()->Clear();
+ for (set<commands::KeyEvent::ModifierKey>::const_iterator it =
+ modifiers_to_be_sent_.begin();
+ it != modifiers_to_be_sent_.end();
+ ++it) {
+ key_event->add_modifier_keys(*it);
+ }
+ modifiers_to_be_sent_.clear();
+ } else if (is_modifier_only) {
+ // TODO(hsumita): Supports a key sequence below.
+ // - Ctrl down
+ // - a down
+ // - Alt down
+ // We should add Alt key to |currently_pressed_modifiers|, but current
+ // implementation does NOT do it.
+ if (currently_pressed_modifiers_.empty() ||
+ !modifiers_to_be_sent_.empty()) {
+ for (size_t i = 0; i < key_event->modifier_keys_size(); ++i) {
+ modifiers_to_be_sent_.insert(key_event->modifier_keys(i));
+ }
+ AddAdditionalModifiers(&modifiers_to_be_sent_);
+ }
+ currently_pressed_modifiers_.insert(keyval);
+ return false;
+ }
+
+ // Clear modifier data just in case if |key| has no modifier keys.
+ if (!IsModifierToBeSentOnKeyUp(*key_event)) {
+ Clear();
+ }
+
+ return true;
+}
+
+} // namespace ibus
+} // namespace mozc
diff --git a/src/unix/fcitx/fcitx_key_event_handler.h b/src/unix/fcitx/fcitx_key_event_handler.h
new file mode 100644
index 0000000..bc3043b
--- /dev/null
+++ b/src/unix/fcitx/fcitx_key_event_handler.h
@@ -0,0 +1,79 @@
+// Copyright 2010-2012, Google Inc.
+// Copyright 2012~2013, Weng Xuetian <wengxt@gmail.com>
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef MOZC_UNIX_FCITX_KEY_EVENT_HANDLER_H_
+#define MOZC_UNIX_FCITX_KEY_EVENT_HANDLER_H_
+
+#include <set>
+
+#include "base/port.h"
+#include "base/scoped_ptr.h"
+#include "config/config.pb.h"
+#include "session/commands.pb.h"
+#include "unix/fcitx/fcitx_key_translator.h"
+
+namespace mozc {
+namespace fcitx {
+
+class KeyEventHandler {
+ public:
+ KeyEventHandler();
+
+ // Converts a key event came from fcitx to commands::KeyEvent. This is a
+ // stateful method. It stores modifier keys states since ibus doesn't send
+ // an enough information about the modifier keys.
+ bool GetKeyEvent(FcitxKeySym keyval, uint32 keycode, uint32 modifiers,
+ config::Config::PreeditMethod preedit_method,
+ bool layout_is_jp, bool is_key_up, commands::KeyEvent *key);
+
+ // Clears states.
+ void Clear();
+
+ private:
+
+ // Manages modifier keys. Returns false if it should not be sent to server.
+ bool ProcessModifiers(bool is_key_up, uint32 keyval,
+ commands::KeyEvent *key_event);
+
+ scoped_ptr<KeyTranslator> key_translator_;
+ // Non modifier key is pressed or not after all keys are released.
+ bool is_non_modifier_key_pressed_;
+ // Currently pressed modifier keys. It is set of keyval.
+ set<uint32> currently_pressed_modifiers_;
+ // Pending modifier keys.
+ set<commands::KeyEvent::ModifierKey> modifiers_to_be_sent_;
+
+ DISALLOW_COPY_AND_ASSIGN(KeyEventHandler);
+};
+
+} // namespace fcitx
+} // namespace mozc
+
+#endif // MOZC_UNIX_FCITX_KEY_EVENT_HANDLER_H_
diff --git a/src/unix/fcitx/fcitx_key_translator.cc b/src/unix/fcitx/fcitx_key_translator.cc
new file mode 100644
index 0000000..bfeca8c
--- /dev/null
+++ b/src/unix/fcitx/fcitx_key_translator.cc
@@ -0,0 +1,527 @@
+// Copyright 2010-2012, Google Inc.
+// Copyright 2012~2013, Weng Xuetian <wengxt@gmail.com>
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// workaround
+#define _FCITX_LOG_H_
+
+#include "unix/fcitx/fcitx_key_translator.h"
+
+#include "base/logging.h"
+
+namespace {
+
+const struct SpecialKeyMap {
+ uint32 from;
+ mozc::commands::KeyEvent::SpecialKey to;
+} special_key_map[] = {
+ {FcitxKey_space, mozc::commands::KeyEvent::SPACE},
+ {FcitxKey_Return, mozc::commands::KeyEvent::ENTER},
+ {FcitxKey_Left, mozc::commands::KeyEvent::LEFT},
+ {FcitxKey_Right, mozc::commands::KeyEvent::RIGHT},
+ {FcitxKey_Up, mozc::commands::KeyEvent::UP},
+ {FcitxKey_Down, mozc::commands::KeyEvent::DOWN},
+ {FcitxKey_Escape, mozc::commands::KeyEvent::ESCAPE},
+ {FcitxKey_Delete, mozc::commands::KeyEvent::DEL},
+ {FcitxKey_BackSpace, mozc::commands::KeyEvent::BACKSPACE},
+ {FcitxKey_Insert, mozc::commands::KeyEvent::INSERT},
+ {FcitxKey_Henkan, mozc::commands::KeyEvent::HENKAN},
+ {FcitxKey_Muhenkan, mozc::commands::KeyEvent::MUHENKAN},
+ {FcitxKey_Hiragana, mozc::commands::KeyEvent::KANA},
+ {FcitxKey_Hiragana_Katakana, mozc::commands::KeyEvent::KANA},
+ {FcitxKey_Katakana, mozc::commands::KeyEvent::KATAKANA},
+ {FcitxKey_Zenkaku, mozc::commands::KeyEvent::HANKAKU},
+ {FcitxKey_Hankaku, mozc::commands::KeyEvent::HANKAKU},
+ {FcitxKey_Zenkaku_Hankaku, mozc::commands::KeyEvent::HANKAKU},
+ {FcitxKey_Eisu_toggle, mozc::commands::KeyEvent::EISU},
+ {FcitxKey_Home, mozc::commands::KeyEvent::HOME},
+ {FcitxKey_End, mozc::commands::KeyEvent::END},
+ {FcitxKey_Tab, mozc::commands::KeyEvent::TAB},
+ {FcitxKey_F1, mozc::commands::KeyEvent::F1},
+ {FcitxKey_F2, mozc::commands::KeyEvent::F2},
+ {FcitxKey_F3, mozc::commands::KeyEvent::F3},
+ {FcitxKey_F4, mozc::commands::KeyEvent::F4},
+ {FcitxKey_F5, mozc::commands::KeyEvent::F5},
+ {FcitxKey_F6, mozc::commands::KeyEvent::F6},
+ {FcitxKey_F7, mozc::commands::KeyEvent::F7},
+ {FcitxKey_F8, mozc::commands::KeyEvent::F8},
+ {FcitxKey_F9, mozc::commands::KeyEvent::F9},
+ {FcitxKey_F10, mozc::commands::KeyEvent::F10},
+ {FcitxKey_F11, mozc::commands::KeyEvent::F11},
+ {FcitxKey_F12, mozc::commands::KeyEvent::F12},
+ {FcitxKey_F13, mozc::commands::KeyEvent::F13},
+ {FcitxKey_F14, mozc::commands::KeyEvent::F14},
+ {FcitxKey_F15, mozc::commands::KeyEvent::F15},
+ {FcitxKey_F16, mozc::commands::KeyEvent::F16},
+ {FcitxKey_F17, mozc::commands::KeyEvent::F17},
+ {FcitxKey_F18, mozc::commands::KeyEvent::F18},
+ {FcitxKey_F19, mozc::commands::KeyEvent::F19},
+ {FcitxKey_F20, mozc::commands::KeyEvent::F20},
+ {FcitxKey_F21, mozc::commands::KeyEvent::F21},
+ {FcitxKey_F22, mozc::commands::KeyEvent::F22},
+ {FcitxKey_F23, mozc::commands::KeyEvent::F23},
+ {FcitxKey_F24, mozc::commands::KeyEvent::F24},
+ {FcitxKey_Page_Up, mozc::commands::KeyEvent::PAGE_UP},
+ {FcitxKey_Page_Down, mozc::commands::KeyEvent::PAGE_DOWN},
+
+ // Keypad (10-key).
+ {FcitxKey_KP_0, mozc::commands::KeyEvent::NUMPAD0},
+ {FcitxKey_KP_1, mozc::commands::KeyEvent::NUMPAD1},
+ {FcitxKey_KP_2, mozc::commands::KeyEvent::NUMPAD2},
+ {FcitxKey_KP_3, mozc::commands::KeyEvent::NUMPAD3},
+ {FcitxKey_KP_4, mozc::commands::KeyEvent::NUMPAD4},
+ {FcitxKey_KP_5, mozc::commands::KeyEvent::NUMPAD5},
+ {FcitxKey_KP_6, mozc::commands::KeyEvent::NUMPAD6},
+ {FcitxKey_KP_7, mozc::commands::KeyEvent::NUMPAD7},
+ {FcitxKey_KP_8, mozc::commands::KeyEvent::NUMPAD8},
+ {FcitxKey_KP_9, mozc::commands::KeyEvent::NUMPAD9},
+ {FcitxKey_KP_Equal, mozc::commands::KeyEvent::EQUALS}, // [=]
+ {FcitxKey_KP_Multiply, mozc::commands::KeyEvent::MULTIPLY}, // [*]
+ {FcitxKey_KP_Add, mozc::commands::KeyEvent::ADD}, // [+]
+ {FcitxKey_KP_Separator, mozc::commands::KeyEvent::SEPARATOR}, // enter
+ {FcitxKey_KP_Subtract, mozc::commands::KeyEvent::SUBTRACT}, // [-]
+ {FcitxKey_KP_Decimal, mozc::commands::KeyEvent::DECIMAL}, // [.]
+ {FcitxKey_KP_Divide, mozc::commands::KeyEvent::DIVIDE}, // [/]
+ {FcitxKey_KP_Space, mozc::commands::KeyEvent::SPACE},
+ {FcitxKey_KP_Tab, mozc::commands::KeyEvent::TAB},
+ {FcitxKey_KP_Enter, mozc::commands::KeyEvent::ENTER},
+ {FcitxKey_KP_Home, mozc::commands::KeyEvent::HOME},
+ {FcitxKey_KP_Left, mozc::commands::KeyEvent::LEFT},
+ {FcitxKey_KP_Up, mozc::commands::KeyEvent::UP},
+ {FcitxKey_KP_Right, mozc::commands::KeyEvent::RIGHT},
+ {FcitxKey_KP_Down, mozc::commands::KeyEvent::DOWN},
+ {FcitxKey_KP_Page_Up, mozc::commands::KeyEvent::PAGE_UP},
+ {FcitxKey_KP_Page_Down, mozc::commands::KeyEvent::PAGE_DOWN},
+ {FcitxKey_KP_End, mozc::commands::KeyEvent::END},
+ {FcitxKey_KP_Delete, mozc::commands::KeyEvent::DEL},
+ {FcitxKey_KP_Insert, mozc::commands::KeyEvent::INSERT},
+ {FcitxKey_Caps_Lock, mozc::commands::KeyEvent::CAPS_LOCK},
+
+ // This key event is for hangul IME. Do not handle this key event on Japanese
+ // IME.
+ {FcitxKey_Hangul_Hanja, mozc::commands::KeyEvent::HANJA},
+
+ // Shift+TAB.
+ {FcitxKey_ISO_Left_Tab, mozc::commands::KeyEvent::TAB},
+
+ // TODO(mazda): Handle following keys?
+ // - FcitxKey_Kana_Lock? FcitxKey_KEY_Kana_Shift?
+};
+
+const struct ModifierKeyMap {
+ uint32 from;
+ mozc::commands::KeyEvent::ModifierKey to;
+} modifier_key_map[] = {
+ {FcitxKey_Shift_L, mozc::commands::KeyEvent::LEFT_SHIFT},
+ {FcitxKey_Shift_R, mozc::commands::KeyEvent::RIGHT_SHIFT},
+ {FcitxKey_Control_L, mozc::commands::KeyEvent::LEFT_CTRL},
+ {FcitxKey_Control_R, mozc::commands::KeyEvent::RIGHT_CTRL},
+ {FcitxKey_Alt_L, mozc::commands::KeyEvent::LEFT_ALT},
+ {FcitxKey_Alt_R, mozc::commands::KeyEvent::RIGHT_ALT},
+ {FcitxKeyState_CapsLock, mozc::commands::KeyEvent::CAPS},
+};
+
+const struct ModifierMaskMap {
+ uint32 from;
+ mozc::commands::KeyEvent::ModifierKey to;
+} modifier_mask_map[] = {
+ {FcitxKeyState_Shift, mozc::commands::KeyEvent::SHIFT},
+ {FcitxKeyState_Ctrl, mozc::commands::KeyEvent::CTRL},
+ {FcitxKeyState_Alt, mozc::commands::KeyEvent::ALT},
+};
+
+// TODO(team): Add kana_map_dv to support Dvoraklayout.
+const struct KanaMap {
+ uint32 code;
+ const char *no_shift;
+ const char *shift;
+} kana_map_jp[] = {
+ { '1' , "\xe3\x81\xac", "\xe3\x81\xac" }, // "ぬ", "ぬ"
+ { '!' , "\xe3\x81\xac", "\xe3\x81\xac" }, // "ぬ", "ぬ"
+ { '2' , "\xe3\x81\xb5", "\xe3\x81\xb5" }, // "ふ", "ふ"
+ { '\"', "\xe3\x81\xb5", "\xe3\x81\xb5" }, // "ふ", "ふ"
+ { '3' , "\xe3\x81\x82", "\xe3\x81\x81" }, // "あ", "ぁ"
+ { '#' , "\xe3\x81\x82", "\xe3\x81\x81" }, // "あ", "ぁ"
+ { '4' , "\xe3\x81\x86", "\xe3\x81\x85" }, // "う", "ぅ"
+ { '$' , "\xe3\x81\x86", "\xe3\x81\x85" }, // "う", "ぅ"
+ { '5' , "\xe3\x81\x88", "\xe3\x81\x87" }, // "え", "ぇ"
+ { '%' , "\xe3\x81\x88", "\xe3\x81\x87" }, // "え", "ぇ"
+ { '6' , "\xe3\x81\x8a", "\xe3\x81\x89" }, // "お", "ぉ"
+ { '&' , "\xe3\x81\x8a", "\xe3\x81\x89" }, // "お", "ぉ"
+ { '7' , "\xe3\x82\x84", "\xe3\x82\x83" }, // "や", "ゃ"
+ { '\'', "\xe3\x82\x84", "\xe3\x82\x83" }, // "や", "ゃ"
+ { '8' , "\xe3\x82\x86", "\xe3\x82\x85" }, // "ゆ", "ゅ"
+ { '(' , "\xe3\x82\x86", "\xe3\x82\x85" }, // "ゆ", "ゅ"
+ { '9' , "\xe3\x82\x88", "\xe3\x82\x87" }, // "よ", "ょ"
+ { ')' , "\xe3\x82\x88", "\xe3\x82\x87" }, // "よ", "ょ"
+ { '0' , "\xe3\x82\x8f", "\xe3\x82\x92" }, // "わ", "を"
+ { '-' , "\xe3\x81\xbb", "\xe3\x81\xbb" }, // "ほ", "ほ"
+ { '=' , "\xe3\x81\xbb", "\xe3\x81\xbb" }, // "ほ", "ほ"
+ { '^' , "\xe3\x81\xb8", "\xe3\x82\x92" }, // "へ", "を"
+ { '~' , "\xe3\x81\xb8", "\xe3\x82\x92" }, // "へ", "を"
+ { '|' , "\xe3\x83\xbc", "\xe3\x83\xbc" }, // "ー", "ー"
+ { 'q' , "\xe3\x81\x9f", "\xe3\x81\x9f" }, // "た", "た"
+ { 'Q' , "\xe3\x81\x9f", "\xe3\x81\x9f" }, // "た", "た"
+ { 'w' , "\xe3\x81\xa6", "\xe3\x81\xa6" }, // "て", "て"
+ { 'W' , "\xe3\x81\xa6", "\xe3\x81\xa6" }, // "て", "て"
+ { 'e' , "\xe3\x81\x84", "\xe3\x81\x83" }, // "い", "ぃ"
+ { 'E' , "\xe3\x81\x84", "\xe3\x81\x83" }, // "い", "ぃ"
+ { 'r' , "\xe3\x81\x99", "\xe3\x81\x99" }, // "す", "す"
+ { 'R' , "\xe3\x81\x99", "\xe3\x81\x99" }, // "す", "す"
+ { 't' , "\xe3\x81\x8b", "\xe3\x81\x8b" }, // "か", "か"
+ { 'T' , "\xe3\x81\x8b", "\xe3\x81\x8b" }, // "か", "か"
+ { 'y' , "\xe3\x82\x93", "\xe3\x82\x93" }, // "ん", "ん"
+ { 'Y' , "\xe3\x82\x93", "\xe3\x82\x93" }, // "ん", "ん"
+ { 'u' , "\xe3\x81\xaa", "\xe3\x81\xaa" }, // "な", "な"
+ { 'U' , "\xe3\x81\xaa", "\xe3\x81\xaa" }, // "な", "な"
+ { 'i' , "\xe3\x81\xab", "\xe3\x81\xab" }, // "に", "に"
+ { 'I' , "\xe3\x81\xab", "\xe3\x81\xab" }, // "に", "に"
+ { 'o' , "\xe3\x82\x89", "\xe3\x82\x89" }, // "ら", "ら"
+ { 'O' , "\xe3\x82\x89", "\xe3\x82\x89" }, // "ら", "ら"
+ { 'p' , "\xe3\x81\x9b", "\xe3\x81\x9b" }, // "せ", "せ"
+ { 'P' , "\xe3\x81\x9b", "\xe3\x81\x9b" }, // "せ", "せ"
+ { '@' , "\xe3\x82\x9b", "\xe3\x82\x9b" }, // "゛", "゛"
+ { '`' , "\xe3\x82\x9b", "\xe3\x82\x9b" }, // "゛", "゛"
+ { '[' , "\xe3\x82\x9c", "\xe3\x80\x8c" }, // "゜", "「"
+ { '{' , "\xe3\x82\x9c", "\xe3\x80\x8c" }, // "゜", "「"
+ { 'a' , "\xe3\x81\xa1", "\xe3\x81\xa1" }, // "ち", "ち"
+ { 'A' , "\xe3\x81\xa1", "\xe3\x81\xa1" }, // "ち", "ち"
+ { 's' , "\xe3\x81\xa8", "\xe3\x81\xa8" }, // "と", "と"
+ { 'S' , "\xe3\x81\xa8", "\xe3\x81\xa8" }, // "と", "と"
+ { 'd' , "\xe3\x81\x97", "\xe3\x81\x97" }, // "し", "し"
+ { 'D' , "\xe3\x81\x97", "\xe3\x81\x97" }, // "し", "し"
+ { 'f' , "\xe3\x81\xaf", "\xe3\x81\xaf" }, // "は", "は"
+ { 'F' , "\xe3\x81\xaf", "\xe3\x81\xaf" }, // "は", "は"
+ { 'g' , "\xe3\x81\x8d", "\xe3\x81\x8d" }, // "き", "き"
+ { 'G' , "\xe3\x81\x8d", "\xe3\x81\x8d" }, // "き", "き"
+ { 'h' , "\xe3\x81\x8f", "\xe3\x81\x8f" }, // "く", "く"
+ { 'H' , "\xe3\x81\x8f", "\xe3\x81\x8f" }, // "く", "く"
+ { 'j' , "\xe3\x81\xbe", "\xe3\x81\xbe" }, // "ま", "ま"
+ { 'J' , "\xe3\x81\xbe", "\xe3\x81\xbe" }, // "ま", "ま"
+ { 'k' , "\xe3\x81\xae", "\xe3\x81\xae" }, // "の", "の"
+ { 'K' , "\xe3\x81\xae", "\xe3\x81\xae" }, // "の", "の"
+ { 'l' , "\xe3\x82\x8a", "\xe3\x82\x8a" }, // "り", "り"
+ { 'L' , "\xe3\x82\x8a", "\xe3\x82\x8a" }, // "り", "り"
+ { ';' , "\xe3\x82\x8c", "\xe3\x82\x8c" }, // "れ", "れ"
+ { '+' , "\xe3\x82\x8c", "\xe3\x82\x8c" }, // "れ", "れ"
+ { ':' , "\xe3\x81\x91", "\xe3\x81\x91" }, // "け", "け"
+ { '*' , "\xe3\x81\x91", "\xe3\x81\x91" }, // "け", "け"
+ { ']' , "\xe3\x82\x80", "\xe3\x80\x8d" }, // "む", "」"
+ { '}' , "\xe3\x82\x80", "\xe3\x80\x8d" }, // "む", "」"
+ { 'z' , "\xe3\x81\xa4", "\xe3\x81\xa3" }, // "つ", "っ"
+ { 'Z' , "\xe3\x81\xa4", "\xe3\x81\xa3" }, // "つ", "っ"
+ { 'x' , "\xe3\x81\x95", "\xe3\x81\x95" }, // "さ", "さ"
+ { 'X' , "\xe3\x81\x95", "\xe3\x81\x95" }, // "さ", "さ"
+ { 'c' , "\xe3\x81\x9d", "\xe3\x81\x9d" }, // "そ", "そ"
+ { 'C' , "\xe3\x81\x9d", "\xe3\x81\x9d" }, // "そ", "そ"
+ { 'v' , "\xe3\x81\xb2", "\xe3\x81\xb2" }, // "ひ", "ひ"
+ { 'V' , "\xe3\x81\xb2", "\xe3\x81\xb2" }, // "ひ", "ひ"
+ { 'b' , "\xe3\x81\x93", "\xe3\x81\x93" }, // "こ", "こ"
+ { 'B' , "\xe3\x81\x93", "\xe3\x81\x93" }, // "こ", "こ"
+ { 'n' , "\xe3\x81\xbf", "\xe3\x81\xbf" }, // "み", "み"
+ { 'N' , "\xe3\x81\xbf", "\xe3\x81\xbf" }, // "み", "み"
+ { 'm' , "\xe3\x82\x82", "\xe3\x82\x82" }, // "も", "も"
+ { 'M' , "\xe3\x82\x82", "\xe3\x82\x82" }, // "も", "も"
+ { ',' , "\xe3\x81\xad", "\xe3\x80\x81" }, // "ね", "、"
+ { '<' , "\xe3\x81\xad", "\xe3\x80\x81" }, // "ね", "、"
+ { '.' , "\xe3\x82\x8b", "\xe3\x80\x82" }, // "る", "。"
+ { '>' , "\xe3\x82\x8b", "\xe3\x80\x82" }, // "る", "。"
+ { '/' , "\xe3\x82\x81", "\xe3\x83\xbb" }, // "め", "・"
+ { '?' , "\xe3\x82\x81", "\xe3\x83\xbb" }, // "め", "・"
+ { '_' , "\xe3\x82\x8d", "\xe3\x82\x8d" }, // "ろ", "ろ"
+ // A backslash is handled in a special way because it is input by
+ // two different keys (the one next to Backslash and the one next
+ // to Right Shift).
+ { '\\', "", "" },
+}, kana_map_us[] = {
+ { '`' , "\xe3\x82\x8d", "\xe3\x82\x8d" }, // "ろ", "ろ"
+ { '~' , "\xe3\x82\x8d", "\xe3\x82\x8d" }, // "ろ", "ろ"
+ { '1' , "\xe3\x81\xac", "\xe3\x81\xac" }, // "ぬ", "ぬ"
+ { '!' , "\xe3\x81\xac", "\xe3\x81\xac" }, // "ぬ", "ぬ"
+ { '2' , "\xe3\x81\xb5", "\xe3\x81\xb5" }, // "ふ", "ふ"
+ { '@' , "\xe3\x81\xb5", "\xe3\x81\xb5" }, // "ふ", "ふ"
+ { '3' , "\xe3\x81\x82", "\xe3\x81\x81" }, // "あ", "ぁ"
+ { '#' , "\xe3\x81\x82", "\xe3\x81\x81" }, // "あ", "ぁ"
+ { '4' , "\xe3\x81\x86", "\xe3\x81\x85" }, // "う", "ぅ"
+ { '$' , "\xe3\x81\x86", "\xe3\x81\x85" }, // "う", "ぅ"
+ { '5' , "\xe3\x81\x88", "\xe3\x81\x87" }, // "え", "ぇ"
+ { '%' , "\xe3\x81\x88", "\xe3\x81\x87" }, // "え", "ぇ"
+ { '6' , "\xe3\x81\x8a", "\xe3\x81\x89" }, // "お", "ぉ"
+ { '^' , "\xe3\x81\x8a", "\xe3\x81\x89" }, // "お", "ぉ"
+ { '7' , "\xe3\x82\x84", "\xe3\x82\x83" }, // "や", "ゃ"
+ { '&' , "\xe3\x82\x84", "\xe3\x82\x83" }, // "や", "ゃ"
+ { '8' , "\xe3\x82\x86", "\xe3\x82\x85" }, // "ゆ", "ゅ"
+ { '*' , "\xe3\x82\x86", "\xe3\x82\x85" }, // "ゆ", "ゅ"
+ { '9' , "\xe3\x82\x88", "\xe3\x82\x87" }, // "よ", "ょ"
+ { '(' , "\xe3\x82\x88", "\xe3\x82\x87" }, // "よ", "ょ"
+ { '0' , "\xe3\x82\x8f", "\xe3\x82\x92" }, // "わ", "を"
+ { ')' , "\xe3\x82\x8f", "\xe3\x82\x92" }, // "わ", "を"
+ { '-' , "\xe3\x81\xbb", "\xe3\x83\xbc" }, // "ほ", "ー"
+ { '_' , "\xe3\x81\xbb", "\xe3\x83\xbc" }, // "ほ", "ー"
+ { '=' , "\xe3\x81\xb8", "\xe3\x81\xb8" }, // "へ", "へ"
+ { '+' , "\xe3\x81\xb8", "\xe3\x81\xb8" }, // "へ", "へ"
+ { 'q' , "\xe3\x81\x9f", "\xe3\x81\x9f" }, // "た", "た"
+ { 'Q' , "\xe3\x81\x9f", "\xe3\x81\x9f" }, // "た", "た"
+ { 'w' , "\xe3\x81\xa6", "\xe3\x81\xa6" }, // "て", "て"
+ { 'W' , "\xe3\x81\xa6", "\xe3\x81\xa6" }, // "て", "て"
+ { 'e' , "\xe3\x81\x84", "\xe3\x81\x83" }, // "い", "ぃ"
+ { 'E' , "\xe3\x81\x84", "\xe3\x81\x83" }, // "い", "ぃ"
+ { 'r' , "\xe3\x81\x99", "\xe3\x81\x99" }, // "す", "す"
+ { 'R' , "\xe3\x81\x99", "\xe3\x81\x99" }, // "す", "す"
+ { 't' , "\xe3\x81\x8b", "\xe3\x81\x8b" }, // "か", "か"
+ { 'T' , "\xe3\x81\x8b", "\xe3\x81\x8b" }, // "か", "か"
+ { 'y' , "\xe3\x82\x93", "\xe3\x82\x93" }, // "ん", "ん"
+ { 'Y' , "\xe3\x82\x93", "\xe3\x82\x93" }, // "ん", "ん"
+ { 'u' , "\xe3\x81\xaa", "\xe3\x81\xaa" }, // "な", "な"
+ { 'U' , "\xe3\x81\xaa", "\xe3\x81\xaa" }, // "な", "な"
+ { 'i' , "\xe3\x81\xab", "\xe3\x81\xab" }, // "に", "に"
+ { 'I' , "\xe3\x81\xab", "\xe3\x81\xab" }, // "に", "に"
+ { 'o' , "\xe3\x82\x89", "\xe3\x82\x89" }, // "ら", "ら"
+ { 'O' , "\xe3\x82\x89", "\xe3\x82\x89" }, // "ら", "ら"
+ { 'p' , "\xe3\x81\x9b", "\xe3\x81\x9b" }, // "せ", "せ"
+ { 'P' , "\xe3\x81\x9b", "\xe3\x81\x9b" }, // "せ", "せ"
+ { '[' , "\xe3\x82\x9b", "\xe3\x82\x9b" }, // "゛", "゛"
+ { '{' , "\xe3\x82\x9b", "\xe3\x82\x9b" }, // "゛", "゛"
+ { ']' , "\xe3\x82\x9c", "\xe3\x80\x8c" }, // "゜", "「"
+ { '}' , "\xe3\x82\x9c", "\xe3\x80\x8c" }, // "゜", "「"
+ { '\\', "\xe3\x82\x80", "\xe3\x80\x8d" }, // "む", "」"
+ { '|' , "\xe3\x82\x80", "\xe3\x80\x8d" }, // "む", "」"
+ { 'a' , "\xe3\x81\xa1", "\xe3\x81\xa1" }, // "ち", "ち"
+ { 'A' , "\xe3\x81\xa1", "\xe3\x81\xa1" }, // "ち", "ち"
+ { 's' , "\xe3\x81\xa8", "\xe3\x81\xa8" }, // "と", "と"
+ { 'S' , "\xe3\x81\xa8", "\xe3\x81\xa8" }, // "と", "と"
+ { 'd' , "\xe3\x81\x97", "\xe3\x81\x97" }, // "し", "し"
+ { 'D' , "\xe3\x81\x97", "\xe3\x81\x97" }, // "し", "し"
+ { 'f' , "\xe3\x81\xaf", "\xe3\x81\xaf" }, // "は", "は"
+ { 'F' , "\xe3\x81\xaf", "\xe3\x81\xaf" }, // "は", "は"
+ { 'g' , "\xe3\x81\x8d", "\xe3\x81\x8d" }, // "き", "き"
+ { 'G' , "\xe3\x81\x8d", "\xe3\x81\x8d" }, // "き", "き"
+ { 'h' , "\xe3\x81\x8f", "\xe3\x81\x8f" }, // "く", "く"
+ { 'H' , "\xe3\x81\x8f", "\xe3\x81\x8f" }, // "く", "く"
+ { 'j' , "\xe3\x81\xbe", "\xe3\x81\xbe" }, // "ま", "ま"
+ { 'J' , "\xe3\x81\xbe", "\xe3\x81\xbe" }, // "ま", "ま"
+ { 'k' , "\xe3\x81\xae", "\xe3\x81\xae" }, // "の", "の"
+ { 'K' , "\xe3\x81\xae", "\xe3\x81\xae" }, // "の", "の"
+ { 'l' , "\xe3\x82\x8a", "\xe3\x82\x8a" }, // "り", "り"
+ { 'L' , "\xe3\x82\x8a", "\xe3\x82\x8a" }, // "り", "り"
+ { ';' , "\xe3\x82\x8c", "\xe3\x82\x8c" }, // "れ", "れ"
+ { ':' , "\xe3\x82\x8c", "\xe3\x82\x8c" }, // "れ", "れ"
+ { '\'', "\xe3\x81\x91", "\xe3\x81\x91" }, // "け", "け"
+ { '\"', "\xe3\x81\x91", "\xe3\x81\x91" }, // "け", "け"
+ { 'z' , "\xe3\x81\xa4", "\xe3\x81\xa3" }, // "つ", "っ"
+ { 'Z' , "\xe3\x81\xa4", "\xe3\x81\xa3" }, // "つ", "っ"
+ { 'x' , "\xe3\x81\x95", "\xe3\x81\x95" }, // "さ", "さ"
+ { 'X' , "\xe3\x81\x95", "\xe3\x81\x95" }, // "さ", "さ"
+ { 'c' , "\xe3\x81\x9d", "\xe3\x81\x9d" }, // "そ", "そ"
+ { 'C' , "\xe3\x81\x9d", "\xe3\x81\x9d" }, // "そ", "そ"
+ { 'v' , "\xe3\x81\xb2", "\xe3\x81\xb2" }, // "ひ", "ひ"
+ { 'V' , "\xe3\x81\xb2", "\xe3\x81\xb2" }, // "ひ", "ひ"
+ { 'b' , "\xe3\x81\x93", "\xe3\x81\x93" }, // "こ", "こ"
+ { 'B' , "\xe3\x81\x93", "\xe3\x81\x93" }, // "こ", "こ"
+ { 'n' , "\xe3\x81\xbf", "\xe3\x81\xbf" }, // "み", "み"
+ { 'N' , "\xe3\x81\xbf", "\xe3\x81\xbf" }, // "み", "み"
+ { 'm' , "\xe3\x82\x82", "\xe3\x82\x82" }, // "も", "も"
+ { 'M' , "\xe3\x82\x82", "\xe3\x82\x82" }, // "も", "も"
+ { ',' , "\xe3\x81\xad", "\xe3\x80\x81" }, // "ね", "、"
+ { '<' , "\xe3\x81\xad", "\xe3\x80\x81" }, // "ね", "、"
+ { '.' , "\xe3\x82\x8b", "\xe3\x80\x82" }, // "る", "。"
+ { '>' , "\xe3\x82\x8b", "\xe3\x80\x82" }, // "る", "。"
+ { '/' , "\xe3\x82\x81", "\xe3\x83\xbb" }, // "め", "・"
+ { '?' , "\xe3\x82\x81", "\xe3\x83\xbb" }, // "め", "・"
+};
+
+} // namespace
+
+namespace mozc {
+namespace fcitx {
+
+KeyTranslator::KeyTranslator() {
+ Init();
+}
+
+KeyTranslator::~KeyTranslator() {
+}
+
+// TODO(nona): Fix 'Shift-0' behavior b/4338394
+bool KeyTranslator::Translate(FcitxKeySym keyval,
+ uint32 keycode,
+ uint32 modifiers,
+ config::Config::PreeditMethod method,
+ bool layout_is_jp,
+ commands::KeyEvent *out_event) const {
+ DCHECK(out_event) << "out_event is NULL";
+ out_event->Clear();
+
+ /* this is key we cannot handle, don't process it */
+ if (modifiers & FcitxKeyState_Super)
+ return false;
+
+ // Due to historical reasons, many linux ditributions set Hiragana_Katakana
+ // key as Hiragana key (which is Katkana key with shift modifier). So, we
+ // translate Hiragana_Katanaka key as Hiragana key by mapping table, and
+ // Shift + Hiragana_Katakana key as Katakana key by functionally.
+ // TODO(nona): Fix process modifier to handle right shift
+ if (IsHiraganaKatakanaKeyWithShift(keyval, keycode, modifiers)) {
+ modifiers &= ~FcitxKeyState_Shift;
+ keyval = FcitxKey_Katakana;
+ }
+ string kana_key_string;
+ if ((method == config::Config::KANA) && IsKanaAvailable(
+ keyval, keycode, modifiers, layout_is_jp, &kana_key_string)) {
+ out_event->set_key_code(keyval);
+ out_event->set_key_string(kana_key_string);
+ } else if (IsAscii(keyval, keycode, modifiers)) {
+ if (FcitxKeyState_CapsLock & modifiers) {
+ out_event->add_modifier_keys(commands::KeyEvent::CAPS);
+ }
+ out_event->set_key_code(keyval);
+ } else if (IsModifierKey(keyval, keycode, modifiers)) {
+ ModifierKeyMap::const_iterator i = modifier_key_map_.find(keyval);
+ DCHECK(i != modifier_key_map_.end());
+ out_event->add_modifier_keys(i->second);
+ } else if (IsSpecialKey(keyval, keycode, modifiers)) {
+ SpecialKeyMap::const_iterator i = special_key_map_.find(keyval);
+ DCHECK(i != special_key_map_.end());
+ out_event->set_special_key(i->second);
+ } else {
+ VLOG(1) << "Unknown keyval: " << keyval;
+ return false;
+ }
+
+ for (ModifierKeyMap::const_iterator i = modifier_mask_map_.begin();
+ i != modifier_mask_map_.end(); ++i) {
+ // Do not set a SHIFT modifier when |keyval| is a printable key by following
+ // the Mozc's rule.
+ if ((i->second == commands::KeyEvent::SHIFT) &&
+ IsPrintable(keyval, keycode, modifiers)) {
+ continue;
+ }
+
+ if (i->first & modifiers) {
+ out_event->add_modifier_keys(i->second);
+ }
+ }
+
+ return true;
+}
+
+void KeyTranslator::Init() {
+ for (int i = 0; i < arraysize(special_key_map); ++i) {
+ CHECK(special_key_map_.insert(make_pair(special_key_map[i].from,
+ special_key_map[i].to)).second);
+ }
+ for (int i = 0; i < arraysize(modifier_key_map); ++i) {
+ CHECK(modifier_key_map_.insert(make_pair(modifier_key_map[i].from,
+ modifier_key_map[i].to)).second);
+ }
+ for (int i = 0; i < arraysize(modifier_mask_map); ++i) {
+ CHECK(modifier_mask_map_.insert(make_pair(modifier_mask_map[i].from,
+ modifier_mask_map[i].to)).second);
+ }
+ for (int i = 0; i < arraysize(kana_map_jp); ++i) {
+ CHECK(kana_map_jp_.insert(
+ make_pair(kana_map_jp[i].code, make_pair(
+ kana_map_jp[i].no_shift, kana_map_jp[i].shift))).second);
+ }
+ for (int i = 0; i < arraysize(kana_map_us); ++i) {
+ CHECK(kana_map_us_.insert(
+ make_pair(kana_map_us[i].code, make_pair(
+ kana_map_us[i].no_shift, kana_map_us[i].shift))).second);
+ }
+}
+
+bool KeyTranslator::IsModifierKey(uint32 keyval,
+ uint32 keycode,
+ uint32 modifiers) const {
+ return modifier_key_map_.find(keyval) != modifier_key_map_.end();
+}
+
+bool KeyTranslator::IsSpecialKey(uint32 keyval,
+ uint32 keycode,
+ uint32 modifiers) const {
+ return special_key_map_.find(keyval) != special_key_map_.end();
+}
+
+bool KeyTranslator::IsHiraganaKatakanaKeyWithShift(uint32 keyval,
+ uint32 keycode,
+ uint32 modifiers) {
+ return ((modifiers & FcitxKeyState_Shift) && (keyval == FcitxKey_Hiragana_Katakana));
+}
+
+bool KeyTranslator::IsKanaAvailable(uint32 keyval,
+ uint32 keycode,
+ uint32 modifiers,
+ bool layout_is_jp,
+ string *out) const {
+ if ((modifiers & FcitxKeyState_Ctrl) || (modifiers & FcitxKeyState_Alt)) {
+ return false;
+ }
+ const KanaMap &kana_map = layout_is_jp ? kana_map_jp_ : kana_map_us_;
+ KanaMap::const_iterator iter = kana_map.find(keyval);
+ if (iter == kana_map.end()) {
+ return false;
+ }
+
+ if (out) {
+ // When a Japanese keyboard is in use, the yen-sign key and the backslash
+ // key generate the same |keyval|. In this case, we have to check |keycode|
+ // to return an appropriate string. See the following IBus issue for
+ // details: http://code.google.com/p/ibus/issues/detail?id=52
+ if (keyval == '\\' && layout_is_jp) {
+ if (keycode == FcitxKey_bar) {
+ *out = "\xe3\x83\xbc"; // "ー"
+ } else {
+ *out = "\xe3\x82\x8d"; // "ろ"
+ }
+ } else {
+ *out = (modifiers & FcitxKeyState_Shift) ?
+ iter->second.second : iter->second.first;
+ }
+ }
+ return true;
+}
+
+// TODO(nona): resolve S-'0' problem (b/4338394).
+// TODO(nona): Current printable detection is weak. To enhance accuracy, use xkb
+// key map
+bool KeyTranslator::IsPrintable(uint32 keyval, uint32 keycode, uint32 modifiers) {
+ if ((modifiers & FcitxKeyState_Ctrl) || (modifiers & FcitxKeyState_Alt)) {
+ return false;
+ }
+ return IsAscii(keyval, keycode, modifiers);
+}
+
+bool KeyTranslator::IsAscii(uint32 keyval, uint32 keycode, uint32 modifiers) {
+ return (keyval > FcitxKey_space &&
+ // Note: Space key (0x20) is a special key in Mozc.
+ keyval <= FcitxKey_asciitilde); // 0x7e.
+}
+
+} // namespace ibus
+} // namespace mozc
diff --git a/src/unix/fcitx/fcitx_key_translator.h b/src/unix/fcitx/fcitx_key_translator.h
new file mode 100644
index 0000000..b3a6f18
--- /dev/null
+++ b/src/unix/fcitx/fcitx_key_translator.h
@@ -0,0 +1,121 @@
+// Copyright 2010-2012, Google Inc.
+// Copyright 2012~2013, Weng Xuetian <wengxt@gmail.com>
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef MOZC_UNIX_FCITX_FCITX_KEY_TRANSLATOR_H_
+#define MOZC_UNIX_FCITX_FCITX_KEY_TRANSLATOR_H_
+
+#include <map>
+#include <set>
+#include <string>
+
+#include <fcitx-config/hotkey.h>
+
+#include "base/base.h" // for DISALLOW_COPY_AND_ASSIGN.
+#include "session/commands.pb.h"
+#include <fcitx/ime.h>
+
+namespace mozc {
+
+namespace fcitx {
+
+// This class is responsible for converting scim::KeyEvent object (defined in
+// /usr/include/scim-1.0/scim_event.h) to IPC input for mozc_server.
+class KeyTranslator {
+public:
+ KeyTranslator();
+ virtual ~KeyTranslator();
+
+ // Converts scim_key into Mozc key code and stores them on out_translated.
+ // scim_key must satisfy the following precondition: CanConvert(scim_key)
+ bool Translate(FcitxKeySym keyval,
+ uint32 keycode,
+ uint32 modifiers,
+ mozc::config::Config::PreeditMethod method,
+ bool layout_is_jp,
+ mozc::commands::KeyEvent *out_event) const;
+
+private:
+ typedef map<uint32, commands::KeyEvent::SpecialKey> SpecialKeyMap;
+ typedef map<uint32, commands::KeyEvent::ModifierKey> ModifierKeyMap;
+ typedef map<uint32, pair<string, string> > KanaMap;
+
+ // Returns true iff key is modifier key such as SHIFT, ALT, or CAPSLOCK.
+ bool IsModifierKey(uint32 keyval,
+ uint32 keycode,
+ uint32 modifiers) const;
+
+ // Returns true iff key is special key such as ENTER, ESC, or PAGE_UP.
+ bool IsSpecialKey(uint32 keyval,
+ uint32 keycode,
+ uint32 modifiers) const;
+
+ // Returns true iff |keyval| is a key with a kana assigned.
+ bool IsKanaAvailable(uint32 keyval,
+ uint32 keycode,
+ uint32 modifiers,
+ bool layout_is_jp,
+ string *out) const;
+
+ // Returns true iff key is ASCII such as '0', 'A', or '!'.
+ static bool IsAscii(uint32 keyval,
+ uint32 keycode,
+ uint32 modifiers);
+
+ // Returns true iff key is printable.
+ static bool IsPrintable(uint32 keyval, uint32 keycode, uint32 modifiers);
+
+ // Returns true iff key is HiraganaKatakana with shift modifier.
+ static bool IsHiraganaKatakanaKeyWithShift(uint32 keyval,
+ uint32 keycode,
+ uint32 modifiers);
+
+ // Initializes private fields.
+ void Init();
+
+ // Stores a mapping from ibus keys to Mozc's special keys.
+ SpecialKeyMap special_key_map_;
+ // Stores a mapping from ibus modifier keys to Mozc's modifier keys.
+ ModifierKeyMap modifier_key_map_;
+ // Stores a mapping from ibus modifier masks to Mozc's modifier keys.
+ ModifierKeyMap modifier_mask_map_;
+ // Stores a mapping from ASCII to Kana character. For example, ASCII character
+ // '4' is mapped to Japanese 'Hiragana Letter U' (without Shift modifier) and
+ // 'Hiragana Letter Small U' (with Shift modifier).
+ KanaMap kana_map_jp_; // mapping for JP keyboard.
+ KanaMap kana_map_us_; // mapping for US keyboard.
+
+ DISALLOW_COPY_AND_ASSIGN(KeyTranslator);
+};
+
+} // namespace fcitx
+
+} // namespace mozc
+
+#endif // MOZC_UNIX_FCITX_FCITX_KEY_TRANSLATOR_H_
diff --git a/src/unix/fcitx/fcitx_mozc.cc b/src/unix/fcitx/fcitx_mozc.cc
new file mode 100644
index 0000000..f2c3794
--- /dev/null
+++ b/src/unix/fcitx/fcitx_mozc.cc
@@ -0,0 +1,530 @@
+// Copyright 2012~2013, Weng Xuetian <wengxt@gmail.com>
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// workaround
+#define _FCITX_LOG_H_
+
+#include "unix/fcitx/fcitx_mozc.h"
+
+#include <string>
+#include <fcitx/candidate.h>
+#include <fcitx/module.h>
+#include <fcitx-config/xdg.h>
+
+#include "base/const.h"
+#include "base/logging.h"
+#include "base/process.h"
+#include "base/util.h"
+#include "unix/fcitx/mozc_connection.h"
+#include "unix/fcitx/mozc_response_parser.h"
+#include <fcitx/context.h>
+
+#define N_(x) (x)
+
+namespace
+{
+
+static const std::string empty_string;
+
+const struct CompositionMode
+{
+ const char *icon;
+ const char *label;
+ const char *description;
+ mozc::commands::CompositionMode mode;
+} kPropCompositionModes[] =
+{
+ {
+ "mozc-direct.png",
+ "A",
+ N_("Direct"),
+ mozc::commands::DIRECT,
+ }, {
+ "mozc-hiragana.png",
+ "\xe3\x81\x82", // Hiragana letter A in UTF-8.
+ N_("Hiragana"),
+ mozc::commands::HIRAGANA,
+ }, {
+ "mozc-katakana_full.png",
+ "\xe3\x82\xa2", // Katakana letter A.
+ N_("Full Katakana"),
+ mozc::commands::FULL_KATAKANA,
+ }, {
+ "mozc-alpha_half.png",
+ "A",
+ N_("Half ASCII"),
+ mozc::commands::HALF_ASCII,
+ }, {
+ "mozc-alpha_full.png",
+ "\xef\xbc\xa1", // Full width ASCII letter A.
+ N_("Full ASCII"),
+ mozc::commands::FULL_ASCII,
+ }, {
+ "mozc-katakana_half.png",
+ "\xef\xbd\xb1", // Half width Katakana letter A.
+ N_("Half Katakana"),
+ mozc::commands::HALF_KATAKANA,
+ },
+};
+const size_t kNumCompositionModes = arraysize ( kPropCompositionModes );
+
+// This array must correspond with the CompositionMode enum in the
+// mozc/session/command.proto file.
+COMPILE_ASSERT (
+ mozc::commands::NUM_OF_COMPOSITIONS == arraysize ( kPropCompositionModes ),
+ bad_number_of_modes );
+
+} // namespace
+
+INPUT_RETURN_VALUE FcitxMozcGetCandidateWord(void* arg, FcitxCandidateWord* candWord)
+{
+ mozc::fcitx::FcitxMozc* fcitx_mozc = (mozc::fcitx::FcitxMozc*) arg;
+ fcitx_mozc->select_candidate(candWord);
+
+ return IRV_DISPLAY_CANDWORDS;
+}
+
+
+namespace mozc
+{
+
+namespace fcitx
+{
+
+// For unittests.
+FcitxMozc::FcitxMozc ( FcitxInstance* inst,
+ MozcConnectionInterface *connection,
+ MozcResponseParser *parser ) :
+ instance(inst),
+ input(FcitxInstanceGetInputState(inst)),
+ connection_ ( connection ),
+ parser_ ( parser ),
+ composition_mode_ ( mozc::commands::HIRAGANA )
+{
+ VLOG ( 1 ) << "FcitxMozc created.";
+ const bool is_vertical
+ = false;
+ parser_->set_use_annotation ( is_vertical );
+ InitializeBar();
+ InitializeMenu();
+ SetCompositionMode( mozc::commands::HIRAGANA );
+}
+
+FcitxMozc::~FcitxMozc()
+{
+ VLOG ( 1 ) << "FcitxMozc destroyed.";
+}
+
+// This function is called from SCIM framework when users press or release a
+// key.
+bool FcitxMozc::process_key_event (FcitxKeySym sym, uint32 keycode, uint32 state, bool layout_is_jp, bool is_key_up)
+{
+ string error;
+ mozc::commands::Output raw_response;
+ if ( !connection_->TrySendKeyEvent (
+ sym, keycode, state, composition_mode_, layout_is_jp, is_key_up, &raw_response, &error ) )
+ {
+ // TODO(yusukes): Show |error|.
+ return false; // not consumed.
+ }
+
+ return ParseResponse ( raw_response );
+}
+
+// This function is called from SCIM framework when users click the candidate
+// window.
+void FcitxMozc::select_candidate ( FcitxCandidateWord* candWord )
+{
+ int32 *id = (int32*) candWord->priv;
+
+ if ( *id == kBadCandidateId )
+ {
+ LOG ( ERROR ) << "The clicked candidate doesn't have unique ID.";
+ return;
+ }
+ VLOG ( 1 ) << "select_candidate, id=" << *id;
+
+ string error;
+ mozc::commands::Output raw_response;
+ if ( !connection_->TrySendClick ( *id, &raw_response, &error ) )
+ {
+ LOG ( ERROR ) << "IPC failed. error=" << error;
+ SetAuxString ( error );
+ DrawAll();
+ }
+ else
+ {
+ ParseResponse ( raw_response );
+ }
+}
+
+// This function is called from SCIM framework.
+void FcitxMozc::resetim()
+{
+ VLOG ( 1 ) << "resetim";
+ string error;
+ mozc::commands::Output raw_response;
+ if ( connection_->TrySendCommand (
+ mozc::commands::SessionCommand::REVERT, &raw_response, &error ) )
+ {
+ parser_->ParseResponse ( raw_response, this );
+ }
+ ClearAll(); // just in case.
+ DrawAll();
+
+}
+
+void FcitxMozc::reset()
+{
+ FcitxIM* im = FcitxInstanceGetCurrentIM(instance);
+ if (!im || strcmp(im->uniqueName, "mozc") != 0) {
+ FcitxUISetStatusVisable(instance, "mozc-tool", false);
+ FcitxUISetStatusVisable(instance, "mozc-composition-mode", false);
+ }
+ else {
+ FcitxUISetStatusVisable(instance, "mozc-tool", true);
+ FcitxUISetStatusVisable(instance, "mozc-composition-mode", true);
+ }
+}
+
+
+// This function is called from SCIM framework when the ic gets focus.
+void FcitxMozc::init()
+{
+ VLOG ( 1 ) << "focus_in";
+ boolean flag = true;
+ FcitxInstanceSetContext(instance, CONTEXT_DISABLE_AUTOENG, &flag);
+ FcitxInstanceSetContext(instance, CONTEXT_DISABLE_QUICKPHRASE, &flag);
+ FcitxInstanceSetContext(instance, CONTEXT_IM_KEYBOARD_LAYOUT, "jp");
+ FcitxInstanceSetContext(instance, "CONTEXT_DISABLE_AUTO_FIRST_CANDIDATE_HIGHTLIGHT", &flag);
+
+ DrawAll();
+}
+
+// This function is called when the ic loses focus.
+void FcitxMozc::focus_out()
+{
+ VLOG ( 1 ) << "focus_out";
+ string error;
+ mozc::commands::Output raw_response;
+ if ( connection_->TrySendCommand (
+ mozc::commands::SessionCommand::REVERT, &raw_response, &error ) )
+ {
+ parser_->ParseResponse ( raw_response, this );
+ }
+ ClearAll(); // just in case.
+ DrawAll();
+ // TODO(yusukes): Call client::SyncData() like ibus-mozc.
+}
+
+
+bool FcitxMozc::ParseResponse ( const mozc::commands::Output &raw_response )
+{
+ ClearAll();
+ const bool consumed = parser_->ParseResponse ( raw_response, this );
+ if ( !consumed )
+ {
+ VLOG ( 1 ) << "The input was not consumed by Mozc.";
+ }
+ OpenUrl();
+ DrawAll();
+ return consumed;
+}
+
+void FcitxMozc::SetResultString ( const std::string &result_string )
+{
+ FcitxInstanceCommitString(instance, FcitxInstanceGetCurrentIC(instance), result_string.c_str());
+}
+
+void FcitxMozc::SetPreeditInfo ( const PreeditInfo *preedit_info )
+{
+ preedit_info_.reset ( preedit_info );
+}
+
+void FcitxMozc::SetAuxString ( const std::string &str )
+{
+ aux_ = str;
+}
+
+void FcitxMozc::SetCompositionMode ( mozc::commands::CompositionMode mode )
+{
+ composition_mode_ = mode;
+ DCHECK(composition_mode_ < kNumCompositionModes);
+ if (composition_mode_ < kNumCompositionModes) {
+ FcitxUISetStatusString(instance,
+ "mozc-composition-mode",
+ _(kPropCompositionModes[composition_mode_].label),
+ _(kPropCompositionModes[composition_mode_].description));
+ }
+}
+
+void FcitxMozc::SendCompositionMode(mozc::commands::CompositionMode mode)
+{
+ // Send the SWITCH_INPUT_MODE command.
+ string error;
+ mozc::commands::Output raw_response;
+ if (connection_->TrySendCompositionMode(
+ kPropCompositionModes[mode].mode, &raw_response, &error)) {
+ parser_->ParseResponse(raw_response, this);
+ }
+}
+
+
+void FcitxMozc::SetUrl ( const string &url )
+{
+ url_ = url;
+}
+
+void FcitxMozc::ClearAll()
+{
+ SetPreeditInfo ( NULL );
+ SetAuxString ( "" );
+ FcitxCandidateWordReset(FcitxInputStateGetCandidateList(input));
+ url_.clear();
+}
+
+void FcitxMozc::DrawPreeditInfo()
+{
+ FcitxProfile* profile = FcitxInstanceGetProfile(instance);
+ FcitxMessages* preedit = FcitxInputStateGetPreedit(input);
+ FcitxMessages* clientpreedit = FcitxInputStateGetClientPreedit(input);
+ FcitxMessagesSetMessageCount(preedit, 0);
+ FcitxMessagesSetMessageCount(clientpreedit, 0);
+ FcitxInputContext* ic = FcitxInstanceGetCurrentIC(instance);
+ if ( preedit_info_.get() )
+ {
+ VLOG ( 1 ) << "DrawPreeditInfo: cursor=" << preedit_info_->cursor_pos;
+
+ if (ic && ((ic->contextCaps & CAPACITY_PREEDIT) == 0 || !profile->bUsePreedit))
+ FcitxInputStateSetShowCursor(input, true);
+
+ for (int i = 0; i < preedit_info_->preedit.size(); i ++) {
+ if (ic && ((ic->contextCaps & CAPACITY_PREEDIT) == 0 || !profile->bUsePreedit))
+ FcitxMessagesAddMessageAtLast(preedit, preedit_info_->preedit[i].type, "%s", preedit_info_->preedit[i].str.c_str());
+ FcitxMessagesAddMessageAtLast(clientpreedit, preedit_info_->preedit[i].type, "%s", preedit_info_->preedit[i].str.c_str());
+ }
+ if (ic && ((ic->contextCaps & CAPACITY_PREEDIT) == 0 || !profile->bUsePreedit))
+ FcitxInputStateSetCursorPos(input, preedit_info_->cursor_pos);
+ FcitxInputStateSetClientCursorPos(input, preedit_info_->cursor_pos);
+ }
+ else {
+ FcitxInputStateSetShowCursor(input, true);
+ }
+}
+
+void FcitxMozc::DrawAux()
+{
+ FcitxMessages* auxUp = FcitxInputStateGetAuxUp(input);
+ FcitxMessages* auxDown = FcitxInputStateGetAuxDown(input);
+ FcitxMessagesSetMessageCount(auxUp, 0);
+ FcitxMessagesSetMessageCount(auxDown, 0);
+ if ( !aux_.empty() ) {
+ FcitxMessagesAddMessageAtLast(auxUp, MSG_TIPS, "%s ", aux_.c_str());
+ }
+}
+
+void FcitxMozc::DrawAll()
+{
+ DrawPreeditInfo();
+ DrawAux();
+}
+
+void FcitxMozc::OpenUrl()
+{
+ if ( url_.empty() )
+ {
+ return;
+ }
+ mozc::Process::OpenBrowser ( url_ );
+ url_.clear();
+}
+
+static const char* GetCompositionIconName(void* arg)
+{
+ FcitxMozc* mozc = (FcitxMozc*) arg;
+ return mozc->GetCurrentCompositionModeIcon().c_str();
+}
+
+
+static const char* GetMozcToolIcon(void* arg)
+{
+ FcitxMozc* mozc = (FcitxMozc*) arg;
+ return mozc->GetIconFile("mozc-tool.png").c_str();
+}
+
+void FcitxMozc::InitializeBar()
+{
+ VLOG ( 1 ) << "Registering properties";
+
+ FcitxUIRegisterComplexStatus(instance, this,
+ "mozc-composition-mode",
+ _("Composition Mode"),
+ _("Composition Mode"),
+ NULL,
+ GetCompositionIconName
+ );
+
+ if ( mozc::Util::FileExists ( mozc::Util::JoinPath (
+ mozc::Util::GetServerDirectory(), mozc::kMozcTool ) ) )
+ {
+ FcitxUIRegisterComplexStatus(instance, this,
+ "mozc-tool",
+ _("Tool"),
+ _("Tool"),
+ NULL,
+ GetMozcToolIcon
+ );
+ }
+ FcitxUISetStatusVisable(instance, "mozc-tool", false);
+ FcitxUISetStatusVisable(instance, "mozc-composition-mode", false);
+}
+
+FcitxMozc::FcitxMozc(const mozc::fcitx::FcitxMozc& )
+{
+
+}
+
+boolean CompositionMenuAction(struct _FcitxUIMenu *menu, int index)
+{
+ FcitxMozc* mozc = (FcitxMozc*) menu->priv;
+ if (index == mozc::commands::DIRECT) {
+ FcitxInstanceCloseIM(mozc->GetInstance(), FcitxInstanceGetCurrentIC(mozc->GetInstance()));
+ }
+ else {
+ mozc->SendCompositionMode((mozc::commands::CompositionMode) index);
+ }
+ return true;
+}
+
+void UpdateCompositionMenu(struct _FcitxUIMenu *menu)
+{
+ FcitxMozc* mozc = (FcitxMozc*) menu->priv;
+ menu->mark = mozc->GetCompositionMode();
+}
+
+boolean ToolMenuAction(struct _FcitxUIMenu *menu, int index)
+{
+ string args;
+ switch(index) {
+ case 0:
+ args = "--mode=config_dialog";
+ break;
+ case 1:
+ args = "--mode=dictionary_tool";
+ break;
+ case 2:
+ args = "--mode=hand_writing";
+ break;
+ case 3:
+ args = "--mode=character_palette";
+ break;
+ case 4:
+ args = "--mode=word_register_dialog";
+ break;
+ case 5:
+ args = "--mode=about_dialog";
+ break;
+ }
+ mozc::Process::SpawnMozcProcess("mozc_tool", args);
+ return true;
+}
+
+void UpdateToolMenu(struct _FcitxUIMenu *menu)
+{
+ return;
+}
+
+void FcitxMozc::InitializeMenu()
+{
+ FcitxMenuInit(&this->compositionMenu);
+ compositionMenu.name = strdup(_("Composition Mode"));
+ compositionMenu.candStatusBind = strdup("mozc-composition-mode");
+ compositionMenu.UpdateMenu = UpdateCompositionMenu;
+ compositionMenu.MenuAction = CompositionMenuAction;
+ compositionMenu.priv = this;
+ compositionMenu.isSubMenu = false;
+ int i;
+ for (i = 0; i < kNumCompositionModes; i ++)
+ FcitxMenuAddMenuItem(&compositionMenu, _(kPropCompositionModes[i].description), MENUTYPE_SIMPLE, NULL);
+
+ FcitxUIRegisterMenu(instance, &compositionMenu);
+
+ FcitxMenuInit(&this->toolMenu);
+ toolMenu.name = strdup(_("Mozc Tool"));
+ toolMenu.candStatusBind = strdup("mozc-tool");
+ toolMenu.UpdateMenu = UpdateToolMenu;
+ toolMenu.MenuAction = ToolMenuAction;
+ toolMenu.priv = this;
+ toolMenu.isSubMenu = false;
+ FcitxMenuAddMenuItem(&toolMenu, _("Configuration Tool"), MENUTYPE_SIMPLE, NULL);
+ FcitxMenuAddMenuItem(&toolMenu, _("Dictionary Tool"), MENUTYPE_SIMPLE, NULL);
+ FcitxMenuAddMenuItem(&toolMenu, _("Hand Writing"), MENUTYPE_SIMPLE, NULL);
+ FcitxMenuAddMenuItem(&toolMenu, _("Character Palette"), MENUTYPE_SIMPLE, NULL);
+ FcitxMenuAddMenuItem(&toolMenu, _("Add Word"), MENUTYPE_SIMPLE, NULL);
+ FcitxMenuAddMenuItem(&toolMenu, _("About Mozc"), MENUTYPE_SIMPLE, NULL);
+ FcitxUIRegisterMenu(instance, &toolMenu);
+}
+
+FcitxInputState* FcitxMozc::GetInputState()
+{
+ return input;
+}
+
+const std::string& FcitxMozc::GetIconFile(const std::string key)
+{
+ if (iconMap.count(key)) {
+ return iconMap[key];
+ }
+
+ char* retFile;
+ FILE* fp = FcitxXDGGetFileWithPrefix("mozc/icon", key.c_str(), "r", &retFile);
+ if (fp)
+ fclose(fp);
+ if (retFile) {
+ iconMap[key] = std::string(retFile);
+ free(retFile);
+ }
+ else {
+ iconMap[key] = "";
+ }
+ return iconMap[key];
+}
+
+
+const std::string& FcitxMozc::GetCurrentCompositionModeIcon() {
+ DCHECK(composition_mode_ < kNumCompositionModes);
+ if (composition_mode_ < kNumCompositionModes) {
+ return GetIconFile(kPropCompositionModes[composition_mode_].icon);
+ }
+ return empty_string;
+}
+
+} // namespace fcitx
+
+} // namespace mozc_unix_scim
diff --git a/src/unix/fcitx/fcitx_mozc.h b/src/unix/fcitx/fcitx_mozc.h
new file mode 100644
index 0000000..8d14c46
--- /dev/null
+++ b/src/unix/fcitx/fcitx_mozc.h
@@ -0,0 +1,159 @@
+// Copyright 2012~2013, Weng Xuetian <wengxt@gmail.com>
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef MOZC_UNIX_FCITX_FCITX_MOZC_H_
+#define MOZC_UNIX_FCITX_FCITX_MOZC_H_
+
+#include <fcitx/instance.h>
+#include <fcitx/candidate.h>
+#include <fcitx-config/hotkey.h>
+#include <libintl.h>
+#include "base/base.h" // for DISALLOW_COPY_AND_ASSIGN.
+#include "base/run_level.h"
+#include "session/commands.pb.h"
+
+#define _(x) dgettext("fcitx-mozc", (x))
+
+INPUT_RETURN_VALUE FcitxMozcGetCandidateWord(void* arg, FcitxCandidateWord* candWord);;
+
+namespace mozc
+{
+
+namespace fcitx
+{
+const int32 kBadCandidateId = -12345;
+class IMEngineFactory;
+class MozcConnectionInterface;
+class MozcResponseParser;
+class KeyTranslator;
+
+struct PreeditItem {
+ std::string str;
+ FcitxMessageType type;
+};
+
+// Preedit string and its attributes.
+struct PreeditInfo
+{
+ uint32 cursor_pos;
+
+ std::vector<PreeditItem> preedit;
+};
+
+class FcitxMozc
+{
+public:
+ // This constructor is used by unittests.
+ FcitxMozc ( FcitxInstance* instance,
+ MozcConnectionInterface *connection,
+ MozcResponseParser *parser );
+ virtual ~FcitxMozc();
+
+ bool process_key_event (FcitxKeySym sym, uint32 keycode, uint32 state, bool layout_is_jp, bool is_key_up);
+ void select_candidate ( FcitxCandidateWord* candWord );
+ void resetim();
+ void reset();
+ void init();
+ void focus_out();
+
+ // Functions called by the MozcResponseParser class to update UI.
+
+ // Displays a 'result' (aka 'commit string') on FCITX UI.
+ void SetResultString ( const std::string &result_string );
+ // Displays a 'preedit' string on FCITX UI. This function takes ownership
+ // of preedit_info. If the parameter is NULL, hides the string currently
+ // displayed.
+ void SetPreeditInfo ( const PreeditInfo *preedit_info );
+ // Displays an auxiliary message (e.g., an error message, a title of
+ // candidate window). If the string is empty (""), hides the message
+ // currently being displayed.
+ void SetAuxString ( const std::string &str );
+ // Sets a current composition mode (e.g., Hankaku Katakana).
+ void SetCompositionMode ( mozc::commands::CompositionMode mode );
+
+ void SendCompositionMode ( mozc::commands::CompositionMode mode );
+
+ // Sets the url to be opened by the default browser.
+ void SetUrl ( const string &url );
+
+ const std::string& GetIconFile(const std::string key);
+
+ const std::string& GetCurrentCompositionModeIcon();
+
+ mozc::commands::CompositionMode GetCompositionMode() { return composition_mode_; }
+
+ FcitxInstance* GetInstance() { return instance; }
+
+ FcitxInputState* GetInputState();
+
+private:
+ friend class FcitxMozcTest;
+
+ // Adds Mozc-specific icons to FCITX toolbar.
+ void InitializeBar();
+
+ void InitializeMenu();
+
+ // Parses the response from mozc_server. Returns whether the server consumes
+ // the input or not (true means 'consumed').
+ bool ParseResponse ( const mozc::commands::Output &request );
+
+ void ClearAll();
+ void DrawAll();
+ void DrawPreeditInfo();
+ void DrawAux();
+
+ // Open url_ with a default browser.
+ void OpenUrl();
+
+ FcitxInstance* instance;
+ FcitxInputState* input;
+ const scoped_ptr<MozcConnectionInterface> connection_;
+ const scoped_ptr<MozcResponseParser> parser_;
+
+ // Strings and a window currently displayed on FCITX UI.
+ scoped_ptr<const PreeditInfo> preedit_info_;
+ std::string aux_; // error tooltip, or candidate window title.
+ string url_; // URL to be opened by a browser.
+ mozc::commands::CompositionMode composition_mode_;
+
+ std::map<std::string, std::string> iconMap;
+
+ FcitxUIMenu compositionMenu;
+ FcitxUIMenu toolMenu;
+
+ DISALLOW_COPY_AND_ASSIGN ( FcitxMozc );
+};
+
+} // namespace fcitx
+
+} // namespace mozc
+
+#endif // MOZC_UNIX_FCITX_FCITX_MOZC_H_
+
diff --git a/src/unix/fcitx/gen_fcitx_mozc_i18n.sh b/src/unix/fcitx/gen_fcitx_mozc_i18n.sh
new file mode 100755
index 0000000..b730b82
--- /dev/null
+++ b/src/unix/fcitx/gen_fcitx_mozc_i18n.sh
@@ -0,0 +1,11 @@
+#!/bin/sh
+objdir="$1"
+
+cd po || exit 1
+
+mkdir -p "$1"
+
+for pofile in *.po
+do
+ msgfmt "$pofile" -o "$1/${pofile/po/mo}"
+done
\ No newline at end of file
diff --git a/src/unix/fcitx/mozc.conf b/src/unix/fcitx/mozc.conf
new file mode 100644
index 0000000..ad19230
--- /dev/null
+++ b/src/unix/fcitx/mozc.conf
@@ -0,0 +1,7 @@
+[InputMethod]
+UniqueName=mozc
+Name=Mozc
+IconName=/usr/share/fcitx/mozc/icon/mozc.png
+Priority=1
+LangCode=ja
+Parent=fcitx-mozc
diff --git a/src/unix/fcitx/mozc_connection.cc b/src/unix/fcitx/mozc_connection.cc
new file mode 100755
index 0000000..fa44275
--- /dev/null
+++ b/src/unix/fcitx/mozc_connection.cc
@@ -0,0 +1,178 @@
+// Copyright 2010-2012, Google Inc.
+// Copyright 2012~2013, Weng Xuetian <wengxt@gmail.com>
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// workaround
+#define _FCITX_LOG_H_
+
+#include "unix/fcitx/mozc_connection.h"
+
+#include <string>
+
+#include "base/logging.h"
+#include "base/util.h"
+#include "client/client.h"
+#include "ipc/ipc.h"
+#include "session/commands.pb.h"
+#include "session/ime_switch_util.h"
+#include "unix/fcitx/fcitx_key_event_handler.h"
+
+namespace mozc {
+namespace fcitx {
+
+MozcConnectionInterface::~MozcConnectionInterface() {
+}
+
+MozcConnection::MozcConnection(
+ mozc::client::ServerLauncherInterface *server_launcher,
+ mozc::IPCClientFactoryInterface *client_factory)
+ : handler_(new KeyEventHandler),
+ preedit_method_(mozc::config::Config::ROMAN),
+ client_factory_(client_factory) {
+ VLOG(1) << "MozcConnection is created";
+ mozc::client::ClientInterface *client =
+ mozc::client::ClientFactory::NewClient();
+ client->SetServerLauncher(server_launcher);
+ client->SetIPCClientFactory(client_factory_.get());
+ client_.reset(client);
+
+ mozc::config::Config config;
+ if (client_->EnsureConnection() &&
+ client_->GetConfig(&config) && config.has_preedit_method()) {
+ preedit_method_ = config.preedit_method();
+ }
+ VLOG(1)
+ << "Current preedit method is "
+ << (preedit_method_ == mozc::config::Config::ROMAN ? "Roman" : "Kana");
+}
+
+MozcConnection::~MozcConnection() {
+ client_->SyncData();
+ VLOG(1) << "MozcConnection is destroyed";
+}
+
+bool MozcConnection::TrySendKeyEvent(
+ FcitxKeySym sym, uint32 keycode, uint32 state,
+ mozc::commands::CompositionMode composition_mode,
+ bool layout_is_jp,
+ bool is_key_up,
+ mozc::commands::Output *out,
+ string *out_error) const {
+ DCHECK(out);
+ DCHECK(out_error);
+
+ // Call EnsureConnection just in case MozcConnection::MozcConnection() fails
+ // to establish the server connection.
+ if (!client_->EnsureConnection()) {
+ *out_error = "EnsureConnection failed";
+ VLOG(1) << "EnsureConnection failed";
+ return false;
+ }
+
+ mozc::commands::KeyEvent event;
+ if (!handler_->GetKeyEvent(sym, keycode, state, preedit_method_, layout_is_jp, is_key_up, &event))
+ return false;
+
+ if ((composition_mode == mozc::commands::DIRECT) &&
+ !mozc::config::ImeSwitchUtil::IsDirectModeCommand(event)) {
+ VLOG(1) << "In DIRECT mode. Not consumed.";
+ return false; // not consumed.
+ }
+
+ VLOG(1) << "TrySendKeyEvent: " << endl << event.DebugString();
+ if (!client_->SendKey(event, out)) {
+ *out_error = "SendKey failed";
+ VLOG(1) << "ERROR";
+ return false;
+ }
+ VLOG(1) << "OK: " << endl << out->DebugString();
+ return true;
+}
+
+bool MozcConnection::TrySendClick(int32 unique_id,
+ mozc::commands::Output *out,
+ string *out_error) const {
+ DCHECK(out);
+ DCHECK(out_error);
+
+ mozc::commands::SessionCommand command;
+ command.set_type(mozc::commands::SessionCommand::SELECT_CANDIDATE);
+ command.set_id(unique_id);
+ return TrySendCommandInternal(command, out, out_error);
+}
+
+bool MozcConnection::TrySendCompositionMode(
+ mozc::commands::CompositionMode mode,
+ mozc::commands::Output *out,
+ string *out_error) const {
+ DCHECK(out);
+ DCHECK(out_error);
+
+ mozc::commands::SessionCommand command;
+ command.set_type(mozc::commands::SessionCommand::SWITCH_INPUT_MODE);
+ command.set_composition_mode(mode);
+ return TrySendCommandInternal(command, out, out_error);
+}
+
+bool MozcConnection::TrySendCommand(
+ mozc::commands::SessionCommand::CommandType type,
+ mozc::commands::Output *out,
+ string *out_error) const {
+ DCHECK(out);
+ DCHECK(out_error);
+
+ mozc::commands::SessionCommand command;
+ command.set_type(type);
+ return TrySendCommandInternal(command, out, out_error);
+}
+
+bool MozcConnection::TrySendCommandInternal(
+ const mozc::commands::SessionCommand& command,
+ mozc::commands::Output *out,
+ string *out_error) const {
+ VLOG(1) << "TrySendCommandInternal: " << endl << command.DebugString();
+ if (!client_->SendCommand(command, out)) {
+ *out_error = "SendCommand failed";
+ VLOG(1) << "ERROR";
+ return false;
+ }
+ VLOG(1) << "OK: " << endl << out->DebugString();
+ return true;
+}
+
+MozcConnection *MozcConnection::CreateMozcConnection() {
+ mozc::client::ServerLauncher *server_launcher
+ = new mozc::client::ServerLauncher;
+
+ return new MozcConnection(server_launcher, new mozc::IPCClientFactory);
+}
+
+} // namespace fcitx
+
+} // namespace mozc
diff --git a/src/unix/fcitx/mozc_connection.h b/src/unix/fcitx/mozc_connection.h
new file mode 100755
index 0000000..99fca0d
--- /dev/null
+++ b/src/unix/fcitx/mozc_connection.h
@@ -0,0 +1,138 @@
+// Copyright 2010-2012, Google Inc.
+// Copyright 2012~2013, Weng Xuetian <wengxt@gmail.com>
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef MOZC_UNIX_FCITX_MOZC_CONNECTION_H_
+#define MOZC_UNIX_FCITX_MOZC_CONNECTION_H_
+
+#include <string>
+#include <fcitx-config/hotkey.h>
+
+#include "base/base.h"
+#include "session/commands.pb.h"
+#include "unix/fcitx/fcitx_key_event_handler.h"
+
+namespace mozc {
+
+class IPCClientInterface;
+class IPCClientFactoryInterface;
+
+namespace client {
+class ClientInterface;
+class ServerLauncherInterface;
+} // namespace client
+
+} // namespace mozc
+
+namespace mozc {
+
+namespace fcitx {
+
+class KeyTranslator;
+
+// This class is for mozc_response_parser_test.cc.
+class MozcConnectionInterface {
+ public:
+ virtual ~MozcConnectionInterface();
+
+ virtual bool TrySendKeyEvent(FcitxKeySym sym, uint32 keycode, uint32 state,
+ mozc::commands::CompositionMode composition_mode,
+ bool layout_is_jp,
+ bool is_key_up,
+ mozc::commands::Output *out,
+ string *out_error) const = 0;
+ virtual bool TrySendClick(int32 unique_id,
+ mozc::commands::Output *out,
+ string *out_error) const = 0;
+ virtual bool TrySendCompositionMode(mozc::commands::CompositionMode mode,
+ mozc::commands::Output *out,
+ string *out_error) const = 0;
+ virtual bool TrySendCommand(mozc::commands::SessionCommand::CommandType type,
+ mozc::commands::Output *out,
+ string *out_error) const = 0;
+};
+
+class MozcConnection : public MozcConnectionInterface {
+ public:
+ static const int kNoSession;
+
+ static MozcConnection *CreateMozcConnection();
+ virtual ~MozcConnection();
+
+ // Sends key event to the server. If the IPC succeeds, returns true and the
+ // response is stored on 'out' (and 'out_error' is not modified). If the IPC
+ // fails, returns false and the error message is stored on 'out_error'. In
+ // this case, 'out' is not modified.
+ virtual bool TrySendKeyEvent(FcitxKeySym sym, uint32 keycode, uint32 state,
+ mozc::commands::CompositionMode composition_mode,
+ bool layout_is_jp,
+ bool is_key_up,
+ mozc::commands::Output *out,
+ string *out_error) const;
+
+ // Sends 'mouse click on the candidate window' event to the server.
+ virtual bool TrySendClick(int32 unique_id,
+ mozc::commands::Output *out,
+ string *out_error) const;
+
+ // Sends composition mode to the server.
+ virtual bool TrySendCompositionMode(mozc::commands::CompositionMode mode,
+ mozc::commands::Output *out,
+ string *out_error) const;
+
+ // Sends a command to the server.
+ virtual bool TrySendCommand(mozc::commands::SessionCommand::CommandType type,
+ mozc::commands::Output *out,
+ string *out_error) const;
+
+ private:
+ friend class MozcConnectionTest;
+ MozcConnection(mozc::client::ServerLauncherInterface *server_launcher,
+ mozc::IPCClientFactoryInterface *client_factory);
+
+ bool TrySendCommandInternal(
+ const mozc::commands::SessionCommand& command,
+ mozc::commands::Output *out,
+ string *out_error) const;
+
+ const scoped_ptr<KeyEventHandler> handler_;
+ mozc::config::Config::PreeditMethod preedit_method_;
+ // Keep definition order of client_factory_ and client_.
+ // We should delete client_ before deleting client_factory_.
+ scoped_ptr<mozc::IPCClientFactoryInterface> client_factory_;
+ scoped_ptr<mozc::client::ClientInterface> client_;
+
+ DISALLOW_COPY_AND_ASSIGN(MozcConnection);
+};
+
+} // namespace fcitx
+
+} // namespace mozc
+
+#endif // MOZC_UNIX_SCIM_MOZC_CONNECTION_H_
diff --git a/src/unix/fcitx/mozc_response_parser.cc b/src/unix/fcitx/mozc_response_parser.cc
new file mode 100755
index 0000000..ed4330f
--- /dev/null
+++ b/src/unix/fcitx/mozc_response_parser.cc
@@ -0,0 +1,289 @@
+// Copyright 2010-2012, Google Inc.
+// Copyright 2012~2013, Weng Xuetian <wengxt@gmail.com>
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// workaround
+#define _FCITX_LOG_H_
+
+#include "unix/fcitx/mozc_response_parser.h"
+
+#include <string>
+#include <vector>
+
+#include "base/logging.h"
+#include "session/commands.pb.h"
+#include "unix/fcitx/fcitx_mozc.h"
+#include <fcitx/candidate.h>
+
+namespace {
+
+// Returns true if the candidate window contains only suggestions.
+bool IsSuggestion(const mozc::commands::Candidates &candidates) {
+ return !candidates.has_focused_index();
+}
+
+// Returns a position that determines a preedit cursor position _AND_ top-left
+// position of a candidate window. Note that we can't set these two positions
+// independently. That's a SCIM's limitation.
+uint32 GetCursorPosition(const mozc::commands::Output &response) {
+ if (!response.has_preedit()) {
+ return 0;
+ }
+ if (response.preedit().has_highlighted_position()) {
+ return response.preedit().highlighted_position();
+ }
+ return response.preedit().cursor();
+}
+
+string CreateDescriptionString(const string &description) {
+ return " [" + description + "]";
+}
+
+} // namespace
+
+namespace mozc {
+
+namespace fcitx {
+
+MozcResponseParser::MozcResponseParser()
+ : use_annotation_(false) {
+}
+
+MozcResponseParser::~MozcResponseParser() {
+}
+
+bool MozcResponseParser::ParseResponse(const mozc::commands::Output &response,
+ FcitxMozc *fcitx_mozc) const {
+ DCHECK(fcitx_mozc);
+ if (!fcitx_mozc) {
+ return false;
+ }
+
+ // We should check the mode field first since the response for a
+ // SWITCH_INPUT_MODE request only contains mode and id fields.
+ if (response.has_mode()) {
+ fcitx_mozc->SetCompositionMode(response.mode());
+ }
+
+ if (!response.consumed()) {
+ // The key was not consumed by Mozc.
+ return false;
+ }
+
+ if (response.has_result()) {
+ const mozc::commands::Result &result = response.result();
+ ParseResult(result, fcitx_mozc);
+ }
+
+ // First, determine the cursor position.
+ if (response.has_preedit()) {
+ const mozc::commands::Preedit &preedit = response.preedit();
+ ParsePreedit(preedit, GetCursorPosition(response), fcitx_mozc);
+ }
+
+ // Then show the candidate window.
+ if (response.has_candidates()) {
+ const mozc::commands::Candidates &candidates = response.candidates();
+ ParseCandidates(candidates, fcitx_mozc);
+ }
+
+ if (response.has_url()) {
+ const string &url = response.url();
+ fcitx_mozc->SetUrl(url);
+ }
+
+ return true; // mozc consumed the key.
+}
+
+void MozcResponseParser::set_use_annotation(bool use_annotation) {
+ use_annotation_ = use_annotation;
+}
+
+void MozcResponseParser::ParseResult(const mozc::commands::Result &result,
+ FcitxMozc *fcitx_mozc) const {
+ switch (result.type()) {
+ case mozc::commands::Result::NONE: {
+ fcitx_mozc->SetAuxString("No result"); // not a fatal error.
+ break;
+ }
+ case mozc::commands::Result::STRING: {
+ fcitx_mozc->SetResultString(result.value());
+ break;
+ }
+ }
+}
+
+void MozcResponseParser::ParseCandidates(
+ const mozc::commands::Candidates &candidates, FcitxMozc *fcitx_mozc) const {
+ const commands::Footer &footer = candidates.footer();
+ if (candidates.has_footer()) {
+ string auxString;
+ if (footer.has_label()) {
+ // TODO(yusukes,mozc-team): label() is not localized. Currently, it's always
+ // written in Japanese (in UTF-8).
+ auxString += footer.label();
+ } else if (footer.has_sub_label()) {
+ // Windows client shows sub_label() only when label() is not specified. We
+ // follow the policy.
+ auxString += footer.sub_label();
+ }
+
+ if (footer.has_index_visible() && footer.index_visible()) {
+ // Max size of candidates is 200 so 128 is sufficient size for the buffer.
+ char index_buf[128] = {0};
+ const int result = snprintf(index_buf,
+ sizeof(index_buf) - 1,
+ "%s%d/%d",
+ (auxString.empty() ? "" : " "),
+ candidates.focused_index() + 1,
+ candidates.size());
+ DCHECK_GE(result, 0) << "snprintf in ComposeAuxiliaryText failed";
+ auxString += index_buf;
+ }
+ fcitx_mozc->SetAuxString(auxString);
+ }
+
+ FcitxCandidateWordList* candList = FcitxInputStateGetCandidateList(fcitx_mozc->GetInputState());
+ FcitxCandidateWordReset(candList);
+ FcitxCandidateWordSetPageSize(candList, 9);
+ char strChoose[] = "\0\0\0\0\0\0\0\0\0\0\0";
+
+ int focused_index = -1;
+ int local_index = -1;
+ if (candidates.has_focused_index()) {
+ focused_index = candidates.focused_index();
+ }
+ for (int i = 0; i < candidates.candidate_size(); ++i) {
+ const uint32 index = candidates.candidate(i).index();
+ FcitxMessageType type;
+ if (focused_index != -1 && index == focused_index) {
+ local_index = i;
+ type = MSG_FIRSTCAND;
+ }
+ else
+ type = MSG_OTHER;
+ int32* id = (int32*) fcitx_utils_malloc0(sizeof(int32));
+ FcitxCandidateWord candWord;
+ candWord.callback = FcitxMozcGetCandidateWord;
+ candWord.extraType = MSG_OTHER;
+ candWord.strExtra = NULL;
+ candWord.priv = id;
+ candWord.strWord = NULL;
+ candWord.wordType = type;
+ candWord.owner = fcitx_mozc;
+
+ string value;
+ if (use_annotation_ &&
+ candidates.candidate(i).has_annotation() &&
+ candidates.candidate(i).annotation().has_prefix()) {
+ value = candidates.candidate(i).annotation().prefix();
+ }
+ value += candidates.candidate(i).value();
+ if (use_annotation_ &&
+ candidates.candidate(i).has_annotation() &&
+ candidates.candidate(i).annotation().has_suffix()) {
+ value += candidates.candidate(i).annotation().suffix();
+ }
+ if (use_annotation_ &&
+ candidates.candidate(i).has_annotation() &&
+ candidates.candidate(i).annotation().has_description()) {
+ // Display descriptions ([HALF][KATAKANA], [GREEK], [Black square], etc).
+ value += CreateDescriptionString(
+ candidates.candidate(i).annotation().description());
+ }
+
+ candWord.strWord = strdup(value.c_str());
+
+ if (candidates.candidate(i).has_id()) {
+ const int32 cid = candidates.candidate(i).id();
+ DCHECK_NE(kBadCandidateId, cid) << "Unexpected id is passed.";
+ *id = cid;
+ } else {
+ // The parent node of the cascading window does not have an id since the
+ // node does not contain a candidate word.
+ *id = kBadCandidateId;
+ }
+ FcitxCandidateWordAppend(candList, &candWord);
+ }
+
+ if (footer.has_index_visible() && footer.index_visible())
+ FcitxCandidateWordSetChoose(candList, DIGIT_STR_CHOOSE);
+ else
+ FcitxCandidateWordSetChoose(candList, strChoose);
+ FcitxCandidateWordSetFocus(candList, local_index);
+}
+
+static int GetRawCursorPos(const char * str, int upos)
+{
+ unsigned int i;
+ int pos = 0;
+ for (i = 0; i < upos; i++) {
+ pos += fcitx_utf8_char_len(fcitx_utf8_get_nth_char((char*)str, i));
+ }
+ return pos;
+}
+
+
+void MozcResponseParser::ParsePreedit(const mozc::commands::Preedit &preedit,
+ uint32 position,
+ FcitxMozc *fcitx_mozc) const {
+ PreeditInfo *info = new PreeditInfo;
+ std::string s;
+
+ for (int i = 0; i < preedit.segment_size(); ++i) {
+ const mozc::commands::Preedit_Segment &segment = preedit.segment(i);
+ const std::string &str = segment.value();
+ FcitxMessageType type = MSG_INPUT;
+
+ switch (segment.annotation()) {
+ case mozc::commands::Preedit_Segment::NONE:
+ type = (FcitxMessageType) (MSG_INPUT | MSG_NOUNDERLINE);
+ break;
+ case mozc::commands::Preedit_Segment::UNDERLINE:
+ type = (FcitxMessageType) (MSG_TIPS);
+ break;
+ case mozc::commands::Preedit_Segment::HIGHLIGHT:
+ type = (FcitxMessageType) (MSG_CODE | MSG_NOUNDERLINE | MSG_HIGHLIGHT);
+ break;
+ }
+ s += str;
+
+ PreeditItem item;
+ item.type = type;
+ item.str = str;
+ info->preedit.push_back(item);
+ }
+ info->cursor_pos = GetRawCursorPos(s.c_str(), position);
+
+ fcitx_mozc->SetPreeditInfo(info);
+}
+
+} // namespace fcitx
+
+} // namespace mozc
diff --git a/src/unix/fcitx/mozc_response_parser.h b/src/unix/fcitx/mozc_response_parser.h
new file mode 100755
index 0000000..ff84c53
--- /dev/null
+++ b/src/unix/fcitx/mozc_response_parser.h
@@ -0,0 +1,94 @@
+// Copyright 2010-2012, Google Inc.
+// Copyright 2012~2013, Weng Xuetian <wengxt@gmail.com>
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef MOZC_UNIX_FCITX_MOZC_RESPONSE_PARSER_H_
+#define MOZC_UNIX_FCITX_MOZC_RESPONSE_PARSER_H_
+
+#include "base/base.h" // for DISALLOW_COPY_AND_ASSIGN.
+
+namespace mozc
+{
+namespace commands
+{
+
+class Candidates;
+class Input;
+class Output;
+class Preedit;
+class Result;
+
+} // namespace commands
+} // namespace mozc
+
+namespace mozc
+{
+
+namespace fcitx
+{
+
+class FcitxMozc;
+
+// This class parses IPC response from mozc_server (mozc::commands::Output) and
+// updates the FCITX UI.
+class MozcResponseParser
+{
+public:
+ MozcResponseParser();
+ ~MozcResponseParser();
+
+ // Parses a response from Mozc server and sets persed information on fcitx_mozc
+ // object. Returns true if response.consumed() is true. fcitx_mozc must be non
+ // NULL. This function does not take ownership of fcitx_mozc.
+ bool ParseResponse ( const mozc::commands::Output &response,
+ FcitxMozc *fcitx_mozc ) const;
+
+ // Setter for use_annotation_. If use_annotation_ is true, ParseCandidates()
+ // uses annotation infomation.
+ void set_use_annotation ( bool use_annotation );
+
+private:
+ void ParseResult ( const mozc::commands::Result &result,
+ FcitxMozc *fcitx_mozc ) const;
+ void ParseCandidates ( const mozc::commands::Candidates &candidates,
+ FcitxMozc *fcitx_mozc ) const;
+ void ParsePreedit ( const mozc::commands::Preedit &preedit,
+ uint32 position,
+ FcitxMozc *fcitx_mozc ) const;
+
+ bool use_annotation_;
+
+ DISALLOW_COPY_AND_ASSIGN ( MozcResponseParser );
+};
+
+} // namespace fcitx
+
+} // namespace mozc
+
+#endif // MOZC_UNIX_FCITX_MOZC_RESPONSE_PARSER_H_
diff --git a/src/unix/fcitx/po/Messages.sh b/src/unix/fcitx/po/Messages.sh
new file mode 100755
index 0000000..be34171
--- /dev/null
+++ b/src/unix/fcitx/po/Messages.sh
@@ -0,0 +1,33 @@
+#!/bin/sh
+
+BASEDIR="../" # root of translatable sources
+PROJECT="fcitx-mozc" # project name
+BUGADDR="fcitx-dev@googlegroups.com" # MSGID-Bugs
+WDIR="`pwd`" # working dir
+
+echo "Preparing rc files"
+
+echo "Done preparing rc files"
+echo "Extracting messages"
+
+# see above on sorting
+
+find "${BASEDIR}" -name '*.cc' -o -name '*.h' -o -name '*.c' | sort > "${WDIR}/infiles.list"
+
+xgettext --from-code=UTF-8 -k_ -kN_ --msgid-bugs-address="${BUGADDR}" --files-from=infiles.list \
+ -D "${BASEDIR}" -D "${WDIR}" -o "${PROJECT}.pot" || \
+ { echo "error while calling xgettext. aborting."; exit 1; }
+echo "Done extracting messages"
+
+echo "Merging translations"
+catalogs=`find . -name '*.po'`
+for cat in $catalogs; do
+ echo "$cat"
+ msgmerge -o "$cat.new" "$cat" "${WDIR}/${PROJECT}.pot"
+ mv "$cat.new" "$cat"
+done
+
+echo "Done merging translations"
+echo "Cleaning up"
+rm "${WDIR}/infiles.list"
+echo "Done"
diff --git a/src/unix/fcitx/po/fcitx-mozc.pot b/src/unix/fcitx/po/fcitx-mozc.pot
new file mode 100644
index 0000000..5a6536a
--- /dev/null
+++ b/src/unix/fcitx/po/fcitx-mozc.pot
@@ -0,0 +1,78 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: fcitx-dev@googlegroups.com\n"
+"POT-Creation-Date: 2012-12-04 20:03-0500\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=CHARSET\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: ../fcitx_mozc.cc:56
+msgid "Direct"
+msgstr ""
+
+#: ../fcitx_mozc.cc:61
+msgid "Hiragana"
+msgstr ""
+
+#: ../fcitx_mozc.cc:66
+msgid "Full Katakana"
+msgstr ""
+
+#: ../fcitx_mozc.cc:71
+msgid "Half ASCII"
+msgstr ""
+
+#: ../fcitx_mozc.cc:76
+msgid "Full ASCII"
+msgstr ""
+
+#: ../fcitx_mozc.cc:81
+msgid "Half Katakana"
+msgstr ""
+
+#: ../fcitx_mozc.cc:377 ../fcitx_mozc.cc:378 ../fcitx_mozc.cc:456
+msgid "Composition Mode"
+msgstr ""
+
+#: ../fcitx_mozc.cc:388 ../fcitx_mozc.cc:389
+msgid "Tool"
+msgstr ""
+
+#: ../fcitx_mozc.cc:469
+msgid "Mozc Tool"
+msgstr ""
+
+#: ../fcitx_mozc.cc:475
+msgid "Configuration Tool"
+msgstr ""
+
+#: ../fcitx_mozc.cc:476
+msgid "Dictionary Tool"
+msgstr ""
+
+#: ../fcitx_mozc.cc:477
+msgid "Hand Writing"
+msgstr ""
+
+#: ../fcitx_mozc.cc:478
+msgid "Character Palette"
+msgstr ""
+
+#: ../fcitx_mozc.cc:479
+msgid "Add Word"
+msgstr ""
+
+#: ../fcitx_mozc.cc:480
+msgid "About Mozc"
+msgstr ""
diff --git a/src/unix/fcitx/po/ja.po b/src/unix/fcitx/po/ja.po
new file mode 100644
index 0000000..a1685e6
--- /dev/null
+++ b/src/unix/fcitx/po/ja.po
@@ -0,0 +1,80 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+#
+# Translators:
+# いくや あわしろ <ikunya@gmail.com>, 2012.
+# <wengxt@gmail.com>, 2012.
+msgid ""
+msgstr ""
+"Project-Id-Version: fcitx\n"
+"Report-Msgid-Bugs-To: fcitx-dev@googlegroups.com\n"
+"POT-Creation-Date: 2012-12-04 20:03-0500\n"
+"PO-Revision-Date: 2012-11-24 01:03+0000\n"
+"Last-Translator: いくや あわしろ <ikunya@gmail.com>\n"
+"Language-Team: Japanese (http://www.transifex.com/projects/p/fcitx/language/ja/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: ja\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+
+#: ../fcitx_mozc.cc:56
+msgid "Direct"
+msgstr "直接入力"
+
+#: ../fcitx_mozc.cc:61
+msgid "Hiragana"
+msgstr "ひらがな"
+
+#: ../fcitx_mozc.cc:66
+msgid "Full Katakana"
+msgstr "全角カタカナ"
+
+#: ../fcitx_mozc.cc:71
+msgid "Half ASCII"
+msgstr "半角英数"
+
+#: ../fcitx_mozc.cc:76
+msgid "Full ASCII"
+msgstr "全角英数"
+
+#: ../fcitx_mozc.cc:81
+msgid "Half Katakana"
+msgstr "半角カタカナ"
+
+#: ../fcitx_mozc.cc:377 ../fcitx_mozc.cc:378 ../fcitx_mozc.cc:456
+msgid "Composition Mode"
+msgstr "変換モード"
+
+#: ../fcitx_mozc.cc:388 ../fcitx_mozc.cc:389
+msgid "Tool"
+msgstr "ツール"
+
+#: ../fcitx_mozc.cc:469
+msgid "Mozc Tool"
+msgstr "Mozc ツール"
+
+#: ../fcitx_mozc.cc:475
+msgid "Configuration Tool"
+msgstr "設定ツール"
+
+#: ../fcitx_mozc.cc:476
+msgid "Dictionary Tool"
+msgstr "辞書ツール"
+
+#: ../fcitx_mozc.cc:477
+msgid "Hand Writing"
+msgstr "手書き文字認識"
+
+#: ../fcitx_mozc.cc:478
+msgid "Character Palette"
+msgstr "文字パレット"
+
+#: ../fcitx_mozc.cc:479
+msgid "Add Word"
+msgstr "単語登録"
+
+#: ../fcitx_mozc.cc:480
+msgid "About Mozc"
+msgstr "Mozc について"
diff --git a/src/unix/fcitx/po/zh_CN.po b/src/unix/fcitx/po/zh_CN.po
new file mode 100644
index 0000000..01b0b4d
--- /dev/null
+++ b/src/unix/fcitx/po/zh_CN.po
@@ -0,0 +1,79 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+#
+# Translators:
+# Weng Xuetian <wengxt@gmail.com>, 2012.
+msgid ""
+msgstr ""
+"Project-Id-Version: fcitx\n"
+"Report-Msgid-Bugs-To: fcitx-dev@googlegroups.com\n"
+"POT-Creation-Date: 2012-12-04 20:03-0500\n"
+"PO-Revision-Date: 2012-04-07 03:47+0000\n"
+"Last-Translator: Xuetian Weng <wengxt@gmail.com>\n"
+"Language-Team: Chinese (China) <fcitx-dev@googlegroups.com>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: zh_CN\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+
+#: ../fcitx_mozc.cc:56
+msgid "Direct"
+msgstr "直接键盘输入"
+
+#: ../fcitx_mozc.cc:61
+msgid "Hiragana"
+msgstr "平假名"
+
+#: ../fcitx_mozc.cc:66
+msgid "Full Katakana"
+msgstr "全角片假名"
+
+#: ../fcitx_mozc.cc:71
+msgid "Half ASCII"
+msgstr "半角 ASCII"
+
+#: ../fcitx_mozc.cc:76
+msgid "Full ASCII"
+msgstr "全角 ASCII"
+
+#: ../fcitx_mozc.cc:81
+msgid "Half Katakana"
+msgstr "半角片假名"
+
+#: ../fcitx_mozc.cc:377 ../fcitx_mozc.cc:378 ../fcitx_mozc.cc:456
+msgid "Composition Mode"
+msgstr "编辑模式"
+
+#: ../fcitx_mozc.cc:388 ../fcitx_mozc.cc:389
+msgid "Tool"
+msgstr "工具"
+
+#: ../fcitx_mozc.cc:469
+msgid "Mozc Tool"
+msgstr "Mozc 工具"
+
+#: ../fcitx_mozc.cc:475
+msgid "Configuration Tool"
+msgstr "配置工具"
+
+#: ../fcitx_mozc.cc:476
+msgid "Dictionary Tool"
+msgstr "词典工具"
+
+#: ../fcitx_mozc.cc:477
+msgid "Hand Writing"
+msgstr "手写输入"
+
+#: ../fcitx_mozc.cc:478
+msgid "Character Palette"
+msgstr "字符映射表"
+
+#: ../fcitx_mozc.cc:479
+msgid "Add Word"
+msgstr "添加单词"
+
+#: ../fcitx_mozc.cc:480
+msgid "About Mozc"
+msgstr "关于 Mozc"
diff --git a/src/unix/fcitx/po/zh_TW.po b/src/unix/fcitx/po/zh_TW.po
new file mode 100644
index 0000000..6631a5f
--- /dev/null
+++ b/src/unix/fcitx/po/zh_TW.po
@@ -0,0 +1,80 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+#
+# Translators:
+# Alisha <alisha.4m@gmail.com>, 2012.
+# Weng Xuetian <wengxt@gmail.com>, 2012.
+msgid ""
+msgstr ""
+"Project-Id-Version: fcitx\n"
+"Report-Msgid-Bugs-To: fcitx-dev@googlegroups.com\n"
+"POT-Creation-Date: 2012-12-04 20:03-0500\n"
+"PO-Revision-Date: 2012-04-07 03:47+0000\n"
+"Last-Translator: Xuetian Weng <wengxt@gmail.com>\n"
+"Language-Team: Chinese (Taiwan) (http://www.transifex.com/projects/p/fcitx/language/zh_TW/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: zh_TW\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+
+#: ../fcitx_mozc.cc:56
+msgid "Direct"
+msgstr "直接鍵盤輸入"
+
+#: ../fcitx_mozc.cc:61
+msgid "Hiragana"
+msgstr "平假名"
+
+#: ../fcitx_mozc.cc:66
+msgid "Full Katakana"
+msgstr "全形片假名"
+
+#: ../fcitx_mozc.cc:71
+msgid "Half ASCII"
+msgstr "半形 ASCII"
+
+#: ../fcitx_mozc.cc:76
+msgid "Full ASCII"
+msgstr "全形 ASCII"
+
+#: ../fcitx_mozc.cc:81
+msgid "Half Katakana"
+msgstr "半形片假名"
+
+#: ../fcitx_mozc.cc:377 ../fcitx_mozc.cc:378 ../fcitx_mozc.cc:456
+msgid "Composition Mode"
+msgstr "編輯模式"
+
+#: ../fcitx_mozc.cc:388 ../fcitx_mozc.cc:389
+msgid "Tool"
+msgstr "工具"
+
+#: ../fcitx_mozc.cc:469
+msgid "Mozc Tool"
+msgstr "Mozc 工具"
+
+#: ../fcitx_mozc.cc:475
+msgid "Configuration Tool"
+msgstr "設定工具"
+
+#: ../fcitx_mozc.cc:476
+msgid "Dictionary Tool"
+msgstr "字典工具"
+
+#: ../fcitx_mozc.cc:477
+msgid "Hand Writing"
+msgstr "手寫輸入"
+
+#: ../fcitx_mozc.cc:478
+msgid "Character Palette"
+msgstr "字符映射表"
+
+#: ../fcitx_mozc.cc:479
+msgid "Add Word"
+msgstr "添加單詞"
+
+#: ../fcitx_mozc.cc:480
+msgid "About Mozc"
+msgstr "關於 Mozc"