Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
SUSE:SLE-15-SP3:GA
mozc
fcitx-mozc-2.23.2815.102.1.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File fcitx-mozc-2.23.2815.102.1.patch of Package mozc
diff --git a/src/unix/fcitx/eim.cc b/src/unix/fcitx/eim.cc new file mode 100644 index 00000000..ce3e205a --- /dev/null +++ b/src/unix/fcitx/eim.cc @@ -0,0 +1,271 @@ +// 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. + +#include <fcitx/instance.h> +#include <fcitx/ime.h> +#include <fcitx/hook.h> +#include <fcitx/module.h> +#include <fcitx/keys.h> +#include <fcitx-config/xdg.h> +#include "fcitx_mozc.h" +#include "mozc_connection.h" +#include "mozc_response_parser.h" +#include "base/init_mozc.h" + +typedef struct _FcitxMozcState { + mozc::fcitx::FcitxMozc* mozc; + int inUsageState; +} 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); + bind_textdomain_codeset("fcitx-mozc", "UTF-8"); + + int argc = 1; + char argv0[] = "fcitx_mozc"; + char *_argv[] = { argv0 }; + char **argv = _argv; + mozc::InitMozc(argv[0], &argc, &argv, true); + 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); +} + +static const FcitxHotkey MOZC_CTRL_ALT_H[2] = { + {NULL, FcitxKey_H, FcitxKeyState_Ctrl_Alt}, + {NULL, FcitxKey_None, 0} +}; + +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()); + + if (mozcState->inUsageState) { + if (FcitxHotkeyIsHotKey(_sym, _state, FCITX_ESCAPE)) { + mozcState->inUsageState = false; + // send a dummy key to let server send us the candidate info back without side effect + mozcState->mozc->process_key_event(FcitxKey_VoidSymbol, 0, 0, CheckLayout(instance), false); + return IRV_DISPLAY_CANDWORDS; + } else { + return IRV_DO_NOTHING; + } + } + + if (FcitxHotkeyIsHotKey(_sym, _state, MOZC_CTRL_ALT_H)) { + std::pair< string, string > usage = mozcState->mozc->GetUsage(); + if (usage.first.size() != 0 || usage.second.size() != 0) { + mozcState->inUsageState = true; + FcitxCandidateWordList* candList = FcitxInputStateGetCandidateList(mozcState->mozc->GetInputState()); + + // clear preedit, but keep client preedit + FcitxMessages* preedit = FcitxInputStateGetPreedit(input); + FcitxMessagesSetMessageCount(preedit, 0); + FcitxInputStateSetShowCursor(input, false); + + // clear aux + FcitxMessages* auxUp = FcitxInputStateGetAuxUp(input); + FcitxMessages* auxDown = FcitxInputStateGetAuxDown(input); + FcitxMessagesSetMessageCount(auxUp, 0); + FcitxMessagesSetMessageCount(auxDown, 0); + + // clear candidate table + FcitxCandidateWordReset(candList); + FcitxCandidateWordSetPageSize(candList, 9); + FcitxCandidateWordSetLayoutHint(candList, CLH_Vertical); + FcitxCandidateWordSetChoose(candList, "\0\0\0\0\0\0\0\0\0\0"); + FcitxMessagesAddMessageAtLast(preedit, MSG_TIPS, "%s [%s]", usage.first.c_str(), _("Press Escape to go back")); + + UT_array* lines = fcitx_utils_split_string(usage.second.c_str(), '\n'); + utarray_foreach(line, lines, char*) { + FcitxCandidateWord candWord; + candWord.callback = NULL; + candWord.extraType = MSG_OTHER; + candWord.strExtra = NULL; + candWord.priv = NULL; + candWord.strWord = strdup(*line); + candWord.wordType = MSG_OTHER; + candWord.owner = NULL; + FcitxCandidateWordAppend(candList, &candWord); + } + utarray_free(lines); + return IRV_DISPLAY_MESSAGE; + } + } + + 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); + + if (mozcState->inUsageState) { + return IRV_DONOT_PROCESS; + } + + 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->inUsageState = false; + 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 00000000..65d0e113 --- /dev/null +++ b/src/unix/fcitx/fcitx-mozc.conf @@ -0,0 +1,11 @@ +[Addon] +Name=fcitx-mozc +GeneralName=Mozc +Comment=Mozc support for Fcitx +Category=InputMethod +Enabled=True +Library=fcitx-mozc.so +Type=SharedLibrary +SubConfig= +IMRegisterMethod=ConfigFile +LoadLocal=True diff --git a/src/unix/fcitx/fcitx.gyp b/src/unix/fcitx/fcitx.gyp new file mode 100644 index 00000000..e59d43a3 --- /dev/null +++ b/src/unix/fcitx/fcitx.gyp @@ -0,0 +1,111 @@ +# +# 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': { + 'use_fcitx%': 'YES', + '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', + '../../protocol/protocol.gyp:commands_proto', + ], + }, + 'conditions': [['use_fcitx=="YES"', { + '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', + '<(gen_out_dir)/po/de.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', + 'surrounding_text_util.cc', + 'eim.cc', + ], + 'dependencies': [ + '<@(fcitx_dependencies)', + 'gen_fcitx_mozc_i18n', + ], + 'cflags': [ + '<!@(pkg-config --cflags <@(pkg_config_libs))', + ], + 'include_dirs': [ + '<@(fcitx_dep_include_dirs)', + ], + 'defines': [ + 'LOCALEDIR="<!@(fcitx4-config --prefix)/share/locale/"', + ], + }, + ], + }, { + 'targets': [ + { + 'target_name': 'no_fcitx_dummy', + 'type': 'none', + } + ]} + ]], +} 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 00000000..4ec8b5d0 --- /dev/null +++ b/src/unix/fcitx/fcitx_key_event_handler.cc @@ -0,0 +1,243 @@ +// 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. + +#include "unix/fcitx/fcitx_key_event_handler.h" + +#include <map> + +#include "base/logging.h" +#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 std::map<uint32, commands::KeyEvent::ModifierKey> &data() { + return data_; + } + + private: + std::map<uint32, commands::KeyEvent::ModifierKey> data_; +}; + +// TODO(hsumita): Moves this function into member functions of +// KeyEventHandler. +void AddAdditionalModifiers( + std::set<commands::KeyEvent::ModifierKey> *modifier_keys_set) { + DCHECK(modifier_keys_set); + + const std::map<uint32, commands::KeyEvent::ModifierKey> &data = + Singleton<AdditionalModifiersData>::get()->data(); + + // Adds MODIFIER if there are (LEFT|RIGHT)_MODIFIER like LEFT_SHIFT. + for (std::set<commands::KeyEvent::ModifierKey>::const_iterator it = + modifier_keys_set->begin(); it != modifier_keys_set->end(); ++it) { + std::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 (std::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 00000000..7b759a18 --- /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 <memory> + +#include "base/port.h" +#include "protocol/config.pb.h" +#include "protocol/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); + + std::unique_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. + std::set<uint32> currently_pressed_modifiers_; + // Pending modifier keys. + std::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 00000000..44347abf --- /dev/null +++ b/src/unix/fcitx/fcitx_key_translator.cc @@ -0,0 +1,521 @@ +// 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. + +#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_VoidSymbol, mozc::commands::KeyEvent::NO_SPECIALKEY}, + {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}, + + // 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(std::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(std::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(std::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( + std::make_pair(kana_map_jp[i].code, std::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( + std::make_pair(kana_map_us[i].code, std::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 == 132 || keycode == 133) { + *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 00000000..b473672f --- /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/port.h" +#include "protocol/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 std::map<uint32, commands::KeyEvent::SpecialKey> SpecialKeyMap; + typedef std::map<uint32, commands::KeyEvent::ModifierKey> ModifierKeyMap; + typedef std::map<uint32, std::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 00000000..0c8ce651 --- /dev/null +++ b/src/unix/fcitx/fcitx_mozc.cc @@ -0,0 +1,557 @@ +// 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. + +#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 "base/file_util.h" +#include "base/system_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. +static_assert ( + mozc::commands::NUM_OF_COMPOSITIONS == arraysize ( kPropCompositionModes ), + "number of modes must match" ); + +} // 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 ) +{ + // mozc::Logging::SetVerboseLevel(1); + VLOG ( 1 ) << "FcitxMozc created."; + const bool is_vertical = true; + 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 ( + GetInstance(), 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); + connection_->UpdatePreeditMethod(); + } +} + +bool FcitxMozc::paging(bool prev) +{ + VLOG ( 1 ) << "paging"; + string error; + mozc::commands::SessionCommand::CommandType command = + prev ? mozc::commands::SessionCommand::CONVERT_PREV_PAGE + : mozc::commands::SessionCommand::CONVERT_NEXT_PAGE; + mozc::commands::Output raw_response; + if ( connection_->TrySendCommand ( + command, &raw_response, &error ) ) + { + parser_->ParseResponse ( raw_response, this ); + return true; + } + return false; +} + +// This function is called from SCIM framework when the ic gets focus. +void FcitxMozc::init() +{ + VLOG ( 1 ) << "init"; + boolean flag = true; + FcitxInstanceSetContext(instance, CONTEXT_DISABLE_AUTOENG, &flag); + FcitxInstanceSetContext(instance, CONTEXT_DISABLE_FULLWIDTH, &flag); + FcitxInstanceSetContext(instance, CONTEXT_DISABLE_QUICKPHRASE, &flag); + FcitxInstanceSetContext(instance, CONTEXT_IM_KEYBOARD_LAYOUT, "jp"); + FcitxInstanceSetContext(instance, "CONTEXT_DISABLE_AUTO_FIRST_CANDIDATE_HIGHTLIGHT", &flag); + + connection_->UpdatePreeditMethod(); + 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() +{ + FcitxMessages* preedit = FcitxInputStateGetPreedit(input); + FcitxMessages* clientpreedit = FcitxInputStateGetClientPreedit(input); + FcitxMessagesSetMessageCount(preedit, 0); + FcitxMessagesSetMessageCount(clientpreedit, 0); + if ( preedit_info_.get() ) + { + VLOG ( 1 ) << "DrawPreeditInfo: cursor=" << preedit_info_->cursor_pos; + + FcitxInputContext* ic = FcitxInstanceGetCurrentIC(instance); + boolean supportPreedit = FcitxInstanceICSupportPreedit(instance, ic); + + if (!supportPreedit) + FcitxInputStateSetShowCursor(input, true); + + for (int i = 0; i < preedit_info_->preedit.size(); i ++) { + if (!supportPreedit) + 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 (!supportPreedit) + FcitxInputStateSetCursorPos(input, preedit_info_->cursor_pos); + FcitxInputStateSetClientCursorPos(input, preedit_info_->cursor_pos); + } + else { + FcitxInputStateSetShowCursor(input, false); + } + if ( !aux_.empty() ) { + FcitxMessagesAddMessageAtLast(preedit, MSG_TIPS, "%s[%s]", preedit_info_.get() ? " " : "", aux_.c_str()); + } +} + +void FcitxMozc::DrawAux() +{ + FcitxMessages* auxUp = FcitxInputStateGetAuxUp(input); + FcitxMessages* auxDown = FcitxInputStateGetAuxDown(input); + FcitxMessagesSetMessageCount(auxUp, 0); + FcitxMessagesSetMessageCount(auxDown, 0); +} + +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::FileUtil::FileExists ( mozc::FileUtil::JoinPath ( + mozc::SystemUtil::GetServerDirectory(), mozc::kMozcTool ) ) ) + { + FcitxUIRegisterComplexStatus(instance, this, + "mozc-tool", + _("Tool"), + _("Tool"), + NULL, + GetMozcToolIcon + ); + } + FcitxUISetStatusVisable(instance, "mozc-tool", false); + FcitxUISetStatusVisable(instance, "mozc-composition-mode", false); +} + +boolean CompositionMenuAction(struct _FcitxUIMenu *menu, int index) +{ + FcitxMozc* mozc = (FcitxMozc*) menu->priv; + 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); +} + +bool FcitxMozc::SendCommand(const mozc::commands::SessionCommand& session_command, commands::Output* new_output) +{ + string error; + return connection_->TrySendRawCommand(session_command, new_output, &error); +} + + +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; +} + +void FcitxMozc::SetUsage(const string& title_, const string& description_) +{ + title = title_; + description = description_; +} + +std::pair< string, string > FcitxMozc::GetUsage() +{ + return make_pair(title, description); +} + +} // 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 00000000..0610d6e2 --- /dev/null +++ b/src/unix/fcitx/fcitx_mozc.h @@ -0,0 +1,176 @@ +// 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 <memory> + +#include <fcitx/instance.h> +#include <fcitx/candidate.h> +#include <fcitx-config/hotkey.h> +#include <libintl.h> + +#include "base/port.h" +#include "base/run_level.h" +#include "protocol/commands.pb.h" +#include "client/client_interface.h" +#include "mozc_connection.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(); + bool paging(bool prev); + + // 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(); + + mozc::client::ClientInterface* GetClient() { return connection_->GetClient(); } + + bool SendCommand(const mozc::commands::SessionCommand& session_command, mozc::commands::Output* new_output); + + void SetUsage(const std::string& title, const std::string& description); + + std::pair<std::string, std::string> GetUsage(); + + void DrawAll(); + +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 DrawPreeditInfo(); + void DrawAux(); + + // Open url_ with a default browser. + void OpenUrl(); + + FcitxInstance* instance; + FcitxInputState* input; + const std::unique_ptr<MozcConnectionInterface> connection_; + const std::unique_ptr<MozcResponseParser> parser_; + + // Strings and a window currently displayed on FCITX UI. + std::unique_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; + string description; + string title; + + 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 00000000..97ff4a49 --- /dev/null +++ b/src/unix/fcitx/gen_fcitx_mozc_i18n.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +objdir="$1" + +mkdir -p "$1" + +for pofile in po/*.po +do + msgfmt "$pofile" -o "$1/`basename ${pofile} .po`.mo" +done diff --git a/src/unix/fcitx/mozc.conf b/src/unix/fcitx/mozc.conf new file mode 100644 index 00000000..ad192303 --- /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 00000000..068dcf46 --- /dev/null +++ b/src/unix/fcitx/mozc_connection.cc @@ -0,0 +1,209 @@ +// 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. + +#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 "protocol/commands.pb.h" +#include "session/ime_switch_util.h" +#include "unix/fcitx/fcitx_key_event_handler.h" +#include "unix/fcitx/surrounding_text_util.h" +#include "fcitx_mozc.h" + +namespace mozc { +namespace fcitx { + +MozcConnectionInterface::~MozcConnectionInterface() { +} + +mozc::client::ClientInterface* CreateAndConfigureClient() { + mozc::client::ClientInterface *client = client::ClientFactory::NewClient(); + // Currently client capability is fixed. + commands::Capability capability; + capability.set_text_deletion(commands::Capability::DELETE_PRECEDING_TEXT); + client->set_client_capability(capability); + return client; +} + +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 = CreateAndConfigureClient(); + client->SetServerLauncher(server_launcher); + client->SetIPCClientFactory(client_factory_.get()); + client_.reset(client); + + if (client_->EnsureConnection()) { + UpdatePreeditMethod(); + } + VLOG(1) + << "Current preedit method is " + << (preedit_method_ == mozc::config::Config::ROMAN ? "Roman" : "Kana"); +} + +MozcConnection::~MozcConnection() { + client_->SyncData(); + VLOG(1) << "MozcConnection is destroyed"; +} + +void MozcConnection::UpdatePreeditMethod() { + mozc::config::Config config; + if (!client_->GetConfig(&config)) { + LOG(ERROR) << "GetConfig failed"; + return; + } + preedit_method_ = config.has_preedit_method() ? + config.preedit_method() : config::Config::ROMAN; +} + +bool MozcConnection::TrySendKeyEvent( + FcitxInstance* instance, + 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. + } + + commands::Context context; + SurroundingTextInfo surrounding_text_info; + if (GetSurroundingText(instance, + &surrounding_text_info)) { + context.set_preceding_text(surrounding_text_info.preceding_text); + context.set_following_text(surrounding_text_info.following_text); + } + + VLOG(1) << "TrySendKeyEvent: " << std::endl << event.DebugString(); + if (!client_->SendKeyWithContext(event, context, out)) { + *out_error = "SendKey failed"; + VLOG(1) << "ERROR"; + return false; + } + VLOG(1) << "OK: " << std::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 TrySendRawCommand(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 TrySendRawCommand(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 TrySendRawCommand(command, out, out_error); +} + + + +bool MozcConnection::TrySendRawCommand( + const mozc::commands::SessionCommand& command, + mozc::commands::Output *out, + string *out_error) const { + VLOG(1) << "TrySendRawCommand: " << std::endl << command.DebugString(); + if (!client_->SendCommand(command, out)) { + *out_error = "SendCommand failed"; + VLOG(1) << "ERROR"; + return false; + } + VLOG(1) << "OK: " << std::endl << out->DebugString(); + return true; +} + +mozc::client::ClientInterface* MozcConnection::GetClient() +{ + return client_.get(); +} + +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 00000000..0181bf73 --- /dev/null +++ b/src/unix/fcitx/mozc_connection.h @@ -0,0 +1,152 @@ +// 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 <memory> + +#include <fcitx-config/hotkey.h> +#include <fcitx/instance.h> + +#include "base/port.h" +#include "protocol/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(FcitxInstance* instance, + 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; + + virtual bool TrySendRawCommand(const mozc::commands::SessionCommand& command, + mozc::commands::Output *out, + string *out_error) const = 0; + virtual mozc::client::ClientInterface* GetClient() = 0; + virtual void UpdatePreeditMethod() = 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(FcitxInstance* instance, + 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; + + virtual bool TrySendRawCommand(const mozc::commands::SessionCommand& command, + mozc::commands::Output *out, + string *out_error) const; + + virtual mozc::client::ClientInterface* GetClient(); + + virtual void UpdatePreeditMethod(); + + private: + friend class MozcConnectionTest; + MozcConnection(mozc::client::ServerLauncherInterface *server_launcher, + mozc::IPCClientFactoryInterface *client_factory); + + const std::unique_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_. + std::unique_ptr<mozc::IPCClientFactoryInterface> client_factory_; + std::unique_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 00000000..cd9ed811 --- /dev/null +++ b/src/unix/fcitx/mozc_response_parser.cc @@ -0,0 +1,448 @@ +// 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. + +#include "unix/fcitx/mozc_response_parser.h" + +#include <string> +#include <vector> + +#include "base/logging.h" +#include "base/process.h" +#include "base/util.h" +#include "protocol/commands.pb.h" +#include "unix/fcitx/fcitx_mozc.h" +#include "unix/fcitx/surrounding_text_util.h" +#include <fcitx/candidate.h> + +namespace { + +// 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() { +} + +void MozcResponseParser::UpdateDeletionRange(const mozc::commands::Output& response, FcitxMozc* fcitx_mozc) const +{ + if (response.has_deletion_range() && + response.deletion_range().offset() <= 0 && + response.deletion_range().offset() + response.deletion_range().length() >= 0) { + FcitxInstanceDeleteSurroundingText(fcitx_mozc->GetInstance(), + FcitxInstanceGetCurrentIC(fcitx_mozc->GetInstance()), + response.deletion_range().offset(), + response.deletion_range().length()); + } +} + +void MozcResponseParser::LaunchTool(const mozc::commands::Output& response, FcitxMozc* fcitx_mozc) const +{ + FCITX_UNUSED(fcitx_mozc); + if (response.has_launch_tool_mode()) { + fcitx_mozc->GetClient()->LaunchToolWithProtoBuf(response); + } +} + +void MozcResponseParser::ExecuteCallback(const mozc::commands::Output& response, FcitxMozc* fcitx_mozc) const +{ + if (!response.has_callback()) { + return; + } + + if (!response.callback().has_session_command()) { + LOG(ERROR) << "callback does not have session_command"; + return; + } + + const commands::SessionCommand &callback_command = + response.callback().session_command(); + + if (!callback_command.has_type()) { + LOG(ERROR) << "callback_command has no type"; + return; + } + + commands::SessionCommand session_command; + session_command.set_type(callback_command.type()); + + // TODO(nona): Make a function to handle CONVERT_REVERSE. + // Used by CONVERT_REVERSE and/or UNDO + // This value represents how many characters are selected as a relative + // distance of characters. Positive value represents forward text selection + // and negative value represents backword text selection. + // Note that you should not allow 0x80000000 for |relative_selected_length| + // because you cannot safely use |-relative_selected_length| nor + // |abs(relative_selected_length)| in this case due to integer overflow. + SurroundingTextInfo surrounding_text_info; + + switch (callback_command.type()) { + case commands::SessionCommand::UNDO: + break; + case commands::SessionCommand::CONVERT_REVERSE: { + + if (!GetSurroundingText(fcitx_mozc->GetInstance(), + &surrounding_text_info)) { + return; + } + + session_command.set_text(surrounding_text_info.selection_text); + break; + } + default: + return; + } + + commands::Output new_output; + if (!fcitx_mozc->SendCommand(session_command, &new_output)) { + LOG(ERROR) << "Callback Command Failed"; + return; + } + + if (callback_command.type() == commands::SessionCommand::CONVERT_REVERSE) { + // We need to remove selected text as a first step of reconversion. + commands::DeletionRange *range = new_output.mutable_deletion_range(); + // Use DeletionRange field to remove the selected text. + // For forward selection (that is, |relative_selected_length > 0|), the + // offset should be a negative value to delete preceding text. + // For backward selection (that is, |relative_selected_length < 0|), + // IBus and/or some applications seem to expect |offset == 0| somehow. + const int32 offset = surrounding_text_info.relative_selected_length > 0 + ? -surrounding_text_info.relative_selected_length // forward selection + : 0; // backward selection + range->set_offset(offset); + range->set_length(abs(surrounding_text_info.relative_selected_length)); + } + + VLOG(1) << "New output" << new_output.DebugString(); + + ParseResponse(new_output, fcitx_mozc); +} + +bool MozcResponseParser::ParseResponse(const mozc::commands::Output &response, + FcitxMozc *fcitx_mozc) const { + DCHECK(fcitx_mozc); + if (!fcitx_mozc) { + return false; + } + + fcitx_mozc->SetUsage("", ""); + + UpdateDeletionRange(response, fcitx_mozc); + + // 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); + } + LaunchTool(response, fcitx_mozc); + ExecuteCallback(response, fcitx_mozc); + + 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; + } + } +} + +static boolean FcitxMozcPaging(void* arg, boolean prev) +{ + FcitxMozc* mozc = static_cast<FcitxMozc*>(arg); + return mozc->paging(prev); +} + +void MozcResponseParser::ParseCandidates( + const mozc::commands::Candidates &candidates, FcitxMozc *fcitx_mozc) const { + const commands::Footer &footer = candidates.footer(); + bool hasPrev = false; + bool hasNext = false; + 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; + + if (candidates.candidate_size() > 0) { + + if (candidates.candidate(0).index() > 0) { + hasPrev = true; + } + if (candidates.candidate(candidates.candidate_size() - 1).index() + 1 < candidates.size()) { + hasNext = true; + } + } + } + fcitx_mozc->SetAuxString(auxString); + } + + FcitxCandidateWordList* candList = FcitxInputStateGetCandidateList(fcitx_mozc->GetInputState()); + FcitxCandidateWordReset(candList); + FcitxCandidateWordSetPageSize(candList, 9); + if (candidates.has_direction() && + candidates.direction() == commands::Candidates::HORIZONTAL) { + FcitxCandidateWordSetLayoutHint(candList, CLH_Horizontal); + } else { + FcitxCandidateWordSetLayoutHint(candList, CLH_Vertical); + } + + std::map<int32, std::pair<string, string> > usage_map; + if (candidates.has_usages()) { + const commands::InformationList& usages = candidates.usages(); + for (size_t i = 0; i < usages.information().size(); ++i) { + const commands::Information& information = usages.information(i); + if (!information.has_id() || !information.has_description()) + continue; + usage_map[information.id()].first = information.title(); + usage_map[information.id()].second = information.description(); + } + } + +#define EMPTY_STR_CHOOSE "\0\0\0\0\0\0\0\0\0\0" + std::vector<char> choose; + + 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 commands::Candidates::Candidate& candidate = candidates.candidate(i); + const uint32 index = candidate.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_ && + candidate.has_annotation() && + candidate.annotation().has_prefix()) { + value = candidate.annotation().prefix(); + } + value += candidate.value(); + if (use_annotation_ && + candidate.has_annotation() && + candidate.annotation().has_suffix()) { + value += candidate.annotation().suffix(); + } + if (use_annotation_ && + candidate.has_annotation() && + candidate.annotation().has_description()) { + // Display descriptions ([HALF][KATAKANA], [GREEK], [Black square], etc). + value += CreateDescriptionString( + candidate.annotation().description()); + } + + if (use_annotation_ && focused_index != -1 && index == focused_index) { + local_index = i; + type = MSG_FIRSTCAND; + + if (candidate.has_information_id()) { + std::map<int32, std::pair<string, string> >::iterator it = + usage_map.find(candidate.information_id()); + if (it != usage_map.end()) { + fcitx_mozc->SetUsage(it->second.first, it->second.second); + } + value += CreateDescriptionString(_("Press Ctrl+Alt+H to show usages.")); + } + } + + if (candidate.has_annotation() && + candidate.annotation().has_shortcut()) { + choose.push_back(candidate.annotation().shortcut().c_str()[0]); + } + + candWord.strWord = strdup(value.c_str()); + + if (candidate.has_id()) { + const int32 cid = candidate.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); + } + + while (choose.size() < 10) { + choose.push_back('\0'); + } + + if (footer.has_index_visible() && footer.index_visible()) + FcitxCandidateWordSetChoose(candList, choose.data()); + else + FcitxCandidateWordSetChoose(candList, EMPTY_STR_CHOOSE); + FcitxCandidateWordSetFocus(candList, local_index); + FcitxCandidateWordSetOverridePaging(candList, hasPrev, hasNext, FcitxMozcPaging, fcitx_mozc, NULL); +} + +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 00000000..beeef709 --- /dev/null +++ b/src/unix/fcitx/mozc_response_parser.h @@ -0,0 +1,97 @@ +// 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/port.h" + +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 UpdateDeletionRange(const mozc::commands::Output& response, FcitxMozc* fcitx_mozc) const; + void LaunchTool(const mozc::commands::Output& response, FcitxMozc* fcitx_mozc) const; + void ExecuteCallback(const mozc::commands::Output& response, FcitxMozc* fcitx_mozc) const; + 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 00000000..be341716 --- /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/ca.po b/src/unix/fcitx/po/ca.po new file mode 100644 index 00000000..b8b10f5a --- /dev/null +++ b/src/unix/fcitx/po/ca.po @@ -0,0 +1,88 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Robert Antoni Buj Gelonch <rbuj@fedoraproject.org>, 2017 +# Walter Garcia-Fontes <walter.garcia@upf.edu>, 2017 +msgid "" +msgstr "" +"Project-Id-Version: fcitx\n" +"Report-Msgid-Bugs-To: fcitx-dev@googlegroups.com\n" +"POT-Creation-Date: 2017-12-21 00:03-0800\n" +"PO-Revision-Date: 2017-12-20 05:19+0000\n" +"Last-Translator: Robert Antoni Buj Gelonch <rbuj@fedoraproject.org>\n" +"Language-Team: Catalan (http://www.transifex.com/fcitx/fcitx/language/ca/)\n" +"Language: ca\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: ../eim.cc:187 +msgid "Press Escape to go back" +msgstr "Premeu Esc per tornar" + +#: ../fcitx_mozc.cc:65 +msgid "Direct" +msgstr "Directe" + +#: ../fcitx_mozc.cc:70 +msgid "Hiragana" +msgstr "Hiragana" + +#: ../fcitx_mozc.cc:75 +msgid "Full Katakana" +msgstr "Katakana complet" + +#: ../fcitx_mozc.cc:80 +msgid "Half ASCII" +msgstr "ASCII mig" + +#: ../fcitx_mozc.cc:85 +msgid "Full ASCII" +msgstr "ASCII complet" + +#: ../fcitx_mozc.cc:90 +msgid "Half Katakana" +msgstr "Mig katakana" + +#: ../fcitx_mozc.cc:406 ../fcitx_mozc.cc:407 ../fcitx_mozc.cc:475 +msgid "Composition Mode" +msgstr "Mode de composició" + +#: ../fcitx_mozc.cc:417 ../fcitx_mozc.cc:418 +msgid "Tool" +msgstr "Eina" + +#: ../fcitx_mozc.cc:488 +msgid "Mozc Tool" +msgstr "Eina mozc" + +#: ../fcitx_mozc.cc:494 +msgid "Configuration Tool" +msgstr "Eina de configuració" + +#: ../fcitx_mozc.cc:495 +msgid "Dictionary Tool" +msgstr "Eina de diccionari" + +#: ../fcitx_mozc.cc:496 +msgid "Hand Writing" +msgstr "Escriptura a mà" + +#: ../fcitx_mozc.cc:497 +msgid "Character Palette" +msgstr "Paleta de caràcters" + +#: ../fcitx_mozc.cc:498 +msgid "Add Word" +msgstr "Afegeix una paraula" + +#: ../fcitx_mozc.cc:499 +msgid "About Mozc" +msgstr "Quant al mozc" + +#: ../mozc_response_parser.cc:366 +msgid "Press Ctrl+Alt+H to show usages." +msgstr "Premeu Ctrl+Alt+H per mostrar els usos." diff --git a/src/unix/fcitx/po/da.po b/src/unix/fcitx/po/da.po new file mode 100644 index 00000000..21f8e0b3 --- /dev/null +++ b/src/unix/fcitx/po/da.po @@ -0,0 +1,87 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# scootergrisen, 2017 +msgid "" +msgstr "" +"Project-Id-Version: fcitx\n" +"Report-Msgid-Bugs-To: fcitx-dev@googlegroups.com\n" +"POT-Creation-Date: 2017-10-18 06:03-0700\n" +"PO-Revision-Date: 2017-10-18 12:19+0000\n" +"Last-Translator: scootergrisen\n" +"Language-Team: Danish (http://www.transifex.com/fcitx/fcitx/language/da/)\n" +"Language: da\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: ../eim.cc:187 +msgid "Press Escape to go back" +msgstr "Tryk på Escape for at gå tilbage" + +#: ../fcitx_mozc.cc:65 +msgid "Direct" +msgstr "Direkte" + +#: ../fcitx_mozc.cc:70 +msgid "Hiragana" +msgstr "Hiragana" + +#: ../fcitx_mozc.cc:75 +msgid "Full Katakana" +msgstr "Fuld Katakana" + +#: ../fcitx_mozc.cc:80 +msgid "Half ASCII" +msgstr "Halv ASCII" + +#: ../fcitx_mozc.cc:85 +msgid "Full ASCII" +msgstr "Fuld ASCII" + +#: ../fcitx_mozc.cc:90 +msgid "Half Katakana" +msgstr "Halv Katakana" + +#: ../fcitx_mozc.cc:406 ../fcitx_mozc.cc:407 ../fcitx_mozc.cc:475 +msgid "Composition Mode" +msgstr "Kompositionstilstand" + +#: ../fcitx_mozc.cc:417 ../fcitx_mozc.cc:418 +msgid "Tool" +msgstr "Værktøj" + +#: ../fcitx_mozc.cc:488 +msgid "Mozc Tool" +msgstr "Mozc-værktøj" + +#: ../fcitx_mozc.cc:494 +msgid "Configuration Tool" +msgstr "Konfigurationsværktøj" + +#: ../fcitx_mozc.cc:495 +msgid "Dictionary Tool" +msgstr "Ordbogsværktøj" + +#: ../fcitx_mozc.cc:496 +msgid "Hand Writing" +msgstr "Håndskrift" + +#: ../fcitx_mozc.cc:497 +msgid "Character Palette" +msgstr "Tegnpalet" + +#: ../fcitx_mozc.cc:498 +msgid "Add Word" +msgstr "Tilføj ord" + +#: ../fcitx_mozc.cc:499 +msgid "About Mozc" +msgstr "Om Mozc" + +#: ../mozc_response_parser.cc:366 +msgid "Press Ctrl+Alt+H to show usages." +msgstr "Tryk på Ctrl+Alt+H for at vise anvendelser." diff --git a/src/unix/fcitx/po/de.po b/src/unix/fcitx/po/de.po new file mode 100644 index 00000000..11960867 --- /dev/null +++ b/src/unix/fcitx/po/de.po @@ -0,0 +1,91 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Lucius Annaeus Seneca, 2013 +# mar well <m.wellendorf@gmx.de>, 2013 +# Seneca, 2013 +# csslayer <wengxt@gmail.com>, 2013 +# csslayer <wengxt@gmail.com>, 2013 +msgid "" +msgstr "" +"Project-Id-Version: fcitx\n" +"Report-Msgid-Bugs-To: fcitx-dev@googlegroups.com\n" +"POT-Creation-Date: 2015-08-03 18:01+0200\n" +"PO-Revision-Date: 2013-11-16 14:13+0000\n" +"Last-Translator: csslayer <wengxt@gmail.com>\n" +"Language-Team: German (http://www.transifex.com/fcitx/fcitx/language/de/)\n" +"Language: de\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: ../eim.cc:181 +msgid "Press Escape to go back" +msgstr "ESC drücken um zurück zu kehren" + +#: ../fcitx_mozc.cc:65 +msgid "Direct" +msgstr "Direkt" + +#: ../fcitx_mozc.cc:70 +msgid "Hiragana" +msgstr "Hiragana" + +#: ../fcitx_mozc.cc:75 +msgid "Full Katakana" +msgstr "Normalbreite Katakana" + +#: ../fcitx_mozc.cc:80 +msgid "Half ASCII" +msgstr "6-Bit ASCII (Half ASCII)" + +#: ../fcitx_mozc.cc:85 +msgid "Full ASCII" +msgstr "7-Bit ASCII (Full ASCII)" + +#: ../fcitx_mozc.cc:90 +msgid "Half Katakana" +msgstr "Halbbreite Katakana" + +#: ../fcitx_mozc.cc:406 ../fcitx_mozc.cc:407 ../fcitx_mozc.cc:475 +msgid "Composition Mode" +msgstr "Kompositionsmodus" + +#: ../fcitx_mozc.cc:417 ../fcitx_mozc.cc:418 +msgid "Tool" +msgstr "Werkzeug" + +#: ../fcitx_mozc.cc:488 +msgid "Mozc Tool" +msgstr "Mozc Werkzeug" + +#: ../fcitx_mozc.cc:494 +msgid "Configuration Tool" +msgstr "Konfigurationswerkzeug" + +#: ../fcitx_mozc.cc:495 +msgid "Dictionary Tool" +msgstr "Wörterbuchwerkzeug" + +#: ../fcitx_mozc.cc:496 +msgid "Hand Writing" +msgstr "Eingabe Handschrift" + +#: ../fcitx_mozc.cc:497 +msgid "Character Palette" +msgstr "Palette Symbole" + +#: ../fcitx_mozc.cc:498 +msgid "Add Word" +msgstr "Wort hinzufügen" + +#: ../fcitx_mozc.cc:499 +msgid "About Mozc" +msgstr "Über Mozc" + +#: ../mozc_response_parser.cc:366 +msgid "Press Ctrl+Alt+H to show usages." +msgstr "Ctrl+Alt+H um die Hilfe anzuzeigen" diff --git a/src/unix/fcitx/po/fcitx-mozc.pot b/src/unix/fcitx/po/fcitx-mozc.pot new file mode 100644 index 00000000..d9983e40 --- /dev/null +++ b/src/unix/fcitx/po/fcitx-mozc.pot @@ -0,0 +1,86 @@ +# 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: 2013-11-14 13:55-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" + +#: ../eim.cc:184 +msgid "Press Escape to go back" +msgstr "" + +#: ../fcitx_mozc.cc:68 +msgid "Direct" +msgstr "" + +#: ../fcitx_mozc.cc:73 +msgid "Hiragana" +msgstr "" + +#: ../fcitx_mozc.cc:78 +msgid "Full Katakana" +msgstr "" + +#: ../fcitx_mozc.cc:83 +msgid "Half ASCII" +msgstr "" + +#: ../fcitx_mozc.cc:88 +msgid "Full ASCII" +msgstr "" + +#: ../fcitx_mozc.cc:93 +msgid "Half Katakana" +msgstr "" + +#: ../fcitx_mozc.cc:410 ../fcitx_mozc.cc:411 ../fcitx_mozc.cc:484 +msgid "Composition Mode" +msgstr "" + +#: ../fcitx_mozc.cc:421 ../fcitx_mozc.cc:422 +msgid "Tool" +msgstr "" + +#: ../fcitx_mozc.cc:497 +msgid "Mozc Tool" +msgstr "" + +#: ../fcitx_mozc.cc:503 +msgid "Configuration Tool" +msgstr "" + +#: ../fcitx_mozc.cc:504 +msgid "Dictionary Tool" +msgstr "" + +#: ../fcitx_mozc.cc:505 +msgid "Hand Writing" +msgstr "" + +#: ../fcitx_mozc.cc:506 +msgid "Character Palette" +msgstr "" + +#: ../fcitx_mozc.cc:507 +msgid "Add Word" +msgstr "" + +#: ../fcitx_mozc.cc:508 +msgid "About Mozc" +msgstr "" + +#: ../mozc_response_parser.cc:374 +msgid "Press Ctrl+Alt+H to show usages." +msgstr "" diff --git a/src/unix/fcitx/po/ja.po b/src/unix/fcitx/po/ja.po new file mode 100644 index 00000000..a3f4d5f3 --- /dev/null +++ b/src/unix/fcitx/po/ja.po @@ -0,0 +1,93 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# しろう, 2013 +# AWASHIRO Ikuya <ikunya@gmail.com>, 2013 +# AWASHIRO Ikuya <ikunya@gmail.com>, 2012-2013 +# WAKAYAMA Shirou <shirou.faw@gmail.com>, 2013 +# csslayer <wengxt@gmail.com>, 2013 +# csslayer <wengxt@gmail.com>, 2012 +# csslayer <wengxt@gmail.com>, 2012-2013 +msgid "" +msgstr "" +"Project-Id-Version: fcitx\n" +"Report-Msgid-Bugs-To: fcitx-dev@googlegroups.com\n" +"POT-Creation-Date: 2015-08-03 18:01+0200\n" +"PO-Revision-Date: 2013-11-16 14:13+0000\n" +"Last-Translator: csslayer <wengxt@gmail.com>\n" +"Language-Team: Japanese (http://www.transifex.com/fcitx/fcitx/language/ja/)\n" +"Language: ja\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\n" + +#: ../eim.cc:181 +msgid "Press Escape to go back" +msgstr "Escキーを押して戻る" + +#: ../fcitx_mozc.cc:65 +msgid "Direct" +msgstr "直接入力" + +#: ../fcitx_mozc.cc:70 +msgid "Hiragana" +msgstr "ひらがな" + +#: ../fcitx_mozc.cc:75 +msgid "Full Katakana" +msgstr "全角カタカナ" + +#: ../fcitx_mozc.cc:80 +msgid "Half ASCII" +msgstr "半角英数" + +#: ../fcitx_mozc.cc:85 +msgid "Full ASCII" +msgstr "全角英数" + +#: ../fcitx_mozc.cc:90 +msgid "Half Katakana" +msgstr "半角カタカナ" + +#: ../fcitx_mozc.cc:406 ../fcitx_mozc.cc:407 ../fcitx_mozc.cc:475 +msgid "Composition Mode" +msgstr "変換モード" + +#: ../fcitx_mozc.cc:417 ../fcitx_mozc.cc:418 +msgid "Tool" +msgstr "ツール" + +#: ../fcitx_mozc.cc:488 +msgid "Mozc Tool" +msgstr "Mozc ツール" + +#: ../fcitx_mozc.cc:494 +msgid "Configuration Tool" +msgstr "設定ツール" + +#: ../fcitx_mozc.cc:495 +msgid "Dictionary Tool" +msgstr "辞書ツール" + +#: ../fcitx_mozc.cc:496 +msgid "Hand Writing" +msgstr "手書き文字認識" + +#: ../fcitx_mozc.cc:497 +msgid "Character Palette" +msgstr "文字パレット" + +#: ../fcitx_mozc.cc:498 +msgid "Add Word" +msgstr "単語登録" + +#: ../fcitx_mozc.cc:499 +msgid "About Mozc" +msgstr "Mozc について" + +#: ../mozc_response_parser.cc:366 +msgid "Press Ctrl+Alt+H to show usages." +msgstr "Ctrl+Alt+H キーを押して用例を表示" diff --git a/src/unix/fcitx/po/ko.po b/src/unix/fcitx/po/ko.po new file mode 100644 index 00000000..0d4b46a6 --- /dev/null +++ b/src/unix/fcitx/po/ko.po @@ -0,0 +1,87 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Bon Keun Seo <scobyseo@gmail.com>, 2017 +msgid "" +msgstr "" +"Project-Id-Version: fcitx\n" +"Report-Msgid-Bugs-To: fcitx-dev@googlegroups.com\n" +"POT-Creation-Date: 2017-07-15 14:20-0700\n" +"PO-Revision-Date: 2017-07-09 21:58+0000\n" +"Last-Translator: Bon Keun Seo <scobyseo@gmail.com>\n" +"Language-Team: Korean (http://www.transifex.com/fcitx/fcitx/language/ko/)\n" +"Language: ko\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\n" + +#: ../eim.cc:187 +msgid "Press Escape to go back" +msgstr "돌아가려면 ESC를 누르세요" + +#: ../fcitx_mozc.cc:65 +msgid "Direct" +msgstr "직접입력" + +#: ../fcitx_mozc.cc:70 +msgid "Hiragana" +msgstr "히라가나" + +#: ../fcitx_mozc.cc:75 +msgid "Full Katakana" +msgstr "전각 가타가나" + +#: ../fcitx_mozc.cc:80 +msgid "Half ASCII" +msgstr "반각 아스키" + +#: ../fcitx_mozc.cc:85 +msgid "Full ASCII" +msgstr "전각 아스키" + +#: ../fcitx_mozc.cc:90 +msgid "Half Katakana" +msgstr "반각 가타가나" + +#: ../fcitx_mozc.cc:406 ../fcitx_mozc.cc:407 ../fcitx_mozc.cc:475 +msgid "Composition Mode" +msgstr "조합 모드" + +#: ../fcitx_mozc.cc:417 ../fcitx_mozc.cc:418 +msgid "Tool" +msgstr "도구" + +#: ../fcitx_mozc.cc:488 +msgid "Mozc Tool" +msgstr "Mozc 도구" + +#: ../fcitx_mozc.cc:494 +msgid "Configuration Tool" +msgstr "설정 도구" + +#: ../fcitx_mozc.cc:495 +msgid "Dictionary Tool" +msgstr "사전 도구" + +#: ../fcitx_mozc.cc:496 +msgid "Hand Writing" +msgstr "손 글씨" + +#: ../fcitx_mozc.cc:497 +msgid "Character Palette" +msgstr "글자 팔레트" + +#: ../fcitx_mozc.cc:498 +msgid "Add Word" +msgstr "단어 추가" + +#: ../fcitx_mozc.cc:499 +msgid "About Mozc" +msgstr "Mozc에 대해" + +#: ../mozc_response_parser.cc:366 +msgid "Press Ctrl+Alt+H to show usages." +msgstr "사용법을 보려면 Ctrl+Alt+H를 누르세요." diff --git a/src/unix/fcitx/po/ru.po b/src/unix/fcitx/po/ru.po new file mode 100644 index 00000000..22c1fcc3 --- /dev/null +++ b/src/unix/fcitx/po/ru.po @@ -0,0 +1,89 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# TotalCaesar659 <x24cm5b8c54q6szxw@yandex.ru>, 2016 +msgid "" +msgstr "" +"Project-Id-Version: fcitx\n" +"Report-Msgid-Bugs-To: fcitx-dev@googlegroups.com\n" +"POT-Creation-Date: 2016-12-01 08:40-0800\n" +"PO-Revision-Date: 2016-11-30 23:24+0000\n" +"Last-Translator: TotalCaesar659 <x24cm5b8c54q6szxw@yandex.ru>\n" +"Language-Team: Russian (http://www.transifex.com/fcitx/fcitx/language/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" +"%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n" +"%100>=11 && n%100<=14)? 2 : 3);\n" + +#: ../eim.cc:187 +msgid "Press Escape to go back" +msgstr "Нажмите для перехода назад" + +#: ../fcitx_mozc.cc:65 +msgid "Direct" +msgstr "Прямой" + +#: ../fcitx_mozc.cc:70 +msgid "Hiragana" +msgstr "Хирагана" + +#: ../fcitx_mozc.cc:75 +msgid "Full Katakana" +msgstr "Полная катакана" + +#: ../fcitx_mozc.cc:80 +msgid "Half ASCII" +msgstr "Половинный " + +#: ../fcitx_mozc.cc:85 +msgid "Full ASCII" +msgstr "Полный " + +#: ../fcitx_mozc.cc:90 +msgid "Half Katakana" +msgstr "Половинная катакана" + +#: ../fcitx_mozc.cc:406 ../fcitx_mozc.cc:407 ../fcitx_mozc.cc:475 +msgid "Composition Mode" +msgstr "Режим композиции" + +#: ../fcitx_mozc.cc:417 ../fcitx_mozc.cc:418 +msgid "Tool" +msgstr "Инструмент" + +#: ../fcitx_mozc.cc:488 +msgid "Mozc Tool" +msgstr "Инструмент Mozc" + +#: ../fcitx_mozc.cc:494 +msgid "Configuration Tool" +msgstr "Инструмент настройки" + +#: ../fcitx_mozc.cc:495 +msgid "Dictionary Tool" +msgstr "Инструмент словаря" + +#: ../fcitx_mozc.cc:496 +msgid "Hand Writing" +msgstr "Ручное письмо" + +#: ../fcitx_mozc.cc:497 +msgid "Character Palette" +msgstr "Палитра символов" + +#: ../fcitx_mozc.cc:498 +msgid "Add Word" +msgstr "Добавить слово" + +#: ../fcitx_mozc.cc:499 +msgid "About Mozc" +msgstr "О " + +#: ../mozc_response_parser.cc:366 +msgid "Press Ctrl+Alt+H to show usages." +msgstr "Нажмите Ctrl+Alt+H, чтобы показать использование." diff --git a/src/unix/fcitx/po/zh_CN.po b/src/unix/fcitx/po/zh_CN.po new file mode 100644 index 00000000..6e46bc64 --- /dev/null +++ b/src/unix/fcitx/po/zh_CN.po @@ -0,0 +1,92 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# csslayer <wengxt@gmail.com>, 2013 +# csslayer <wengxt@gmail.com>, 2012 +# wwj402 <wwj402@gmail.com>, 2013 +# wwj402 <wwj402@gmail.com>, 2013 +# csslayer <wengxt@gmail.com>, 2012-2013 +msgid "" +msgstr "" +"Project-Id-Version: fcitx\n" +"Report-Msgid-Bugs-To: fcitx-dev@googlegroups.com\n" +"POT-Creation-Date: 2015-07-21 12:01+0200\n" +"PO-Revision-Date: 2013-11-16 14:13+0000\n" +"Last-Translator: csslayer <wengxt@gmail.com>\n" +"Language-Team: Chinese (China) (http://www.transifex.com/projects/p/fcitx/" +"language/zh_CN/)\n" +"Language: zh_CN\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\n" + +#: ../eim.cc:181 +msgid "Press Escape to go back" +msgstr "按下 Escape 返回" + +#: ../fcitx_mozc.cc:65 +msgid "Direct" +msgstr "直接键盘输入" + +#: ../fcitx_mozc.cc:70 +msgid "Hiragana" +msgstr "平假名" + +#: ../fcitx_mozc.cc:75 +msgid "Full Katakana" +msgstr "全角片假名" + +#: ../fcitx_mozc.cc:80 +msgid "Half ASCII" +msgstr "半角 ASCII" + +#: ../fcitx_mozc.cc:85 +msgid "Full ASCII" +msgstr "全角 ASCII" + +#: ../fcitx_mozc.cc:90 +msgid "Half Katakana" +msgstr "半角片假名" + +#: ../fcitx_mozc.cc:405 ../fcitx_mozc.cc:406 ../fcitx_mozc.cc:474 +msgid "Composition Mode" +msgstr "编辑模式" + +#: ../fcitx_mozc.cc:416 ../fcitx_mozc.cc:417 +msgid "Tool" +msgstr "工具" + +#: ../fcitx_mozc.cc:487 +msgid "Mozc Tool" +msgstr "Mozc 工具" + +#: ../fcitx_mozc.cc:493 +msgid "Configuration Tool" +msgstr "配置工具" + +#: ../fcitx_mozc.cc:494 +msgid "Dictionary Tool" +msgstr "词典工具" + +#: ../fcitx_mozc.cc:495 +msgid "Hand Writing" +msgstr "手写输入" + +#: ../fcitx_mozc.cc:496 +msgid "Character Palette" +msgstr "字符映射表" + +#: ../fcitx_mozc.cc:497 +msgid "Add Word" +msgstr "添加单词" + +#: ../fcitx_mozc.cc:498 +msgid "About Mozc" +msgstr "关于 Mozc" + +#: ../mozc_response_parser.cc:366 +msgid "Press Ctrl+Alt+H to show usages." +msgstr "按下 Ctrl+Alt+H 显示用法。" diff --git a/src/unix/fcitx/po/zh_TW.po b/src/unix/fcitx/po/zh_TW.po new file mode 100644 index 00000000..69a6c5c0 --- /dev/null +++ b/src/unix/fcitx/po/zh_TW.po @@ -0,0 +1,91 @@ +# 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 +# csslayer <wengxt@gmail.com>, 2013 +# csslayer <wengxt@gmail.com>, 2012 +# csslayer <wengxt@gmail.com>, 2012-2013 +msgid "" +msgstr "" +"Project-Id-Version: fcitx\n" +"Report-Msgid-Bugs-To: fcitx-dev@googlegroups.com\n" +"POT-Creation-Date: 2015-07-21 12:01+0200\n" +"PO-Revision-Date: 2013-11-16 14:13+0000\n" +"Last-Translator: csslayer <wengxt@gmail.com>\n" +"Language-Team: Chinese (Taiwan) (http://www.transifex.com/projects/p/fcitx/" +"language/zh_TW/)\n" +"Language: zh_TW\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\n" + +#: ../eim.cc:181 +msgid "Press Escape to go back" +msgstr "按下 Escape 返回" + +#: ../fcitx_mozc.cc:65 +msgid "Direct" +msgstr "直接鍵盤輸入" + +#: ../fcitx_mozc.cc:70 +msgid "Hiragana" +msgstr "平假名" + +#: ../fcitx_mozc.cc:75 +msgid "Full Katakana" +msgstr "全形片假名" + +#: ../fcitx_mozc.cc:80 +msgid "Half ASCII" +msgstr "半形 ASCII" + +#: ../fcitx_mozc.cc:85 +msgid "Full ASCII" +msgstr "全形 ASCII" + +#: ../fcitx_mozc.cc:90 +msgid "Half Katakana" +msgstr "半形片假名" + +#: ../fcitx_mozc.cc:405 ../fcitx_mozc.cc:406 ../fcitx_mozc.cc:474 +msgid "Composition Mode" +msgstr "編輯模式" + +#: ../fcitx_mozc.cc:416 ../fcitx_mozc.cc:417 +msgid "Tool" +msgstr "工具" + +#: ../fcitx_mozc.cc:487 +msgid "Mozc Tool" +msgstr "Mozc 工具" + +#: ../fcitx_mozc.cc:493 +msgid "Configuration Tool" +msgstr "設定工具" + +#: ../fcitx_mozc.cc:494 +msgid "Dictionary Tool" +msgstr "字典工具" + +#: ../fcitx_mozc.cc:495 +msgid "Hand Writing" +msgstr "手寫輸入" + +#: ../fcitx_mozc.cc:496 +msgid "Character Palette" +msgstr "字符映射表" + +#: ../fcitx_mozc.cc:497 +msgid "Add Word" +msgstr "添加單詞" + +#: ../fcitx_mozc.cc:498 +msgid "About Mozc" +msgstr "關於 Mozc" + +#: ../mozc_response_parser.cc:366 +msgid "Press Ctrl+Alt+H to show usages." +msgstr "按下 Ctrl+Alt+H 顯示用法。" diff --git a/src/unix/fcitx/surrounding_text_util.cc b/src/unix/fcitx/surrounding_text_util.cc new file mode 100644 index 00000000..d6ccc34a --- /dev/null +++ b/src/unix/fcitx/surrounding_text_util.cc @@ -0,0 +1,242 @@ +// Copyright 2010-2013, Google Inc. +// 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. + +#include "unix/fcitx/surrounding_text_util.h" + +#include <limits> +#include <string> +#include <fcitx/instance.h> +#include <fcitx/module/clipboard/fcitx-clipboard.h> + +#include "base/port.h" +#include "base/logging.h" +#include "base/util.h" + +namespace mozc { +namespace fcitx { + +bool SurroundingTextUtil::GetSafeDelta(uint from, uint to, int32 *delta) { + DCHECK(delta); + + static_assert(sizeof(int64) >= sizeof(uint), + "int64 must be sufficient to store a guint value."); + static_assert(sizeof(int64) == sizeof(llabs(0)), + "|llabs(0)| must returns a 64-bit integer."); + const int64 kInt32AbsMax = + llabs(static_cast<int64>(std::numeric_limits<int32>::max())); + const int64 kInt32AbsMin = + llabs(static_cast<int64>(std::numeric_limits<int32>::min())); + const int64 kInt32SafeAbsMax = + std::min(kInt32AbsMax, kInt32AbsMin); + + const int64 diff = static_cast<int64>(from) - static_cast<int64>(to); + if (llabs(diff) > kInt32SafeAbsMax) { + return false; + } + + *delta = static_cast<int32>(diff); + return true; +} + +namespace { + +// Moves |iter| with |skip_count| characters. +// Returns false if |iter| reaches to the end before skipping +// |skip_count| characters. +bool Skip(ConstChar32Iterator *iter, size_t skip_count) { + for (size_t i = 0; i < skip_count; ++i) { + if (iter->Done()) { + return false; + } + iter->Next(); + } + return true; +} + +// Returns true if |prefix_iter| is the prefix of |iter|. +// Returns false if |prefix_iter| is an empty sequence. +// Otherwise returns false. +// This function receives ConstChar32Iterator as pointer because +// ConstChar32Iterator is defined as non-copyable. +bool StartsWith(ConstChar32Iterator *iter, + ConstChar32Iterator *prefix_iter) { + if (iter->Done() || prefix_iter->Done()) { + return false; + } + + while (true) { + if (iter->Get() != prefix_iter->Get()) { + return false; + } + prefix_iter->Next(); + if (prefix_iter->Done()) { + return true; + } + iter->Next(); + if (iter->Done()) { + return false; + } + } +} + + +// Returns true if |surrounding_text| contains |selected_text| +// from |cursor_pos| to |*anchor_pos|. +// Otherwise returns false. +bool SearchAnchorPosForward( + const string &surrounding_text, + const string &selected_text, + size_t selected_chars_len, + uint cursor_pos, + uint *anchor_pos) { + + ConstChar32Iterator iter(surrounding_text); + // Move |iter| to cursor pos. + if (!Skip(&iter, cursor_pos)) { + return false; + } + + ConstChar32Iterator sel_iter(selected_text); + if (!StartsWith(&iter, &sel_iter)) { + return false; + } + *anchor_pos = cursor_pos + selected_chars_len; + return true; +} + +// Returns true if |surrounding_text| contains |selected_text| +// from |*anchor_pos| to |cursor_pos|. +// Otherwise returns false. +bool SearchAnchorPosBackward( + const string &surrounding_text, + const string &selected_text, + size_t selected_chars_len, + uint cursor_pos, + uint *anchor_pos) { + if (cursor_pos < selected_chars_len) { + return false; + } + + ConstChar32Iterator iter(surrounding_text); + // Skip |iter| to (potential) anchor pos. + const uint skip_count = cursor_pos - selected_chars_len; + DCHECK_LE(skip_count, cursor_pos); + if (!Skip(&iter, skip_count)) { + return false; + } + + ConstChar32Iterator sel_iter(selected_text); + if (!StartsWith(&iter, &sel_iter)) { + return false; + } + *anchor_pos = cursor_pos - selected_chars_len; + return true; +} + +} // namespace + +bool SurroundingTextUtil::GetAnchorPosFromSelection( + const string &surrounding_text, + const string &selected_text, + uint cursor_pos, + uint *anchor_pos) { + DCHECK(anchor_pos); + + if (surrounding_text.empty()) { + return false; + } + + if (selected_text.empty()) { + return false; + } + + const size_t selected_chars_len = Util::CharsLen(selected_text); + + if (SearchAnchorPosForward(surrounding_text, selected_text, + selected_chars_len, + cursor_pos, anchor_pos)) { + return true; + } + + return SearchAnchorPosBackward(surrounding_text, selected_text, + selected_chars_len, + cursor_pos, anchor_pos); +} + +bool GetSurroundingText(FcitxInstance* instance, + SurroundingTextInfo *info) { + FcitxInputContext* ic = FcitxInstanceGetCurrentIC(instance); + if (!ic || !(ic->contextCaps & CAPACITY_SURROUNDING_TEXT)) { + return false; + } + + uint cursor_pos = 0; + uint anchor_pos = 0; + char* str = NULL; + + if (!FcitxInstanceGetSurroundingText(instance, ic, &str, &cursor_pos, &anchor_pos)) { + return false; + } + + const string surrounding_text(str); + free(str); + + if (cursor_pos == anchor_pos) { + const char* primary = NULL; + + if ((primary = FcitxClipboardGetPrimarySelection(instance, NULL)) != NULL) { + uint new_anchor_pos = 0; + const string primary_text(primary); + if (SurroundingTextUtil::GetAnchorPosFromSelection( + surrounding_text, primary_text, + cursor_pos, &new_anchor_pos)) { + anchor_pos = new_anchor_pos; + } + } + } + + if (!SurroundingTextUtil::GetSafeDelta(cursor_pos, anchor_pos, + &info->relative_selected_length)) { + LOG(ERROR) << "Too long text selection."; + return false; + } + + const size_t selection_start = std::min(cursor_pos, anchor_pos); + const size_t selection_length = std::abs(info->relative_selected_length); + Util::SubStringPiece(surrounding_text, 0, selection_start) + .CopyToString(&info->preceding_text); + Util::SubStringPiece(surrounding_text, selection_start, selection_length) + .CopyToString(&info->selection_text); + Util::SubStringPiece(surrounding_text, selection_start + selection_length) + .CopyToString(&info->following_text); + return true; +} + +} // namespace fcitx +} // namespace mozc diff --git a/src/unix/fcitx/surrounding_text_util.h b/src/unix/fcitx/surrounding_text_util.h new file mode 100644 index 00000000..5bf661db --- /dev/null +++ b/src/unix/fcitx/surrounding_text_util.h @@ -0,0 +1,87 @@ +// Copyright 2010-2013, Google Inc. +// 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_SURROUNDING_TEXT_URIL_H_ +#define MOZC_UNIX_FCITX_SURROUNDING_TEXT_URIL_H_ + +#include <string> +#include <fcitx/instance.h> + +#include "base/port.h" + +namespace mozc { +namespace fcitx { + +struct SurroundingTextInfo { + SurroundingTextInfo() + : relative_selected_length(0) {} + + int32 relative_selected_length; + std::string preceding_text; + std::string selection_text; + std::string following_text; +}; + +class SurroundingTextUtil { + public: + // Calculates |from| - |to| and stores the result into |delta| with + // checking integer overflow. + // Returns true when neither |abs(delta)| nor |-delta| does not cause + // integer overflow, that is, |delta| is in a safe range. + // Returns false otherwise. + static bool GetSafeDelta(uint from, uint to, int32 *delta); + + // Returns true if + // 1. |surrounding_text| contains |selected_text| + // from |cursor_pos| to |*anchor_pos|. + // or, + // 2. |surrounding_text| contains |selected_text| + // from |*anchor_pos| to |cursor_pos|. + // with calculating |*anchor_pos|, + // where |cursor_pos| and |*anchor_pos| are counts of Unicode characters. + // When both 1) and 2) are satisfied, this function calculates + // |*anchor_pos| for case 1). + // Otherwise returns false. + static bool GetAnchorPosFromSelection( + const string &surrounding_text, + const string &selected_text, + uint cursor_pos, + uint *anchor_pos); + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(SurroundingTextUtil); +}; + +bool GetSurroundingText(FcitxInstance* instance, + SurroundingTextInfo *info); + +} // namespace fcitx +} // namespace mozc + +#endif // MOZC_UNIX_FCITX_SURROUNDING_TEXT_URIL_H_ diff --git a/src/unix/fcitx5/Messages.sh b/src/unix/fcitx5/Messages.sh new file mode 100755 index 00000000..3bd2d00d --- /dev/null +++ b/src/unix/fcitx5/Messages.sh @@ -0,0 +1,29 @@ +#!/bin/bash +DOMAIN=fcitx5-mozc +POT_FILE=po/$DOMAIN.pot +set -x +XGETTEXT="xgettext --package-name=$DOMAIN --add-comments --sort-output --msgid-bugs-address=fcitx-dev@googlegroups.com" +source_files=$(find . -name \*.cpp -o -name \*.cc -o -name \*.h) +$XGETTEXT --keyword=_ --keyword=N_ --language=C++ -o ${POT_FILE} $source_files +#desktop_files=$(find . -name \*.conf.in -o -name \*.conf.in.in -o -name \*.desktop.in) +#$XGETTEXT --language=Desktop $desktop_files -j -o ${POT_FILE} + +sed -i 's|^"Content-Type: text/plain; charset=CHARSET\\n"|"Content-Type: text/plain; charset=utf-8\\n"|g' ${POT_FILE} + +# Due to transifex problem, delete the date. +#sed -i '/^"PO-Revision-Date/d' ${POT_FILE} +#sed -i '/^"PO-Revision-Date/d' ${POT_FILE} +sed -i '/^# FIRST AUTHOR/d' ${POT_FILE} +sed -i '/^#, fuzzy/d' ${POT_FILE} +sed -i 's|^"Language: \\n"|"Language: LANG\\n"|g' ${POT_FILE} + +echo > po/LINGUAS + +for pofile in $(ls po/*.po | sort); do + pofilebase=$(basename $pofile) + pofilebase=${pofilebase/.po/} + msgmerge -U --backup=none $pofile ${POT_FILE} + project_line=$(grep "Project-Id-Version" ${POT_FILE} | head -n 1 | tr --delete '\n' | sed -e 's/[\/&]/\\&/g') + sed -i "s|.*Project-Id-Version.*|$project_line|g" $pofile + echo $pofilebase >> po/LINGUAS +done diff --git a/src/unix/fcitx5/fcitx5.gyp b/src/unix/fcitx5/fcitx5.gyp new file mode 100644 index 00000000..7f6941db --- /dev/null +++ b/src/unix/fcitx5/fcitx5.gyp @@ -0,0 +1,140 @@ +# +# Copyright (c) 2010-2017 fcitx Project http://github.com/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': { + 'use_fcitx5%': 'YES', + 'relative_dir': 'unix/fcitx5', + 'gen_out_dir': '<(SHARED_INTERMEDIATE_DIR)/<(relative_dir)', + 'fcitx_dependencies': [ + '../../base/base.gyp:base', + '../../client/client.gyp:client', + '../../ipc/ipc.gyp:ipc', + '../../session/session_base.gyp:ime_switch_util', + '../../protocol/protocol.gyp:commands_proto', + ], + }, + 'conditions': [['use_fcitx5=="YES"', { + 'targets': [ + { + # Meta target to set up build environment for ibus. Required 'cflags' + # and 'link_settings' will be automatically injected into any target + # which directly or indirectly depends on this target. + 'target_name': 'fcitx5_build_environment', + 'type': 'none', + 'variables': { + 'target_libs': [ + 'Fcitx5Core', + 'Fcitx5Config', + 'Fcitx5Utils', + 'Fcitx5Module', + ], + }, + 'all_dependent_settings': { + 'cflags': [ + '<!@(pkg-config --cflags <@(target_libs))', + ], + 'link_settings': { + 'libraries': [ + '<!@(pkg-config --libs-only-l <@(target_libs))', + ], + 'ldflags': [ + '<!@(pkg-config --libs-only-L <@(target_libs))', + ], + }, + }, + }, + { + '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', + '<(gen_out_dir)/po/de.mo', + ], + 'action': [ + 'sh', + './gen_fcitx_mozc_i18n.sh', + '<(gen_out_dir)/po', + ], + }], + }, + { + 'target_name': 'fcitx5-mozc', + 'product_prefix': '', + 'type': 'loadable_module', + 'sources': [ + 'fcitx_key_translator.cc', + 'fcitx_key_event_handler.cc', + 'surrounding_text_util.cc', + 'mozc_connection.cc', + 'mozc_response_parser.cc', + 'mozc_engine.cc', + 'mozc_state.cc', + ], + 'dependencies': [ + '<@(fcitx_dependencies)', + 'gen_fcitx_mozc_i18n', + 'fcitx5_build_environment', + ], + 'cflags_cc': [ + '-std=c++14', + ], + 'cflags_cc!': [ + '-std=gnu++0x' + ], + 'cflags!': [ + '-fno-exceptions', + ], + 'ldflags': [ + '-Wl,--no-undefined', + '-Wl,--as-needed', + ], + 'defines': [ + 'FCITX_GETTEXT_DOMAIN="fcitx5-mozc"', + ], + }, + ], + }, { + 'targets': [ + { + 'target_name': 'no_fcitx5_dummy', + 'type': 'none', + } + ]} + ]], +} diff --git a/src/unix/fcitx5/fcitx_key_event_handler.cc b/src/unix/fcitx5/fcitx_key_event_handler.cc new file mode 100644 index 00000000..0203a92c --- /dev/null +++ b/src/unix/fcitx5/fcitx_key_event_handler.cc @@ -0,0 +1,244 @@ +// Copyright 2010-2012, Google Inc. +// Copyright 2012-2017, 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. + +#include "unix/fcitx5/fcitx_key_event_handler.h" + +#include <map> + +#include "base/logging.h" +#include "base/singleton.h" + +namespace fcitx { + +namespace { +// TODO(hsumita): Removes this class, and moves |data_| into member +// variables of KeyEventhandler. +class AdditionalModifiersData { + public: + AdditionalModifiersData() { + data_[mozc::commands::KeyEvent::LEFT_ALT] = mozc::commands::KeyEvent::ALT; + data_[mozc::commands::KeyEvent::RIGHT_ALT] = mozc::commands::KeyEvent::ALT; + data_[mozc::commands::KeyEvent::LEFT_CTRL] = mozc::commands::KeyEvent::CTRL; + data_[mozc::commands::KeyEvent::RIGHT_CTRL] = + mozc::commands::KeyEvent::CTRL; + data_[mozc::commands::KeyEvent::LEFT_SHIFT] = + mozc::commands::KeyEvent::SHIFT; + data_[mozc::commands::KeyEvent::RIGHT_SHIFT] = + mozc::commands::KeyEvent::SHIFT; + } + const std::map<uint32, mozc::commands::KeyEvent::ModifierKey> &data() { + return data_; + } + + private: + std::map<uint32, mozc::commands::KeyEvent::ModifierKey> data_; +}; + +// TODO(hsumita): Moves this function into member functions of +// KeyEventHandler. +void AddAdditionalModifiers( + std::set<mozc::commands::KeyEvent::ModifierKey> *modifier_keys_set) { + DCHECK(modifier_keys_set); + + const std::map<uint32, mozc::commands::KeyEvent::ModifierKey> &data = + mozc::Singleton<AdditionalModifiersData>::get()->data(); + + // Adds MODIFIER if there are (LEFT|RIGHT)_MODIFIER like LEFT_SHIFT. + for (std::set<mozc::commands::KeyEvent::ModifierKey>::const_iterator it = + modifier_keys_set->begin(); + it != modifier_keys_set->end(); ++it) { + std::map<uint32, mozc::commands::KeyEvent::ModifierKey>::const_iterator + item = data.find(*it); + if (item != data.end()) { + modifier_keys_set->insert(item->second); + } + } +} + +bool IsModifierToBeSentOnKeyUp(const mozc::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) == mozc::commands::KeyEvent::CAPS) { + return false; + } + + return true; +} +} // namespace + +KeyEventHandler::KeyEventHandler() : key_translator_(new KeyTranslator) { + Clear(); +} + +bool KeyEventHandler::GetKeyEvent( + KeySym keyval, uint32 keycode, KeyStates modifiers, + mozc::config::Config::PreeditMethod preedit_method, bool layout_is_jp, + bool is_key_up, mozc::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, + mozc::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 (std::set<mozc::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 fcitx diff --git a/src/unix/fcitx5/fcitx_key_event_handler.h b/src/unix/fcitx5/fcitx_key_event_handler.h new file mode 100644 index 00000000..555718da --- /dev/null +++ b/src/unix/fcitx5/fcitx_key_event_handler.h @@ -0,0 +1,78 @@ +// Copyright 2010-2012, Google Inc. +// Copyright 2012-2017, 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 UNIX_FCITX5_FCITX_KEY_EVENT_HANDLER_H_ +#define UNIX_FCITX5_FCITX_KEY_EVENT_HANDLER_H_ + +#include <fcitx-utils/key.h> +#include <memory> +#include <set> + +#include "base/port.h" +#include "protocol/commands.pb.h" +#include "protocol/config.pb.h" +#include "unix/fcitx5/fcitx_key_translator.h" + +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(KeySym keyval, uint32 keycode, KeyStates modifiers, + mozc::config::Config::PreeditMethod preedit_method, + bool layout_is_jp, bool is_key_up, + mozc::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, + mozc::commands::KeyEvent *key_event); + + std::unique_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. + std::set<uint32> currently_pressed_modifiers_; + // Pending modifier keys. + std::set<mozc::commands::KeyEvent::ModifierKey> modifiers_to_be_sent_; + + DISALLOW_COPY_AND_ASSIGN(KeyEventHandler); +}; + +} // namespace fcitx + +#endif // UNIX_FCITX5_FCITX_KEY_EVENT_HANDLER_H_ diff --git a/src/unix/fcitx5/fcitx_key_translator.cc b/src/unix/fcitx5/fcitx_key_translator.cc new file mode 100644 index 00000000..3c06d119 --- /dev/null +++ b/src/unix/fcitx5/fcitx_key_translator.cc @@ -0,0 +1,526 @@ +// Copyright 2010-2012, Google Inc. +// Copyright 2012-2017, 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. + +#include "unix/fcitx5/fcitx_key_translator.h" + +#include "base/logging.h" + +namespace fcitx { + +namespace { + +const struct { + uint32 from; + mozc::commands::KeyEvent::SpecialKey to; +} special_key_map[] = { + {FcitxKey_VoidSymbol, mozc::commands::KeyEvent::NO_SPECIALKEY}, + {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}, + + // Shift+TAB. + {FcitxKey_ISO_Left_Tab, mozc::commands::KeyEvent::TAB}, + + // TODO(mazda): Handle following keys? + // - FcitxKey_Kana_Lock? FcitxKey_KEY_Kana_Shift? +}; + +const struct { + 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}, + {static_cast<uint32_t>(KeyState::CapsLock), mozc::commands::KeyEvent::CAPS}, +}; + +const struct { + uint32 from; + mozc::commands::KeyEvent::ModifierKey to; +} modifier_mask_map[] = { + {static_cast<uint32_t>(KeyState::Shift), mozc::commands::KeyEvent::SHIFT}, + {static_cast<uint32_t>(KeyState::Ctrl), mozc::commands::KeyEvent::CTRL}, + {static_cast<uint32_t>(KeyState::Alt), mozc::commands::KeyEvent::ALT}, +}; + +// TODO(team): Add kana_map_dv to support Dvoraklayout. +const struct { + 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 + +KeyTranslator::KeyTranslator() { Init(); } + +KeyTranslator::~KeyTranslator() {} + +// TODO(nona): Fix 'Shift-0' behavior b/4338394 +bool KeyTranslator::Translate(KeySym keyval, uint32 keycode, + KeyStates modifiers, + mozc::config::Config::PreeditMethod method, + bool layout_is_jp, + mozc::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 & KeyState::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.unset(KeyState::Shift); + keyval = FcitxKey_Katakana; + } + string kana_key_string; + if ((method == mozc::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 (modifiers & KeyState::CapsLock) { + out_event->add_modifier_keys(mozc::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 == mozc::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(std::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(std::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(std::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(std::make_pair(kana_map_jp[i].code, + std::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(std::make_pair(kana_map_us[i].code, + std::make_pair(kana_map_us[i].no_shift, + kana_map_us[i].shift))) + .second); + } +} + +bool KeyTranslator::IsModifierKey(KeySym keyval, uint32 keycode, + KeyStates modifiers) const { + return modifier_key_map_.find(keyval) != modifier_key_map_.end(); +} + +bool KeyTranslator::IsSpecialKey(KeySym keyval, uint32 keycode, + KeyStates modifiers) const { + return special_key_map_.find(keyval) != special_key_map_.end(); +} + +bool KeyTranslator::IsHiraganaKatakanaKeyWithShift(KeySym keyval, + uint32 keycode, + KeyStates modifiers) { + return ((modifiers & KeyState::Shift) && + (keyval == FcitxKey_Hiragana_Katakana)); +} + +bool KeyTranslator::IsKanaAvailable(KeySym keyval, uint32 keycode, + KeyStates modifiers, bool layout_is_jp, + string *out) const { + if ((modifiers & KeyState::Ctrl) || (modifiers & KeyState::Alt)) { + return false; + } + const auto &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 == 132 || keycode == 133) { + *out = "\xe3\x83\xbc"; // "ー" + } else { + *out = "\xe3\x82\x8d"; // "ろ" + } + } else { + *out = (modifiers & KeyState::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(KeySym keyval, uint32 keycode, + KeyStates modifiers) { + if ((modifiers & KeyState::Ctrl) || (modifiers & KeyState::Alt)) { + return false; + } + return IsAscii(keyval, keycode, modifiers); +} + +bool KeyTranslator::IsAscii(KeySym keyval, uint32 keycode, + KeyStates modifiers) { + return (keyval > FcitxKey_space && + // Note: Space key (0x20) is a special key in Mozc. + keyval <= FcitxKey_asciitilde); // 0x7e. +} + +} // namespace fcitx diff --git a/src/unix/fcitx5/fcitx_key_translator.h b/src/unix/fcitx5/fcitx_key_translator.h new file mode 100644 index 00000000..a78be640 --- /dev/null +++ b/src/unix/fcitx5/fcitx_key_translator.h @@ -0,0 +1,102 @@ +// Copyright 2010-2012, Google Inc. +// Copyright 2012-2017, 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 UNIX_FCITX5_FCITX_KEY_TRANSLATOR_H_ +#define UNIX_FCITX5_FCITX_KEY_TRANSLATOR_H_ + +#include <fcitx-utils/key.h> +#include <map> +#include <set> +#include <string> + +#include "base/port.h" +#include "protocol/commands.pb.h" + +namespace fcitx { + +// This class is responsible for converting fcitx's key to IPC input for +// mozc_server. +class KeyTranslator { + public: + KeyTranslator(); + virtual ~KeyTranslator(); + + // Converts fcitx key into Mozc key code and stores them on out_translated. + bool Translate(KeySym keyval, uint32 keycode, KeyStates modifiers, + mozc::config::Config::PreeditMethod method, bool layout_is_jp, + mozc::commands::KeyEvent *out_event) const; + + private: + typedef std::map<uint32, mozc::commands::KeyEvent::SpecialKey> SpecialKeyMap; + typedef std::map<uint32, mozc::commands::KeyEvent::ModifierKey> + ModifierKeyMap; + typedef std::map<uint32, std::pair<string, string> > KanaMap; + + // Returns true iff key is modifier key such as SHIFT, ALT, or CAPSLOCK. + bool IsModifierKey(KeySym keyval, uint32 keycode, KeyStates modifiers) const; + + // Returns true iff key is special key such as ENTER, ESC, or PAGE_UP. + bool IsSpecialKey(KeySym keyval, uint32 keycode, KeyStates modifiers) const; + + // Returns true iff |keyval| is a key with a kana assigned. + bool IsKanaAvailable(KeySym keyval, uint32 keycode, KeyStates modifiers, + bool layout_is_jp, string *out) const; + + // Returns true iff key is ASCII such as '0', 'A', or '!'. + static bool IsAscii(KeySym keyval, uint32 keycode, KeyStates modifiers); + + // Returns true iff key is printable. + static bool IsPrintable(KeySym keyval, uint32 keycode, KeyStates modifiers); + + // Returns true iff key is HiraganaKatakana with shift modifier. + static bool IsHiraganaKatakanaKeyWithShift(KeySym keyval, uint32 keycode, + KeyStates 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 + +#endif // MOZC_UNIX_FCITX_FCITX_KEY_TRANSLATOR_H_ diff --git a/src/unix/fcitx5/gen_fcitx_mozc_i18n.sh b/src/unix/fcitx5/gen_fcitx_mozc_i18n.sh new file mode 100755 index 00000000..97ff4a49 --- /dev/null +++ b/src/unix/fcitx5/gen_fcitx_mozc_i18n.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +objdir="$1" + +mkdir -p "$1" + +for pofile in po/*.po +do + msgfmt "$pofile" -o "$1/`basename ${pofile} .po`.mo" +done diff --git a/src/unix/fcitx5/mozc-addon.conf b/src/unix/fcitx5/mozc-addon.conf new file mode 100644 index 00000000..e520d4b9 --- /dev/null +++ b/src/unix/fcitx5/mozc-addon.conf @@ -0,0 +1,9 @@ +[Addon] +Name=mozc +Category=InputMethod +Library=fcitx5-mozc +Type=SharedLibrary +OnDemand=True + +[Addon/OptionalDependencies] +0=clipboard diff --git a/src/unix/fcitx5/mozc.conf b/src/unix/fcitx5/mozc.conf new file mode 100644 index 00000000..ab0b7aa5 --- /dev/null +++ b/src/unix/fcitx5/mozc.conf @@ -0,0 +1,6 @@ +[InputMethod] +Name=Mozc +Icon=PREFIX/share/fcitx5/mozc/icon/mozc.png +Label=あ +LangCode=ja +Addon=mozc diff --git a/src/unix/fcitx5/mozc_connection.cc b/src/unix/fcitx5/mozc_connection.cc new file mode 100644 index 00000000..0cc930b0 --- /dev/null +++ b/src/unix/fcitx5/mozc_connection.cc @@ -0,0 +1,69 @@ +// 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. + +#include "unix/fcitx5/mozc_connection.h" + +#include <string> + +#include "base/logging.h" +#include "base/util.h" +#include "client/client.h" +#include "ipc/ipc.h" +#include "protocol/commands.pb.h" +#include "session/ime_switch_util.h" + +namespace fcitx { + +mozc::client::ClientInterface *CreateAndConfigureClient() { + mozc::client::ClientInterface *client = + mozc::client::ClientFactory::NewClient(); + // Currently client capability is fixed. + mozc::commands::Capability capability; + capability.set_text_deletion( + mozc::commands::Capability::DELETE_PRECEDING_TEXT); + client->set_client_capability(capability); + return client; +} + +MozcConnection::MozcConnection() + : client_factory_(mozc::IPCClientFactory::GetIPCClientFactory()) { + VLOG(1) << "MozcConnection is created"; +} + +MozcConnection::~MozcConnection() { VLOG(1) << "MozcConnection is destroyed"; } + +mozc::client::ClientInterface *MozcConnection::CreateClient() { + mozc::client::ClientInterface *client = CreateAndConfigureClient(); + client->SetServerLauncher(new mozc::client::ServerLauncher); + client->SetIPCClientFactory(client_factory_); + return client; +} + +} // namespace fcitx diff --git a/src/unix/fcitx5/mozc_connection.h b/src/unix/fcitx5/mozc_connection.h new file mode 100644 index 00000000..4f7b09c2 --- /dev/null +++ b/src/unix/fcitx5/mozc_connection.h @@ -0,0 +1,72 @@ +// 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_FCITX5_MOZC_CONNECTION_H_ +#define MOZC_UNIX_FCITX5_MOZC_CONNECTION_H_ + +#include <memory> +#include <string> + +#include "base/port.h" +#include "protocol/commands.pb.h" +#include "unix/fcitx5/fcitx_key_event_handler.h" + +namespace mozc { + +class IPCClientInterface; +class IPCClientFactoryInterface; + +namespace client { +class ClientInterface; +class ServerLauncherInterface; +} // namespace client + +} // namespace mozc + +namespace fcitx { + +class MozcConnection { + public: + static const int kNoSession; + + MozcConnection(); + virtual ~MozcConnection(); + + mozc::client::ClientInterface* CreateClient(); + + private: + mozc::IPCClientFactoryInterface* client_factory_; + + DISALLOW_COPY_AND_ASSIGN(MozcConnection); +}; + +} // namespace fcitx + +#endif // MOZC_UNIX_FCITX5_MOZC_CONNECTION_H_ diff --git a/src/unix/fcitx5/mozc_engine.cc b/src/unix/fcitx5/mozc_engine.cc new file mode 100644 index 00000000..59c3b0aa --- /dev/null +++ b/src/unix/fcitx5/mozc_engine.cc @@ -0,0 +1,273 @@ +/* + * Copyright (C) 2017~2017 by CSSlayer + * wengxt@gmail.com + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, + * see <http://www.gnu.org/licenses/>. + */ + +#include "unix/fcitx5/mozc_engine.h" + +#include <fcitx-utils/i18n.h> +#include <fcitx-utils/log.h> +#include <fcitx-utils/standardpath.h> +#include <fcitx/inputcontext.h> +#include <fcitx/inputcontextmanager.h> +#include <fcitx/userinterfacemanager.h> +#include <vector> + +#include "base/init_mozc.h" +#include "base/process.h" +#include "unix/fcitx5/mozc_connection.h" +#include "unix/fcitx5/mozc_response_parser.h" + +namespace fcitx { + +const struct CompositionMode { + const char *name; + const char *icon; + const char *label; + const char *description; + mozc::commands::CompositionMode mode; +} kPropCompositionModes[] = { + { + "mozc-mode-direct", + "mozc-direct.png", + "A", + N_("Direct"), + mozc::commands::DIRECT, + }, + { + "mozc-mode-hiragana", + "mozc-hiragana.png", + "\xe3\x81\x82", // Hiragana letter A in UTF-8. + N_("Hiragana"), + mozc::commands::HIRAGANA, + }, + { + "mozc-mode-katakana_full", + "mozc-katakana_full.png", + "\xe3\x82\xa2", // Katakana letter A. + N_("Full Katakana"), + mozc::commands::FULL_KATAKANA, + }, + { + + "mozc-mode-alpha_half", + "mozc-alpha_half.png", + "A", + N_("Half ASCII"), + mozc::commands::HALF_ASCII, + }, + { + + "mozc-mode-alpha_full", + "mozc-alpha_full.png", + "\xef\xbc\xa1", // Full width ASCII letter A. + N_("Full ASCII"), + mozc::commands::FULL_ASCII, + }, + { + "mozc-mode-katakana_full", + "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); + +std::string MozcModeAction::shortText(InputContext *ic) const { + return _("Composition Mode"); +} + +std::string MozcModeAction::longText(InputContext *ic) const { + auto mozc_state = engine_->mozcState(ic); + return _(kPropCompositionModes[mozc_state->GetCompositionMode()].description); +} + +std::string MozcModeAction::icon(InputContext *ic) const { + auto mozc_state = engine_->mozcState(ic); + return stringutils::joinPath( + StandardPath::global().fcitxPath("pkgdatadir"), "mozc/icon", + kPropCompositionModes[mozc_state->GetCompositionMode()].icon); +} + +MozcModeSubAction::MozcModeSubAction(MozcEngine *engine, + mozc::commands::CompositionMode mode) + : engine_(engine), mode_(mode) { + setShortText(kPropCompositionModes[mode].label); + setLongText(_(kPropCompositionModes[mode].description)); + setIcon(stringutils::joinPath(StandardPath::global().fcitxPath("pkgdatadir"), + "mozc/icon", kPropCompositionModes[mode].icon)); + setCheckable(true); +} + +bool MozcModeSubAction::isChecked(InputContext *ic) const { + auto mozc_state = engine_->mozcState(ic); + return mozc_state->GetCompositionMode() == mode_; +} + +void MozcModeSubAction::activate(InputContext *ic) { + auto mozc_state = engine_->mozcState(ic); + mozc_state->SendCompositionMode(mode_); +} + +// This array must correspond with the CompositionMode enum in the +// mozc/session/command.proto file. +static_assert(mozc::commands::NUM_OF_COMPOSITIONS == kNumCompositionModes, + "number of modes must match"); + +Instance *Init(Instance *instance) { + int argc = 1; + char argv0[] = "fcitx_mozc"; + char *_argv[] = {argv0}; + char **argv = _argv; + mozc::InitMozc(argv[0], &argc, &argv, true); + return instance; +} + +MozcEngine::MozcEngine(Instance *instance) + : instance_(Init(instance)), + connection_(std::make_unique<MozcConnection>()), + factory_([this](InputContext &ic) { + return new MozcState(&ic, connection_->CreateClient(), this); + }), + modeAction_(this) { + instance_->inputContextManager().registerProperty("mozcState", &factory_); + instance_->userInterfaceManager().registerAction("mozc-mode", &modeAction_); + instance_->userInterfaceManager().registerAction("mozc-tool", &toolAction_); + toolAction_.setShortText(_("Tool")); + toolAction_.setLongText(_("Tool")); + toolAction_.setIcon( + stringutils::joinPath(StandardPath::global().fcitxPath("pkgdatadir"), + "mozc/icon", "mozc-tool.png")); + + int i = 0; + for (auto &modeAction : modeActions_) { + instance_->userInterfaceManager().registerAction( + kPropCompositionModes[i].name, &modeAction); + modeMenu_.addAction(&modeAction); + i++; + } + + instance_->userInterfaceManager().registerAction("mozc-tool-config", + &configToolAction_); + configToolAction_.setShortText(_("Configuration Tool")); + configToolAction_.connect<SimpleAction::Activated>([](InputContext *) { + mozc::Process::SpawnMozcProcess("mozc_tool", "--mode=config_dialog"); + }); + + instance_->userInterfaceManager().registerAction("mozc-tool-dict", + &dictionaryToolAction_); + dictionaryToolAction_.setShortText(_("Dictionary Tool")); + dictionaryToolAction_.connect<SimpleAction::Activated>([](InputContext *) { + mozc::Process::SpawnMozcProcess("mozc_tool", "--mode=dictionary_tool"); + }); + + instance_->userInterfaceManager().registerAction("mozc-tool-handwriting", + &handWritingAction_); + handWritingAction_.setShortText(_("Hand Writing")); + handWritingAction_.connect<SimpleAction::Activated>([](InputContext *) { + mozc::Process::SpawnMozcProcess("mozc_tool", "--mode=hand_writing"); + }); + + instance_->userInterfaceManager().registerAction("mozc-tool-character", + &characterPaletteAction_); + characterPaletteAction_.setShortText(_("Character Palette")); + characterPaletteAction_.connect<SimpleAction::Activated>([](InputContext *) { + mozc::Process::SpawnMozcProcess("mozc_tool", "--mode=character_palette"); + }); + + instance_->userInterfaceManager().registerAction("mozc-tool-add", + &addWordAction_); + addWordAction_.setShortText("Add Word"); + addWordAction_.connect<SimpleAction::Activated>([](InputContext *) { + mozc::Process::SpawnMozcProcess("mozc_tool", "--mode=word_register_dialog"); + }); + + instance_->userInterfaceManager().registerAction("mozc-tool-about", + &aboutAction_); + aboutAction_.setShortText("About Mozc"); + aboutAction_.connect<SimpleAction::Activated>([](InputContext *) { + mozc::Process::SpawnMozcProcess("mozc_tool", "--mode=about_dialog"); + }); + + toolMenu_.addAction(&configToolAction_); + toolMenu_.addAction(&dictionaryToolAction_); + toolMenu_.addAction(&handWritingAction_); + toolMenu_.addAction(&characterPaletteAction_); + toolMenu_.addAction(&addWordAction_); + toolMenu_.addAction(&aboutAction_); + + modeAction_.setMenu(&modeMenu_); + toolAction_.setMenu(&toolMenu_); +} + +MozcEngine::~MozcEngine() {} + +void MozcEngine::reloadConfig() {} +void MozcEngine::activate(const fcitx::InputMethodEntry &, + fcitx::InputContextEvent &event) { + auto ic = event.inputContext(); + auto mozc_state = mozcState(ic); + mozc_state->FocusIn(); + ic->statusArea().addAction(StatusGroup::InputMethod, &modeAction_); + ic->statusArea().addAction(StatusGroup::InputMethod, &toolAction_); +} +void MozcEngine::deactivate(const fcitx::InputMethodEntry &entry, + fcitx::InputContextEvent &event) { + auto ic = event.inputContext(); + auto mozc_state = mozcState(ic); + mozc_state->FocusOut(); + ic->statusArea().clearGroup(StatusGroup::InputMethod); +} +void MozcEngine::keyEvent(const InputMethodEntry &, KeyEvent &event) { + auto mozc_state = mozcState(event.inputContext()); + + // TODO: check layout + if (mozc_state->ProcessKeyEvent(event.rawKey().sym(), event.rawKey().code(), + event.rawKey().states(), false, + event.isRelease())) { + event.filterAndAccept(); + } +} + +void MozcEngine::reset(const InputMethodEntry &, InputContextEvent &event) { + auto mozc_state = mozcState(event.inputContext()); + mozc_state->Reset(); +} + +void MozcEngine::save() {} + +std::string MozcEngine::subMode(const fcitx::InputMethodEntry &, + fcitx::InputContext &ic) { + return modeAction_.longText(&ic); +} + +MozcState *MozcEngine::mozcState(InputContext *ic) { + return ic->propertyFor(&factory_); +} + +void MozcEngine::compositionModeUpdated(InputContext *ic) { + modeAction_.update(ic); + for (auto &modeAction : modeActions_) { + modeAction.update(ic); + } +} + +AddonInstance *MozcEngine::clipboardAddon() { return clipboard(); } +} // namespace fcitx + +FCITX_ADDON_FACTORY(fcitx::MozcEngineFactory) diff --git a/src/unix/fcitx5/mozc_engine.h b/src/unix/fcitx5/mozc_engine.h new file mode 100644 index 00000000..d75c8dba --- /dev/null +++ b/src/unix/fcitx5/mozc_engine.h @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2017~2017 by CSSlayer + * wengxt@gmail.com + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, + * see <http://www.gnu.org/licenses/>. + */ +#ifndef _FCITX_UNIX_FCITX5_MOZC_ENGINE_H_ +#define _FCITX_UNIX_FCITX5_MOZC_ENGINE_H_ + +#include <fcitx/action.h> +#include <fcitx/addonfactory.h> +#include <fcitx/addonmanager.h> +#include <fcitx/inputmethodengine.h> +#include <fcitx/instance.h> +#include <fcitx/menu.h> + +#include "unix/fcitx5/mozc_state.h" + +namespace fcitx { + +class MozcConnection; +class MozcResponseParser; +class MozcEngine; + +class MozcModeAction : public Action { + public: + MozcModeAction(MozcEngine *engine) : engine_(engine) {} + + std::string shortText(fcitx::InputContext *) const override; + std::string longText(fcitx::InputContext *) const override; + std::string icon(fcitx::InputContext *) const override; + + private: + MozcEngine *engine_; +}; + +class MozcModeSubAction : public SimpleAction { + public: + MozcModeSubAction(MozcEngine *engine, mozc::commands::CompositionMode mode); + bool isChecked(fcitx::InputContext *) const override; + void activate(fcitx::InputContext *) override; + + private: + MozcEngine *engine_; + mozc::commands::CompositionMode mode_; +}; + +class MozcEngine final : public InputMethodEngine { + public: + MozcEngine(Instance *instance); + ~MozcEngine(); + Instance *instance() { return instance_; } + void activate(const InputMethodEntry &entry, + InputContextEvent &event) override; + void deactivate(const fcitx::InputMethodEntry &entry, + fcitx::InputContextEvent &event) override; + void keyEvent(const InputMethodEntry &entry, KeyEvent &keyEvent) override; + void reloadConfig() override; + void reset(const InputMethodEntry &entry, InputContextEvent &event) override; + void save() override; + std::string subMode(const fcitx::InputMethodEntry &, + fcitx::InputContext &) override; + + MozcState *mozcState(InputContext *ic); + AddonInstance *clipboardAddon(); + + void compositionModeUpdated(InputContext *ic); + + private: + Instance *instance_; + std::unique_ptr<MozcConnection> connection_; + FactoryFor<MozcState> factory_; + MozcModeAction modeAction_; + SimpleAction toolAction_; + MozcModeSubAction modeActions_[mozc::commands::NUM_OF_COMPOSITIONS] = { + {this, mozc::commands::DIRECT}, + {this, mozc::commands::HIRAGANA}, + {this, mozc::commands::FULL_KATAKANA}, + {this, mozc::commands::FULL_ASCII}, + {this, mozc::commands::HALF_ASCII}, + {this, mozc::commands::HALF_KATAKANA}, + }; + + SimpleAction configToolAction_, dictionaryToolAction_, handWritingAction_, + characterPaletteAction_, addWordAction_, aboutAction_; + Menu toolMenu_; + Menu modeMenu_; + + FCITX_ADDON_DEPENDENCY_LOADER(clipboard, instance_->addonManager()); +}; + +class MozcEngineFactory : public AddonFactory { + public: + AddonInstance *create(AddonManager *manager) override { + return new MozcEngine(manager->instance()); + } +}; +} // namespace fcitx + +#endif // _FCITX_UNIX_FCITX5_MOZC_ENGINE_H_ diff --git a/src/unix/fcitx5/mozc_response_parser.cc b/src/unix/fcitx5/mozc_response_parser.cc new file mode 100644 index 00000000..248416ed --- /dev/null +++ b/src/unix/fcitx5/mozc_response_parser.cc @@ -0,0 +1,483 @@ +// 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. + +#include "unix/fcitx5/mozc_response_parser.h" + +#include <fcitx-utils/i18n.h> +#include <fcitx-utils/utf8.h> +#include <fcitx/candidatelist.h> +#include <fcitx/inputcontext.h> +#include <fcitx/inputpanel.h> +#include <string> +#include <vector> + +#include "base/logging.h" +#include "base/process.h" +#include "base/util.h" +#include "protocol/commands.pb.h" +#include "unix/fcitx5/mozc_engine.h" +#include "unix/fcitx5/surrounding_text_util.h" + +namespace fcitx { + +namespace { + +// 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 + "]"; +} + +class MozcCandidateWord final : public CandidateWord { + public: + MozcCandidateWord(int id, string text, MozcEngine *engine) + : CandidateWord(Text(text)), id_(id), engine_(engine) {} + + void select(InputContext *inputContext) const override { + auto mozc_state = engine_->mozcState(inputContext); + mozc_state->SelectCandidate(id_); + } + + private: + int id_; + MozcEngine *engine_; +}; + +class MozcCandidateList final : public CandidateList, + public PageableCandidateList { + public: + MozcCandidateList(const mozc::commands::Candidates &candidates, + InputContext *ic, MozcEngine *engine, bool use_annotation) { + setPageable(this); + bool index_visible = false; + if (candidates.has_footer()) { + const auto &footer = candidates.footer(); + index_visible = footer.has_index_visible() && footer.index_visible(); + } + + if (candidates.candidate_size() > 0) { + if (candidates.candidate(0).index() > 0) { + hasPrev_ = true; + } + if (candidates.candidate(candidates.candidate_size() - 1).index() + 1 < + candidates.size()) { + hasNext_ = true; + } + } + if (candidates.has_direction() && + candidates.direction() == mozc::commands::Candidates::HORIZONTAL) { + layout_ = CandidateLayoutHint::Horizontal; + } + + int focused_index = -1; + cursor_ = -1; + if (candidates.has_focused_index()) { + focused_index = candidates.focused_index(); + } + + labels_.reserve(candidates.candidate_size()); + + for (int i = 0; i < candidates.candidate_size(); ++i) { + const mozc::commands::Candidates::Candidate &candidate = + candidates.candidate(i); + const uint32 index = candidate.index(); + + string value; + if (use_annotation && candidate.has_annotation() && + candidate.annotation().has_prefix()) { + value = candidate.annotation().prefix(); + } + value += candidate.value(); + if (use_annotation && candidate.has_annotation() && + candidate.annotation().has_suffix()) { + value += candidate.annotation().suffix(); + } + if (use_annotation && candidate.has_annotation() && + candidate.annotation().has_description()) { + // Display descriptions ([HALF][KATAKANA], [GREEK], [Black square], + // etc). + value += CreateDescriptionString(candidate.annotation().description()); + } + + if (use_annotation && candidates.has_focused_index() && + index == focused_index) { + if (candidate.has_information_id()) { + value += + CreateDescriptionString(_("Press Ctrl+Alt+H to show usages.")); + } + cursor_ = i; + } + + if (candidate.has_annotation() && candidate.annotation().has_shortcut()) { + labels_.emplace_back(candidate.annotation().shortcut()); + } else if (index_visible) { + labels_.emplace_back(std::to_string(i + 1)); + } else { + labels_.emplace_back(); + } + + int32 id = kBadCandidateId; + if (candidate.has_id()) { + id = candidate.id(); + DCHECK_NE(kBadCandidateId, id) << "Unexpected id is passed."; + } + candidateWords_.emplace_back( + std::make_shared<MozcCandidateWord>(id, value, engine)); + } + } + + const Text &label(int idx) const override { + checkIndex(idx); + return labels_[idx]; + } + + std::shared_ptr<const CandidateWord> candidate(int idx) const override { + checkIndex(idx); + return candidateWords_[idx]; + } + int size() const override { return candidateWords_.size(); } + + int cursorIndex() const override { return cursor_; } + + CandidateLayoutHint layoutHint() const override { return layout_; } + + bool hasPrev() const override { return hasPrev_; } + bool hasNext() const override { return hasNext_; } + void prev() override { + auto mozc_state = engine_->mozcState(ic_); + mozc_state->Paging(true); + } + void next() override { + auto mozc_state = engine_->mozcState(ic_); + mozc_state->Paging(false); + } + + bool usedNextBefore() const override { return true; } + + private: + void checkIndex(int idx) const { + if (idx < 0 && idx >= size()) { + throw std::invalid_argument("invalid index"); + } + } + + InputContext *ic_; + MozcEngine *engine_; + std::vector<Text> labels_; + bool hasPrev_ = false; + bool hasNext_ = false; + CandidateLayoutHint layout_ = CandidateLayoutHint::Vertical; + int cursor_ = -1; + std::vector<std::shared_ptr<CandidateWord>> candidateWords_; +}; + +} // namespace + +MozcResponseParser::MozcResponseParser(MozcEngine *engine) + : engine_(engine), use_annotation_(false) {} + +MozcResponseParser::~MozcResponseParser() {} + +void MozcResponseParser::UpdateDeletionRange( + const mozc::commands::Output &response, InputContext *ic) const { + if (response.has_deletion_range() && + response.deletion_range().offset() <= 0 && + response.deletion_range().offset() + response.deletion_range().length() >= + 0) { + ic->deleteSurroundingText(response.deletion_range().offset(), + response.deletion_range().length()); + } +} + +void MozcResponseParser::LaunchTool(const mozc::commands::Output &response, + InputContext *ic) const { + if (response.has_launch_tool_mode()) { + auto mozc_state = engine_->mozcState(ic); + mozc_state->GetClient()->LaunchToolWithProtoBuf(response); + } +} + +void MozcResponseParser::ExecuteCallback(const mozc::commands::Output &response, + InputContext *ic) const { + if (!response.has_callback()) { + return; + } + + if (!response.callback().has_session_command()) { + LOG(ERROR) << "callback does not have session_command"; + return; + } + + const mozc::commands::SessionCommand &callback_command = + response.callback().session_command(); + + if (!callback_command.has_type()) { + LOG(ERROR) << "callback_command has no type"; + return; + } + + mozc::commands::SessionCommand session_command; + session_command.set_type(callback_command.type()); + + // TODO(nona): Make a function to handle CONVERT_REVERSE. + // Used by CONVERT_REVERSE and/or UNDO + // This value represents how many characters are selected as a relative + // distance of characters. Positive value represents forward text selection + // and negative value represents backword text selection. + // Note that you should not allow 0x80000000 for |relative_selected_length| + // because you cannot safely use |-relative_selected_length| nor + // |abs(relative_selected_length)| in this case due to integer overflow. + SurroundingTextInfo surrounding_text_info; + + switch (callback_command.type()) { + case mozc::commands::SessionCommand::UNDO: + break; + case mozc::commands::SessionCommand::CONVERT_REVERSE: { + if (!GetSurroundingText(ic, &surrounding_text_info, + engine_->clipboardAddon())) { + return; + } + + session_command.set_text(surrounding_text_info.selection_text); + break; + } + default: + return; + } + + auto mozc_state = engine_->mozcState(ic); + mozc::commands::Output new_output; + if (!mozc_state->SendCommand(session_command, &new_output)) { + LOG(ERROR) << "Callback Command Failed"; + return; + } + + if (callback_command.type() == + mozc::commands::SessionCommand::CONVERT_REVERSE) { + // We need to remove selected text as a first step of reconversion. + mozc::commands::DeletionRange *range = new_output.mutable_deletion_range(); + // Use DeletionRange field to remove the selected text. + // For forward selection (that is, |relative_selected_length > 0|), the + // offset should be a negative value to delete preceding text. + // For backward selection (that is, |relative_selected_length < 0|), + // IBus and/or some applications seem to expect |offset == 0| somehow. + const int32 offset = + surrounding_text_info.relative_selected_length > 0 + ? -surrounding_text_info + .relative_selected_length // forward selection + : 0; // backward selection + range->set_offset(offset); + range->set_length(abs(surrounding_text_info.relative_selected_length)); + } + + VLOG(1) << "New output" << new_output.DebugString(); + + ParseResponse(new_output, ic); +} + +bool MozcResponseParser::ParseResponse(const mozc::commands::Output &response, + InputContext *ic) const { + auto mozc_state = engine_->mozcState(ic); + mozc_state->SetUsage("", ""); + + UpdateDeletionRange(response, ic); + + // 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()) { + mozc_state->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, ic); + } + + // First, determine the cursor position. + if (response.has_preedit()) { + const mozc::commands::Preedit &preedit = response.preedit(); + ParsePreedit(preedit, GetCursorPosition(response), ic); + } + + // Then show the candidate window. + if (response.has_candidates()) { + const mozc::commands::Candidates &candidates = response.candidates(); + ParseCandidates(candidates, ic); + } + + if (response.has_url()) { + const string &url = response.url(); + mozc_state->SetUrl(url); + } + LaunchTool(response, ic); + ExecuteCallback(response, ic); + + return true; // mozc consumed the key. +} + +void MozcResponseParser::SetUseAnnotation(bool use_annotation) { + use_annotation_ = use_annotation; +} + +void MozcResponseParser::ParseResult(const mozc::commands::Result &result, + InputContext *ic) const { + auto mozc_state = engine_->mozcState(ic); + switch (result.type()) { + case mozc::commands::Result::NONE: { + mozc_state->SetAuxString("No result"); // not a fatal error. + break; + } + case mozc::commands::Result::STRING: { + mozc_state->SetResultString(result.value()); + break; + } + } +} + +void MozcResponseParser::ParseCandidates( + const mozc::commands::Candidates &candidates, InputContext *ic) const { + auto mozc_state = engine_->mozcState(ic); + const mozc::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()) { + if (!auxString.empty()) { + auxString += " "; + } + auxString += std::to_string(candidates.focused_index() + 1); + auxString += "/"; + auxString += std::to_string(candidates.size()); + } + mozc_state->SetAuxString(auxString); + } + + ic->inputPanel().setCandidateList(std::make_unique<MozcCandidateList>( + candidates, ic, engine_, use_annotation_)); + + int focused_index = -1; + if (candidates.has_focused_index()) { + focused_index = candidates.focused_index(); + } + + if (focused_index >= 0) { + std::map<int32, std::pair<string, string>> usage_map; + if (candidates.has_usages()) { + const mozc::commands::InformationList &usages = candidates.usages(); + for (size_t i = 0; i < usages.information().size(); ++i) { + const mozc::commands::Information &information = usages.information(i); + if (!information.has_id() || !information.has_description()) continue; + usage_map[information.id()].first = information.title(); + usage_map[information.id()].second = information.description(); + } + } + + for (int i = 0; i < candidates.candidate_size(); ++i) { + const mozc::commands::Candidates::Candidate &candidate = + candidates.candidate(i); + const uint32 index = candidate.index(); + if (use_annotation_ && index == focused_index && + candidate.has_information_id()) { + std::map<int32, std::pair<string, string>>::iterator it = + usage_map.find(candidate.information_id()); + if (it != usage_map.end()) { + mozc_state->SetUsage(it->second.first, it->second.second); + } + } + } + } +} + +void MozcResponseParser::ParsePreedit(const mozc::commands::Preedit &preedit, + uint32 position, InputContext *ic) const { + auto mozc_state = engine_->mozcState(ic); + Text preedit_text; + string s; + + for (int i = 0; i < preedit.segment_size(); ++i) { + const mozc::commands::Preedit_Segment &segment = preedit.segment(i); + const string &str = segment.value(); + if (!utf8::validate(str)) { + continue; + } + TextFormatFlags format_flag; + + switch (segment.annotation()) { + case mozc::commands::Preedit_Segment::NONE: + break; + case mozc::commands::Preedit_Segment::UNDERLINE: + format_flag = TextFormatFlag::Underline; + break; + case mozc::commands::Preedit_Segment::HIGHLIGHT: + format_flag = TextFormatFlag::HighLight; + break; + } + s += str; + + preedit_text.append(str, format_flag); + } + + int cursor = -1; + auto charLength = utf8::length(s); + if (charLength >= position) { + cursor = utf8::ncharByteLength(s.begin(), position); + } + preedit_text.setCursor(cursor); + + mozc_state->SetPreeditInfo(std::move(preedit_text)); +} + +} // namespace fcitx diff --git a/src/unix/fcitx5/mozc_response_parser.h b/src/unix/fcitx5/mozc_response_parser.h new file mode 100644 index 00000000..8bb8366b --- /dev/null +++ b/src/unix/fcitx5/mozc_response_parser.h @@ -0,0 +1,93 @@ +// 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/port.h" + +namespace mozc { +namespace commands { + +class Candidates; +class Input; +class Output; +class Preedit; +class Result; + +} // namespace commands +} // namespace mozc + +namespace fcitx { + +class InputContext; +class MozcEngine; + +// This class parses IPC response from mozc_server (mozc::commands::Output) and +// updates the FCITX UI. +class MozcResponseParser { + public: + MozcResponseParser(MozcEngine *engine); + ~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, + InputContext *ic) const; + + // Setter for use_annotation_. If use_annotation_ is true, ParseCandidates() + // uses annotation infomation. + void SetUseAnnotation(bool use_annotation); + + private: + void UpdateDeletionRange(const mozc::commands::Output &response, + InputContext *ic) const; + void LaunchTool(const mozc::commands::Output &response, + InputContext *ic) const; + void ExecuteCallback(const mozc::commands::Output &response, + InputContext *ic) const; + void ParseResult(const mozc::commands::Result &result, + InputContext *ic) const; + void ParseCandidates(const mozc::commands::Candidates &candidates, + InputContext *ic) const; + void ParsePreedit(const mozc::commands::Preedit &preedit, uint32 position, + InputContext *ic) const; + + MozcEngine *engine_; + bool use_annotation_; + + DISALLOW_COPY_AND_ASSIGN(MozcResponseParser); +}; + +} // namespace fcitx + +#endif // MOZC_UNIX_FCITX_MOZC_RESPONSE_PARSER_H_ diff --git a/src/unix/fcitx5/mozc_state.cc b/src/unix/fcitx5/mozc_state.cc new file mode 100644 index 00000000..3dc3bdd2 --- /dev/null +++ b/src/unix/fcitx5/mozc_state.cc @@ -0,0 +1,441 @@ +// 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. + +#include "unix/fcitx5/mozc_state.h" + +#include <fcitx-utils/i18n.h> +#include <fcitx-utils/log.h> +#include <fcitx-utils/stringutils.h> +#include <fcitx/candidatelist.h> +#include <fcitx/inputpanel.h> +#include <string> + +#include "base/const.h" +#include "base/file_util.h" +#include "base/logging.h" +#include "base/process.h" +#include "base/system_util.h" +#include "base/util.h" +#include "session/ime_switch_util.h" +#include "unix/fcitx5/fcitx_key_event_handler.h" +#include "unix/fcitx5/mozc_connection.h" +#include "unix/fcitx5/mozc_engine.h" +#include "unix/fcitx5/mozc_response_parser.h" +#include "unix/fcitx5/surrounding_text_util.h" + +namespace { + +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. +static_assert(mozc::commands::NUM_OF_COMPOSITIONS == + arraysize(kPropCompositionModes), + "number of modes must match"); + +} // namespace + +namespace fcitx { + +// For unittests. +MozcState::MozcState(InputContext* ic, mozc::client::ClientInterface* client, + MozcEngine* engine) + : ic_(ic), + client_(client), + engine_(engine), + handler_(std::make_unique<KeyEventHandler>()), + parser_(std::make_unique<MozcResponseParser>(engine_)) { + // mozc::Logging::SetVerboseLevel(1); + VLOG(1) << "MozcState created."; + const bool is_vertical = true; + parser_->SetUseAnnotation(is_vertical); + + if (client_->EnsureConnection()) { + UpdatePreeditMethod(); + } +} + +MozcState::~MozcState() { + client_->SyncData(); + VLOG(1) << "MozcState destroyed."; +} + +void MozcState::UpdatePreeditMethod() { + mozc::config::Config config; + if (!client_->GetConfig(&config)) { + LOG(ERROR) << "GetConfig failed"; + return; + } + preedit_method_ = config.has_preedit_method() ? config.preedit_method() + : mozc::config::Config::ROMAN; +} + +bool MozcState::TrySendKeyEvent( + InputContext* ic, KeySym sym, uint32 keycode, KeyStates 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 MozcState::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. + } + + mozc::commands::Context context; + SurroundingTextInfo surrounding_text_info; + if (GetSurroundingText(ic, &surrounding_text_info, + engine_->clipboardAddon())) { + context.set_preceding_text(surrounding_text_info.preceding_text); + context.set_following_text(surrounding_text_info.following_text); + } + + VLOG(1) << "TrySendKeyEvent: " << std::endl << event.DebugString(); + if (!client_->SendKeyWithContext(event, context, out)) { + *out_error = "SendKey failed"; + VLOG(1) << "ERROR"; + return false; + } + VLOG(1) << "OK: " << std::endl << out->DebugString(); + return true; +} + +bool MozcState::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 TrySendRawCommand(command, out, out_error); +} + +bool MozcState::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 TrySendRawCommand(command, out, out_error); +} + +bool MozcState::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 TrySendRawCommand(command, out, out_error); +} + +bool MozcState::TrySendRawCommand(const mozc::commands::SessionCommand& command, + mozc::commands::Output* out, + string* out_error) const { + VLOG(1) << "TrySendRawCommand: " << std::endl << command.DebugString(); + if (!client_->SendCommand(command, out)) { + *out_error = "SendCommand failed"; + VLOG(1) << "ERROR"; + return false; + } + VLOG(1) << "OK: " << std::endl << out->DebugString(); + return true; +} + +// This function is called when users press or release a key. +bool MozcState::ProcessKeyEvent(KeySym sym, uint32 keycode, KeyStates state, + bool layout_is_jp, bool is_key_up) { + auto normalized_key = Key(sym, state).normalize(); + if (displayUsage_) { + if (is_key_up) { + return true; + } + + if (normalized_key.check(Key(FcitxKey_Escape))) { + displayUsage_ = false; + ProcessKeyEvent(FcitxKey_VoidSymbol, 0, KeyState::None, layout_is_jp, + false); + } + return true; + } + + if (normalized_key.check(Key(FcitxKey_H, KeyState::Ctrl_Alt))) { + if (!title_.empty() || !description_.empty()) { + DisplayUsage(); + return true; + } + } + + string error; + mozc::commands::Output raw_response; + if (!TrySendKeyEvent(ic_, 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 MozcState::SelectCandidate(int32 id) { + 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 (!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 MozcState::Reset() { + VLOG(1) << "resetim"; + string error; + mozc::commands::Output raw_response; + if (TrySendCommand(mozc::commands::SessionCommand::REVERT, &raw_response, + &error)) { + parser_->ParseResponse(raw_response, ic_); + } + ClearAll(); // just in case. + DrawAll(); +} + +bool MozcState::Paging(bool prev) { + VLOG(1) << "paging"; + string error; + mozc::commands::SessionCommand::CommandType command = + prev ? mozc::commands::SessionCommand::CONVERT_PREV_PAGE + : mozc::commands::SessionCommand::CONVERT_NEXT_PAGE; + mozc::commands::Output raw_response; + if (TrySendCommand(command, &raw_response, &error)) { + parser_->ParseResponse(raw_response, ic_); + return true; + } + return false; +} + +// This function is called when the ic gets focus. +void MozcState::FocusIn() { + VLOG(1) << "MozcState::FocusIn()"; + + UpdatePreeditMethod(); + DrawAll(); +} + +// This function is called when the ic loses focus. +void MozcState::FocusOut() { + VLOG(1) << "MozcState::FocusOut()"; + string error; + mozc::commands::Output raw_response; + if (TrySendCommand(mozc::commands::SessionCommand::REVERT, &raw_response, + &error)) { + parser_->ParseResponse(raw_response, ic_); + } + ClearAll(); // just in case. + DrawAll(); + // TODO(yusukes): Call client::SyncData() like ibus-mozc. +} + +bool MozcState::ParseResponse(const mozc::commands::Output& raw_response) { + ClearAll(); + const bool consumed = parser_->ParseResponse(raw_response, ic_); + if (!consumed) { + VLOG(1) << "The input was not consumed by Mozc."; + } + OpenUrl(); + DrawAll(); + return consumed; +} + +void MozcState::SetResultString(const string& result_string) { + ic_->commitString(result_string); +} + +void MozcState::SetPreeditInfo(Text preedit_info) { + preedit_ = std::move(preedit_info); +} + +void MozcState::SetAuxString(const string& str) { aux_ = str; } + +void MozcState::SetCompositionMode(mozc::commands::CompositionMode mode) { + composition_mode_ = mode; + DCHECK(composition_mode_ < kNumCompositionModes); + engine_->compositionModeUpdated(ic_); +} + +void MozcState::SendCompositionMode(mozc::commands::CompositionMode mode) { + // Send the SWITCH_INPUT_MODE command. + string error; + mozc::commands::Output raw_response; + if (TrySendCompositionMode(kPropCompositionModes[mode].mode, &raw_response, + &error)) { + auto oldMode = composition_mode_; + parser_->ParseResponse(raw_response, ic_); + if (oldMode != composition_mode_) { + engine_->instance()->showInputMethodInformation(ic_); + } + } +} + +void MozcState::SetUrl(const string& url) { url_ = url; } + +void MozcState::ClearAll() { + SetPreeditInfo(Text()); + SetAuxString(""); + ic_->inputPanel().reset(); + url_.clear(); +} + +void MozcState::DrawAll() { + Text preedit = preedit_; + if (!aux_.empty()) { + string aux; + if (preedit.size()) { + aux += " "; + } + aux += "["; + aux += aux_; + aux += "]"; + preedit.append(aux); + } + ic_->inputPanel().setPreedit(std::move(preedit)); + ic_->inputPanel().setClientPreedit(preedit_); + ic_->updatePreedit(); + ic_->updateUserInterface(UserInterfaceComponent::InputPanel); +} + +void MozcState::OpenUrl() { + if (url_.empty()) { + return; + } + mozc::Process::OpenBrowser(url_); + url_.clear(); +} + +bool MozcState::SendCommand( + const mozc::commands::SessionCommand& session_command, + mozc::commands::Output* new_output) { + string error; + return TrySendRawCommand(session_command, new_output, &error); +} + +void MozcState::SetUsage(const string& title, const string& description) { + title_ = title; + description_ = description; +} + +void MozcState::DisplayUsage() { + displayUsage_ = true; + + ic_->inputPanel().reset(); + auto candidateList = std::make_unique<DisplayOnlyCandidateList>(); + + auto lines = stringutils::split(description_, "\n"); + candidateList->setLayoutHint(CandidateLayoutHint::Vertical); + candidateList->setContent(lines); + ic_->inputPanel().setCandidateList(std::move(candidateList)); + auto str = title_ + " [" + _("Press Escape to go back") + "]"; + ic_->inputPanel().setAuxUp(Text(str)); + ic_->updatePreedit(); + ic_->updateUserInterface(UserInterfaceComponent::InputPanel); +} + +} // namespace fcitx diff --git a/src/unix/fcitx5/mozc_state.h b/src/unix/fcitx5/mozc_state.h new file mode 100644 index 00000000..01eb59a1 --- /dev/null +++ b/src/unix/fcitx5/mozc_state.h @@ -0,0 +1,165 @@ +// 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 <memory> + +#include <fcitx-utils/key.h> +#include <fcitx/inputcontextproperty.h> +#include <fcitx/text.h> + +#include "base/port.h" +#include "base/run_level.h" +#include "client/client_interface.h" +#include "protocol/commands.pb.h" + +namespace fcitx { +const int32 kBadCandidateId = -12345; +class MozcConnectionInterface; +class MozcResponseParser; +class KeyTranslator; +class KeyEventHandler; +class MozcEngine; + +class MozcState : public InputContextProperty { + public: + // This constructor is used by unittests. + MozcState(InputContext* ic, mozc::client::ClientInterface* client, + MozcEngine* engine); + virtual ~MozcState(); + + void UpdatePreeditMethod(); + + bool ProcessKeyEvent(KeySym sym, uint32 keycode, KeyStates state, + bool layout_is_jp, bool is_key_up); + void SelectCandidate(int idx); + void Reset(); + void FocusIn(); + void FocusOut(); + bool Paging(bool prev); + + // Functions called by the MozcResponseParser class to update UI. + + // Displays a 'result' (aka 'commit string') on FCITX UI. + void SetResultString(const 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(Text 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 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 string& GetIconFile(const string key); + + mozc::commands::CompositionMode GetCompositionMode() { + return composition_mode_; + } + + mozc::client::ClientInterface* GetClient() { return client_.get(); } + + bool SendCommand(const mozc::commands::SessionCommand& session_command, + mozc::commands::Output* new_output); + + void SetUsage(const string& title, const string& description); + + void DrawAll(); + + private: + void DisplayUsage(); + // 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. + bool TrySendKeyEvent(InputContext* ic, KeySym sym, uint32 keycode, + KeyStates 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. + bool TrySendClick(int32 unique_id, mozc::commands::Output* out, + string* out_error) const; + + // Sends composition mode to the server. + bool TrySendCompositionMode(mozc::commands::CompositionMode mode, + mozc::commands::Output* out, + string* out_error) const; + + // Sends a command to the server. + bool TrySendCommand(mozc::commands::SessionCommand::CommandType type, + mozc::commands::Output* out, string* out_error) const; + + bool TrySendRawCommand(const mozc::commands::SessionCommand& command, + mozc::commands::Output* out, string* out_error) const; + + // 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 DrawPreeditInfo(); + void DrawAux(); + + // Open url_ with a default browser. + void OpenUrl(); + + InputContext* ic_; + std::unique_ptr<mozc::client::ClientInterface> client_; + MozcEngine* engine_; + + mozc::commands::CompositionMode composition_mode_ = mozc::commands::HIRAGANA; + mozc::config::Config::PreeditMethod preedit_method_ = + mozc::config::Config::ROMAN; + const std::unique_ptr<KeyEventHandler> handler_; + const std::unique_ptr<MozcResponseParser> parser_; + + bool displayUsage_ = false; + Text preedit_; + string aux_; // error tooltip, or candidate window title. + string url_; // URL to be opened by a browser. + string description_; + string title_; + + DISALLOW_COPY_AND_ASSIGN(MozcState); +}; + +} // namespace fcitx + +#endif // MOZC_UNIX_FCITX_FCITX_MOZC_H_ diff --git a/src/unix/fcitx5/po/LINGUAS b/src/unix/fcitx5/po/LINGUAS new file mode 100644 index 00000000..68faae50 --- /dev/null +++ b/src/unix/fcitx5/po/LINGUAS @@ -0,0 +1,9 @@ + +ca +da +de +ja +ko +ru +zh_CN +zh_TW diff --git a/src/unix/fcitx5/po/ca.po b/src/unix/fcitx5/po/ca.po new file mode 100644 index 00000000..29dc3af8 --- /dev/null +++ b/src/unix/fcitx5/po/ca.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 fcitx5-mozc package. +# +# Translators: +# Robert Antoni Buj Gelonch <rbuj@fedoraproject.org>, 2017 +# csslayer <wengxt@gmail.com>, 2017 +msgid "" +msgstr "" +"Project-Id-Version: fcitx5-mozc\n" +"Report-Msgid-Bugs-To: fcitx-dev@googlegroups.com\n" +"POT-Creation-Date: 2017-12-20 00:01-0800\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: csslayer <wengxt@gmail.com>, 2017\n" +"Language-Team: Catalan (https://www.transifex.com/fcitx/teams/12005/ca/)\n" +"Language: ca\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: mozc_engine.cc:174 +msgid "Character Palette" +msgstr "Paleta de caràcters" + +#: mozc_engine.cc:78 +msgid "Composition Mode" +msgstr "Mode de composició" + +#: mozc_engine.cc:153 +msgid "Configuration Tool" +msgstr "Eina de configuració" + +#: mozc_engine.cc:160 +msgid "Dictionary Tool" +msgstr "Eina de diccionari" + +#: mozc_state.cc:61 mozc_engine.cc:45 +msgid "Direct" +msgstr "Directe" + +#. Full width ASCII letter A. +#: mozc_state.cc:80 mozc_engine.cc:67 +msgid "Full ASCII" +msgstr "ASCII complet" + +#. Katakana letter A. +#: mozc_state.cc:71 mozc_engine.cc:56 +msgid "Full Katakana" +msgstr "Katakana complet" + +#: mozc_state.cc:74 mozc_engine.cc:60 +msgid "Half ASCII" +msgstr "ASCII mig" + +#. Half width Katakana letter A. +#: mozc_state.cc:85 mozc_engine.cc:72 +msgid "Half Katakana" +msgstr "Mig katakana" + +#: mozc_engine.cc:167 +msgid "Hand Writing" +msgstr "Escriptura a mà" + +#. Hiragana letter A in UTF-8. +#: mozc_state.cc:66 mozc_engine.cc:51 +msgid "Hiragana" +msgstr "Hiragana" + +#: mozc_response_parser.cc:144 +msgid "Press Ctrl+Alt+H to show usages." +msgstr "Premeu Ctrl+Alt+H per mostrar els usos." + +#: mozc_state.cc:426 +msgid "Press Escape to go back" +msgstr "Premeu Esc per tornar" + +#: mozc_engine.cc:137 mozc_engine.cc:138 +msgid "Tool" +msgstr "Eina" diff --git a/src/unix/fcitx5/po/da.po b/src/unix/fcitx5/po/da.po new file mode 100644 index 00000000..679bce1f --- /dev/null +++ b/src/unix/fcitx5/po/da.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 fcitx5-mozc package. +# +# Translators: +# scootergrisen <scootergrisen@gmail.com>, 2017 +msgid "" +msgstr "" +"Project-Id-Version: fcitx5-mozc\n" +"Report-Msgid-Bugs-To: fcitx-dev@googlegroups.com\n" +"POT-Creation-Date: 2017-12-12 09:52-0800\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: scootergrisen <scootergrisen@gmail.com>, 2017\n" +"Language-Team: Danish (https://www.transifex.com/fcitx/teams/12005/da/)\n" +"Language: da\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: mozc_engine.cc:174 +msgid "Character Palette" +msgstr "Tegnpalet" + +#: mozc_engine.cc:78 +msgid "Composition Mode" +msgstr "Kompositionstilstand" + +#: mozc_engine.cc:153 +msgid "Configuration Tool" +msgstr "Konfigurationsværktøj" + +#: mozc_engine.cc:160 +msgid "Dictionary Tool" +msgstr "Ordbogsværktøj" + +#: mozc_state.cc:61 mozc_engine.cc:45 +msgid "Direct" +msgstr "Direkte" + +#. Full width ASCII letter A. +#: mozc_state.cc:80 mozc_engine.cc:67 +msgid "Full ASCII" +msgstr "Fuld ASCII" + +#. Katakana letter A. +#: mozc_state.cc:71 mozc_engine.cc:56 +msgid "Full Katakana" +msgstr "Fuld Katakana" + +#: mozc_state.cc:74 mozc_engine.cc:60 +msgid "Half ASCII" +msgstr "Halv ASCII" + +#. Half width Katakana letter A. +#: mozc_state.cc:85 mozc_engine.cc:72 +msgid "Half Katakana" +msgstr "Halv Katakana" + +#: mozc_engine.cc:167 +msgid "Hand Writing" +msgstr "Håndskrift" + +#. Hiragana letter A in UTF-8. +#: mozc_state.cc:66 mozc_engine.cc:51 +msgid "Hiragana" +msgstr "Hiragana" + +#: mozc_response_parser.cc:144 +msgid "Press Ctrl+Alt+H to show usages." +msgstr "Tryk på Ctrl+Alt+H for at vise anvendelser." + +#: mozc_state.cc:426 +msgid "Press Escape to go back" +msgstr "Tryk på Escape for at gå tilbage" + +#: mozc_engine.cc:137 mozc_engine.cc:138 +msgid "Tool" +msgstr "Værktøj" diff --git a/src/unix/fcitx5/po/de.po b/src/unix/fcitx5/po/de.po new file mode 100644 index 00000000..e13a6852 --- /dev/null +++ b/src/unix/fcitx5/po/de.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 fcitx5-mozc package. +# +# Translators: +# csslayer <wengxt@gmail.com>, 2017 +msgid "" +msgstr "" +"Project-Id-Version: fcitx5-mozc\n" +"Report-Msgid-Bugs-To: fcitx-dev@googlegroups.com\n" +"POT-Creation-Date: 2017-12-12 09:52-0800\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: csslayer <wengxt@gmail.com>, 2017\n" +"Language-Team: German (https://www.transifex.com/fcitx/teams/12005/de/)\n" +"Language: de\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: mozc_engine.cc:174 +msgid "Character Palette" +msgstr "Palette Symbole" + +#: mozc_engine.cc:78 +msgid "Composition Mode" +msgstr "Kompositionsmodus" + +#: mozc_engine.cc:153 +msgid "Configuration Tool" +msgstr "Konfigurationswerkzeug" + +#: mozc_engine.cc:160 +msgid "Dictionary Tool" +msgstr "Wörterbuchwerkzeug" + +#: mozc_state.cc:61 mozc_engine.cc:45 +msgid "Direct" +msgstr "Direkt" + +#. Full width ASCII letter A. +#: mozc_state.cc:80 mozc_engine.cc:67 +msgid "Full ASCII" +msgstr "7-Bit ASCII (Full ASCII)" + +#. Katakana letter A. +#: mozc_state.cc:71 mozc_engine.cc:56 +msgid "Full Katakana" +msgstr "Normalbreite Katakana" + +#: mozc_state.cc:74 mozc_engine.cc:60 +msgid "Half ASCII" +msgstr "6-Bit ASCII (Half ASCII)" + +#. Half width Katakana letter A. +#: mozc_state.cc:85 mozc_engine.cc:72 +msgid "Half Katakana" +msgstr "Halbbreite Katakana" + +#: mozc_engine.cc:167 +msgid "Hand Writing" +msgstr "Eingabe Handschrift" + +#. Hiragana letter A in UTF-8. +#: mozc_state.cc:66 mozc_engine.cc:51 +msgid "Hiragana" +msgstr "Hiragana" + +#: mozc_response_parser.cc:144 +msgid "Press Ctrl+Alt+H to show usages." +msgstr "Ctrl+Alt+H um die Hilfe anzuzeigen" + +#: mozc_state.cc:426 +msgid "Press Escape to go back" +msgstr "ESC drücken um zurück zu kehren" + +#: mozc_engine.cc:137 mozc_engine.cc:138 +msgid "Tool" +msgstr "Werkzeug" diff --git a/src/unix/fcitx5/po/fcitx5-mozc.pot b/src/unix/fcitx5/po/fcitx5-mozc.pot new file mode 100644 index 00000000..c18c29ac --- /dev/null +++ b/src/unix/fcitx5/po/fcitx5-mozc.pot @@ -0,0 +1,76 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the fcitx5-mozc package. +# +msgid "" +msgstr "" +"Project-Id-Version: fcitx5-mozc\n" +"Report-Msgid-Bugs-To: fcitx-dev@googlegroups.com\n" +"POT-Creation-Date: 2017-12-12 09:52-0800\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: LANG\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: mozc_engine.cc:174 +msgid "Character Palette" +msgstr "" + +#: mozc_engine.cc:78 +msgid "Composition Mode" +msgstr "" + +#: mozc_engine.cc:153 +msgid "Configuration Tool" +msgstr "" + +#: mozc_engine.cc:160 +msgid "Dictionary Tool" +msgstr "" + +#: mozc_state.cc:61 mozc_engine.cc:45 +msgid "Direct" +msgstr "" + +#. Full width ASCII letter A. +#: mozc_state.cc:80 mozc_engine.cc:67 +msgid "Full ASCII" +msgstr "" + +#. Katakana letter A. +#: mozc_state.cc:71 mozc_engine.cc:56 +msgid "Full Katakana" +msgstr "" + +#: mozc_state.cc:74 mozc_engine.cc:60 +msgid "Half ASCII" +msgstr "" + +#. Half width Katakana letter A. +#: mozc_state.cc:85 mozc_engine.cc:72 +msgid "Half Katakana" +msgstr "" + +#: mozc_engine.cc:167 +msgid "Hand Writing" +msgstr "" + +#. Hiragana letter A in UTF-8. +#: mozc_state.cc:66 mozc_engine.cc:51 +msgid "Hiragana" +msgstr "" + +#: mozc_response_parser.cc:144 +msgid "Press Ctrl+Alt+H to show usages." +msgstr "" + +#: mozc_state.cc:426 +msgid "Press Escape to go back" +msgstr "" + +#: mozc_engine.cc:137 mozc_engine.cc:138 +msgid "Tool" +msgstr "" diff --git a/src/unix/fcitx5/po/ja.po b/src/unix/fcitx5/po/ja.po new file mode 100644 index 00000000..03dcef0b --- /dev/null +++ b/src/unix/fcitx5/po/ja.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 fcitx5-mozc package. +# +# Translators: +# csslayer <wengxt@gmail.com>, 2017 +msgid "" +msgstr "" +"Project-Id-Version: fcitx5-mozc\n" +"Report-Msgid-Bugs-To: fcitx-dev@googlegroups.com\n" +"POT-Creation-Date: 2017-12-12 09:52-0800\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: csslayer <wengxt@gmail.com>, 2017\n" +"Language-Team: Japanese (https://www.transifex.com/fcitx/teams/12005/ja/)\n" +"Language: ja\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\n" + +#: mozc_engine.cc:174 +msgid "Character Palette" +msgstr "文字パレット" + +#: mozc_engine.cc:78 +msgid "Composition Mode" +msgstr "変換モード" + +#: mozc_engine.cc:153 +msgid "Configuration Tool" +msgstr "設定ツール" + +#: mozc_engine.cc:160 +msgid "Dictionary Tool" +msgstr "辞書ツール" + +#: mozc_state.cc:61 mozc_engine.cc:45 +msgid "Direct" +msgstr "直接入力" + +#. Full width ASCII letter A. +#: mozc_state.cc:80 mozc_engine.cc:67 +msgid "Full ASCII" +msgstr "全角英数" + +#. Katakana letter A. +#: mozc_state.cc:71 mozc_engine.cc:56 +msgid "Full Katakana" +msgstr "全角カタカナ" + +#: mozc_state.cc:74 mozc_engine.cc:60 +msgid "Half ASCII" +msgstr "半角英数" + +#. Half width Katakana letter A. +#: mozc_state.cc:85 mozc_engine.cc:72 +msgid "Half Katakana" +msgstr "半角カタカナ" + +#: mozc_engine.cc:167 +msgid "Hand Writing" +msgstr "手書き文字認識" + +#. Hiragana letter A in UTF-8. +#: mozc_state.cc:66 mozc_engine.cc:51 +msgid "Hiragana" +msgstr "ひらがな" + +#: mozc_response_parser.cc:144 +msgid "Press Ctrl+Alt+H to show usages." +msgstr "Ctrl+Alt+H キーを押して用例を表示" + +#: mozc_state.cc:426 +msgid "Press Escape to go back" +msgstr "Escキーを押して戻る" + +#: mozc_engine.cc:137 mozc_engine.cc:138 +msgid "Tool" +msgstr "ツール" diff --git a/src/unix/fcitx5/po/ko.po b/src/unix/fcitx5/po/ko.po new file mode 100644 index 00000000..f6c6c78c --- /dev/null +++ b/src/unix/fcitx5/po/ko.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 fcitx5-mozc package. +# +# Translators: +# csslayer <wengxt@gmail.com>, 2017 +msgid "" +msgstr "" +"Project-Id-Version: fcitx5-mozc\n" +"Report-Msgid-Bugs-To: fcitx-dev@googlegroups.com\n" +"POT-Creation-Date: 2017-12-12 09:52-0800\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: csslayer <wengxt@gmail.com>, 2017\n" +"Language-Team: Korean (https://www.transifex.com/fcitx/teams/12005/ko/)\n" +"Language: ko\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\n" + +#: mozc_engine.cc:174 +msgid "Character Palette" +msgstr "글자 팔레트" + +#: mozc_engine.cc:78 +msgid "Composition Mode" +msgstr "조합 모드" + +#: mozc_engine.cc:153 +msgid "Configuration Tool" +msgstr "설정 도구" + +#: mozc_engine.cc:160 +msgid "Dictionary Tool" +msgstr "사전 도구" + +#: mozc_state.cc:61 mozc_engine.cc:45 +msgid "Direct" +msgstr "직접입력" + +#. Full width ASCII letter A. +#: mozc_state.cc:80 mozc_engine.cc:67 +msgid "Full ASCII" +msgstr "전각 아스키" + +#. Katakana letter A. +#: mozc_state.cc:71 mozc_engine.cc:56 +msgid "Full Katakana" +msgstr "전각 가타가나" + +#: mozc_state.cc:74 mozc_engine.cc:60 +msgid "Half ASCII" +msgstr "반각 아스키" + +#. Half width Katakana letter A. +#: mozc_state.cc:85 mozc_engine.cc:72 +msgid "Half Katakana" +msgstr "반각 가타가나" + +#: mozc_engine.cc:167 +msgid "Hand Writing" +msgstr "손 글씨" + +#. Hiragana letter A in UTF-8. +#: mozc_state.cc:66 mozc_engine.cc:51 +msgid "Hiragana" +msgstr "히라가나" + +#: mozc_response_parser.cc:144 +msgid "Press Ctrl+Alt+H to show usages." +msgstr "사용법을 보려면 Ctrl+Alt+H를 누르세요." + +#: mozc_state.cc:426 +msgid "Press Escape to go back" +msgstr "돌아가려면 ESC를 누르세요" + +#: mozc_engine.cc:137 mozc_engine.cc:138 +msgid "Tool" +msgstr "도구" diff --git a/src/unix/fcitx5/po/ru.po b/src/unix/fcitx5/po/ru.po new file mode 100644 index 00000000..22c312dd --- /dev/null +++ b/src/unix/fcitx5/po/ru.po @@ -0,0 +1,81 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the fcitx5-mozc package. +# +# Translators: +# csslayer <wengxt@gmail.com>, 2017 +msgid "" +msgstr "" +"Project-Id-Version: fcitx5-mozc\n" +"Report-Msgid-Bugs-To: fcitx-dev@googlegroups.com\n" +"POT-Creation-Date: 2017-12-12 09:52-0800\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: csslayer <wengxt@gmail.com>, 2017\n" +"Language-Team: Russian (https://www.transifex.com/fcitx/teams/12005/ru/)\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" +"%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n" +"%100>=11 && n%100<=14)? 2 : 3);\n" + +#: mozc_engine.cc:174 +msgid "Character Palette" +msgstr "Палитра символов" + +#: mozc_engine.cc:78 +msgid "Composition Mode" +msgstr "Режим композиции" + +#: mozc_engine.cc:153 +msgid "Configuration Tool" +msgstr "Инструмент настройки" + +#: mozc_engine.cc:160 +msgid "Dictionary Tool" +msgstr "Инструмент словаря" + +#: mozc_state.cc:61 mozc_engine.cc:45 +msgid "Direct" +msgstr "Прямой" + +#. Full width ASCII letter A. +#: mozc_state.cc:80 mozc_engine.cc:67 +msgid "Full ASCII" +msgstr "Полный " + +#. Katakana letter A. +#: mozc_state.cc:71 mozc_engine.cc:56 +msgid "Full Katakana" +msgstr "Полная катакана" + +#: mozc_state.cc:74 mozc_engine.cc:60 +msgid "Half ASCII" +msgstr "Половинный " + +#. Half width Katakana letter A. +#: mozc_state.cc:85 mozc_engine.cc:72 +msgid "Half Katakana" +msgstr "Половинная катакана" + +#: mozc_engine.cc:167 +msgid "Hand Writing" +msgstr "Ручное письмо" + +#. Hiragana letter A in UTF-8. +#: mozc_state.cc:66 mozc_engine.cc:51 +msgid "Hiragana" +msgstr "Хирагана" + +#: mozc_response_parser.cc:144 +msgid "Press Ctrl+Alt+H to show usages." +msgstr "Нажмите Ctrl+Alt+H, чтобы показать использование." + +#: mozc_state.cc:426 +msgid "Press Escape to go back" +msgstr "Нажмите для перехода назад" + +#: mozc_engine.cc:137 mozc_engine.cc:138 +msgid "Tool" +msgstr "Инструмент" diff --git a/src/unix/fcitx5/po/zh_CN.po b/src/unix/fcitx5/po/zh_CN.po new file mode 100644 index 00000000..90d282cf --- /dev/null +++ b/src/unix/fcitx5/po/zh_CN.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 fcitx5-mozc package. +# +# Translators: +# csslayer <wengxt@gmail.com>, 2017 +msgid "" +msgstr "" +"Project-Id-Version: fcitx5-mozc\n" +"Report-Msgid-Bugs-To: fcitx-dev@googlegroups.com\n" +"POT-Creation-Date: 2017-12-12 09:52-0800\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: csslayer <wengxt@gmail.com>, 2017\n" +"Language-Team: Chinese (China) (https://www.transifex.com/fcitx/teams/12005/" +"zh_CN/)\n" +"Language: zh_CN\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\n" + +#: mozc_engine.cc:174 +msgid "Character Palette" +msgstr "字符映射表" + +#: mozc_engine.cc:78 +msgid "Composition Mode" +msgstr "编辑模式" + +#: mozc_engine.cc:153 +msgid "Configuration Tool" +msgstr "配置工具" + +#: mozc_engine.cc:160 +msgid "Dictionary Tool" +msgstr "词典工具" + +#: mozc_state.cc:61 mozc_engine.cc:45 +msgid "Direct" +msgstr "直接键盘输入" + +#. Full width ASCII letter A. +#: mozc_state.cc:80 mozc_engine.cc:67 +msgid "Full ASCII" +msgstr "全角 ASCII" + +#. Katakana letter A. +#: mozc_state.cc:71 mozc_engine.cc:56 +msgid "Full Katakana" +msgstr "全角片假名" + +#: mozc_state.cc:74 mozc_engine.cc:60 +msgid "Half ASCII" +msgstr "半角 ASCII" + +#. Half width Katakana letter A. +#: mozc_state.cc:85 mozc_engine.cc:72 +msgid "Half Katakana" +msgstr "半角片假名" + +#: mozc_engine.cc:167 +msgid "Hand Writing" +msgstr "手写输入" + +#. Hiragana letter A in UTF-8. +#: mozc_state.cc:66 mozc_engine.cc:51 +msgid "Hiragana" +msgstr "平假名" + +#: mozc_response_parser.cc:144 +msgid "Press Ctrl+Alt+H to show usages." +msgstr "按下 Ctrl+Alt+H 显示用法。" + +#: mozc_state.cc:426 +msgid "Press Escape to go back" +msgstr "按下 Escape 返回" + +#: mozc_engine.cc:137 mozc_engine.cc:138 +msgid "Tool" +msgstr "工具" diff --git a/src/unix/fcitx5/po/zh_TW.po b/src/unix/fcitx5/po/zh_TW.po new file mode 100644 index 00000000..5b58a31e --- /dev/null +++ b/src/unix/fcitx5/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 fcitx5-mozc package. +# +# Translators: +# csslayer <wengxt@gmail.com>, 2017 +msgid "" +msgstr "" +"Project-Id-Version: fcitx5-mozc\n" +"Report-Msgid-Bugs-To: fcitx-dev@googlegroups.com\n" +"POT-Creation-Date: 2017-12-12 09:52-0800\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: csslayer <wengxt@gmail.com>, 2017\n" +"Language-Team: Chinese (Taiwan) (https://www.transifex.com/fcitx/teams/12005/" +"zh_TW/)\n" +"Language: zh_TW\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\n" + +#: mozc_engine.cc:174 +msgid "Character Palette" +msgstr "字符映射表" + +#: mozc_engine.cc:78 +msgid "Composition Mode" +msgstr "編輯模式" + +#: mozc_engine.cc:153 +msgid "Configuration Tool" +msgstr "設定工具" + +#: mozc_engine.cc:160 +msgid "Dictionary Tool" +msgstr "字典工具" + +#: mozc_state.cc:61 mozc_engine.cc:45 +msgid "Direct" +msgstr "直接鍵盤輸入" + +#. Full width ASCII letter A. +#: mozc_state.cc:80 mozc_engine.cc:67 +msgid "Full ASCII" +msgstr "全形 ASCII" + +#. Katakana letter A. +#: mozc_state.cc:71 mozc_engine.cc:56 +msgid "Full Katakana" +msgstr "全形片假名" + +#: mozc_state.cc:74 mozc_engine.cc:60 +msgid "Half ASCII" +msgstr "半形 ASCII" + +#. Half width Katakana letter A. +#: mozc_state.cc:85 mozc_engine.cc:72 +msgid "Half Katakana" +msgstr "半形片假名" + +#: mozc_engine.cc:167 +msgid "Hand Writing" +msgstr "手寫輸入" + +#. Hiragana letter A in UTF-8. +#: mozc_state.cc:66 mozc_engine.cc:51 +msgid "Hiragana" +msgstr "平假名" + +#: mozc_response_parser.cc:144 +msgid "Press Ctrl+Alt+H to show usages." +msgstr "按下 Ctrl+Alt+H 顯示用法。" + +#: mozc_state.cc:426 +msgid "Press Escape to go back" +msgstr "按下 Escape 返回" + +#: mozc_engine.cc:137 mozc_engine.cc:138 +msgid "Tool" +msgstr "工具" diff --git a/src/unix/fcitx5/surrounding_text_util.cc b/src/unix/fcitx5/surrounding_text_util.cc new file mode 100644 index 00000000..b4847fc2 --- /dev/null +++ b/src/unix/fcitx5/surrounding_text_util.cc @@ -0,0 +1,220 @@ +// Copyright 2010-2013, Google Inc. +// 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. + +#include "unix/fcitx5/surrounding_text_util.h" + +#include <fcitx-module/clipboard/clipboard_public.h> +#include <fcitx/inputcontext.h> +#include <limits> +#include <string> + +#include "base/logging.h" +#include "base/port.h" +#include "base/util.h" + +namespace fcitx { + +using namespace mozc; + +bool SurroundingTextUtil::GetSafeDelta(uint from, uint to, int32 *delta) { + DCHECK(delta); + + static_assert(sizeof(int64) >= sizeof(uint), + "int64 must be sufficient to store a guint value."); + static_assert(sizeof(int64) == sizeof(llabs(0)), + "|llabs(0)| must returns a 64-bit integer."); + const int64 kInt32AbsMax = + llabs(static_cast<int64>(std::numeric_limits<int32>::max())); + const int64 kInt32AbsMin = + llabs(static_cast<int64>(std::numeric_limits<int32>::min())); + const int64 kInt32SafeAbsMax = std::min(kInt32AbsMax, kInt32AbsMin); + + const int64 diff = static_cast<int64>(from) - static_cast<int64>(to); + if (llabs(diff) > kInt32SafeAbsMax) { + return false; + } + + *delta = static_cast<int32>(diff); + return true; +} + +namespace { + +// Moves |iter| with |skip_count| characters. +// Returns false if |iter| reaches to the end before skipping +// |skip_count| characters. +bool Skip(ConstChar32Iterator *iter, size_t skip_count) { + for (size_t i = 0; i < skip_count; ++i) { + if (iter->Done()) { + return false; + } + iter->Next(); + } + return true; +} + +// Returns true if |prefix_iter| is the prefix of |iter|. +// Returns false if |prefix_iter| is an empty sequence. +// Otherwise returns false. +// This function receives ConstChar32Iterator as pointer because +// ConstChar32Iterator is defined as non-copyable. +bool StartsWith(ConstChar32Iterator *iter, ConstChar32Iterator *prefix_iter) { + if (iter->Done() || prefix_iter->Done()) { + return false; + } + + while (true) { + if (iter->Get() != prefix_iter->Get()) { + return false; + } + prefix_iter->Next(); + if (prefix_iter->Done()) { + return true; + } + iter->Next(); + if (iter->Done()) { + return false; + } + } +} + +// Returns true if |surrounding_text| contains |selected_text| +// from |cursor_pos| to |*anchor_pos|. +// Otherwise returns false. +bool SearchAnchorPosForward(const string &surrounding_text, + const string &selected_text, + size_t selected_chars_len, uint cursor_pos, + uint *anchor_pos) { + ConstChar32Iterator iter(surrounding_text); + // Move |iter| to cursor pos. + if (!Skip(&iter, cursor_pos)) { + return false; + } + + ConstChar32Iterator sel_iter(selected_text); + if (!StartsWith(&iter, &sel_iter)) { + return false; + } + *anchor_pos = cursor_pos + selected_chars_len; + return true; +} + +// Returns true if |surrounding_text| contains |selected_text| +// from |*anchor_pos| to |cursor_pos|. +// Otherwise returns false. +bool SearchAnchorPosBackward(const string &surrounding_text, + const string &selected_text, + size_t selected_chars_len, uint cursor_pos, + uint *anchor_pos) { + if (cursor_pos < selected_chars_len) { + return false; + } + + ConstChar32Iterator iter(surrounding_text); + // Skip |iter| to (potential) anchor pos. + const uint skip_count = cursor_pos - selected_chars_len; + DCHECK_LE(skip_count, cursor_pos); + if (!Skip(&iter, skip_count)) { + return false; + } + + ConstChar32Iterator sel_iter(selected_text); + if (!StartsWith(&iter, &sel_iter)) { + return false; + } + *anchor_pos = cursor_pos - selected_chars_len; + return true; +} + +} // namespace + +bool SurroundingTextUtil::GetAnchorPosFromSelection( + const string &surrounding_text, const string &selected_text, + uint cursor_pos, uint *anchor_pos) { + DCHECK(anchor_pos); + + if (surrounding_text.empty()) { + return false; + } + + if (selected_text.empty()) { + return false; + } + + const size_t selected_chars_len = Util::CharsLen(selected_text); + + if (SearchAnchorPosForward(surrounding_text, selected_text, + selected_chars_len, cursor_pos, anchor_pos)) { + return true; + } + + return SearchAnchorPosBackward(surrounding_text, selected_text, + selected_chars_len, cursor_pos, anchor_pos); +} + +bool GetSurroundingText(InputContext *ic, SurroundingTextInfo *info, + AddonInstance *clipboard) { + if (!ic->capabilityFlags().test(CapabilityFlag::SurroundingText) || + !ic->surroundingText().isValid()) { + return false; + } + + const auto surrounding_text = ic->surroundingText().text(); + uint cursor_pos = ic->surroundingText().cursor(); + uint anchor_pos = ic->surroundingText().anchor(); + + if (cursor_pos == anchor_pos && clipboard) { + string primary = clipboard->call<IClipboard::primary>(ic); + if (!primary.empty()) { + uint new_anchor_pos = 0; + if (SurroundingTextUtil::GetAnchorPosFromSelection( + surrounding_text, primary, cursor_pos, &new_anchor_pos)) { + anchor_pos = new_anchor_pos; + } + } + } + + if (!SurroundingTextUtil::GetSafeDelta(cursor_pos, anchor_pos, + &info->relative_selected_length)) { + LOG(ERROR) << "Too long text selection."; + return false; + } + + const size_t selection_start = std::min(cursor_pos, anchor_pos); + const size_t selection_length = std::abs(info->relative_selected_length); + Util::SubStringPiece(surrounding_text, 0, selection_start) + .CopyToString(&info->preceding_text); + Util::SubStringPiece(surrounding_text, selection_start, selection_length) + .CopyToString(&info->selection_text); + Util::SubStringPiece(surrounding_text, selection_start + selection_length) + .CopyToString(&info->following_text); + return true; +} + +} // namespace fcitx diff --git a/src/unix/fcitx5/surrounding_text_util.h b/src/unix/fcitx5/surrounding_text_util.h new file mode 100644 index 00000000..68a23b46 --- /dev/null +++ b/src/unix/fcitx5/surrounding_text_util.h @@ -0,0 +1,84 @@ +// Copyright 2010-2013, Google Inc. +// 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_SURROUNDING_TEXT_URIL_H_ +#define MOZC_UNIX_FCITX_SURROUNDING_TEXT_URIL_H_ + +#include <fcitx/inputcontext.h> +#include <string> + +#include "base/port.h" + +namespace fcitx { + +class AddonInstance; + +struct SurroundingTextInfo { + SurroundingTextInfo() : relative_selected_length(0) {} + + int32 relative_selected_length; + string preceding_text; + string selection_text; + string following_text; +}; + +class SurroundingTextUtil { + public: + // Calculates |from| - |to| and stores the result into |delta| with + // checking integer overflow. + // Returns true when neither |abs(delta)| nor |-delta| does not cause + // integer overflow, that is, |delta| is in a safe range. + // Returns false otherwise. + static bool GetSafeDelta(uint from, uint to, int32 *delta); + + // Returns true if + // 1. |surrounding_text| contains |selected_text| + // from |cursor_pos| to |*anchor_pos|. + // or, + // 2. |surrounding_text| contains |selected_text| + // from |*anchor_pos| to |cursor_pos|. + // with calculating |*anchor_pos|, + // where |cursor_pos| and |*anchor_pos| are counts of Unicode characters. + // When both 1) and 2) are satisfied, this function calculates + // |*anchor_pos| for case 1). + // Otherwise returns false. + static bool GetAnchorPosFromSelection(const string &surrounding_text, + const string &selected_text, + uint cursor_pos, uint *anchor_pos); + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(SurroundingTextUtil); +}; + +bool GetSurroundingText(InputContext *ic, SurroundingTextInfo *info, + AddonInstance *clipboard); + +} // namespace fcitx + +#endif // MOZC_UNIX_FCITX_SURROUNDING_TEXT_URIL_H_
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor