diff options
Diffstat (limited to 'engine')
-rw-r--r-- | engine/Makefile.am | 130 | ||||
-rw-r--r-- | engine/python2/Makefile.am | 144 | ||||
-rw-r--r-- | engine/python2/_config.py.in (renamed from engine/_config.py.in) | 0 | ||||
-rw-r--r-- | engine/python2/anthy.i (renamed from engine/anthy.i) | 0 | ||||
-rw-r--r-- | engine/python2/anthy.xml.in.in (renamed from engine/anthy.xml.in.in) | 0 | ||||
-rw-r--r-- | engine/python2/default.xml.in.in (renamed from engine/default.xml.in.in) | 0 | ||||
-rw-r--r-- | engine/python2/engine.py (renamed from engine/engine.py) | 4 | ||||
-rw-r--r-- | engine/python2/factory.py (renamed from engine/factory.py) | 0 | ||||
-rw-r--r-- | engine/python2/ibus-engine-anthy.in (renamed from engine/ibus-engine-anthy.in) | 0 | ||||
-rw-r--r-- | engine/python2/jastring.py (renamed from engine/jastring.py) | 0 | ||||
-rw-r--r-- | engine/python2/kana.py (renamed from engine/kana.py) | 0 | ||||
-rw-r--r-- | engine/python2/main.py (renamed from engine/main.py) | 0 | ||||
-rw-r--r-- | engine/python2/romaji.py (renamed from engine/romaji.py) | 0 | ||||
-rw-r--r-- | engine/python2/segment.py (renamed from engine/segment.py) | 0 | ||||
-rw-r--r-- | engine/python2/tables.py (renamed from engine/tables.py) | 0 | ||||
-rw-r--r-- | engine/python2/test.py (renamed from engine/test.py) | 0 | ||||
-rw-r--r-- | engine/python2/thumb.py (renamed from engine/thumb.py) | 0 | ||||
-rw-r--r-- | engine/python3/Makefile.am | 144 | ||||
-rw-r--r-- | engine/python3/_config.py.in | 30 | ||||
-rw-r--r-- | engine/python3/anthy.i | 125 | ||||
-rw-r--r-- | engine/python3/anthy.xml.in.in | 19 | ||||
-rw-r--r-- | engine/python3/default.xml.in.in | 19 | ||||
-rw-r--r-- | engine/python3/engine.py | 2780 | ||||
-rw-r--r-- | engine/python3/factory.py | 80 | ||||
-rw-r--r-- | engine/python3/ibus-engine-anthy.in | 33 | ||||
-rw-r--r-- | engine/python3/jastring.py | 304 | ||||
-rw-r--r-- | engine/python3/kana.py | 170 | ||||
-rw-r--r-- | engine/python3/main.py | 188 | ||||
-rw-r--r-- | engine/python3/romaji.py | 263 | ||||
-rw-r--r-- | engine/python3/segment.py | 95 | ||||
-rw-r--r-- | engine/python3/tables.py | 731 | ||||
-rw-r--r-- | engine/python3/test.py | 21 | ||||
-rw-r--r-- | engine/python3/thumb.py | 657 |
33 files changed, 5813 insertions, 124 deletions
diff --git a/engine/Makefile.am b/engine/Makefile.am index a96f831..3c64425 100644 --- a/engine/Makefile.am +++ b/engine/Makefile.am @@ -2,9 +2,8 @@ # # ibus-anthy - The Anthy engine for IBus # -# Copyright (c) 2007-2008 Peng Huang <shawn.p.huang@gmail.com> -# Copyright (c) 2010-2013 Takao Fujiwara <takao.fujiwara1@gmail.com> -# Copyright (c) 2007-2013 Red Hat, Inc. +# Copyright (c) 2014 Takao Fujiwara <takao.fujiwara1@gmail.com> +# Copyright (c) 2014 Red Hat, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -20,125 +19,12 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -BUILT_SOURCES = _config.py +SUBDIRS = -engine_anthy_PYTHON = \ - _config.py \ - engine.py \ - factory.py \ - jastring.py \ - kana.py \ - main.py \ - romaji.py \ - segment.py \ - tables.py \ - thumb.py \ - $(NULL) -engine_anthydir = $(pkgdatadir)/engine -engine_anthy_built_files = $(BUILT_SOURCES) -engine_anthy_built_in_files = $(addsuffix .in, $(engine_anthy_built_files)) - -PYGTK2_ANTHY_RAW = anthy.i -PYGTK2_ANTHY_GEN = anthy.py anthy_wrap.c - -if HAVE_PYGTK2_ANTHY -anthy_DATA = \ - anthy.py \ - $(NULL) -anthy_LTLIBRARIES = _anthy.la -anthydir = @pyexecdir@ - -_anthy_la_SOURCES = \ - $(NULL) - -nodist__anthy_la_SOURCES = \ - anthy_wrap.c \ - $(NULL) - -_anthy_la_CFLAGS = \ - @ANTHY_CFLAGS@ \ - @PYTHON_CFLAGS@ \ - $(NULL) - -_anthy_la_LDFLAGS = \ - @ANTHY_LIBS@ \ - @PYTHON_LIBS@ \ - -avoid-version \ - -module \ - $(NULL) - -anthy.py anthy_wrap.c: anthy.i - $(SWIG) -python $(ANTHY_CFLAGS) -I/usr/include -o anthy_wrap.c $(srcdir)/anthy.i +if ENABLE_PYTHON2 +SUBDIRS += python2 endif -libexec_SCRIPTS = ibus-engine-anthy -component_DATA = anthy.xml -componentdir = $(datadir)/ibus/component -engine_DATA = default.xml -enginedir = $(pkgdatadir)/engine - -anthy.xml: anthy.xml.in - ( \ - libexecdir=${libexecdir}; \ - pkgdatadir=${pkgdatadir}; \ - s=`cat $<`; \ - eval "echo \"$${s}\""; \ - ) > $@ - -default.xml: default.xml.in - ( \ - libexecdir=${libexecdir}; \ - pkgdatadir=${pkgdatadir}; \ - s=`cat $<`; \ - eval "echo \"$${s}\""; \ - ) > $@ - -_config.py: _config.py.in - ( \ - PKGDATADIR=$(pkgdatadir); \ - LIBEXECDIR=$(libexecdir); \ - DATADIR=$(datadir); \ - LAYOUT=$(LAYOUT); \ - SYMBOL_CHAR_INT=$(SYMBOL_CHAR_INT); \ - ICON_PREFERENCE=$(ICON_PREFERENCE); \ - s=`cat $<`; \ - eval "echo \"$${s}\""; \ - ) > $@ - -test: - $(ENV_IBUS_TEST) \ - DBUS_DEBUG=true \ - IBUS_ANTHY_PKGDATADIR=$(abs_top_srcdir) \ - PYTHONPATH=$(builddir)/.libs:@pyexecdir@ \ - $(PYTHON) \ - $(srcdir)/main.py - -EXTRA_DIST = \ - $(engine_anthy_built_in_files) \ - $(PYGTK2_ANTHY_RAW) \ - anthy.xml.in.in \ - default.xml.in.in \ - ibus-engine-anthy.in \ - $(NULL) - -CLEANFILES = \ - $(BUILT_SOURCES) \ - $(PYGTK2_ANTHY_GEN) \ - anthy.xml \ - default.xml \ - *.pyc \ - $(NULL) - -DISTCLEANFILES = \ - $(NULL) - -# Need a time lag between .py and .py.in files to build .py files -# because *_PYTHON valuables are installed in the tarball. -dist-hook: - @sleep 1; \ - for in_file in $(engine_anthy_built_in_files) ; do \ - if [ -f $(distdir)/$(srcdir)/$$in_file ] ; then \ - touch $(distdir)/$(srcdir)/$$in_file; \ - fi; \ - done; - +if ENABLE_PYTHON3 +SUBDIRS += python3 +endif diff --git a/engine/python2/Makefile.am b/engine/python2/Makefile.am new file mode 100644 index 0000000..a96f831 --- /dev/null +++ b/engine/python2/Makefile.am @@ -0,0 +1,144 @@ +# vim:set noet ts=4: +# +# ibus-anthy - The Anthy engine for IBus +# +# Copyright (c) 2007-2008 Peng Huang <shawn.p.huang@gmail.com> +# Copyright (c) 2010-2013 Takao Fujiwara <takao.fujiwara1@gmail.com> +# Copyright (c) 2007-2013 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +BUILT_SOURCES = _config.py + +engine_anthy_PYTHON = \ + _config.py \ + engine.py \ + factory.py \ + jastring.py \ + kana.py \ + main.py \ + romaji.py \ + segment.py \ + tables.py \ + thumb.py \ + $(NULL) +engine_anthydir = $(pkgdatadir)/engine +engine_anthy_built_files = $(BUILT_SOURCES) +engine_anthy_built_in_files = $(addsuffix .in, $(engine_anthy_built_files)) + +PYGTK2_ANTHY_RAW = anthy.i +PYGTK2_ANTHY_GEN = anthy.py anthy_wrap.c + +if HAVE_PYGTK2_ANTHY +anthy_DATA = \ + anthy.py \ + $(NULL) +anthy_LTLIBRARIES = _anthy.la +anthydir = @pyexecdir@ + +_anthy_la_SOURCES = \ + $(NULL) + +nodist__anthy_la_SOURCES = \ + anthy_wrap.c \ + $(NULL) + +_anthy_la_CFLAGS = \ + @ANTHY_CFLAGS@ \ + @PYTHON_CFLAGS@ \ + $(NULL) + +_anthy_la_LDFLAGS = \ + @ANTHY_LIBS@ \ + @PYTHON_LIBS@ \ + -avoid-version \ + -module \ + $(NULL) + +anthy.py anthy_wrap.c: anthy.i + $(SWIG) -python $(ANTHY_CFLAGS) -I/usr/include -o anthy_wrap.c $(srcdir)/anthy.i +endif + +libexec_SCRIPTS = ibus-engine-anthy +component_DATA = anthy.xml +componentdir = $(datadir)/ibus/component +engine_DATA = default.xml +enginedir = $(pkgdatadir)/engine + +anthy.xml: anthy.xml.in + ( \ + libexecdir=${libexecdir}; \ + pkgdatadir=${pkgdatadir}; \ + s=`cat $<`; \ + eval "echo \"$${s}\""; \ + ) > $@ + +default.xml: default.xml.in + ( \ + libexecdir=${libexecdir}; \ + pkgdatadir=${pkgdatadir}; \ + s=`cat $<`; \ + eval "echo \"$${s}\""; \ + ) > $@ + +_config.py: _config.py.in + ( \ + PKGDATADIR=$(pkgdatadir); \ + LIBEXECDIR=$(libexecdir); \ + DATADIR=$(datadir); \ + LAYOUT=$(LAYOUT); \ + SYMBOL_CHAR_INT=$(SYMBOL_CHAR_INT); \ + ICON_PREFERENCE=$(ICON_PREFERENCE); \ + s=`cat $<`; \ + eval "echo \"$${s}\""; \ + ) > $@ + +test: + $(ENV_IBUS_TEST) \ + DBUS_DEBUG=true \ + IBUS_ANTHY_PKGDATADIR=$(abs_top_srcdir) \ + PYTHONPATH=$(builddir)/.libs:@pyexecdir@ \ + $(PYTHON) \ + $(srcdir)/main.py + +EXTRA_DIST = \ + $(engine_anthy_built_in_files) \ + $(PYGTK2_ANTHY_RAW) \ + anthy.xml.in.in \ + default.xml.in.in \ + ibus-engine-anthy.in \ + $(NULL) + +CLEANFILES = \ + $(BUILT_SOURCES) \ + $(PYGTK2_ANTHY_GEN) \ + anthy.xml \ + default.xml \ + *.pyc \ + $(NULL) + +DISTCLEANFILES = \ + $(NULL) + +# Need a time lag between .py and .py.in files to build .py files +# because *_PYTHON valuables are installed in the tarball. +dist-hook: + @sleep 1; \ + for in_file in $(engine_anthy_built_in_files) ; do \ + if [ -f $(distdir)/$(srcdir)/$$in_file ] ; then \ + touch $(distdir)/$(srcdir)/$$in_file; \ + fi; \ + done; + diff --git a/engine/_config.py.in b/engine/python2/_config.py.in index 4d5738b..4d5738b 100644 --- a/engine/_config.py.in +++ b/engine/python2/_config.py.in diff --git a/engine/anthy.i b/engine/python2/anthy.i index 68a3aa9..68a3aa9 100644 --- a/engine/anthy.i +++ b/engine/python2/anthy.i diff --git a/engine/anthy.xml.in.in b/engine/python2/anthy.xml.in.in index 9a93744..9a93744 100644 --- a/engine/anthy.xml.in.in +++ b/engine/python2/anthy.xml.in.in diff --git a/engine/default.xml.in.in b/engine/python2/default.xml.in.in index 27e3c82..27e3c82 100644 --- a/engine/default.xml.in.in +++ b/engine/python2/default.xml.in.in diff --git a/engine/engine.py b/engine/python2/engine.py index f38ebcf..be91802 100644 --- a/engine/engine.py +++ b/engine/python2/engine.py @@ -2350,7 +2350,7 @@ class Engine(IBus.EngineSimple): self.__convert_chars = UN(clipboard_text) for i in xrange(0, len(self.__convert_chars)): keyval = self.__convert_chars[i] - self.__preedit_ja_string.insert(unichr(ord (keyval))) + self.__preedit_ja_string.insert(unichr(ord(keyval))) self.__context.set_string(self.__convert_chars.encode('utf-8')) nr_segments = self.__context.get_nr_segments() @@ -2484,7 +2484,7 @@ class Engine(IBus.EngineSimple): self.__cursor_pos = 0 text, cursor = self.__get_preedit() self.__convert_chars = text - self.__context.set_string(text.encode ('utf-8')) + self.__context.set_string(text.encode('utf-8')) self.__lookup_table.clear() self.__lookup_table.set_cursor_visible(False) diff --git a/engine/factory.py b/engine/python2/factory.py index d4aa075..d4aa075 100644 --- a/engine/factory.py +++ b/engine/python2/factory.py diff --git a/engine/ibus-engine-anthy.in b/engine/python2/ibus-engine-anthy.in index c54b10a..c54b10a 100644 --- a/engine/ibus-engine-anthy.in +++ b/engine/python2/ibus-engine-anthy.in diff --git a/engine/jastring.py b/engine/python2/jastring.py index aeb7e6e..aeb7e6e 100644 --- a/engine/jastring.py +++ b/engine/python2/jastring.py diff --git a/engine/kana.py b/engine/python2/kana.py index c85a199..c85a199 100644 --- a/engine/kana.py +++ b/engine/python2/kana.py diff --git a/engine/main.py b/engine/python2/main.py index b899681..b899681 100644 --- a/engine/main.py +++ b/engine/python2/main.py diff --git a/engine/romaji.py b/engine/python2/romaji.py index 48167c6..48167c6 100644 --- a/engine/romaji.py +++ b/engine/python2/romaji.py diff --git a/engine/segment.py b/engine/python2/segment.py index d423f19..d423f19 100644 --- a/engine/segment.py +++ b/engine/python2/segment.py diff --git a/engine/tables.py b/engine/python2/tables.py index 2e41366..2e41366 100644 --- a/engine/tables.py +++ b/engine/python2/tables.py diff --git a/engine/test.py b/engine/python2/test.py index cd67997..cd67997 100644 --- a/engine/test.py +++ b/engine/python2/test.py diff --git a/engine/thumb.py b/engine/python2/thumb.py index 6a7e3d2..6a7e3d2 100644 --- a/engine/thumb.py +++ b/engine/python2/thumb.py diff --git a/engine/python3/Makefile.am b/engine/python3/Makefile.am new file mode 100644 index 0000000..a96f831 --- /dev/null +++ b/engine/python3/Makefile.am @@ -0,0 +1,144 @@ +# vim:set noet ts=4: +# +# ibus-anthy - The Anthy engine for IBus +# +# Copyright (c) 2007-2008 Peng Huang <shawn.p.huang@gmail.com> +# Copyright (c) 2010-2013 Takao Fujiwara <takao.fujiwara1@gmail.com> +# Copyright (c) 2007-2013 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +BUILT_SOURCES = _config.py + +engine_anthy_PYTHON = \ + _config.py \ + engine.py \ + factory.py \ + jastring.py \ + kana.py \ + main.py \ + romaji.py \ + segment.py \ + tables.py \ + thumb.py \ + $(NULL) +engine_anthydir = $(pkgdatadir)/engine +engine_anthy_built_files = $(BUILT_SOURCES) +engine_anthy_built_in_files = $(addsuffix .in, $(engine_anthy_built_files)) + +PYGTK2_ANTHY_RAW = anthy.i +PYGTK2_ANTHY_GEN = anthy.py anthy_wrap.c + +if HAVE_PYGTK2_ANTHY +anthy_DATA = \ + anthy.py \ + $(NULL) +anthy_LTLIBRARIES = _anthy.la +anthydir = @pyexecdir@ + +_anthy_la_SOURCES = \ + $(NULL) + +nodist__anthy_la_SOURCES = \ + anthy_wrap.c \ + $(NULL) + +_anthy_la_CFLAGS = \ + @ANTHY_CFLAGS@ \ + @PYTHON_CFLAGS@ \ + $(NULL) + +_anthy_la_LDFLAGS = \ + @ANTHY_LIBS@ \ + @PYTHON_LIBS@ \ + -avoid-version \ + -module \ + $(NULL) + +anthy.py anthy_wrap.c: anthy.i + $(SWIG) -python $(ANTHY_CFLAGS) -I/usr/include -o anthy_wrap.c $(srcdir)/anthy.i +endif + +libexec_SCRIPTS = ibus-engine-anthy +component_DATA = anthy.xml +componentdir = $(datadir)/ibus/component +engine_DATA = default.xml +enginedir = $(pkgdatadir)/engine + +anthy.xml: anthy.xml.in + ( \ + libexecdir=${libexecdir}; \ + pkgdatadir=${pkgdatadir}; \ + s=`cat $<`; \ + eval "echo \"$${s}\""; \ + ) > $@ + +default.xml: default.xml.in + ( \ + libexecdir=${libexecdir}; \ + pkgdatadir=${pkgdatadir}; \ + s=`cat $<`; \ + eval "echo \"$${s}\""; \ + ) > $@ + +_config.py: _config.py.in + ( \ + PKGDATADIR=$(pkgdatadir); \ + LIBEXECDIR=$(libexecdir); \ + DATADIR=$(datadir); \ + LAYOUT=$(LAYOUT); \ + SYMBOL_CHAR_INT=$(SYMBOL_CHAR_INT); \ + ICON_PREFERENCE=$(ICON_PREFERENCE); \ + s=`cat $<`; \ + eval "echo \"$${s}\""; \ + ) > $@ + +test: + $(ENV_IBUS_TEST) \ + DBUS_DEBUG=true \ + IBUS_ANTHY_PKGDATADIR=$(abs_top_srcdir) \ + PYTHONPATH=$(builddir)/.libs:@pyexecdir@ \ + $(PYTHON) \ + $(srcdir)/main.py + +EXTRA_DIST = \ + $(engine_anthy_built_in_files) \ + $(PYGTK2_ANTHY_RAW) \ + anthy.xml.in.in \ + default.xml.in.in \ + ibus-engine-anthy.in \ + $(NULL) + +CLEANFILES = \ + $(BUILT_SOURCES) \ + $(PYGTK2_ANTHY_GEN) \ + anthy.xml \ + default.xml \ + *.pyc \ + $(NULL) + +DISTCLEANFILES = \ + $(NULL) + +# Need a time lag between .py and .py.in files to build .py files +# because *_PYTHON valuables are installed in the tarball. +dist-hook: + @sleep 1; \ + for in_file in $(engine_anthy_built_in_files) ; do \ + if [ -f $(distdir)/$(srcdir)/$$in_file ] ; then \ + touch $(distdir)/$(srcdir)/$$in_file; \ + fi; \ + done; + diff --git a/engine/python3/_config.py.in b/engine/python3/_config.py.in new file mode 100644 index 0000000..f68b50a --- /dev/null +++ b/engine/python3/_config.py.in @@ -0,0 +1,30 @@ +# vim:set et sts=4 sw=4: +# -*- coding: utf-8 -*- +# +# ibus-anthy - The Anthy engine for IBus +# +# Copyright (c) 2007-2008 Peng Huang <shawn.p.huang@gmail.com> +# Copyright (c) 2010-2014 Takao Fujiwara <takao.fujiwara1@gmail.com> +# Copyright (c) 2007-2014 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +PKGDATADIR = '${PKGDATADIR}' +LIBEXECDIR = '${LIBEXECDIR}' +LOCALEDIR = '${DATADIR}/locale' +LAYOUT = '${LAYOUT}' +SYMBOL_CHAR = chr(${SYMBOL_CHAR_INT}) +ICON_PREFERENCE = '${ICON_PREFERENCE}' +DEBUG = False diff --git a/engine/python3/anthy.i b/engine/python3/anthy.i new file mode 100644 index 0000000..68a3aa9 --- /dev/null +++ b/engine/python3/anthy.i @@ -0,0 +1,125 @@ +/* vim:set et ts=4: */ +/* + * ibus-anthy - The Anthy engine for IBus + * + * Copyright (c) 2007-2008 Peng Huang <shawn.p.huang@gmail.com> + * Copyright (c) 2010-2013 Takao Fujiwara <takao.fujiwara1@gmail.com> + * Copyright (c) 2007-2013 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +%module anthy +%{ + /* Put header files here or function declarations like below */ +#include <anthy/anthy.h> +%} + +%init %{ + anthy_init (); +%} + +/* anthy_context_t */ +%include anthy/anthy.h +struct anthy_context {}; +%extend anthy_context { + anthy_context () { + return anthy_create_context (); + } + + void reset () { + anthy_reset_context (self); + } + + int set_string (char *str) { + return anthy_set_string (self, str); + } + + void resize_segment (int a1, int a2) { + anthy_resize_segment (self, a1, a2); + } + + int get_stat (struct anthy_conv_stat *a1) { + return anthy_get_stat (self, a1); + } + + int get_segment_stat (int a1, struct anthy_segment_stat *a2) { + return anthy_get_segment_stat (self, a1, a2); + } + + char *get_segment (int a1, int a2) { + int len; + static char temp[512]; + + len = anthy_get_segment (self, a1, a2, temp, sizeof (temp)); + if (len >= 0) + return temp; + else + return NULL; + } + + int commit_segment (int a1, int a2) { + return anthy_commit_segment (self, a1, a2); + } + + int set_prediction_string (const char *a1) { + return anthy_set_prediction_string (self, a1); + } + + int get_prediction_stat (struct anthy_prediction_stat *a1) { + return anthy_get_prediction_stat (self, a1); + } + + char *get_prediction (int a1) { + int len; + static char temp[512]; + + len = anthy_get_prediction (self, a1, temp, sizeof (temp)); + + if (len >= 0) + return temp; + else + return NULL; + } + + int commit_prediction (int a1) { + return anthy_commit_prediction(self, a1); + } + + void _print () { + anthy_print_context (self); + } + + int _set_encoding (int encoding) { + return anthy_context_set_encoding (self, encoding); + } + + int set_reconversion_mode (int mode) { + return anthy_set_reconversion_mode (self, mode); + } + + int init_personality (void) { + return anthy_init_personality (); + } + + int do_set_personality (const char *id) { + return anthy_do_set_personality (id); + } + + ~anthy_context () { + anthy_release_context (self); + } +}; + diff --git a/engine/python3/anthy.xml.in.in b/engine/python3/anthy.xml.in.in new file mode 100644 index 0000000..9a93744 --- /dev/null +++ b/engine/python3/anthy.xml.in.in @@ -0,0 +1,19 @@ +<?xml version=\"1.0\" encoding=\"utf-8\"?> +<!-- filename: anthy.xml --> +<component> + <name>org.freedesktop.IBus.Anthy</name> + <description>Anthy Component</description> + <exec>${libexecdir}/ibus-engine-anthy --ibus</exec> + <version>@VERSION@</version> + <author>Peng Huang <shawn.p.huang@gmail.com></author> + <license>GPL</license> + <homepage>http://code.google.com/p/ibus</homepage> + <textdomain>ibus-anthy</textdomain> + + <!-- for engines --> + <observed-paths> + <path>~/.config/ibus-anthy/engines.xml</path> + <path>${pkgdatadir}/engine/default.xml</path> + </observed-paths> + <engines exec=\"${libexecdir}/ibus-engine-anthy --xml\" /> +</component> diff --git a/engine/python3/default.xml.in.in b/engine/python3/default.xml.in.in new file mode 100644 index 0000000..27e3c82 --- /dev/null +++ b/engine/python3/default.xml.in.in @@ -0,0 +1,19 @@ +<?xml version=\"1.0\" encoding=\"utf-8\"?> +<engines> + <engine> + <name>anthy</name> + <language>ja</language> + <license>GPL</license> + <author>Peng Huang <shawn.p.huang@gmail.com></author> + <icon>ibus-anthy</icon> + @LAYOUT_XML@ + <layout_variant></layout_variant> + <layout_option></layout_option> + <longname>Anthy</longname> + <description>Anthy Input Method</description> + <rank>99</rank> + @HOTKEYS_XML@ + @SYMBOL_XML@ + <version>@PACKAGE_VERSION@</version> + </engine> +</engines> diff --git a/engine/python3/engine.py b/engine/python3/engine.py new file mode 100644 index 0000000..2119c39 --- /dev/null +++ b/engine/python3/engine.py @@ -0,0 +1,2780 @@ +# vim:set et sts=4 sw=4: +# -*- coding: utf-8 -*- +# +# ibus-anthy - The Anthy engine for IBus +# +# Copyright (c) 2007-2008 Peng Huang <shawn.p.huang@gmail.com> +# Copyright (c) 2010-2014 Takao Fujiwara <takao.fujiwara1@gmail.com> +# Copyright (c) 2007-2014 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +import binascii +import os +from os import environ, path +import signal +import sys +from gettext import dgettext + +from main import get_userhome + +try: + from locale import getpreferredencoding +except: + pass + +from gi.repository import GLib +from gi.repository import IBus + +from gi.repository import Anthy +NTH_UNCONVERTED_CANDIDATE = Anthy.NTH_UNCONVERTED_CANDIDATE +NTH_KATAKANA_CANDIDATE = Anthy.NTH_KATAKANA_CANDIDATE +NTH_HIRAGANA_CANDIDATE = Anthy.NTH_HIRAGANA_CANDIDATE +NTH_HALFKANA_CANDIDATE = Anthy.NTH_HALFKANA_CANDIDATE + +import _config as config +from tables import * +import jastring +from segment import unichar_half_to_full + +sys.path.append(path.join(config.PKGDATADIR, 'setup')) +from anthyprefs import AnthyPrefs + +_ = lambda a : dgettext('ibus-anthy', a) +N_ = lambda a : a +UN = lambda a : unicode(a, 'utf-8') + +printerr = AnthyPrefs.printerr + +INPUT_MODE_HIRAGANA, \ +INPUT_MODE_KATAKANA, \ +INPUT_MODE_HALF_WIDTH_KATAKANA, \ +INPUT_MODE_LATIN, \ +INPUT_MODE_WIDE_LATIN = list(range(5)) + +CONV_MODE_OFF, \ +CONV_MODE_ANTHY, \ +CONV_MODE_HIRAGANA, \ +CONV_MODE_KATAKANA, \ +CONV_MODE_HALF_WIDTH_KATAKANA, \ +CONV_MODE_LATIN_0, \ +CONV_MODE_LATIN_1, \ +CONV_MODE_LATIN_2, \ +CONV_MODE_LATIN_3, \ +CONV_MODE_WIDE_LATIN_0, \ +CONV_MODE_WIDE_LATIN_1, \ +CONV_MODE_WIDE_LATIN_2, \ +CONV_MODE_WIDE_LATIN_3, \ +CONV_MODE_PREDICTION = list(range(14)) + +SEGMENT_DEFAULT = 0 +SEGMENT_SINGLE = 1 << 0 +SEGMENT_IMMEDIATE = 1 << 1 + +CLIPBOARD_RECONVERT = list(range(1)) + +LINK_DICT_EMBEDDED, \ +LINK_DICT_SINGLE = list(range(2)) + +IMPORTED_EMBEDDED_DICT_DIR = 'imported_words_default.d' +IMPORTED_EMBEDDED_DICT_PREFIX = 'ibus__' +IMPORTED_SINGLE_DICT_PREFIX = 'imported_words_ibus__' + +KP_Table = {} +for s in dir(IBus): + if s.startswith('KEY_KP_'): + v = IBus.keyval_from_name(s[7:]) + if v: + KP_Table[IBus.keyval_from_name(s[4:])] = v +for k, v in zip(['KEY_KP_Add', 'KEY_KP_Decimal', 'KEY_KP_Divide', 'KEY_KP_Enter', + 'KEY_KP_Equal', 'KEY_KP_Multiply', 'KEY_KP_Separator', + 'KEY_KP_Space', 'KEY_KP_Subtract'], + ['KEY_plus', 'KEY_period', 'KEY_slash', 'KEY_Return', + 'KEY_equal', 'KEY_asterisk', 'KEY_comma', + 'KEY_space', 'KEY_minus']): + KP_Table[getattr(IBus, k)] = getattr(IBus, v) + +class Engine(IBus.EngineSimple): + __input_mode = None + __typing_mode = None + __segment_mode = None + __dict_mode = None + + __setup_pid = 0 + __prefs = None + __keybind = {} + __thumb = None + __latin_with_shift = True + + def __init__(self, bus, object_path): + super(Engine, self).__init__(engine_name="anthy", + connection=bus.get_connection(), + object_path=object_path) + + # create anthy context + self.__context = Anthy.GContext() + self.__context.set_encoding(Anthy.UTF8_ENCODING) + + # init state + self.__idle_id = 0 + self.__prop_dict = {} + self.__input_purpose = 0 + self.__has_input_purpose = False + if hasattr(IBus, 'InputPurpose'): + self.__has_input_purpose = True + try: + self.__is_utf8 = (getpreferredencoding().lower() == 'utf-8') + except: + self.__is_utf8 = False + self.__ibus_version = 0.0 + +# self.__lookup_table = ibus.LookupTable.new(page_size=9, +# cursor_pos=0, +# cursor_visible=True, +# round=True) + size = self.__prefs.get_value('common', 'page_size') + self.__lookup_table = IBus.LookupTable.new(page_size=size, + cursor_pos=0, + cursor_visible=True, + round=True) + self.__prop_list = self.__init_props() + + # Do not use self.do_process_key_event to work ISO 14755 + # with Ctrl+Shift+u . + # The super (parent) method of do_process_key_event is called + # loop infinitely if this class overrides it. + # self.process_key_event is not accessible too. + self.connect('process-key-event', self.__process_key_event) + self.connect('destroy', self.__destroy) + + self.__init_signal() + # use reset to init values + self.__reset() + + ibus_config = bus.get_config() + if ibus_config != None: + ibus_config.connect('value-changed', + self.__config_value_changed_cb) + + def __get_ibus_version(self): + if self.__ibus_version == 0.0: + self.__ibus_version = \ + IBus.MAJOR_VERSION + IBus.MINOR_VERSION / 1000.0 + \ + IBus.MICRO_VERSION / 1000000.0 + return self.__ibus_version + + # reset values of engine + def __reset(self): + self.__preedit_ja_string = jastring.JaString(Engine.__typing_mode, + self.__latin_with_shift) + self.__convert_chars = '' + self.__cursor_pos = 0 + self.__convert_mode = CONV_MODE_OFF + self.__segments = list() + self.__lookup_table.clear() + self.__lookup_table_visible = False + self._MM = 0 + self._SS = 0 + self._H = 0 + self._RMM = 0 + self._RSS = 0 + if self.__idle_id != 0: + GLib.source_remove(self.__idle_id) + self.__idle_id = 0 + + def __init_props(self): + anthy_props = IBus.PropList() + + self.__set_input_mode_props(anthy_props) + self.__set_typing_method_props(anthy_props) + self.__set_segment_mode_props(anthy_props) + self.__set_dict_mode_props(anthy_props) + self.__set_dict_config_props(anthy_props) + + if not self.__prefs.get_value('common', 'show-preferences'): + return anthy_props + + anthy_props.append(IBus.Property(key='setup', + label=IBus.Text.new_from_string(_("Preferences - Anthy")), + icon=config.ICON_PREFERENCE, + tooltip=IBus.Text.new_from_string(_("Configure Anthy")), + sensitive=True, + visible=True)) + + return anthy_props + + def __init_signal(self): + signal.signal(signal.SIGHUP, self.__signal_cb) + signal.signal(signal.SIGINT, self.__signal_cb) + signal.signal(signal.SIGQUIT, self.__signal_cb) + signal.signal(signal.SIGABRT, self.__signal_cb) + signal.signal(signal.SIGTERM, self.__signal_cb) + + def __signal_cb(self, signum, object): + self.__remove_dict_files() + signal.signal(signum, signal.SIG_DFL) + os.kill(os.getpid(), signum) + + def __set_input_mode_props(self, anthy_props): + # The class method is kept even if the engine is switched. + if Engine.__input_mode == None: + # The config value is readonly for initial engine and + # the engine keeps the class method in the memory. + Engine.__input_mode = INPUT_MODE_HIRAGANA + Engine.__input_mode = self.__prefs.get_value('common', + 'input_mode') + + if not self.__prefs.get_value('common', 'show-input-mode'): + return + + # init input mode properties + symbol = 'あ' + ''' + Need to split _() by line for intltool to detect them. + ''' + # Translators: Specify the order of %s with your translation. + # It will be "Input Mode (A)" for example. + label = _("%(description)s (%(symbol)s)") % \ + { 'description' : _("Input mode"), 'symbol' : symbol } + input_mode_prop = IBus.Property(key='InputMode', + prop_type=IBus.PropType.MENU, + label=IBus.Text.new_from_string(label), + symbol=IBus.Text.new_from_string(symbol), + icon='', + tooltip=IBus.Text.new_from_string(_("Switch input mode")), + sensitive=True, + visible=True, + state=IBus.PropState.UNCHECKED, + sub_props=None) + self.__prop_dict['InputMode'] = input_mode_prop + + props = IBus.PropList() + props.append(IBus.Property(key='InputMode.Hiragana', + prop_type=IBus.PropType.RADIO, + label=IBus.Text.new_from_string(_("Hiragana")), + icon=None, + tooltip=None, + sensitive=True, + visible=True, + state=IBus.PropState.UNCHECKED, + sub_props=None)) + props.append(IBus.Property(key='InputMode.Katakana', + prop_type=IBus.PropType.RADIO, + label=IBus.Text.new_from_string(_("Katakana")), + icon=None, + tooltip=None, + sensitive=True, + visible=True, + state=IBus.PropState.UNCHECKED, + sub_props=None)) + props.append(IBus.Property(key='InputMode.HalfWidthKatakana', + prop_type=IBus.PropType.RADIO, + label=IBus.Text.new_from_string(_("Halfwidth Katakana")), + icon=None, + tooltip=None, + sensitive=True, + visible=True, + state=IBus.PropState.UNCHECKED, + sub_props=None)) + props.append(IBus.Property(key='InputMode.Latin', + prop_type=IBus.PropType.RADIO, + label=IBus.Text.new_from_string(_("Latin")), + icon=None, + tooltip=None, + sensitive=True, + visible=True, + state=IBus.PropState.UNCHECKED, + sub_props=None)) + props.append(IBus.Property(key='InputMode.WideLatin', + prop_type=IBus.PropType.RADIO, + label=IBus.Text.new_from_string(_("Wide Latin")), + icon=None, + tooltip=None, + sensitive=True, + visible=True, + state=IBus.PropState.UNCHECKED, + sub_props=None)) + + props.get(Engine.__input_mode).set_state(IBus.PropState.CHECKED) + + i = 0 + while props.get(i) != None: + prop = props.get(i) + self.__prop_dict[prop.get_key()] = prop + i += 1 + + input_mode_prop.set_sub_props(props) + anthy_props.append(input_mode_prop) + + mode = Engine.__input_mode + mode = 'InputMode.' + ['Hiragana', 'Katakana', 'HalfWidthKatakana', + 'Latin', 'WideLatin'][mode] + self.__input_mode_activate(mode, IBus.PropState.CHECKED) + + def __set_typing_method_props(self, anthy_props): + if Engine.__typing_mode == None: + Engine.__typing_mode = jastring.TYPING_MODE_ROMAJI + Engine.__typing_mode = self.__prefs.get_value('common', + 'typing_method') + + if not self.__prefs.get_value('common', 'show-typing-method'): + return + + # typing input mode properties + symbol = 'R' + label = _("%(description)s (%(symbol)s)") % \ + { 'description' : _("Typing method"), 'symbol' : symbol } + typing_mode_prop = IBus.Property(key='TypingMode', + prop_type=IBus.PropType.MENU, + label=IBus.Text.new_from_string(label), + symbol=IBus.Text.new_from_string(symbol), + icon='', + tooltip=IBus.Text.new_from_string(_("Switch typing method")), + sensitive=True, + visible=True, + state=IBus.PropState.UNCHECKED, + sub_props=None) + self.__prop_dict['TypingMode'] = typing_mode_prop + + props = IBus.PropList() + props.append(IBus.Property(key='TypingMode.Romaji', + prop_type=IBus.PropType.RADIO, + label=IBus.Text.new_from_string(_("Romaji")), + icon=None, + tooltip=None, + sensitive=True, + visible=True, + state=IBus.PropState.UNCHECKED, + sub_props=None)) + props.append(IBus.Property(key='TypingMode.Kana', + prop_type=IBus.PropType.RADIO, + label=IBus.Text.new_from_string(_("Kana")), + icon=None, + tooltip=None, + sensitive=True, + visible=True, + state=IBus.PropState.UNCHECKED, + sub_props=None)) + props.append(IBus.Property(key='TypingMode.ThumbShift', + prop_type=IBus.PropType.RADIO, + label=IBus.Text.new_from_string(_("Thumb shift")), + icon=None, + tooltip=None, + sensitive=True, + visible=True, + state=IBus.PropState.UNCHECKED, + sub_props=None)) + props.get(Engine.__typing_mode).set_state(IBus.PropState.CHECKED) + + i = 0 + while props.get(i) != None: + prop = props.get(i) + self.__prop_dict[prop.get_key()] = prop + i += 1 + + typing_mode_prop.set_sub_props(props) + anthy_props.append(typing_mode_prop) + + mode = Engine.__typing_mode + mode = 'TypingMode.' + ['Romaji', 'Kana', 'ThumbShift'][mode] + self.__typing_mode_activate(mode, IBus.PropState.CHECKED) + + def __set_segment_mode_props(self, anthy_props): + if Engine.__segment_mode == None: + Engine.__segment_mode = SEGMENT_DEFAULT + Engine.__segment_mode = self.__prefs.get_value('common', + 'conversion_segment_mode') + + if not self.__prefs.get_value('common', 'show-segment-mode'): + return + + symbol = '連' + label = _("%(description)s (%(symbol)s)") % \ + { 'description' : _("Segment mode"), 'symbol' : symbol } + segment_mode_prop = IBus.Property(key='SegmentMode', + prop_type=IBus.PropType.MENU, + label=IBus.Text.new_from_string(label), + symbol=IBus.Text.new_from_string(symbol), + icon=None, + tooltip=IBus.Text.new_from_string(_("Switch conversion mode")), + sensitive=True, + visible=True, + state=IBus.PropState.UNCHECKED, + sub_props=None) + self.__prop_dict['SegmentMode'] = segment_mode_prop + + props = IBus.PropList() + props.append(IBus.Property(key='SegmentMode.Multi', + prop_type=IBus.PropType.RADIO, + label=IBus.Text.new_from_string(_("Multiple segment")), + icon=None, + tooltip=None, + sensitive=True, + visible=True, + state=IBus.PropState.UNCHECKED, + sub_props=None)) + props.append(IBus.Property(key='SegmentMode.Single', + prop_type=IBus.PropType.RADIO, + label=IBus.Text.new_from_string(_("Single segment")), + icon=None, + tooltip=None, + sensitive=True, + visible=True, + state=IBus.PropState.UNCHECKED, + sub_props=None)) + props.append(IBus.Property(key='SegmentMode.ImmediateMulti', + prop_type=IBus.PropType.RADIO, + label=IBus.Text.new_from_string(_("Immediate conversion (multiple segment)")), + icon=None, + tooltip=None, + sensitive=True, + visible=True, + state=IBus.PropState.UNCHECKED, + sub_props=None)) + props.append(IBus.Property(key='SegmentMode.ImmediateSingle', + prop_type=IBus.PropType.RADIO, + label=IBus.Text.new_from_string(_("Immediate conversion (single segment)")), + icon=None, + tooltip=None, + sensitive=True, + visible=True, + state=IBus.PropState.UNCHECKED, + sub_props=None)) + props.get(Engine.__segment_mode).set_state(IBus.PropState.CHECKED) + + i = 0 + while props.get(i) != None: + prop = props.get(i) + self.__prop_dict[prop.get_key()] = prop + i += 1 + + segment_mode_prop.set_sub_props(props) + anthy_props.append(segment_mode_prop) + + mode = Engine.__segment_mode + mode = 'SegmentMode.' + ['Multi', 'Single', + 'ImmediateMulti', 'ImmediateSingle'][mode] + self.__segment_mode_activate(mode, IBus.PropState.CHECKED) + + def __set_dict_mode_props(self, anthy_props, update_prop=False): + if Engine.__dict_mode == None: + Engine.__dict_mode = 0 + + if not self.__prefs.get_value('common', 'show-dict-mode'): + return + + short_label = self.__prefs.get_value('dict/file/embedded', + 'short_label') + label = _("%(description)s (%(symbol)s)") % \ + { 'description' : _("Dictionary mode"), 'symbol' : short_label } + dict_mode_prop = IBus.Property(key='DictMode', + prop_type=IBus.PropType.MENU, + label=IBus.Text.new_from_string(label), + symbol=IBus.Text.new_from_string(short_label), + icon=None, + tooltip=IBus.Text.new_from_string(_("Switch dictionary")), + sensitive=True, + visible=True, + state=IBus.PropState.UNCHECKED, + sub_props=None) + self.__prop_dict['DictMode'] = dict_mode_prop + props = IBus.PropList() + + long_label = self.__prefs.get_value('dict/file/embedded', + 'long_label') + props.append(IBus.Property(key='DictMode.embedded', + prop_type=IBus.PropType.RADIO, + # if long_label is UTF-8 + label=IBus.Text.new_from_string(_(long_label)), + icon=None, + tooltip=None, + sensitive=True, + visible=True, + state=IBus.PropState.UNCHECKED, + sub_props=None)) + for file in self.__prefs.get_value('dict', 'files'): + if not self.__link_dict_file(file): + continue + id = self.__get_dict_id_from_file(file) + section = 'dict/file/' + id + if not self.__prefs.get_value(section, 'single'): + continue + key = 'DictMode.' + id + long_label = self.__prefs.get_value(section, 'long_label') + + # ibus-config 'value-changed' signal updated dict/files but + # not dict/file/new yet. + if long_label == None: + continue + + if 'is_system' in self.__prefs.keys(section) and \ + self.__prefs.get_value(section, 'is_system'): + uni_long_label = _(long_label) + else: + uni_long_label = long_label + props.append(IBus.Property(key=key, + prop_type=IBus.PropType.RADIO, + label=IBus.Text.new_from_string(uni_long_label), + icon=None, + tooltip=None, + sensitive=True, + visible=True, + state=IBus.PropState.UNCHECKED, + sub_props=None)) + + props.get(Engine.__dict_mode).set_state(IBus.PropState.CHECKED) + + i = 0 + while props.get(i) != None: + prop = props.get(i) + self.__prop_dict[prop.get_key()] = prop + i += 1 + + dict_mode_prop.set_sub_props(props) + + if update_prop: + # focus-in event will call register_properties(). + # Need to switch another IME to update menus on GtkStatusIcon? + anthy_props.update_property(dict_mode_prop) + else: + anthy_props.append(dict_mode_prop) + + prop_name = self.__dict_mode_get_prop_name(Engine.__dict_mode) + if prop_name == None: + return + self.__dict_mode_activate(prop_name, + IBus.PropState.CHECKED) + + def __set_dict_config_props(self, anthy_props): + if not self.__prefs.get_value('common', 'show-dict-config'): + return + + admin_command = self.__prefs.get_value('common', 'dict_admin_command') + icon_path = self.__prefs.get_value('common', 'dict_config_icon') + + if not path.exists(admin_command[0]): + return + label = _("Dictionary - Anthy") + if icon_path and path.exists(icon_path): + icon = icon_path + else: + # Translators: "Dic" means 'dictionary', One kanji may be good. + label = _("Dic") + icon = '' + + dict_prop = IBus.Property(key='setup-dict-kasumi', + prop_type=IBus.PropType.MENU, + label=IBus.Text.new_from_string(label), + icon=icon, + tooltip=IBus.Text.new_from_string(_("Configure dictionaries")), + sensitive=True, + visible=True, + state=IBus.PropState.UNCHECKED, + sub_props=None) + self.__prop_dict['setup-dict-kasumi'] = dict_prop + + props = IBus.PropList() + props.append(IBus.Property(key='setup-dict-kasumi-admin', + prop_type=IBus.PropType.NORMAL, + label=IBus.Text.new_from_string(_("Edit dictionaries")), + icon=icon, + tooltip=IBus.Text.new_from_string(_("Launch the dictionary tool")), + sensitive=True, + visible=True, + state=IBus.PropState.UNCHECKED, + sub_props=None)) + props.append(IBus.Property(key='setup-dict-kasumi-word', + prop_type=IBus.PropType.NORMAL, + label=IBus.Text.new_from_string(_("Add words")), + icon=icon, + tooltip=IBus.Text.new_from_string(_("Add words to the dictionary")), + sensitive=True, + visible=True, + state=IBus.PropState.UNCHECKED, + sub_props=None)) + + i = 0 + while props.get(i) != None: + prop = props.get(i) + self.__prop_dict[prop.get_key()] = prop + i += 1 + + dict_prop.set_sub_props(props) + anthy_props.append(dict_prop) + + def __get_clipboard(self, clipboard, text, data): + clipboard_text = clipboard.wait_for_text () + + if data == CLIPBOARD_RECONVERT: + self.__update_reconvert(clipboard_text) + + return clipboard_text + + def __get_single_dict_files(self): + files = self.__prefs.get_value('dict', 'files') + single_files = [] + for file in files: + if not path.exists(file): + continue + id = self.__get_dict_id_from_file(file) + section = 'dict/file/' + id + if self.__prefs.get_value(section, 'single'): + single_files.append(file) + return single_files + + def __remove_dict_files(self): + for file in self.__prefs.get_value('dict', 'files'): + self.__remove_dict_file(file) + + def update_preedit(self, string, attrs, cursor_pos, visible): + text = IBus.Text.new_from_string(string) + i = 0 + while attrs.get(i) != None: + attr = attrs.get(i) + text.append_attribute(attr.get_attr_type(), + attr.get_value(), + attr.get_start_index(), + attr.get_end_index()) + i += 1 + mode = self.__prefs.get_value('common', 'behavior_on_focus_out') + if self.__get_ibus_version() >= 1.003 and mode == 1: + self.update_preedit_text_with_mode(text, + cursor_pos, visible, + IBus.PreeditFocusMode.COMMIT) + else: + self.update_preedit_text(text, + cursor_pos, visible) + + def update_aux_string(self, string, attrs, visible): + text = IBus.Text.new_from_string(string) + i = 0 + while attrs.get(i) != None: + attr = attrs.get(i) + text.append_attribute(attr.get_attr_type(), + attr.get_value(), + attr.get_start_index(), + attr.get_end_index()) + i += 1 + self.update_auxiliary_text(text, visible) + + def do_page_up(self): + # only process cursor down in convert mode + if self.__convert_mode != CONV_MODE_ANTHY: + return False + + if not self.__lookup_table.page_up(): + return False + + index = self.__lookup_table.get_cursor_pos() + candidate = self.__lookup_table.get_candidate(index).get_text() + self.__segments[self.__cursor_pos] = index, candidate + self.__invalidate() + return True + + def do_page_down(self): + # only process cursor down in convert mode + if self.__convert_mode != CONV_MODE_ANTHY: + return False + + if not self.__lookup_table.page_down(): + return False + + index = self.__lookup_table.get_cursor_pos() + candidate = self.__lookup_table.get_candidate(index).get_text() + self.__segments[self.__cursor_pos] = index, candidate + self.__invalidate() + return True + + def do_cursor_up(self): + # only process cursor down in convert mode + # if self.__convert_mode != CONV_MODE_ANTHY: + if self.__convert_mode != CONV_MODE_ANTHY and self.__convert_mode != CONV_MODE_PREDICTION: + return False + + if not self.__lookup_table.cursor_up(): + return False + + index = self.__lookup_table.get_cursor_pos() + candidate = self.__lookup_table.get_candidate(index).get_text() + self.__segments[self.__cursor_pos] = index, candidate + self.__invalidate() + return True + + def do_cursor_down(self): + # only process cursor down in convert mode + # if self.__convert_mode != CONV_MODE_ANTHY: + if self.__convert_mode != CONV_MODE_ANTHY and self.__convert_mode != CONV_MODE_PREDICTION: + return False + + if not self.__lookup_table.cursor_down(): + return False + + index = self.__lookup_table.get_cursor_pos() + candidate = self.__lookup_table.get_candidate(index).get_text() + self.__segments[self.__cursor_pos] = index, candidate + self.__invalidate() + return True + + def do_candidate_clicked(self, index, button, state): + if index == 9: + keyval = IBus.KEY_0 + else: + keyval = IBus.KEY_1 + index + self.__on_key_number(keyval) + + def __commit_string(self, text): + self.__reset() + self.commit_text(IBus.Text.new_from_string(text)) + self.__invalidate() + + def __shrink_segment(self, relative_size): + self.__context.resize_segment(self.__cursor_pos, relative_size) + nr_segments = self.__context.get_nr_segments() + del self.__segments[self.__cursor_pos:] + for i in range(self.__cursor_pos, nr_segments): + buf = self.__context.get_segment(i, 0) + text = buf + self.__segments.append((0, text)) + self.__lookup_table_visible = False + self.__fill_lookup_table() + self.__invalidate() + return True + + def do_property_activate(self, prop_name, state): + + if state == IBus.PropState.CHECKED: + if prop_name == None: + return + elif prop_name.startswith('InputMode.'): + self.__input_mode_activate(prop_name, state) + return + elif prop_name.startswith('TypingMode.'): + self.__typing_mode_activate(prop_name, state) + return + elif prop_name.startswith('SegmentMode.'): + self.__segment_mode_activate(prop_name, state) + return + elif prop_name.startswith('DictMode.'): + self.__dict_mode_activate(prop_name, state) + return + else: + if prop_name == 'setup': + self.__start_setup() + elif prop_name == 'setup-dict-kasumi-admin': + self.__start_dict_admin() + elif prop_name == 'setup-dict-kasumi-word': + self.__start_add_word() + else: + self.__prop_dict[prop_name].set_state(state) + if prop_name == 'DictMode': + sub_name = self.__dict_mode_get_prop_name(self.__dict_mode) + if sub_name == None: + return + self.__dict_mode_activate(sub_name, + IBus.PropState.CHECKED) + + def __input_mode_activate(self, prop_name, state): + input_modes = { + 'InputMode.Hiragana' : (INPUT_MODE_HIRAGANA, 'あ'), + 'InputMode.Katakana' : (INPUT_MODE_KATAKANA, 'ア'), + 'InputMode.HalfWidthKatakana' : (INPUT_MODE_HALF_WIDTH_KATAKANA, '_ア'), + 'InputMode.Latin' : (INPUT_MODE_LATIN, '_A'), + 'InputMode.WideLatin' : (INPUT_MODE_WIDE_LATIN, 'A'), + } + + if prop_name not in input_modes: + printerr('Unknown prop_name = %s' % prop_name) + return + self.__prop_dict[prop_name].set_state(state) + self.update_property(self.__prop_dict[prop_name]) + + mode, symbol = input_modes[prop_name] + + label = _("%(description)s (%(symbol)s)") % \ + { 'description' : _("Input mode"), 'symbol' : symbol } + Engine.__input_mode = mode + prop = self.__prop_dict['InputMode'] + prop.set_symbol(IBus.Text.new_from_string(symbol)) + prop.set_label(IBus.Text.new_from_string(label)) + self.update_property(prop) + + self.__reset() + self.__invalidate() + + def __typing_mode_activate(self, prop_name, state): + typing_modes = { + 'TypingMode.Romaji' : (jastring.TYPING_MODE_ROMAJI, 'R'), + 'TypingMode.Kana' : (jastring.TYPING_MODE_KANA, 'か'), + 'TypingMode.ThumbShift' : (jastring.TYPING_MODE_THUMB_SHIFT, '親'), + } + + if prop_name not in typing_modes: + printerr('Unknown prop_name = %s' % prop_name) + return + self.__prop_dict[prop_name].set_state(state) + self.update_property(self.__prop_dict[prop_name]) + if prop_name == 'TypingMode.ThumbShift': + self._reset_thumb() + + mode, symbol = typing_modes[prop_name] + + label = _("%(description)s (%(symbol)s)") % \ + { 'description' : _("Typing method"), 'symbol' : symbol } + Engine.__typing_mode = mode + prop = self.__prop_dict['TypingMode'] + prop.set_symbol(IBus.Text.new_from_string(symbol)) + prop.set_label(IBus.Text.new_from_string(label)) + self.update_property(prop) + + self.__reset() + self.__invalidate() + + def __refresh_typing_mode_property(self): + if 'TypingMode' not in self.__prop_dict: + return + + prop = self.__prop_dict['TypingMode'] + modes = { + jastring.TYPING_MODE_ROMAJI : ('TypingMode.Romaji', 'R'), + jastring.TYPING_MODE_KANA : ('TypingMode.Kana', 'か'), + jastring.TYPING_MODE_THUMB_SHIFT : ('TypingMode.ThumbShift', '親'), + } + prop_name, symbol = modes.get(Engine.__typing_mode, (None, None)) + if prop_name == None or symbol == None: + return + label = _("%(description)s (%(symbol)s)") % \ + { 'description' : _("Typing method"), 'symbol' : symbol } + _prop = self.__prop_dict[prop_name] + _prop.set_state(IBus.PropState.CHECKED) + self.update_property(_prop) + prop.set_symbol(IBus.Text.new_from_string(symbol)) + prop.set_label(IBus.Text.new_from_string(label)) + self.update_property(prop) + + def __segment_mode_activate(self, prop_name, state): + segment_modes = { + 'SegmentMode.Multi' : (SEGMENT_DEFAULT, '連'), + 'SegmentMode.Single' : (SEGMENT_SINGLE, '単'), + 'SegmentMode.ImmediateMulti' : (SEGMENT_IMMEDIATE, '逐|連'), + 'SegmentMode.ImmediateSingle' : + (SEGMENT_IMMEDIATE | SEGMENT_SINGLE, '逐|単'), + } + + if prop_name not in segment_modes: + printerr('Unknown prop_name = %s' % prop_name) + return + self.__prop_dict[prop_name].set_state(state) + self.update_property(self.__prop_dict[prop_name]) + + mode, symbol = segment_modes[prop_name] + + label = _("%(description)s (%(symbol)s)") % \ + { 'description' : _("Segment mode"), 'symbol' : symbol } + Engine.__segment_mode = mode + prop = self.__prop_dict['SegmentMode'] + prop.set_symbol(IBus.Text.new_from_string(symbol)) + prop.set_label(IBus.Text.new_from_string(label)) + self.update_property(prop) + + self.__reset() + self.__invalidate() + + def __dict_mode_get_prop_name(self, mode): + if mode == 0: + id = 'embedded' + else: + single_files = self.__get_single_dict_files() + file = single_files[mode - 1] + id = self.__get_dict_id_from_file(file) + return 'DictMode.' + id + + def __dict_mode_activate(self, prop_name, state): + if prop_name not in list(self.__prop_dict.keys()): + # The prop_name is added. Need to restart. + return + i = prop_name.find('.') + if i < 0: + return + # The id is already quoted. + id = prop_name[i + 1:] + + file = None + single_files = self.__get_single_dict_files() + + if id == 'embedded': + pass + else: + found = False + for file in single_files: + if id == self.__get_quoted_id(file): + found = True + break + if found == False: + return + + if id == 'embedded': + dict_name = 'default' + Engine.__dict_mode = 0 + else: + if file not in single_files: + printerr('Index error', file, single_files) + return + dict_name = 'ibus__' + id + Engine.__dict_mode = single_files.index(file) + 1 + self.__prop_dict[prop_name].set_state(state) + self.update_property(self.__prop_dict[prop_name]) + self.__context.init_personality() + # dict_name is unicode but the argument is str. + self.__context.do_set_personality(str(dict_name)) + + prop = self.__prop_dict['DictMode'] + section = 'dict/file/' + id + symbol = self.__prefs.get_value(section, 'short_label') + label = _("%(description)s (%(symbol)s)") % \ + { 'description' : _("Dictionary mode"), 'symbol' : symbol } + prop.set_symbol(IBus.Text.new_from_string(symbol)) + prop.set_label(IBus.Text.new_from_string(label)) + self.update_property(prop) + + def __argb(self, a, r, g, b): + return ((a & 0xff)<<24) + ((r & 0xff) << 16) + ((g & 0xff) << 8) + (b & 0xff) + + def __rgb(self, r, g, b): + return self.__argb(255, r, g, b) + + def do_focus_in(self): + self.register_properties(self.__prop_list) + self.__refresh_typing_mode_property() + mode = self.__prefs.get_value('common', 'behavior_on_focus_out') + if mode == 2: + self.__update_input_chars() +# self.__reset() +# self.__invalidate() + size = self.__prefs.get_value('common', 'page_size') + if size != self.__lookup_table.get_page_size(): + self.__lookup_table.set_page_size(size) + + def do_focus_out(self): + if self.__has_input_purpose: + self.__input_purpose = 0 + mode = self.__prefs.get_value('common', 'behavior_on_focus_out') + if mode == 0 or mode == 1: + self.__reset() + self.__invalidate() + + def do_set_content_type(self, purpose, hints): + if self.__has_input_purpose: + self.__input_purpose = purpose + + def do_disable(self): + self.__reset() + self.__invalidate() + + def do_reset(self): + self.__reset() + self.__invalidate() + + def __destroy(self, obj): + if self.__idle_id != 0: + GLib.source_remove(self.__idle_id) + self.__idle_id = 0 + # It seems do_destroy() is called when launch_engine() is called. + #self.__remove_dict_files() + # It seems super.destroy() does not unref the engine. + + def __join_all_segments(self): + while True: + nr_segments = self.__context.get_nr_segments() + seg = nr_segments - self.__cursor_pos + + if seg > 1: + self.__context.resize_segment(self.__cursor_pos, 1) + else: + break + + def __normalize_preedit(self, preedit): + if not self.__is_utf8: + return preedit + for key in list(romaji_normalize_rule.keys()): + if preedit.find(key) >= 0: + for value in romaji_normalize_rule[key]: + preedit = preedit.replace(key, value) + return preedit + + # begine convert + def __begin_anthy_convert(self): + if Engine.__segment_mode & SEGMENT_IMMEDIATE: + self.__end_anthy_convert() + if self.__convert_mode == CONV_MODE_ANTHY: + return + self.__convert_mode = CONV_MODE_ANTHY + +# text, cursor = self.__preedit_ja_string.get_hiragana() + text, cursor = self.__preedit_ja_string.get_hiragana(True) + + text = self.__normalize_preedit(text) + self.__context.set_string(text) + if Engine.__segment_mode & SEGMENT_SINGLE: + self.__join_all_segments() + nr_segments = self.__context.get_nr_segments() + + for i in range(0, nr_segments): + buf = self.__context.get_segment(i, 0) + text = buf + self.__segments.append((0, text)) + + if Engine.__segment_mode & SEGMENT_IMMEDIATE: + self.__cursor_pos = nr_segments - 1 + else: + self.__cursor_pos = 0 + self.__fill_lookup_table() + self.__lookup_table_visible = False + + def __end_anthy_convert(self): + if self.__convert_mode == CONV_MODE_OFF: + return + + self.__convert_mode = CONV_MODE_OFF + self.__convert_chars = '' + self.__segments = list() + self.__cursor_pos = 0 + self.__lookup_table.clear() + self.__lookup_table_visible = False + + def __end_convert(self): + self.__end_anthy_convert() + + # test case 'verudhi' can show U+3046 + U+309B and U+3094 + def __candidate_cb(self, candidate): + if not self.__is_utf8: + return + for key in list(romaji_utf8_rule.keys()): + if candidate.find(key) >= 0: + for value in romaji_utf8_rule[key]: + candidate = candidate.replace(key, value) + self.__lookup_table.append_candidate(IBus.Text.new_from_string(candidate)) + + def __fill_lookup_table(self): + if self.__convert_mode == CONV_MODE_PREDICTION: + nr_predictions = self.__context.get_nr_predictions() + + # fill lookup_table + self.__lookup_table.clear() + for i in range(0, seg_stat.nr_predictions): + buf = self.__context.get_prediction(i) + candidate = buf + self.__lookup_table.append_candidate(IBus.Text.new_from_string(candidate)) + self.__candidate_cb(candidate) + return + + # get segment stat + nr_candidates = self.__context.get_nr_candidates(self.__cursor_pos) + + # fill lookup_table + self.__lookup_table.clear() + for i in range(0, nr_candidates): + buf = self.__context.get_segment(self.__cursor_pos, i) + candidate = buf + self.__lookup_table.append_candidate(IBus.Text.new_from_string(candidate)) + self.__candidate_cb(candidate) + + + def __invalidate(self): + if self.__idle_id != 0: + return + self.__idle_id = GLib.idle_add(self.__update, + priority = GLib.PRIORITY_LOW) + +# def __get_preedit(self): + def __get_preedit(self, commit=False): + if Engine.__input_mode == INPUT_MODE_HIRAGANA: +# text, cursor = self.__preedit_ja_string.get_hiragana() + text, cursor = self.__preedit_ja_string.get_hiragana(commit) + elif Engine.__input_mode == INPUT_MODE_KATAKANA: +# text, cursor = self.__preedit_ja_string.get_katakana() + text, cursor = self.__preedit_ja_string.get_katakana(commit) + elif Engine.__input_mode == INPUT_MODE_HALF_WIDTH_KATAKANA: +# text, cursor = self.__preedit_ja_string.get_half_width_katakana() + text, cursor = self.__preedit_ja_string.get_half_width_katakana(commit) + else: + text, cursor = '', 0 + return text, cursor + + def __update_input_chars(self): + text, cursor = self.__get_preedit() + attrs = IBus.AttrList() + attrs.append(IBus.attr_underline_new( + IBus.AttrUnderline.SINGLE, 0, + len(text))) + + self.update_preedit(text, + attrs, cursor, not self.__preedit_ja_string.is_empty()) + self.update_aux_string('', IBus.AttrList(), False) + self.update_lookup_table(self.__lookup_table, + self.__lookup_table_visible) + + def __update_convert_chars(self): +# if self.__convert_mode == CONV_MODE_ANTHY: + if self.__convert_mode == CONV_MODE_ANTHY or self.__convert_mode == CONV_MODE_PREDICTION: + self.__update_anthy_convert_chars() + return + if self.__convert_mode == CONV_MODE_HIRAGANA: +# text, cursor = self.__preedit_ja_string.get_hiragana() + text, cursor = self.__preedit_ja_string.get_hiragana(True) + elif self.__convert_mode == CONV_MODE_KATAKANA: +# text, cursor = self.__preedit_ja_string.get_katakana() + text, cursor = self.__preedit_ja_string.get_katakana(True) + elif self.__convert_mode == CONV_MODE_HALF_WIDTH_KATAKANA: +# text, cursor = self.__preedit_ja_string.get_half_width_katakana() + text, cursor = self.__preedit_ja_string.get_half_width_katakana(True) + elif self.__convert_mode == CONV_MODE_LATIN_0: + text, cursor = self.__preedit_ja_string.get_latin() + if text == text.lower(): + self.__convert_mode = CONV_MODE_LATIN_1 + elif self.__convert_mode == CONV_MODE_LATIN_1: + text, cursor = self.__preedit_ja_string.get_latin() + text = text.lower() + elif self.__convert_mode == CONV_MODE_LATIN_2: + text, cursor = self.__preedit_ja_string.get_latin() + text = text.upper() + elif self.__convert_mode == CONV_MODE_LATIN_3: + text, cursor = self.__preedit_ja_string.get_latin() + text = text.capitalize() + elif self.__convert_mode == CONV_MODE_WIDE_LATIN_0: + text, cursor = self.__preedit_ja_string.get_wide_latin() + if text == text.lower(): + self.__convert_mode = CONV_MODE_WIDE_LATIN_1 + elif self.__convert_mode == CONV_MODE_WIDE_LATIN_1: + text, cursor = self.__preedit_ja_string.get_wide_latin() + text = text.lower() + elif self.__convert_mode == CONV_MODE_WIDE_LATIN_2: + text, cursor = self.__preedit_ja_string.get_wide_latin() + text = text.upper() + elif self.__convert_mode == CONV_MODE_WIDE_LATIN_3: + text, cursor = self.__preedit_ja_string.get_wide_latin() + text = text.capitalize() + self.__convert_chars = text + attrs = IBus.AttrList() + attrs.append(IBus.attr_underline_new( + IBus.AttrUnderline.SINGLE, 0, len(text))) + attrs.append(IBus.attr_background_new(self.__rgb(200, 200, 240), + 0, len(text))) + attrs.append(IBus.attr_foreground_new(self.__rgb(0, 0, 0), + 0, len(text))) + self.update_preedit(text, attrs, len(text), True) + + self.update_aux_string('', + IBus.AttrList(), self.__lookup_table_visible) + self.update_lookup_table(self.__lookup_table, + self.__lookup_table_visible) + + def __update_anthy_convert_chars(self): + self.__convert_chars = '' + pos = 0 + for i, (seg_index, text) in enumerate(self.__segments): + self.__convert_chars += text + if i < self.__cursor_pos: + pos += len(text) + attrs = IBus.AttrList() + attrs.append(IBus.attr_underline_new( + IBus.AttrUnderline.SINGLE, 0, len(self.__convert_chars))) + attrs.append(IBus.attr_background_new(self.__rgb(200, 200, 240), + pos, pos + len(self.__segments[self.__cursor_pos][1]))) + attrs.append(IBus.attr_foreground_new(self.__rgb(0, 0, 0), + pos, pos + len(self.__segments[self.__cursor_pos][1]))) + self.update_preedit(self.__convert_chars, attrs, pos, True) + aux_string = '( %d / %d )' % (self.__lookup_table.get_cursor_pos() + 1, self.__lookup_table.get_number_of_candidates()) + self.update_aux_string(aux_string, + IBus.AttrList(), self.__lookup_table_visible) + self.update_lookup_table(self.__lookup_table, + self.__lookup_table_visible) + + def __update(self): + if self.__convert_mode == CONV_MODE_OFF: + self.__update_input_chars() + else: + self.__update_convert_chars() + self.__idle_id = 0 + + def __on_key_return(self): + if self.__preedit_ja_string.is_empty(): + return False + + if self.__convert_mode == CONV_MODE_OFF: +# text, cursor = self.__get_preedit() + text, cursor = self.__get_preedit(True) + self.__commit_string(text) + elif self.__convert_mode == CONV_MODE_ANTHY: + for i, (seg_index, text) in enumerate(self.__segments): + self.__context.commit_segment(i, seg_index) + self.__commit_string(self.__convert_chars) + elif self.__convert_mode == CONV_MODE_PREDICTION: + self.__context.commit_prediction(self.__segments[0][0]) + self.__commit_string(self.__convert_chars) + else: + self.__commit_string(self.__convert_chars) + + return True + + def __on_key_escape(self): + if self.__preedit_ja_string.is_empty(): + return False + self.__reset() + self.__invalidate() + return True + + def __on_key_back_space(self): + if self.__preedit_ja_string.is_empty(): + return False + + if self.__convert_mode != CONV_MODE_OFF: + if self.__lookup_table_visible: + if self.__lookup_table.get_number_of_candidates() > 0: + self.__lookup_table.set_cursor_pos(0) + candidate = self.__lookup_table.get_candidate(0).get_text() + self.__segments[self.__cursor_pos] = 0, candidate + self.__lookup_table_visible = False + elif self.__segments[self.__cursor_pos][0] != \ + NTH_UNCONVERTED_CANDIDATE: + buf = self.__context.get_segment(self.__cursor_pos, + NTH_UNCONVERTED_CANDIDATE) + self.__segments[self.__cursor_pos] = \ + NTH_UNCONVERTED_CANDIDATE, buf + #elif self._chk_mode('25'): + ''' + # FIXME: Delete the last char in the active segment. + # + # If we are able to delete a char in the active segment, + # we also should be able to add a char in the active segment. + # Currently plain preedit, no segment mode, i.e. + # using self.__preedit_ja_string, can delete or add a char + # but anthy active segoment mode, i.e. + # using self.__segments, can not delete or add a char. + # Deleting a char could be easy here but adding a char is + # difficult because we need to update both self.__segments + # and self.__preedit_ja_string but self.__preedit_ja_string + # has no segment. To convert self.__segments to + # self.__preedit_ja_string, we may use the reconvert mode + # but no idea to convert keyvals to hiragana + # in self__on_key_common() with multiple key typings. + + # Delete a char in the active segment + all_text = '' + nr_segments = self.__context.get_nr_segments() + for i in xrange(0, nr_segments): + buf = self.__context.get_segment(i, + NTH_UNCONVERTED_CANDIDATE) + text = buf + if i == self.__cursor_pos and len(text) > 0: + text = text[:len(text) - 1] + all_text += text + + if all_text == '': + return + + # Set self.__preedit_ja_string by anthy context. + self.__preedit_ja_string = jastring.JaString(Engine.__typing_mode, + self.__latin_with_shift) + self.__convert_chars = self.__normalize_preedit(all_text) + for i in xrange(0, len(self.__convert_chars)): + keyval = self.__convert_chars[i] + self.__preedit_ja_string.insert(chr(ord(keyval))) + self.__context.set_string(self.__convert_chars) + + # Set self.__segments by anty context + # for editable self.__segments, + # save NTH_UNCONVERTED_CANDIDATE + nr_segments = self.__context.get_nr_segments() + if self.__cursor_pos >= nr_segments and \ + nr_segments > 0: + self.__cursor_pos = nr_segments - 1 + for i in xrange(self.__cursor_pos, nr_segments): + if i == self.__cursor_pos: + index = NTH_UNCONVERTED_CANDIDATE + else: + index = 0 + buf = self.__context.get_segment(i, + index) + text = buf + self.__segments[i] = index, text + + # Update self.__lookup_table + self.__fill_lookup_table() + ''' + else: + self.__end_convert() + else: + self.__preedit_ja_string.remove_before() + + self.__invalidate() + return True + + def __on_key_delete(self): + if self.__preedit_ja_string.is_empty(): + return False + + if self.__convert_mode != CONV_MODE_OFF: + self.__end_convert() + else: + self.__preedit_ja_string.remove_after() + + self.__invalidate() + return True + + '''def __on_key_hiragana_katakana(self): + if self.__convert_mode == CONV_MODE_ANTHY: + self.__end_anthy_convert() + + if Engine.__input_mode >= INPUT_MODE_HIRAGANA and \ + Engine.__input_mode < INPUT_MODE_HALF_WIDTH_KATAKANA: + Engine.__input_mode += 1 + else: + Engine.__input_mode = INPUT_MODE_HIRAGANA + + modes = { INPUT_MODE_HIRAGANA: 'あ', + INPUT_MODE_KATAKANA: 'ア', + INPUT_MODE_HALF_WIDTH_KATAKANA: '_ア' } + + prop = self.__prop_dict[u'InputMode'] + label = modes[Engine.__input_mode] + prop.set_label(IBus.Text.new_from_string(label)) + self.update_property(prop) + + self.__invalidate() + return True''' + + '''def __on_key_muhenka(self): + if self.__preedit_ja_string.is_empty(): + return False + + if self.__convert_mode == CONV_MODE_ANTHY: + self.__end_anthy_convert() + + new_mode = CONV_MODE_HIRAGANA + if self.__convert_mode < CONV_MODE_WIDE_LATIN_3 and \ + self.__convert_mode >= CONV_MODE_HIRAGANA : + self.__convert_mode += 1 + else: + self.__convert_mode = CONV_MODE_HIRAGANA + + self.__invalidate() + + return True''' + + '''def __on_key_henkan(self): + if self.__preedit_ja_string.is_empty(): + return False + if self.__convert_mode != CONV_MODE_ANTHY: + self.__begin_anthy_convert() + self.__invalidate() + elif self.__convert_mode == CONV_MODE_ANTHY: + self.__lookup_table_visible = True + self.do_cursor_down() + return True''' + + '''def __on_key_space(self, wide=False): + if Engine.__input_mode == INPUT_MODE_WIDE_LATIN or wide: + # Input Wide space U+3000 + wide_char = symbol_rule[chr(IBus.KEY_space)] + self.__commit_string(wide_char) + return True + + if self.__preedit_ja_string.is_empty(): + if Engine.__input_mode in (INPUT_MODE_HIRAGANA, INPUT_MODE_KATAKANA): + # Input Wide space U+3000 + wide_char = symbol_rule[chr(IBus.KEY_space)] + self.__commit_string(wide_char) + return True + else: + # Input Half space U+0020 + self.__commit_string(chr(IBus.KEY_space)) + return True + + if self.__convert_mode != CONV_MODE_ANTHY: + self.__begin_anthy_convert() + self.__invalidate() + elif self.__convert_mode == CONV_MODE_ANTHY: + self.__lookup_table_visible = True + self.do_cursor_down() + return True''' + + def __on_key_up(self): + if self.__preedit_ja_string.is_empty(): + return False + self.__lookup_table_visible = True + self.do_cursor_up() + return True + + def __on_key_down(self): + if self.__preedit_ja_string.is_empty(): + return False + self.__lookup_table_visible = True + self.do_cursor_down() + return True + + def __on_key_page_up(self): + if self.__preedit_ja_string.is_empty(): + return False + if self.__lookup_table_visible == True: + self.do_page_up() + return True + + def __on_key_page_down(self): + if self.__preedit_ja_string.is_empty(): + return False + if self.__lookup_table_visible == True: + self.do_page_down() + return True + + '''def __on_key_left(self): + if self.__preedit_ja_string.is_empty(): + return False + + if self.__convert_mode == CONV_MODE_OFF: + self.__preedit_ja_string.move_cursor(-1) + self.__invalidate() + return True + + if self.__convert_mode != CONV_MODE_ANTHY: + return True + + if self.__cursor_pos == 0: + return True + self.__cursor_pos -= 1 + self.__lookup_table_visible = False + self.__fill_lookup_table() + self.__invalidate() + return True''' + + def __on_key_right(self): + if self.__preedit_ja_string.is_empty(): + return False + + if self.__convert_mode == CONV_MODE_OFF: + self.__preedit_ja_string.move_cursor(1) + self.__invalidate() + return True + + if self.__convert_mode != CONV_MODE_ANTHY: + return True + + if self.__cursor_pos + 1 >= len(self.__segments): + return True + + self.__cursor_pos += 1 + self.__lookup_table_visible = False + self.__fill_lookup_table() + self.__invalidate() + return True + + def __on_key_number(self, keyval): + if self.__convert_mode != CONV_MODE_ANTHY: + return False + if not self.__lookup_table_visible: + return False + + if keyval == IBus.KEY_0: + keyval = IBus.KEY_9 + 1 + index = keyval - IBus.KEY_1 + + return self.__on_candidate_index_in_page(index) + + def __on_key_conv(self, mode): + if self.__preedit_ja_string.is_empty(): + return False + + if self.__convert_mode == CONV_MODE_ANTHY: + self.__end_anthy_convert() + + if mode == 0 or mode == 1: + if self.__convert_mode == CONV_MODE_HIRAGANA + mode: + return True + self.__convert_mode = CONV_MODE_HIRAGANA + mode + elif mode == 2: + if self.__convert_mode == CONV_MODE_HALF_WIDTH_KATAKANA: + return True + self.__convert_mode = CONV_MODE_HALF_WIDTH_KATAKANA + elif mode == 3: + if CONV_MODE_WIDE_LATIN_0 <= self.__convert_mode <= CONV_MODE_WIDE_LATIN_3: + self.__convert_mode += 1 + if self.__convert_mode > CONV_MODE_WIDE_LATIN_3: + self.__convert_mode = CONV_MODE_WIDE_LATIN_1 + else: + self.__convert_mode = CONV_MODE_WIDE_LATIN_0 + elif mode == 4: + if CONV_MODE_LATIN_0 <= self.__convert_mode <= CONV_MODE_LATIN_3: + self.__convert_mode += 1 + if self.__convert_mode > CONV_MODE_LATIN_3: + self.__convert_mode = CONV_MODE_LATIN_1 + else: + self.__convert_mode = CONV_MODE_LATIN_0 + else: + printerr('Unkown convert mode (%d)!' % mode) + return False + self.__invalidate() + return True + + def __on_key_common(self, keyval, state=0): + + # If use-system-layout is FALSE in ibus 1.4.y or lower, + # ibus converts the keymap and ibus-anthy needed to use + # self.__commit_string + # ibus 1.5.y uses XKB directly so Latin mode can return FALSE. + if Engine.__input_mode == INPUT_MODE_LATIN: + return False + + elif Engine.__input_mode == INPUT_MODE_WIDE_LATIN: + # Input Wide Latin chars + char = chr(keyval) + wide_char = None#symbol_rule.get(char, None) + if wide_char == None: + wide_char = unichar_half_to_full(char) + self.__commit_string(wide_char) + return True + + # Input Japanese + if Engine.__segment_mode & SEGMENT_IMMEDIATE: + # Commit nothing + pass + elif self.__convert_mode == CONV_MODE_ANTHY: + for i, (seg_index, text) in enumerate(self.__segments): + self.__context.commit_segment(i, seg_index) + self.__commit_string(self.__convert_chars) + elif self.__convert_mode != CONV_MODE_OFF: + self.__commit_string(self.__convert_chars) + + # 'n' + '\'' == 'nn' in romaji + if (keyval >= ord('A') and keyval <= ord('Z')) or \ + (keyval >= ord('a') and keyval <= ord('z')): + shift = (state & IBus.ModifierType.SHIFT_MASK) != 0 + else: + shift = False + self.__preedit_ja_string.set_shift(shift) + self.__preedit_ja_string.insert(chr(keyval)) + if Engine.__segment_mode & SEGMENT_IMMEDIATE: + self.__begin_anthy_convert() + self.__invalidate() + return True + +#======================================================================= + @classmethod + def CONFIG_RELOADED(cls, bus): + if config.DEBUG: + print('RELOADED') + if not cls.__prefs: + cls.__prefs = AnthyPrefs(bus) + cls._init_prefs() + + cls.__keybind = cls._mk_keybind() + + jastring.JaString.SET_PREFS(cls.__prefs) + + @classmethod + def CONFIG_VALUE_CHANGED(cls, bus, section, name, variant): + if config.DEBUG: + print('VALUE_CHAMGED =', section, name, variant) + + if not section.startswith('engine/anthy'): + # This value is used for IBus.config.set_value only. + return + + # The key was deleted by dconf. + # test case: update /desktop/ibus/engine/anthy/thumb/ls + # and reset the key with dconf direclty. + if variant.get_type_string() == '()': + cls.__prefs.undo_item(section, name) + return + + value = cls.__prefs.variant_to_value(variant) + base_sec = section[len(cls.__prefs._prefix) + 1:] + sec = cls._get_shortcut_type() + if base_sec == sec: + cmd = '_Engine__cmd_' + name + old = cls.__prefs.get_value(sec, name) + value = value if value != [''] else [] + for s in set(old).difference(value): + cls.__keybind.get(cls._s_to_key(s), []).remove(cmd) + + keys = cls.__prefs.keys(sec) + for s in set(value).difference(old): + cls.__keybind.setdefault(cls._s_to_key(s), []).append(cmd) + cls.__keybind.get(cls._s_to_key(s)).sort( + key = lambda a: keys.index(a[13:])) + cls.__prefs.set_value(sec, name, value) + elif base_sec == 'common': + cls.__prefs.set_value(base_sec, name, value) + if name == 'shortcut_type': + cls.__keybind = cls._mk_keybind() + if name == 'latin_with_shift': + cls.__latin_with_shift = value + jastring.JaString.RESET(cls.__prefs, base_sec, name, value) + elif base_sec.startswith('kana_typing_rule'): + jastring.JaString.RESET(cls.__prefs, base_sec, name, value) + + @classmethod + def _init_prefs(cls): + prefs = cls.__prefs + value = prefs.get_value('common', 'latin_with_shift') + cls.__latin_with_shift = value + + @classmethod + def _mk_keybind(cls): + keybind = {} + sec = cls._get_shortcut_type() + for k in cls.__prefs.keys(sec): + cmd = '_Engine__cmd_' + k + for s in cls.__prefs.get_value(sec, k): + keybind.setdefault(cls._s_to_key(s), []).append(cmd) + return keybind + + @classmethod + def _get_shortcut_type(cls): + try: + t = 'shortcut/' + cls.__prefs.get_value('common', 'shortcut_type') + except: + t = 'shortcut/default' + return t + + @classmethod + def _s_to_key(cls, s): + keyval = IBus.keyval_from_name(s.split('+')[-1]) + s = s.lower() + state = ('shift+' in s and IBus.ModifierType.SHIFT_MASK or 0) | ( + 'ctrl+' in s and IBus.ModifierType.CONTROL_MASK or 0) | ( + 'alt+' in s and IBus.ModifierType.MOD1_MASK or 0) + return cls._mk_key(keyval, state) + + @classmethod + def _reset_thumb(cls): + if cls.__thumb == None: + import thumb + cls.__thumb = thumb.ThumbShiftKeyboard(cls.__prefs) + + else: + cls.__thumb.reset() + + @staticmethod + def _mk_key(keyval, state): + if state & (IBus.ModifierType.CONTROL_MASK | IBus.ModifierType.MOD1_MASK): + if keyval < 0xff and \ + chr(keyval) in '!"#$%^\'()*+,-./:;<=>?@[\]^_`{|}~': + state |= IBus.ModifierType.SHIFT_MASK + elif IBus.KEY_a <= keyval <= IBus.KEY_z: + keyval -= (IBus.KEY_a - IBus.KEY_A) + + return repr([int(state), int(keyval)]) + + def __process_key_event(self, obj, keyval, keycode, state): + try: + return self.__process_key_event_internal2(keyval, keycode, state) + except: + import traceback + traceback.print_exc() + return False + + def __process_key_event_thumb(self, keyval, keycode, state): + if self.__thumb == None: + self._reset_thumb() + + def on_timeout(keyval): + if self._MM: + insert(self.__thumb.get_char(self._MM)[self._SS]) + else: + cmd_exec([0, RS(), LS()][self._SS]) + self._H = None + + def start(t): + self._H = GLib.timeout_add(t, on_timeout, keyval) + + def stop(): + if self._H: + GLib.source_remove(self._H) + self._H = None + return True + return False + + def insert(keyval): + try: + self._MM = self._SS = 0 + ret = self.__on_key_common(ord(keyval)) + if (keyval in ',.、。' and + self.__prefs.get_value('common', 'behavior_on_period')): + return self.__cmd_convert(keyval, state) + return ret + except: + pass + + def cmd_exec(keyval, state=0): + key = self._mk_key(keyval, state) + for cmd in self.__keybind.get(key, []): + if config.DEBUG: + print('cmd =', cmd) + try: + if getattr(self, cmd)(keyval, state): + return True + except: + printerr('Unknown command = %s' % cmd) + return False + + def RS(): + return self.__thumb.get_rs() + + def LS(): + return self.__thumb.get_ls() + + def T1(): + return self.__thumb.get_t1() + + def T2(): + return self.__thumb.get_t2() + + state = state & (IBus.ModifierType.SHIFT_MASK | + IBus.ModifierType.CONTROL_MASK | + IBus.ModifierType.MOD1_MASK | + IBus.ModifierType.RELEASE_MASK) + + if keyval in KP_Table and self.__prefs.get_value('common', + 'ten_key_mode'): + keyval = KP_Table[keyval] + + if state & IBus.ModifierType.RELEASE_MASK: + if keyval == self._MM: + if stop(): + insert(self.__thumb.get_char(self._MM)[self._SS]) + self._MM = 0 + elif (1 if keyval == RS() else 2) == self._SS: + if stop(): + cmd_exec([0, RS(), LS()][self._SS]) + self._SS = 0 + if keyval in [RS(), LS()]: + self._RSS = 0 + elif keyval == self._RMM: + self._RMM = 0 + else: + if keyval in [LS(), RS()] and state == 0: + if self._SS: + stop() + cmd_exec([0, RS(), LS()][self._SS]) + self._SS = 1 if keyval == RS() else 2 + start(T1()) + elif self._MM: + stop() + self._RMM = self._MM + self._RSS = 1 if keyval == RS() else 2 + insert(self.__thumb.get_char(self._MM)[1 if keyval == RS() else 2]) + else: + if self._RSS == (1 if keyval == RS() else 2): + if self._RMM: + insert(self.__thumb.get_char(self._RMM)[self._RSS]) + else: + self._SS = 1 if keyval == RS() else 2 + start(T1()) + elif keyval in self.__thumb.get_chars() and state == 0: + if self._MM: + stop() + insert(self.__thumb.get_char(self._MM)[self._SS]) + start(T2()) + self._MM = keyval + elif self._SS: + stop() + self._RMM = keyval + self._RSS = self._SS + insert(self.__thumb.get_char(keyval)[self._SS]) + else: + if self._RMM == keyval: + if self._RSS: + insert(self.__thumb.get_char(self._RMM)[self._RSS]) + else: + if cmd_exec(keyval, state): + return True + start(T2()) + self._MM = keyval + else: + if self._MM: + stop() + insert(self.__thumb.get_char(self._MM)[self._SS]) + elif self._SS: + stop() + cmd_exec([0, RS(), LS()][self._SS]) + if cmd_exec(keyval, state): + return True + elif 0x21 <= keyval <= 0x7e and state & \ + (IBus.ModifierType.CONTROL_MASK | IBus.ModifierType.MOD1_MASK) == 0: + if state & IBus.ModifierType.SHIFT_MASK: + insert(self.__thumb.get_shift_char(keyval, chr(keyval))) + elif self._SS == 0: + insert(chr(keyval)) + else: + if not self.__preedit_ja_string.is_empty(): + return True + return False + return True + + def __process_key_event_internal2(self, keyval, keycode, state): + if self.__has_input_purpose and \ + self.__input_purpose == IBus.InputPurpose.PASSWORD: + return False + + if Engine.__typing_mode == jastring.TYPING_MODE_THUMB_SHIFT and \ + Engine.__input_mode not in [INPUT_MODE_LATIN, INPUT_MODE_WIDE_LATIN]: + return self.__process_key_event_thumb(keyval, keycode, state) + + is_press = (state & IBus.ModifierType.RELEASE_MASK) == 0 + + state = state & (IBus.ModifierType.SHIFT_MASK | + IBus.ModifierType.CONTROL_MASK | + IBus.ModifierType.MOD1_MASK) + + # ignore key release events + if not is_press: + return False + + if keyval in KP_Table and self.__prefs.get_value('common', + 'ten_key_mode'): + keyval = KP_Table[keyval] + + key = self._mk_key(keyval, state) + for cmd in self.__keybind.get(key, []): + if config.DEBUG: + print('cmd =', cmd) + try: + if getattr(self, cmd)(keyval, state): + return True + except: + printerr('Unknown command = %s' % cmd) + + # If input mode is not LATIN, eat Ctrl+Shift+u + hex_mod_mask = IBus.ModifierType.SHIFT_MASK | \ + IBus.ModifierType.CONTROL_MASK + if Engine.__input_mode != INPUT_MODE_LATIN and \ + keyval == IBus.KEY_U and \ + state & hex_mod_mask == hex_mod_mask: + return True + + if state & (IBus.ModifierType.CONTROL_MASK | IBus.ModifierType.MOD1_MASK): + return False + + if (IBus.KEY_exclam <= keyval <= IBus.KEY_asciitilde or + keyval == IBus.KEY_yen): + if Engine.__typing_mode == jastring.TYPING_MODE_KANA: + if keyval == IBus.KEY_0 and state == IBus.ModifierType.SHIFT_MASK: + keyval = IBus.KEY_asciitilde + elif keyval == IBus.KEY_backslash and keycode in [132-8, 133-8]: + keyval = IBus.KEY_yen + ret = self.__on_key_common(keyval, state) + if (Engine.__input_mode != INPUT_MODE_LATIN and + chr(keyval) in ',.' and + self.__prefs.get_value('common', 'behavior_on_period')): + return self.__cmd_convert(keyval, state) + return ret + else: + if not self.__preedit_ja_string.is_empty(): + return True + return False + + def _chk_mode(self, mode): + if '0' in mode and self.__preedit_ja_string.is_empty(): + return True + + if self.__convert_mode == CONV_MODE_OFF: + if '1' in mode and not self.__preedit_ja_string.is_empty(): + return True + elif self.__convert_mode == CONV_MODE_ANTHY: + if '2' in mode and not self.__lookup_table_visible: + return True + elif self.__convert_mode == CONV_MODE_PREDICTION: + if '3' in mode and not self.__lookup_table_visible: + return True + else: + if '4' in mode: + return True + + if '5' in mode and self.__lookup_table_visible: + return True + + return False + + def __get_quoted_id(self, file): + id = file + has_mbcs = False + + for i in range(0, len(id)): + if ord(id[i]) >= 0x7f: + has_mbcs = True + break + if has_mbcs: + id = str(binascii.hexlify(id.encode()), 'ascii') + + if id.find('/') >=0: + id = id[id.rindex('/') + 1:] + if id.find('.') >=0: + id = id[:id.rindex('.')] + + if id.startswith('0x'): + id = str(binascii.hexlify(id.encode()), 'ascii') + has_mbcs = True + if has_mbcs: + id = '0x' + id + return id + + def __get_dict_id_from_file(self, file): + return self.__get_quoted_id(file) + + def __link_dict_file_with_id(self, file, id, link_mode): + if id == None: + return + if link_mode == LINK_DICT_EMBEDDED: + directory = get_userhome() + '/.anthy/' + IMPORTED_EMBEDDED_DICT_DIR + name = IMPORTED_EMBEDDED_DICT_PREFIX + id + elif link_mode == LINK_DICT_SINGLE: + directory = get_userhome() + '/.anthy' + name = IMPORTED_SINGLE_DICT_PREFIX + id + else: + return + if path.exists(directory): + if not path.isdir(directory): + printerr(directory + ' is not a directory') + return + else: + os.makedirs(directory, 0o700) + backup_dir = os.getcwd() + os.chdir(directory) + if path.lexists(directory + '/' + name): + if path.islink(directory + '/' + name): + printerr('Removing ' + name) + os.unlink(directory + '/' + name) + else: + alternate = name + str(os.getpid()) + printerr('Moving ' + name + ' to ' + alternate) + os.rename(name, alternate) + os.symlink(file, directory + '/' + name) + if backup_dir != None: + os.chdir(backup_dir) + + def __remove_dict_file_with_id(self, file, id, link_mode): + if id == None: + return + if link_mode == LINK_DICT_EMBEDDED: + directory = get_userhome() + '/.anthy/' + IMPORTED_EMBEDDED_DICT_DIR + name = IMPORTED_EMBEDDED_DICT_PREFIX + id + elif link_mode == LINK_DICT_SINGLE: + directory = get_userhome() + '/.anthy' + name = IMPORTED_SINGLE_DICT_PREFIX + id + else: + return + if path.exists(directory): + if not path.isdir(directory): + printerr(directory + ' is not a directory') + return + backup_dir = os.getcwd() + os.chdir(directory) + if path.lexists(directory + '/' + name): + os.unlink(directory + '/' + name) + if backup_dir != None: + os.chdir(backup_dir) + + def __link_dict_file(self, file): + if not path.exists(file): + printerr(file + ' does not exist') + return False + id = self.__get_dict_id_from_file(file) + section = 'dict/file/' + id + if section not in self.__prefs.sections(): + self.__fetch_dict_values(section) + if self.__prefs.get_value(section, 'embed'): + self.__link_dict_file_with_id(file, id, LINK_DICT_EMBEDDED) + if self.__prefs.get_value(section, 'single'): + self.__link_dict_file_with_id(file, id, LINK_DICT_SINGLE) + return True + + def __remove_dict_file(self, file): + id = self.__get_dict_id_from_file(file) + section = 'dict/file/' + id + if section not in self.__prefs.sections(): + self.__fetch_dict_values(section) + if self.__prefs.get_value(section, 'embed'): + self.__remove_dict_file_with_id(file, id, LINK_DICT_EMBEDDED) + if self.__prefs.get_value(section, 'single'): + self.__remove_dict_file_with_id(file, id, LINK_DICT_SINGLE) + + def __set_dict_files_value(self, base_sec, name, value): + if name == 'files': + str_list = [] + for file in value: + str_list.append(self.__prefs.str(file)) + old_files = self.__prefs.get_value(base_sec, name) + for file in old_files: + if file in str_list: + continue + self.__remove_dict_file(file) + for file in str_list: + if file in old_files: + continue + self.__link_dict_file(file) + self.__prefs.set_value(base_sec, name, str_list) + else: + self.__prefs.set_value(base_sec, name, value) + + def __fetch_dict_values(self, section): + self.__prefs.set_new_section(section) + self.__prefs.set_new_key(section, 'short_label') + self.__prefs.set_no_key_warning(True) + self.__prefs.fetch_item(section, 'short_label') + self.__prefs.set_new_key(section, 'long_label') + self.__prefs.fetch_item(section, 'long_label') + self.__prefs.set_new_key(section, 'embed') + self.__prefs.fetch_item(section, 'embed') + self.__prefs.set_new_key(section, 'single') + self.__prefs.fetch_item(section, 'single') + self.__prefs.set_new_key(section, 'reverse') + self.__prefs.fetch_item(section, 'reverse') + self.__prefs.set_no_key_warning(False) + + def __config_value_changed_cb(self, ibus_config, section, name, variant): + if config.DEBUG: + print('VALUE_CHAMGED =', section, name, variant) + + if not section.startswith('engine/anthy'): + # This value is used for IBus.config.set_value only. + return + + # The key was deleted by dconf. + # test case: update /desktop/ibus/engine/anthy/thumb/ls + # and reset the key with dconf direclty. + if variant.get_type_string() == '()': + self.__prefs.undo_item(section, name) + return + + value = self.__prefs.variant_to_value(variant) + base_sec = section[len(self.__prefs._prefix) + 1:] + sec = self._get_shortcut_type() + + if base_sec == 'thumb': + self.__prefs.set_value(base_sec, name, value) + self._reset_thumb() + elif base_sec == 'dict': + self.__set_dict_files_value(base_sec, name, value) + self.__set_dict_mode_props(self.__prop_list, True) + elif base_sec.startswith('dict/file/'): + if base_sec not in self.__prefs.sections(): + self.__fetch_dict_values(base_sec) + self.__prefs.set_value(base_sec, name, value) + self.__set_dict_mode_props(self.__prop_list, True) + elif base_sec: + self.__prefs.set_value(base_sec, name, value) + else: + self.__prefs.set_value(section, name, value) + + #mod_keys + def __set_input_mode(self, mode): + self.__input_mode_activate(mode, IBus.PropState.CHECKED) + self.__reset() + self.__invalidate() + + return True + + def __unset_current_input_mode(self): + modes = { + INPUT_MODE_HIRAGANA: 'InputMode.Hiragana', + INPUT_MODE_KATAKANA: 'InputMode.Katakana', + INPUT_MODE_HALF_WIDTH_KATAKANA: 'InputMode.HalfWidthKatakana', + INPUT_MODE_LATIN: 'InputMode.Latin', + INPUT_MODE_WIDE_LATIN: 'InputMode.WideLatin' + } + self.__input_mode_activate(modes[Engine.__input_mode], + IBus.PropState.UNCHECKED) + + def __cmd_on_off(self, keyval, state): + # ibus 1.5 or later needs to send UNCHECKED + self.__unset_current_input_mode() + if Engine.__input_mode == INPUT_MODE_LATIN: + return self.__set_input_mode('InputMode.Hiragana') + else: + return self.__set_input_mode('InputMode.Latin') + + def __cmd_circle_input_mode(self, keyval, state): + modes = { + INPUT_MODE_HIRAGANA: 'InputMode.Katakana', + INPUT_MODE_KATAKANA: 'InputMode.HalfWidthKatakana', + INPUT_MODE_HALF_WIDTH_KATAKANA: 'InputMode.Latin', + INPUT_MODE_LATIN: 'InputMode.WideLatin', + INPUT_MODE_WIDE_LATIN: 'InputMode.Hiragana' + } + # ibus 1.5 or later needs to send UNCHECKED + self.__unset_current_input_mode() + return self.__set_input_mode(modes[Engine.__input_mode]) + + def __cmd_circle_kana_mode(self, keyval, state): + modes = { + INPUT_MODE_HIRAGANA: 'InputMode.Katakana', + INPUT_MODE_KATAKANA: 'InputMode.HalfWidthKatakana', + INPUT_MODE_HALF_WIDTH_KATAKANA: 'InputMode.Hiragana', + INPUT_MODE_LATIN: 'InputMode.Hiragana', + INPUT_MODE_WIDE_LATIN: 'InputMode.Hiragana' + } + # ibus 1.5 or later needs to send UNCHECKED + self.__unset_current_input_mode() + return self.__set_input_mode(modes[Engine.__input_mode]) + + def __cmd_latin_mode(self, keyval, state): + # ibus 1.5 or later needs to send UNCHECKED + self.__unset_current_input_mode() + return self.__set_input_mode('InputMode.Latin') + + def __cmd_wide_latin_mode(self, keyval, state): + # ibus 1.5 or later needs to send UNCHECKED + self.__unset_current_input_mode() + return self.__set_input_mode('InputMode.WideLatin') + + def __cmd_hiragana_mode(self, keyval, state): + # ibus 1.5 or later needs to send UNCHECKED + self.__unset_current_input_mode() + return self.__set_input_mode('InputMode.Hiragana') + + def __cmd_katakana_mode(self, keyval, state): + # ibus 1.5 or later needs to send UNCHECKED + self.__unset_current_input_mode() + return self.__set_input_mode('InputMode.Katakana') + + def __cmd_half_katakana(self, keyval, state): + # ibus 1.5 or later needs to send UNCHECKED + self.__unset_current_input_mode() + return self.__set_input_mode('InputMode.HalfWidthKatakana') + +# def __cmd_cancel_pseudo_ascii_mode_key(self, keyval, state): +# pass + + def __unset_current_typing_mode(self): + modes = { + jastring.TYPING_MODE_ROMAJI: 'TypingMode.Romaji', + jastring.TYPING_MODE_KANA: 'TypingMode.Kana', + jastring.TYPING_MODE_THUMB_SHIFT: 'TypingMode.ThumbShift', + } + self.__typing_mode_activate(modes[Engine.__typing_mode], + IBus.PropState.UNCHECKED) + + def __cmd_circle_typing_method(self, keyval, state): + if not self._chk_mode('0'): + return False + + modes = { + jastring.TYPING_MODE_THUMB_SHIFT: 'TypingMode.Romaji', + jastring.TYPING_MODE_KANA: 'TypingMode.ThumbShift', + jastring.TYPING_MODE_ROMAJI: 'TypingMode.Kana', + } + # ibus 1.5 or later needs to send UNCHECKED + self.__unset_current_typing_mode() + self.__typing_mode_activate(modes[Engine.__typing_mode], + IBus.PropState.CHECKED) + return True + + def __cmd_circle_dict_method(self, keyval, state): + if not self._chk_mode('0'): + return False + + # ibus 1.5 or later needs to send UNCHECKED + prop_name = self.__dict_mode_get_prop_name(Engine.__dict_mode) + if prop_name != None: + self.__dict_mode_activate(prop_name, + IBus.PropState.UNCHECKED) + + single_files = self.__get_single_dict_files() + new_mode = Engine.__dict_mode + 1 + if new_mode > len(single_files): + new_mode = 0 + Engine.__dict_mode = new_mode + prop_name = self.__dict_mode_get_prop_name(Engine.__dict_mode) + if prop_name == None: + return False + self.__dict_mode_activate(prop_name, + IBus.PropState.CHECKED) + return True + + #edit_keys + def __cmd_insert_space(self, keyval, state): + if Engine.__input_mode == INPUT_MODE_LATIN: + return False + if (self.__prefs.get_value('common', 'half_width_space') or + Engine.__input_mode == INPUT_MODE_HALF_WIDTH_KATAKANA): + return self.__cmd_insert_half_space(keyval, state) + else: + return self.__cmd_insert_wide_space(keyval, state) + + def __cmd_insert_alternate_space(self, keyval, state): + if Engine.__input_mode == INPUT_MODE_LATIN: + return False + if (self.__prefs.get_value('common', 'half_width_space') or + Engine.__input_mode == INPUT_MODE_HALF_WIDTH_KATAKANA): + return self.__cmd_insert_wide_space(keyval, state) + else: + return self.__cmd_insert_half_space(keyval, state) + + def __cmd_insert_half_space(self, keyval, state): + if not self._chk_mode('0'): + return False + + if not self.__preedit_ja_string.is_empty(): + return False + self.__commit_string(chr(IBus.KEY_space)) + return True + + def __cmd_insert_wide_space(self, keyval, state): + if not self._chk_mode('0'): + return False + + if not self.__preedit_ja_string.is_empty(): + return False + char = chr(IBus.KEY_space) + wide_char = symbol_rule.get(char, None) + if wide_char == None: + wide_char = unichar_half_to_full(char) + self.__commit_string(wide_char) + return True + + def __cmd_backspace(self, keyval, state): + if not self._chk_mode('12345'): + return False + + return self.__on_key_back_space() + + def __cmd_delete(self, keyval, state): + if not self._chk_mode('12345'): + return False + + return self.__on_key_delete() + + def __cmd_commit(self, keyval, state): + if not self._chk_mode('12345'): + return False + + return self.__on_key_return() + + def __cmd_convert(self, keyval, state): + if not self._chk_mode('14'): + return False + + self.__begin_anthy_convert() + self.__invalidate() + + return True + + def __cmd_predict(self, keyval, state): + if not self._chk_mode('14'): + return False + + text, cursor = self.__preedit_ja_string.get_hiragana(True) + + self.__context.set_prediction_string(text) + nr_predictions = self.__context.get_nr_predictions() + +# for i in range(nr_predictions): +# print self.__context.get_prediction(i) + + buf = self.__context.get_prediction(0) + if not buf: + return False + + text = buf + self.__segments.append((0, text)) + + self.__convert_mode = CONV_MODE_PREDICTION + self.__cursor_pos = 0 + self.__fill_lookup_table() + self.__lookup_table_visible = False + self.__invalidate() + + return True + + def __cmd_cancel(self, keyval, state): + return self.__cmd_cancel_all(keyval, state) + + def __cmd_cancel_all(self, keyval, state): + if not self._chk_mode('12345'): + return False + + if self.__convert_mode == CONV_MODE_OFF: + return self.__on_key_escape() + else: + self.__end_convert() + self.__invalidate() + return True + + def __cmd_reconvert(self, keyval, state): + if not self.__preedit_ja_string.is_empty(): + # if user has inputed some chars + return False + + # Move importing Gtk into Engine from the header + # because ibus-engine-anthy --xml does not requre to open X. + try: + from gi.repository import Gtk + clipboard_get = Gtk.Clipboard.get + except ImportError: + clipboard_get = lambda a : None + except RuntimeError: + # Do we support the engine without display? + printerr("Gtk couldn't be initialized") + printerr('Could not open display') + clipboard_get = lambda a : None + + # Use Gtk.Clipboard.request_text() instead of + # Gtk.Clipboard.wait_for_text() because DBus is timed out. + clipboard = clipboard_get ('PRIMARY') + if clipboard: + clipboard.request_text (self.__get_clipboard, CLIPBOARD_RECONVERT) + + return True + + def __update_reconvert(self, clipboard_text): + if clipboard_text == None: + return False + + self.__convert_chars = clipboard_text + for i in range(0, len(self.__convert_chars)): + keyval = self.__convert_chars[i] + self.__preedit_ja_string.insert(chr(ord(keyval))) + + self.__context.set_string(self.__convert_chars) + nr_segments = self.__context.get_nr_segments() + + for i in range(0, nr_segments): + buf = self.__context.get_segment(i, 0) + text = buf + self.__segments.append((0, text)) + + self.__convert_mode = CONV_MODE_ANTHY + self.__cursor_pos = 0 + self.__fill_lookup_table() + self.__lookup_table_visible = False + self.__invalidate() + + return True + +# def __cmd_do_nothing(self, keyval, state): +# return True + + #caret_keys + def __move_caret(self, i): + if not self._chk_mode('1'): + return False + + if self.__convert_mode == CONV_MODE_OFF: + self.__preedit_ja_string.move_cursor( + -len(self.__preedit_ja_string.get_latin()[0]) if i == 0 else + i if i in [-1, 1] else + len(self.__preedit_ja_string.get_latin()[0])) + self.__invalidate() + return True + + return False + + def __cmd_move_caret_first(self, keyval, state): + return self.__move_caret(0) + + def __cmd_move_caret_last(self, keyval, state): + return self.__move_caret(2) + + def __cmd_move_caret_forward(self, keyval, state): + return self.__move_caret(1) + + def __cmd_move_caret_backward(self, keyval, state): + return self.__move_caret(-1) + + #segments_keys + def __select_segment(self, i): + if not self._chk_mode('25'): + return False + + pos = 0 if i == 0 else \ + self.__cursor_pos + i if i in [-1, 1] else \ + len(self.__segments) - 1 + + if 0 <= pos < len(self.__segments) and pos != self.__cursor_pos: + self.__cursor_pos = pos + self.__lookup_table_visible = False + self.__fill_lookup_table() + self.__invalidate() + + return True + + def __cmd_select_first_segment(self, keyval, state): + return self.__select_segment(0) + + def __cmd_select_last_segment(self, keyval, state): + return self.__select_segment(2) + + def __cmd_select_next_segment(self, keyval, state): + return self.__select_segment(1) + + def __cmd_select_prev_segment(self, keyval, state): + return self.__select_segment(-1) + + def __cmd_shrink_segment(self, keyval, state): + if not self._chk_mode('25'): + return False + + if self.__convert_mode == CONV_MODE_ANTHY: + self.__shrink_segment(-1) + return True + + def __cmd_expand_segment(self, keyval, state): + if not self._chk_mode('25'): + return False + + if self.__convert_mode == CONV_MODE_ANTHY: + self.__shrink_segment(1) + return True + + def __move_cursor_char_length(self, length): + if Engine.__input_mode == INPUT_MODE_HIRAGANA: + self.__preedit_ja_string.move_cursor_hiragana_length(length) + elif Engine.__input_mode == INPUT_MODE_KATAKANA: + self.__preedit_ja_string.move_cursor_katakana_length(length) + elif Engine.__input_mode == INPUT_MODE_HALF_WIDTH_KATAKANA: + self.__preedit_ja_string.move_cursor_half_with_katakana_length(length) + else: + self.__preedit_ja_string.move_cursor(length) + + def __commit_nth_segment(self, commit_index, keyval, state): + + if commit_index >= len(self.__segments): + return False + + if self.__convert_mode == CONV_MODE_ANTHY: + for i in range(0, commit_index + 1): + (seg_index, text) = self.__segments[i] + self.commit_text(IBus.Text.new_from_string(text)) + + text, cursor = self.__get_preedit() + commit_length = 0 + for i in range(0, commit_index + 1): + buf = self.__context.get_segment(i, NTH_UNCONVERTED_CANDIDATE) + commit_length += len(buf) + self.__move_cursor_char_length(commit_length - cursor) + for i in range(0, commit_length): + self.__preedit_ja_string.remove_before() + self.__move_cursor_char_length(cursor - commit_length) + + del self.__segments[0:commit_index + 1] + + if len(self.__segments) == 0: + self.__reset() + else: + if self.__cursor_pos > commit_index: + self.__cursor_pos -= (commit_index + 1) + else: + self.__cursor_pos = 0 + text, cursor = self.__get_preedit() + self.__convert_chars = text + self.__context.set_string(text) + + self.__lookup_table.clear() + self.__lookup_table.set_cursor_visible(False) + self.__lookup_table_visible = False + self.update_aux_string('', IBus.AttrList(), + self.__lookup_table_visible) + self.__fill_lookup_table() + self.__invalidate() + self.__update_input_chars() + + return True + + def __cmd_commit_first_segment(self, keyval, state): + return self.__commit_nth_segment(0, keyval, state) + + def __cmd_commit_selected_segment(self, keyval, state): + return self.__commit_nth_segment(self.__cursor_pos, keyval, state) + + #candidates_keys + def __on_candidate_index_in_page(self, index): + if not self._chk_mode('5'): + return False + + if index >= self.__lookup_table.get_page_size(): + return False + cursor_pos = self.__lookup_table.get_cursor_pos() + cursor_in_page = self.__lookup_table.get_cursor_in_page() + real_index = cursor_pos - cursor_in_page + index + if real_index >= self.__lookup_table.get_number_of_candidates(): + return False + self.__lookup_table.set_cursor_pos(real_index) + index = self.__lookup_table.get_cursor_pos() + candidate = self.__lookup_table.get_candidate(index).get_text() + self.__segments[self.__cursor_pos] = index, candidate + self.__lookup_table_visible = False + self.__on_key_right() + self.__invalidate() + return True + + def __cmd_select_first_candidate(self, keyval, state): + return self.__on_candidate_index_in_page(0) + + def __cmd_select_last_candidate(self, keyval, state): + return self.__on_candidate_index_in_page( + self.__lookup_table.get_page_size() - 1) + + def __cmd_select_next_candidate(self, keyval, state): + if not self._chk_mode('235'): + return False + + return self.__on_key_down() + + def __cmd_select_prev_candidate(self, keyval, state): + if not self._chk_mode('235'): + return False + + return self.__on_key_up() + + def __cmd_candidates_page_up(self, keyval, state): + if not self._chk_mode('5'): + return False + + return self.__on_key_page_up() + + def __cmd_candidates_page_down(self, keyval, state): + if not self._chk_mode('5'): + return False + + return self.__on_key_page_down() + + #direct_select_keys + def __select_keyval(self, keyval): + if not self._chk_mode('5'): + return False + + return self.__on_key_number(keyval) + + def __cmd_select_candidates_1(self, keyval, state): + return self.__select_keyval(keyval) + + def __cmd_select_candidates_2(self, keyval, state): + return self.__select_keyval(keyval) + + def __cmd_select_candidates_3(self, keyval, state): + return self.__select_keyval(keyval) + + def __cmd_select_candidates_4(self, keyval, state): + return self.__select_keyval(keyval) + + def __cmd_select_candidates_5(self, keyval, state): + return self.__select_keyval(keyval) + + def __cmd_select_candidates_6(self, keyval, state): + return self.__select_keyval(keyval) + + def __cmd_select_candidates_7(self, keyval, state): + return self.__select_keyval(keyval) + + def __cmd_select_candidates_8(self, keyval, state): + return self.__select_keyval(keyval) + + def __cmd_select_candidates_9(self, keyval, state): + return self.__select_keyval(keyval) + + def __cmd_select_candidates_0(self, keyval, state): + return self.__select_keyval(keyval) + + #convert_keys + def __cmd_convert_to_char_type_forward(self, keyval, state): + if self.__convert_mode == CONV_MODE_ANTHY: + n = self.__segments[self.__cursor_pos][0] + if n == NTH_HIRAGANA_CANDIDATE: + return self.__convert_segment_to_kana(NTH_KATAKANA_CANDIDATE) + elif n == NTH_KATAKANA_CANDIDATE: + return self.__convert_segment_to_kana(NTH_HALFKANA_CANDIDATE) + elif n == NTH_HALFKANA_CANDIDATE: + return self.__convert_segment_to_latin(-100) + elif n == -100: + return self.__convert_segment_to_latin(-101) + else: + return self.__convert_segment_to_kana(NTH_HIRAGANA_CANDIDATE) + + if self.__convert_mode == CONV_MODE_KATAKANA: + return self.__cmd_convert_to_half_katakana(keyval, state) + elif self.__convert_mode == CONV_MODE_HALF_WIDTH_KATAKANA: + return self.__cmd_convert_to_latin(keyval, state) + elif CONV_MODE_LATIN_0 <= self.__convert_mode <= CONV_MODE_LATIN_3: + return self.__cmd_convert_to_wide_latin(keyval, state) + elif (CONV_MODE_WIDE_LATIN_0 <= self.__convert_mode + <= CONV_MODE_WIDE_LATIN_3): + return self.__cmd_convert_to_hiragana(keyval, state) + else: + return self.__cmd_convert_to_katakana(keyval, state) + + def __cmd_convert_to_char_type_backward(self, keyval, state): + if self.__convert_mode == CONV_MODE_ANTHY: + n = self.__segments[self.__cursor_pos][0] + if n == NTH_KATAKANA_CANDIDATE: + return self.__convert_segment_to_kana(NTH_HIRAGANA_CANDIDATE) + elif n == NTH_HALFKANA_CANDIDATE: + return self.__convert_segment_to_kana(NTH_KATAKANA_CANDIDATE) + elif n == -100: + return self.__convert_segment_to_kana(NTH_HALFKANA_CANDIDATE) + elif n == -101: + return self.__convert_segment_to_latin(-100) + else: + return self.__convert_segment_to_latin(-101) + + if self.__convert_mode == CONV_MODE_KATAKANA: + return self.__cmd_convert_to_hiragana(keyval, state) + elif self.__convert_mode == CONV_MODE_HALF_WIDTH_KATAKANA: + return self.__cmd_convert_to_katakana(keyval, state) + elif CONV_MODE_LATIN_0 <= self.__convert_mode <= CONV_MODE_LATIN_3: + return self.__cmd_convert_to_half_katakana(keyval, state) + elif (CONV_MODE_WIDE_LATIN_0 <= self.__convert_mode + <= CONV_MODE_WIDE_LATIN_3): + return self.__cmd_convert_to_latin(keyval, state) + else: + return self.__cmd_convert_to_wide_latin(keyval, state) + + def __convert_segment_to_kana(self, n): + if self.__convert_mode == CONV_MODE_ANTHY and -4 <= n <= -2: + buf = self.__context.get_segment(self.__cursor_pos, n) + self.__segments[self.__cursor_pos] = n, buf + self.__lookup_table_visible = False + self.__invalidate() + return True + + return False + + def __cmd_convert_to_hiragana(self, keyval, state): + if not self._chk_mode('12345'): + return False + + if self.__convert_mode == CONV_MODE_ANTHY: + return self.__convert_segment_to_kana(NTH_HIRAGANA_CANDIDATE) + + return self.__on_key_conv(0) + + def __cmd_convert_to_katakana(self, keyval, state): + if not self._chk_mode('12345'): + return False + + if self.__convert_mode == CONV_MODE_ANTHY: + return self.__convert_segment_to_kana(NTH_KATAKANA_CANDIDATE) + + return self.__on_key_conv(1) + + def __cmd_convert_to_half(self, keyval, state): + if not self._chk_mode('12345'): + return False + + if self.__convert_mode == CONV_MODE_ANTHY: + i, s = self.__segments[self.__cursor_pos] + if i == -101: + return self.__convert_segment_to_latin(-100) + elif i == -100: + return self.__convert_segment_to_latin(-100) + return self.__convert_segment_to_kana(NTH_HALFKANA_CANDIDATE) + + elif CONV_MODE_WIDE_LATIN_0 <= self.__convert_mode <= CONV_MODE_WIDE_LATIN_3: + return self.__on_key_conv(4) + elif CONV_MODE_LATIN_0 <= self.__convert_mode <= CONV_MODE_LATIN_3: + return self.__on_key_conv(4) + return self.__on_key_conv(2) + + def __cmd_convert_to_half_katakana(self, keyval, state): + if not self._chk_mode('12345'): + return False + + if self.__convert_mode == CONV_MODE_ANTHY: + return self.__convert_segment_to_kana(NTH_HALFKANA_CANDIDATE) + + return self.__on_key_conv(2) + + def __convert_segment_to_latin(self, n): + if self.__convert_mode == CONV_MODE_ANTHY and n in [-100, -101]: + start = 0 + for i in range(self.__cursor_pos): + start += len(self.__context.get_segment(i, NTH_UNCONVERTED_CANDIDATE)) + end = start + len(self.__context.get_segment(self.__cursor_pos, NTH_UNCONVERTED_CANDIDATE)) + i, s = self.__segments[self.__cursor_pos] + s2 = self.__preedit_ja_string.get_raw(start, end) + if n == -101: + s2 = ''.join([unichar_half_to_full(c) for c in s2]) + if i == n: + if s == s2.lower(): + s2 = s2.upper() + elif s == s2.upper(): + s2 = s2.capitalize() + elif s == s2 or s == s2.capitalize(): + s2 = s2.lower() + self.__segments[self.__cursor_pos] = n, s2 + self.__lookup_table_visible = False + self.__invalidate() + return True + + return False + + def __cmd_convert_to_wide_latin(self, keyval, state): + if not self._chk_mode('12345'): + return False + + if self.__convert_mode == CONV_MODE_ANTHY: + return self.__convert_segment_to_latin(-101) + + return self.__on_key_conv(3) + + def __cmd_convert_to_latin(self, keyval, state): + if not self._chk_mode('12345'): + return False + + if self.__convert_mode == CONV_MODE_ANTHY: + return self.__convert_segment_to_latin(-100) + + return self.__on_key_conv(4) + + #dictonary_keys + def __cmd_dict_admin(self, keyval, state): + if not self._chk_mode('0'): + return False + + self.__start_dict_admin() + return True + + def __cmd_add_word(self, keyval, state): + if not self._chk_mode('0'): + return False + + self.__start_add_word() + return True + + def __cmd_start_setup(self, keyval, state): + if not self._chk_mode('0'): + return False + + self.__start_setup() + return True + + def __start_dict_admin(self): + command = self.__prefs.get_value('common', 'dict_admin_command') + os.spawnl(os.P_NOWAIT, *command) + + def __start_add_word(self): + command = self.__prefs.get_value('common', 'add_word_command') + os.spawnl(os.P_NOWAIT, *command) + + def __start_setup(self): + if Engine.__setup_pid != 0: + pid, state = os.waitpid(Engine.__setup_pid, os.P_NOWAIT) + if pid != Engine.__setup_pid: + return + Engine.__setup_pid = 0 + setup_cmd = path.join(config.LIBEXECDIR, 'ibus-setup-anthy') + Engine.__setup_pid = os.spawnl(os.P_NOWAIT, setup_cmd, 'ibus-setup-anthy') + + def __cmd_hiragana_for_latin_with_shift(self, keyval, state): + self.__preedit_ja_string.set_hiragana_katakana(True) + diff --git a/engine/python3/factory.py b/engine/python3/factory.py new file mode 100644 index 0000000..8994793 --- /dev/null +++ b/engine/python3/factory.py @@ -0,0 +1,80 @@ +# vim:set et sts=4 sw=4: +# -*- coding: utf-8 -*- +# +# ibus-anthy - The Anthy engine for IBus +# +# Copyright (c) 2007-2008 Peng Huang <shawn.p.huang@gmail.com> +# Copyright (c) 2010-2014 Takao Fujiwara <takao.fujiwara1@gmail.com> +# Copyright (c) 2007-2014 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +import os + +from gi.repository import IBus + +import _config as config +import engine + + +class EngineFactory(IBus.Factory): + FACTORY_PATH = '/com/redhat/IBus/engines/Anthy/Factory' + ENGINE_PATH = '/com/redhat/IBus/engines/Anthy/Engine' + NAME = 'Anthy' + LANG = 'ja' + ICON = config.PKGDATADIR + '/icons/ibus-anthy.png' + AUTHORS = 'Huang Peng <shawn.p.huang@gmail.com>' + CREDITS = 'GPLv2' + + def __init__(self, bus): + self.__bus = bus + engine.Engine.CONFIG_RELOADED(bus) + super(EngineFactory, self).__init__(object_path=IBus.PATH_FACTORY, + connection=bus.get_connection()) + + self.__id = 0 + self.__config = self.__bus.get_config() + + if self.__config != None: + self.__config.connect('value-changed', + self.__config_value_changed_cb) + else: + print('ibus-config is not running or bus address is not correct.', + file=sys.stderr) + + bus.get_connection().signal_subscribe('org.freedesktop.DBus', + 'org.freedesktop.DBus', + 'NameOwnerChanged', + '/org/freedesktop/DBus', + None, + 0, + self.__name_owner_changed_cb, + bus) + + def do_create_engine(self, engine_name): + if engine_name == 'anthy': + self.__id += 1 + return engine.Engine(self.__bus, '%s/%d' % (self.ENGINE_PATH, self.__id)) + + return super(EngineFactory, self).do_create_engine(engine_name) + + def __config_value_changed_cb(self, config, section, name, value): + engine.Engine.CONFIG_VALUE_CHANGED(self.__bus, section, name, value) + + def __name_owner_changed_cb(self, connection, sender_name, object_path, + interface_name, signal_name, parameters, + user_data): + if signal_name == 'NameOwnerChanged': + engine.Engine.CONFIG_RELOADED(self.__bus) diff --git a/engine/python3/ibus-engine-anthy.in b/engine/python3/ibus-engine-anthy.in new file mode 100644 index 0000000..c54b10a --- /dev/null +++ b/engine/python3/ibus-engine-anthy.in @@ -0,0 +1,33 @@ +#!/bin/sh +# +# vim:set noet ts=4: +# +# ibus-anthy - The Anthy engine for IBus +# +# Copyright (c) 2007-2008 Peng Huang <shawn.p.huang@gmail.com> +# Copyright (c) 2010-2013 Takao Fujiwara <takao.fujiwara1@gmail.com> +# Copyright (c) 2007-2013 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +prefix=@prefix@ +datarootdir=@datarootdir@ +exec_prefix=@exec_prefix@ +libexecdir=@libexecdir@ +export IBUS_PREFIX=@prefix@ +export IBUS_ANTHY_PKGDATADIR=@datarootdir@/@PACKAGE@ +export LIBEXECDIR=$libexecdir +exec @ENV_IBUS_ENGINE@ @PYTHON@ @datarootdir@/@PACKAGE@/engine/main.py $@ + diff --git a/engine/python3/jastring.py b/engine/python3/jastring.py new file mode 100644 index 0000000..255799d --- /dev/null +++ b/engine/python3/jastring.py @@ -0,0 +1,304 @@ +# vim:set et sts=4 sw=4: +# -*- coding: utf-8 -*- +# +# ibus-anthy - The Anthy engine for IBus +# +# Copyright (c) 2007-2008 Peng Huang <shawn.p.huang@gmail.com> +# Copyright (c) 2010-2014 Takao Fujiwara <takao.fujiwara1@gmail.com> +# Copyright (c) 2007-2014 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +import romaji +import kana +import thumb + +from segment import unichar_half_to_full + +HalfSymbolTable = {} +for i in range(32, 127): + if not chr(i).isalnum(): + HalfSymbolTable[unichar_half_to_full(chr(i))] = chr(i) + +HalfNumberTable = {} +for i in range(10): + HalfNumberTable[unichar_half_to_full(str(i))] = str(i) + +PeriodTable = {'。': '.', '、': ',', '。': '.', '、': ','} + +SymbolTable = {} +SymbolTable[0] = {'「': '「', '」': '」', '/': '/'} +SymbolTable[1] = {'「': '「', '」': '」', '/': '・'} +SymbolTable[2] = {'「': '[', '」': ']', '/': '/'} +SymbolTable[3] = {'「': '[', '」': ']', '/': '・'} + +TYPING_MODE_ROMAJI, \ +TYPING_MODE_KANA, \ +TYPING_MODE_THUMB_SHIFT = list(range(3)) + +class JaString: + _prefs = None + _mode = TYPING_MODE_ROMAJI + _shift = False + _unshift = False + + def __init__(self, mode=TYPING_MODE_ROMAJI, latin_with_shift=True): + self._init_mode(mode) + if mode == TYPING_MODE_ROMAJI: + romaji.RomajiSegment.SET_LATIN_WITH_SHIFT(latin_with_shift) + + @classmethod + def _init_mode(cls, mode): + cls._mode = mode + cls._shift = False + cls._unshift = False + cls.__cursor = 0 + cls.__segments = list() + if mode == TYPING_MODE_ROMAJI: + romaji.RomajiSegment.INIT_ROMAJI_TYPING_RULE(cls._prefs) + elif mode == TYPING_MODE_KANA: + kana.KanaSegment.INIT_KANA_TYPING_RULE(cls._prefs) + elif mode == TYPING_MODE_THUMB_SHIFT: + thumb.ThumbShiftSegment.INIT_THUMB_TYPING_RULE(cls._prefs) + + @classmethod + def SET_PREFS(cls, prefs): + cls._prefs = prefs + + @classmethod + def RESET(cls, prefs, section, name, value): + cls._prefs = prefs + if section.startswith('kana_typing_rule'): + mode = TYPING_MODE_KANA + kana.KanaSegment.RESET(prefs, section, name, value) + cls._init_mode(mode) + if section == 'common' and name == 'latin_with_shift': + romaji.RomajiSegment.SET_LATIN_WITH_SHIFT(value) + + def set_shift(self, shift): + self._shift = shift + + def set_hiragana_katakana(self, mode): + if mode and self._mode == TYPING_MODE_ROMAJI: + self._unshift = True + + def insert(self, c): + segment_before = None + segment_after = None + new_segments = None + + if self.__cursor >= 1: + segment_before = self.__segments[self.__cursor - 1] + if self.__cursor < len(self.__segments): + segment_after = self.__segments[self.__cursor] + if segment_before and not segment_before.is_finished(): + if type(segment_before) == romaji.RomajiSegment: + new_segments = segment_before.append(c, + self._shift, + self._unshift) + self._unshift = False + else: + new_segments = segment_before.append(c) + elif segment_after and not segment_after.is_finished(): + if type(segment_after) == romaji.RomajiSegment: + new_segments = segment_after.prepend(c, + self._shift, + self._unshift) + self._unshift = False + else: + new_segments = segment_after.prepend(c) + else: + if c != '\0' and c != '': + if self._mode == TYPING_MODE_ROMAJI: + new_segments = [romaji.RomajiSegment(c, + '', + self._shift, + self._unshift)] + self._unshift = False + elif self._mode == TYPING_MODE_KANA: + # kana mode doesn't have shift latin in MS. + new_segments = [kana.KanaSegment(c)] + elif self._mode == TYPING_MODE_THUMB_SHIFT: + new_segments = [thumb.ThumbShiftSegment(c)] + if new_segments: + self.__segments[self.__cursor:self.__cursor] = new_segments + self.__cursor += len(new_segments) + + def remove_before(self): + index = self.__cursor - 1 + if index >= 0: + segment = self.__segments[index] + segment.pop() + if segment.is_empty(): + del self.__segments[index] + self.__cursor = index + return True + + return False + + def remove_after(self): + index = self.__cursor + if index < len(self.__segments): + segment = self.__segments[index] + segment.pop() + if segment.is_empty(): + del self.__segments[index] + return True + + return False + + def get_string(self, type): + pass + + def move_cursor(self, delta): + self.__cursor += delta + if self.__cursor < 0: + self.__cursor = 0 + elif self.__cursor > len(self.__segments): + self.__cursor = len(self.__segments) + + # hiragana segments are not char lengths. + # e.g. 'ya' is 1 segment and 1 char and 'kya' is 1 segment and 2 chars. + def move_cursor_hiragana_length(self, length): + delta = length + if delta < 0: + if self.__cursor >= len(self.__segments): + delta = delta + (self.__cursor - len(self.__segments) + 1) + self.__cursor = len(self.__segments) - 1 + while delta < 0: + text = str(self.__segments[self.__cursor].to_hiragana()) + if len(text) > -delta: + break + delta = delta + len(text) + self.__cursor = self.__cursor - 1 + else: + if self.__cursor >= len(self.__segments): + self.__cursor = len(self.__segments) + return + while delta > 0: + text = str(self.__segments[self.__cursor].to_hiragana()) + if len(text) > delta: + break + delta = delta - len(text) + self.__cursor = self.__cursor + 1 + + def move_cursor_katakana_length(self, length): + delta = length + if delta < 0: + if self.__cursor >= len(self.__segments): + delta = delta + (self.__cursor - len(self.__segments) + 1) + self.__cursor = len(self.__segments) - 1 + while delta < 0: + text = str(self.__segments[self.__cursor].to_katanaka()) + if len(text) > -delta: + break + delta = delta + len(text) + self.__cursor = self.__cursor - 1 + else: + if self.__cursor >= len(self.__segments): + self.__cursor = len(self.__segments) + return + while delta > 0: + text = str(self.__segments[self.__cursor].to_katanaka()) + if len(text) > delta: + break + delta = delta - len(text) + self.__cursor = self.__cursor + 1 + + def move_cursor_half_with_katakana_length(self, length): + delta = length + if delta < 0: + if self.__cursor >= len(self.__segments): + delta = delta + (self.__cursor - len(self.__segments) + 1) + self.__cursor = len(self.__segments) - 1 + while delta < 0: + text = str(self.__segments[self.__cursor].to_half_width_katakana()) + if len(text) > -delta: + break + delta = delta + len(text) + self.__cursor = self.__cursor - 1 + else: + if self.__cursor >= len(self.__segments): + self.__cursor = len(self.__segments) + return + while delta > 0: + text = str(self.__segments[self.__cursor].to_half_width_katakana()) + if len(text) > delta: + break + delta = delta - len(text) + self.__cursor = self.__cursor + 1 + + def _chk_text(self, s): + period = self._prefs.get_value('common', 'period_style') + symbol = self._prefs.get_value('common', 'symbol_style') + half_symbol = self._prefs.get_value('common', 'half_width_symbol') + half_number = self._prefs.get_value('common', 'half_width_number') + ret = '' + for c in s: + c = c if not period else PeriodTable.get(c, c) + # thumb_left + '2' and '/' are different + if self._mode != TYPING_MODE_THUMB_SHIFT: + c = c if not symbol else SymbolTable[symbol].get(c, c) + c = c if not half_symbol else HalfSymbolTable.get(c, c) + c = c if not half_number else HalfNumberTable.get(c, c) + ret += c + return ret + + def get_hiragana(self, commit=False): + conv = lambda s: s.to_hiragana() + R = lambda s: s if not (commit and s[-1:] == 'n') else s[:-1] + 'ん' + text_before = R(''.join(map(conv, self.__segments[:self.__cursor]))) + text_after = R(''.join(map(conv, self.__segments[self.__cursor:]))) + return self._chk_text(text_before + text_after), len(text_before) + + def get_katakana(self, commit=False): + conv = lambda s: s.to_katakana() + R = lambda s: s if not (commit and s[-1:] == 'n') else s[:-1] + 'ン' + text_before = R(''.join(map(conv, self.__segments[:self.__cursor]))) + text_after = R(''.join(map(conv, self.__segments[self.__cursor:]))) + return self._chk_text(text_before + text_after), len(text_before) + + def get_half_width_katakana(self, commit=False): + conv = lambda s: s.to_half_width_katakana() + R = lambda s: s if not (commit and s[-1:] == 'n') else s[:-1] + 'ン' + text_before = R(''.join(map(conv, self.__segments[:self.__cursor]))) + text_after = R(''.join(map(conv, self.__segments[self.__cursor:]))) + return self._chk_text(text_before + text_after), len(text_before) + + def get_latin(self): + conv = lambda s: s.to_latin() + text_before = ''.join(map(conv, self.__segments[:self.__cursor])) + text_after = ''.join(map(conv, self.__segments[self.__cursor:])) + return text_before + text_after, len(text_before) + + def get_wide_latin(self): + conv = lambda s: s.to_wide_latin() + text_before = ''.join(map(conv, self.__segments[:self.__cursor])) + text_after = ''.join(map(conv, self.__segments[self.__cursor:])) + return text_before + text_after, len(text_before) + + def is_empty(self): + return all([s.is_empty() for s in self.__segments]) + + def get_raw(self, start, end): + i = 0 + r = '' + for s in self.__segments: + if i >= end: + break + elif start <= i: + r += s.to_latin() + i += len(s.to_hiragana()) + return r diff --git a/engine/python3/kana.py b/engine/python3/kana.py new file mode 100644 index 0000000..10e1b96 --- /dev/null +++ b/engine/python3/kana.py @@ -0,0 +1,170 @@ +# vim:set et sts=4 sw=4: +# -*- coding: utf-8 -*- +# +# ibus-anthy - The Anthy engine for IBus +# +# Copyright (c) 2007-2008 Peng Huang <shawn.p.huang@gmail.com> +# Copyright (c) 2010-2014 Takao Fujiwara <takao.fujiwara1@gmail.com> +# Copyright (c) 2007-2014 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +import sys + +from tables import * +import segment + +_UNFINISHED_HIRAGANA = set('かきくけこさしすせそたちつてとはひふへほ') + +class KanaSegment(segment.Segment): + _prefs = None + _kana_typing_rule_section = None + _kana_voiced_consonant_rule = None + + def __init__(self, enchars='', jachars=''): + if not jachars: + jachars = self.__get_kana_typing_rule(enchars, '') + super(KanaSegment, self).__init__(enchars, jachars) + + @classmethod + def INIT_KANA_TYPING_RULE(cls, prefs): + cls._prefs = prefs + if prefs == None: + cls._kana_typing_rule_section = None + return + if cls._kana_typing_rule_section == None: + cls._init_kana_typing_method() + if cls._kana_voiced_consonant_rule == None and \ + cls._kana_typing_rule_section != None: + cls._init_kana_voiced_consonant_rule() + + @classmethod + def _init_kana_typing_method(cls, method=None): + prefs = cls._prefs + if method == None: + method = prefs.get_value('kana_typing_rule', 'method') + if method == None: + method = 'jp' + cls._kana_typing_rule_section = 'kana_typing_rule/' + method + if cls._kana_typing_rule_section not in prefs.sections(): + cls._kana_typing_rule_section = None + + @classmethod + def _init_kana_voiced_consonant_rule(cls): + prefs = cls._prefs + # Create kana_voiced_consonant_rule dynamically. + # E.g. 't' + '@' on jp kbd becomes Hiragana GA + # 't' + '[' on us kbd becomes Hiragana GA + # If the customized table provides U+309b with other chars, + # it needs to be detected dynamically. + cls._kana_voiced_consonant_rule = {} + section = cls._kana_typing_rule_section + for gkey in prefs.keys(section): + value = prefs.get_value(section, gkey) + key = prefs.typing_from_config_key(gkey) + if key == '': + continue + if value == chr(0x309b): + for no_voiced, voiced in \ + list(kana_voiced_consonant_no_rule.items()): + rule = no_voiced + key + cls._kana_voiced_consonant_rule[rule] = voiced + if value == chr(0x309c): + for no_voiced, voiced in \ + list(kana_semi_voiced_consonant_no_rule.items()): + rule = no_voiced + key + cls._kana_voiced_consonant_rule[rule] = voiced + + @classmethod + def RESET(cls, prefs, section, name, value): + cls._prefs = prefs + if section == 'kana_typing_rule' and name == 'method' and \ + value != None: + cls._kana_typing_rule_section = None + cls._kana_voiced_consonant_rule = None + cls._init_kana_typing_method(value) + elif section.startswith('kana_typing_rule/'): + # Probably it's better to restart ibus by manual + # instead of saving the emitted values from config. + cls._kana_voiced_consonant_rule = None + + def __get_kana_typing_rule(self, enchars, retval=None): + prefs = self._prefs + value = None + section = self._kana_typing_rule_section + if section != None: + # Need to send Unicode to typing_to_config_key instead of UTF-8 + # not to separate U+A5 + gkey = prefs.typing_to_config_key(enchars) + if gkey == '': + return None + enchars = gkey + if enchars in prefs.keys(section): + value = prefs.str(prefs.str(prefs.get_value(section, enchars))) + else: + prefs.set_no_key_warning(True) + value = prefs.get_value_direct(section, enchars) + prefs.set_no_key_warning(False) + if value != None: + value = prefs.str(prefs.str(value)) + if value == '': + value = None + if value == None: + value = retval + else: + value = kana_typing_rule_static.get(enchars, retval) + return value + + def is_finished(self): + return not (self._jachars in _UNFINISHED_HIRAGANA) + + def append(self, enchar): + if enchar == '\0' or enchar == '': + return [] + if self._jachars: + text = self._jachars + enchar + if self._kana_voiced_consonant_rule != None: + jachars = self._kana_voiced_consonant_rule.get(text, None) + if jachars: + self._enchars = self._enchars + enchar + self._jachars = jachars + return [] + return [KanaSegment(enchar)] + self._enchars = self._enchars + enchar + self._jachars = self.__get_kana_typing_rule(self._enchars, '') + return [] + + def prepend(self, enchar): + if enchar == '\0' or enchar == '': + return [] + if self._enchars == '': + self._enchars = enchar + self._jachars = self.__get_kana_typing_rule(self._enchars, '') + return [] + return [KanaSegment(enchar)] + + def pop(self, index=-1): + if index == -1: + index = len(self._enchars) - 1 + if index < 0 or index >= len(self._enchars): + raise IndexError('Out of bound') + if self.is_finished(): + self._enchars = '' + self._jachars = '' + else: + enchars = list(self._enchars) + del enchars[index] + self._enchars = ''.join(enchars) + self._jachars = self.__get_kana_typing_rule(self._enchars, '') diff --git a/engine/python3/main.py b/engine/python3/main.py new file mode 100644 index 0000000..eb6fe5f --- /dev/null +++ b/engine/python3/main.py @@ -0,0 +1,188 @@ +# vim:set et sts=4 sw=4: +# -*- coding: utf-8 -*- +# +# ibus-anthy - The Anthy engine for IBus +# +# Copyright (c) 2007-2008 Peng Huang <shawn.p.huang@gmail.com> +# Copyright (c) 2010-2014 Takao Fujiwara <takao.fujiwara1@gmail.com> +# Copyright (c) 2007-2014 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +import os +from os import path +import sys +import getopt +import locale +import xml.dom.minidom + +from gi.repository import GLib + +# set_prgname before importing factory to show the name in warning +# messages when import modules are failed. E.g. Gtk. +GLib.set_prgname('ibus-engine-anthy') + +from gi.repository import IBus + +import _config as config +import factory + +class IMApp: + def __init__(self, exec_by_ibus): + command_line = config.LIBEXECDIR + '/ibus-engine-anthy --ibus' + self.__component = IBus.Component(name='org.freedesktop.IBus.Anthy', + description='Anthy Component', + version='0.1.0', + license='GPL', + author='Peng Huang <shawn.p.huang@gmail.com>', + homepage='http://code.google.com/p/ibus/', + command_line=command_line, + textdomain='ibus-anthy') + engine = IBus.EngineDesc(name='anthy', + longname='Anthy', + description='Anthy Input Method', + language='ja', + license='GPL', + author='Peng Huang <shawn.p.huang@gmail.com>', + icon='ibus-anthy', + layout=config.LAYOUT, + symbol=config.SYMBOL_CHAR, + rank=99) + self.__component.add_engine(engine) + self.__mainloop = GLib.MainLoop() + self.__bus = IBus.Bus() + self.__bus.connect('disconnected', self.__bus_disconnected_cb) + self.__factory = factory.EngineFactory(self.__bus) + if exec_by_ibus: + self.__bus.request_name('org.freedesktop.IBus.Anthy', 0) + else: + self.__bus.register_component(self.__component) + + def run(self): + self.__mainloop.run() + + def __bus_disconnected_cb(self, bus): + self.__mainloop.quit() + + +def launch_engine(exec_by_ibus): + IMApp(exec_by_ibus).run() + +def get_userhome(): + if 'HOME' not in os.environ: + import pwd + userhome = pwd.getpwuid(os.getuid()).pw_dir + else: + userhome = os.environ['HOME'] + userhome = userhome.rstrip('/') + return userhome + +def resync_engine_file(): + user_config = path.join(get_userhome(), '.config', + 'ibus-anthy', 'engines.xml') + system_config = path.join(config.PKGDATADIR, 'engine', 'default.xml') + if not path.exists(user_config): + return + if not path.exists(system_config): + os.unlink(user_config) + return + + # path.getmtime depends on the build time rather than install time. + def __get_engine_file_version(engine_file): + version_str = '' + dom = xml.dom.minidom.parse(engine_file) + elements = dom.getElementsByTagName('version') + nodes = [] + if len(elements) > 0: + nodes = elements[0].childNodes + if len(nodes) > 0: + version_str = nodes[0].data + if version_str != '': + version_str = version_str.strip() + return version_str + + user_config_version = __get_engine_file_version(user_config) + system_config_version = __get_engine_file_version(system_config) + if system_config_version > user_config_version: + import shutil + shutil.copyfile(system_config, user_config) + +def print_xml(): + user_config = os.path.join(get_userhome(), '.config', + 'ibus-anthy', 'engines.xml') + system_config = os.path.join(config.PKGDATADIR, 'engine', 'default.xml') + xml = None + for f in [user_config, system_config]: + if os.path.exists(f): + xml = f + break + if xml == None: + print('Not exist: %s' % system_config, file=sys.stderr) + return + file = open(xml, 'r') + print(file.read()) + file.close() + +def print_help(out, v = 0): + print('-i, --ibus executed by ibus.', file=out) + print('-h, --help show this message.', file=out) + print('-d, --daemonize daemonize ibus.', file=out) + print('-x, --xml print engine xml.', file=out) + sys.exit(v) + +def main(): + try: + locale.setlocale(locale.LC_ALL, '') + except: + pass + + exec_by_ibus = False + daemonize = False + xml = False + + shortopt = 'ihdx' + longopt = ['ibus', 'help', 'daemonize', 'xml'] + + try: + opts, args = getopt.getopt(sys.argv[1:], shortopt, longopt) + except getopt.GetoptError as err: + print_help(sys.stderr, 1) + + for o, a in opts: + if o in ('-h', '--help'): + print_help(sys.stdout) + elif o in ('-d', '--daemonize'): + daemonize = True + elif o in ('-i', '--ibus'): + exec_by_ibus = True + elif o in ('-x', '--xml'): + xml = True + else: + print('Unknown argument: %s' % o, file=sys.stderr) + print_help(sys.stderr, 1) + + if daemonize: + if os.fork(): + sys.exit() + + if xml: + resync_engine_file() + print_xml() + return + + launch_engine(exec_by_ibus) + +if __name__ == '__main__': + main() diff --git a/engine/python3/romaji.py b/engine/python3/romaji.py new file mode 100644 index 0000000..d495120 --- /dev/null +++ b/engine/python3/romaji.py @@ -0,0 +1,263 @@ +# vim:set et sts=4 sw=4: +# -*- coding: utf-8 -*- +# +# ibus-anthy - The Anthy engine for IBus +# +# Copyright (c) 2007-2008 Peng Huang <shawn.p.huang@gmail.com> +# Copyright (c) 2010-2014 Takao Fujiwara <takao.fujiwara1@gmail.com> +# Copyright (c) 2007-2014 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +import sys + +from tables import * +import segment + +def romaji_correction_rule_get(k, d): + return ('ん', k[1:2]) if k[0:1] == 'n' and not k[1:2] in "aiueony'" else d + +class RomajiSegment(segment.Segment): + _prefs = None + _romaji_typing_rule_section = None + _latin_with_shift = True + _shift_mode = False + + def __init__(self, enchars='', jachars='', shift=False, unshift=False): + if self._latin_with_shift: + # If Shift key is pressed, Latin mode. + # If Hiragana_Katakana key is pressed, Hiragana mode. + if shift: + self._shift_mode = True + if unshift: + self._shift_mode = False + + enchars_orig = enchars + # Even if the chars are capital with CapsLock, Hiragana + # should be converted. E.g. 'SA' + enchars = enchars.lower() + + if not jachars and not shift: + jachars = self.__get_romaji_typing_rule(enchars, None) + if jachars == None: + jachars = symbol_rule.get(enchars, '') + super(RomajiSegment, self).__init__(enchars_orig, jachars) + + @classmethod + def INIT_ROMAJI_TYPING_RULE(cls, prefs): + cls._prefs = prefs + if prefs == None: + cls._romaji_typing_rule_section = None + return + method = prefs.get_value('romaji_typing_rule', 'method') + if method == None: + method = 'default' + cls._romaji_typing_rule_section = 'romaji_typing_rule/' + method + if cls._romaji_typing_rule_section not in prefs.sections(): + cls._romaji_typing_rule_section = None + + @classmethod + def SET_LATIN_WITH_SHIFT(cls, latin_with_shift): + # Do not use IBus.Config in every conversion for the performance. + cls._latin_with_shift = latin_with_shift + + def __get_romaji_typing_rule(self, enchars, retval=None): + prefs = self._prefs + value = None + section = self._romaji_typing_rule_section + if section != None: + # Need to send Unicode to typing_to_config_key instead of UTF-8 + # not to separate U+A5 + gkey = prefs.typing_to_config_key(enchars) + if gkey == '': + return None + if gkey in prefs.keys(section): + value = prefs.get_value(section, gkey) + else: + prefs.set_no_key_warning(True) + value = prefs.get_value_direct(section, gkey) + prefs.set_no_key_warning(False) + if value == '': + value = None + if value == None: + value = retval + else: + value = romaji_typing_rule_static.get(enchars, retval) + return value + + def is_finished(self): + return self._jachars != '' + + def append(self, enchar, shift=False, unshift=False): + if self.is_finished(): + if enchar == '' and enchar == '\0': + return [] + return [RomajiSegment(enchar)] + + text_orig = self._enchars + enchar + text = text_orig.lower() + + if self._latin_with_shift: + # If Shift key is pressed, Latin mode. + # If Hiragana_Katakana key is pressed, Hiragana mode. + if shift: + self._shift_mode = True + if unshift: + self._shift_mode = False + if self._shift_mode: + self._enchars = text_orig + return [] + + if shift: + self._enchars = text_orig + return [] + + jachars = self.__get_romaji_typing_rule(text, None) + if jachars == None: + jachars = symbol_rule.get(text, None) + if jachars: + self._enchars = text_orig + self._jachars = jachars + return [] + + jachars, c = romaji_double_consonat_typing_rule.get(text, (None, None)) + if jachars: + self._enchars = text_orig[0] + self._jachars = jachars + return [RomajiSegment(c)] + +# jachars, c = romaji_correction_rule.get(text, (None, None)) + jachars, c = romaji_correction_rule_get(text, (None, None)) + if jachars: + self._enchars = text_orig[0] + self._jachars = jachars + return [RomajiSegment(c)] + + for i in range(-min(4, len(text)), 0): + enchars = text[i:] + + jachars = self.__get_romaji_typing_rule(enchars, None) + if jachars == None: + jachars = symbol_rule.get(enchars, None) + if jachars: + jasegment = RomajiSegment(enchars, jachars) + self._enchars = text_orig[:i] + return [jasegment] + + jachars, c = romaji_double_consonat_typing_rule.get(enchars, (None, None)) + if jachars: + jasegment = RomajiSegment(enchars[:-len(c)], jachars) + self._enchars = text_orig[:i] + if c: + return [jasegment, RomajiSegment(c)] + return [jasegment] + +# jachars, c = romaji_correction_rule.get(enchars, (None, None)) + jachars, c = romaji_correction_rule_get(enchars, (None, None)) + if jachars: + jasegment = RomajiSegment(enchars[:-len(c)], jachars) + self._enchars = text_orig[:i] + if c: + return [jasegment, RomajiSegment(c)] + return [jasegment] + + self._enchars = text_orig + return [] + + def prepend(self, enchar, shift=False, unshift=False): + if enchar == '' or enchar == '\0': + return [] + + if self.is_finished(): + return [RomajiSegment(enchar)] + + text_orig = enchar + self._enchars + text = text_orig.lower() + + if self._latin_with_shift: + if shift: + self._shift_mode = True + if unshift: + self._shift_mode = False + if self._shift_mode: + self._enchars = text_orig + return [] + + if shift: + self._enchars = text_orig + return [] + + jachars = self.__get_romaji_typing_rule(text, None) + if jachars == None: + jachars = symbol_rule.get(text, None) + if jachars: + self._enchars = text_orig + self._jachars = jachars + return [] + + jachars, c = romaji_double_consonat_typing_rule.get(text, (None, None)) + if jachars: + self._enchars = c + return [RomajiSegment(text_orig[0], jachars)] + +# jachars, c = romaji_correction_rule.get(text, (None, None)) + jachars, c = romaji_correction_rule_get(text, (None, None)) + if jachars: + self._enchars = c + return [RomajiSegment(text_orig[0], jachars)] + + for i in range(min(4, len(text)), 0, -1): + enchars = text[:i] + + jachars = self.__get_romaji_typing_rule(enchars, None) + if jachars == None: + jachars = symbol_rule.get(enchars, None) + if jachars: + jasegment = RomajiSegment(enchars, jachars) + self._enchars = text_orig[i:] + return [jasegment] + + jachars, c = romaji_double_consonat_typing_rule.get(enchars, (None, None)) + if jachars: + self._enchars = c + text_orig[i:] + return [RomajiSegment(enchars[:-len(c)], jachars)] + +# jachars, c = romaji_correction_rule.get(enchars, (None, None)) + jachars, c = romaji_correction_rule_get(enchars, (None, None)) + if jachars: + self._enchars = c + text_orig[i:] + return [RomajiSegment(enchars[:-len(c)], jachars)] + + self._enchars = text_orig + return [] + + def pop(self, index=-1): + if index == -1: + index = len(self._enchars) - 1 + if index < 0 or index >= len(self._enchars): + raise IndexError('Out of bound') + if self.is_finished(): + self._enchars = '' + self._jachars = '' + else: + enchars = list(self._enchars) + del enchars[index] + self._enchars = ''.join(enchars) + jachars = self.__get_romaji_typing_rule(self._enchars, None) + if jachars == None: + jachars = symbol_rule.get(self._enchars, '') + self._jachars = jachars + + diff --git a/engine/python3/segment.py b/engine/python3/segment.py new file mode 100644 index 0000000..b7450ec --- /dev/null +++ b/engine/python3/segment.py @@ -0,0 +1,95 @@ +# vim:set et sts=4 sw=4: +# -*- coding: utf-8 -*- +# +# ibus-anthy - The Anthy engine for IBus +# +# Copyright (c) 2007-2008 Peng Huang <shawn.p.huang@gmail.com> +# Copyright (c) 2010-2014 Takao Fujiwara <takao.fujiwara1@gmail.com> +# Copyright (c) 2007-2014 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +from tables import * + +_half_full_table = [ + (0x0020, 0x3000, 1), + (0x0021, 0xFF01, 0x5E), +] + +def _h_to_f(c): + code = ord (c) + for half, full, size in _half_full_table: + if code >= half and code < half + size: + return chr(full + code - half) + return c + +def unichar_half_to_full(c): + tdl = {'"': '\u201d', "'": '\u2019', '`': '\u2018'} + return tdl[c] if c in tdl else _h_to_f(c) + +class Segment(object): + def __init__(self, enchars='', jachars=''): + self._enchars = enchars + self._jachars = jachars + + def append(self, enchar): + raise NotImplementedError('append() is not implemented') + + def prepend(self, enchar): + raise NotImplementedError('prepend() is not implemented') + + def pop(self, index=-1): + raise NotImplementedError('pop() is not implemented') + + def is_finished(self): + raise NotImplementedError('is_finised() is not implemented') + + def set_enchars(self, enchars): + self.enchars = enchars + + def get_enchars(self): + return self._enchars + + def set_jachars(self, jachars): + self._jachars = jachars + + def get_jachars(self): + return self._jachars + + def to_hiragana(self): + if self._jachars: + return self._jachars + return self._enchars + + def to_katakana(self): + if self._jachars: + return ''.join([hiragana_katakana_table.get(c, (c, c, c))[0] for c in self._jachars]) + return self._enchars + + def to_half_width_katakana(self): + if self._jachars: + return ''.join([hiragana_katakana_table.get(c, (c, c, c))[1] for c in self._jachars]) + return self._enchars + + def to_latin(self): + return self._enchars + + def to_wide_latin(self): + return ''.join(map(unichar_half_to_full, self._enchars)) + + def is_empty(self): + if self._enchars or self._jachars: + return False + return True diff --git a/engine/python3/tables.py b/engine/python3/tables.py new file mode 100644 index 0000000..b2c4cac --- /dev/null +++ b/engine/python3/tables.py @@ -0,0 +1,731 @@ +# vim:set et sts=4 sw=4: +# -*- coding: utf-8 -*- +# +# ibus-anthy - The Anthy engine for IBus +# +# Copyright (c) 2007-2008 Peng Huang <shawn.p.huang@gmail.com> +# Copyright (c) 2010-2014 Takao Fujiwara <takao.fujiwara1@gmail.com> +# Copyright (c) 2007-2014 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +# string, result, cont +romaji_typing_rule_static = { + '-' : 'ー', + 'a' : 'あ', + 'i' : 'い', + 'u' : 'う', + 'e' : 'え', + 'o' : 'お', + 'xa' : 'ぁ', + 'xi' : 'ぃ', + 'xu' : 'ぅ', + 'xe' : 'ぇ', + 'xo' : 'ぉ', + 'la' : 'ぁ', + 'li' : 'ぃ', + 'lu' : 'ぅ', + 'le' : 'ぇ', + 'lo' : 'ぉ', + 'wha' : 'うぁ', + 'whi' : 'うぃ', + 'whe' : 'うぇ', + 'who' : 'うぉ', + 'va' : 'ヴぁ', + 'vi' : 'ヴぃ', + 'vu' : 'ヴ', + 've' : 'ヴぇ', + 'vo' : 'ヴぉ', + 'ka' : 'か', + 'ki' : 'き', + 'ku' : 'く', + 'ke' : 'け', + 'ko' : 'こ', + 'lka' : 'ヵ', + 'lke' : 'ヶ', +# u'xka' : u'ゕ', + 'xka' : 'ヵ', +# u'xke' : u'ゖ', + 'xke' : 'ヶ', + 'ga' : 'が', + 'gi' : 'ぎ', + 'gu' : 'ぐ', + 'ge' : 'げ', + 'go' : 'ご', + 'kya' : 'きゃ', + 'kyi' : 'きぃ', + 'kyu' : 'きゅ', + 'kye' : 'きぇ', + 'kyo' : 'きょ', + 'kwa' : 'くぁ', + 'gya' : 'ぎゃ', + 'gyi' : 'ぎぃ', + 'gyu' : 'ぎゅ', + 'gye' : 'ぎぇ', + 'gyo' : 'ぎょ', + 'gwa' : 'ぐぁ', + 'sa' : 'さ', + 'si' : 'し', + 'su' : 'す', + 'se' : 'せ', + 'so' : 'そ', + 'za' : 'ざ', + 'zi' : 'じ', + 'zu' : 'ず', + 'ze' : 'ぜ', + 'zo' : 'ぞ', + 'sya' : 'しゃ', + 'syi' : 'しぃ', + 'syu' : 'しゅ', + 'sye' : 'しぇ', + 'syo' : 'しょ', + 'sha' : 'しゃ', + 'shi' : 'し', + 'shu' : 'しゅ', + 'she' : 'しぇ', + 'sho' : 'しょ', + 'zya' : 'じゃ', + 'zyi' : 'じぃ', + 'zyu' : 'じゅ', + 'zye' : 'じぇ', + 'zyo' : 'じょ', + 'ja' : 'じゃ', + 'jya' : 'じゃ', + 'ji' : 'じ', + 'jyi' : 'じぃ', + 'ju' : 'じゅ', + 'jyu' : 'じゅ', + 'je' : 'じぇ', + 'jye' : 'じぇ', + 'jo' : 'じょ', + 'jyo' : 'じょ', + 'ta' : 'た', + 'ti' : 'ち', + 'tu' : 'つ', + 'tsu' : 'つ', + 'te' : 'て', + 'to' : 'と', + 'da' : 'だ', + 'di' : 'ぢ', + 'du' : 'づ', + 'de' : 'で', + 'do' : 'ど', + 'xtu' : 'っ', + 'xtsu' : 'っ', + 'ltu' : 'っ', + 'ltsu' : 'っ', + 'tya' : 'ちゃ', + 'tyi' : 'ちぃ', + 'tyu' : 'ちゅ', + 'tye' : 'ちぇ', + 'tyo' : 'ちょ', + 'cya' : 'ちゃ', + 'cyi' : 'ちぃ', + 'cyu' : 'ちゅ', + 'cye' : 'ちぇ', + 'cyo' : 'ちょ', + 'cha' : 'ちゃ', + 'chi' : 'ち', + 'chu' : 'ちゅ', + 'che' : 'ちぇ', + 'cho' : 'ちょ', + 'dya' : 'ぢゃ', + 'dyi' : 'ぢぃ', + 'dyu' : 'ぢゅ', + 'dye' : 'ぢぇ', + 'dyo' : 'ぢょ', + 'tsa' : 'つぁ', + 'tsi' : 'つぃ', + 'tse' : 'つぇ', + 'tso' : 'つぉ', + 'tha' : 'てゃ', + 'thi' : 'てぃ', + 'thu' : 'てゅ', + 'the' : 'てぇ', + 'tho' : 'てょ', + 'twu' : 'とぅ', + 'dha' : 'でゃ', + 'dhi' : 'でぃ', + 'dhu' : 'でゅ', + 'dhe' : 'でぇ', + 'dho' : 'でょ', + 'dwu' : 'どぅ', + 'na' : 'な', + 'ni' : 'に', + 'nu' : 'ぬ', + 'ne' : 'ね', + 'no' : 'の', + 'nya' : 'にゃ', + 'nyi' : 'にぃ', + 'nyu' : 'にゅ', + 'nye' : 'にぇ', + 'nyo' : 'にょ', + 'ha' : 'は', + 'hi' : 'ひ', + 'hu' : 'ふ', + 'he' : 'へ', + 'ho' : 'ほ', + 'ba' : 'ば', + 'bi' : 'び', + 'bu' : 'ぶ', + 'be' : 'べ', + 'bo' : 'ぼ', + 'pa' : 'ぱ', + 'pi' : 'ぴ', + 'pu' : 'ぷ', + 'pe' : 'ぺ', + 'po' : 'ぽ', + 'hya' : 'ひゃ', + 'hyi' : 'ひぃ', + 'hyu' : 'ひゅ', + 'hye' : 'ひぇ', + 'hyo' : 'ひょ', + 'bya' : 'びゃ', + 'byi' : 'びぃ', + 'byu' : 'びゅ', + 'bye' : 'びぇ', + 'byo' : 'びょ', + 'pya' : 'ぴゃ', + 'pyi' : 'ぴぃ', + 'pyu' : 'ぴゅ', + 'pye' : 'ぴぇ', + 'pyo' : 'ぴょ', + 'fa' : 'ふぁ', + 'fi' : 'ふぃ', + 'fu' : 'ふ', + 'fe' : 'ふぇ', + 'fo' : 'ふぉ', + 'fya' : 'ふゃ', + 'fyi' : 'ふぃ', + 'fyu' : 'ふゅ', + 'fye' : 'ふぇ', + 'fyo' : 'ふょ', + 'ma' : 'ま', + 'mi' : 'み', + 'mu' : 'む', + 'me' : 'め', + 'mo' : 'も', + 'mya' : 'みゃ', + 'myi' : 'みぃ', + 'myu' : 'みゅ', + 'mye' : 'みぇ', + 'myo' : 'みょ', + 'ya' : 'や', + 'yi' : 'い', + 'yu' : 'ゆ', + 'ye' : 'いぇ', + 'yo' : 'よ', + 'lya' : 'ゃ', + 'lyi' : 'ぃ', + 'lyu' : 'ゅ', + 'lye' : 'ぇ', + 'lyo' : 'ょ', + 'xya' : 'ゃ', + 'xyi' : 'ぃ', + 'xyu' : 'ゅ', + 'xye' : 'ぇ', + 'xyo' : 'ょ', + 'ra' : 'ら', + 'ri' : 'り', + 'ru' : 'る', + 're' : 'れ', + 'ro' : 'ろ', + 'rya' : 'りゃ', + 'ryi' : 'りぃ', + 'ryu' : 'りゅ', + 'rye' : 'りぇ', + 'ryo' : 'りょ', + 'wa' : 'わ', + 'wi' : 'うぃ', + 'wu' : 'う', + 'we' : 'うぇ', + 'wo' : 'を', + 'lwa' : 'ゎ', + 'xwa' : 'ゎ', + 'n\'' : 'ん', + 'nn' : 'ん', + 'wyi' : 'ゐ', + 'wye' : 'ゑ', +} + +symbol_rule = { + # symbols + ' ' : ' ', + ',' : '、', + '.' : '。', + '!' : '!', + '"' : '\u201d', + '#' : '#', + '$' : '$', + '%' : '%', + '&' : '&', + '\'' : '\u2019', + '(' : '(', + ')' : ')', + '~' : '\uff5e', + '-' : 'ー', + '=' : '=', + '^' : '^', + '\\' : '\', + '|' : '|', + '`' : '\u2018', + '@' : '@', + '{' : '{', + '[' : '「', + '+' : '+', + ';' : ';', + '*' : '*', + ':' : ':', + '}' : '}', + ']' : '」', + '<' : '<', + '>' : '>', + '?' : '?', + '/' : '/', + '_' : '_', + '¥' : '¥', + + # numbers + '0': '0', + '1': '1', + '2': '2', + '3': '3', + '4': '4', + '5': '5', + '6': '6', + '7': '7', + '8': '8', + '9': '9', +} + +# this is only used with romaji_typing_rule +romaji_double_consonat_typing_rule = { + # double consonant rule + 'bb' : ('っ', 'b'), + 'cc' : ('っ', 'c'), + 'dd' : ('っ', 'd'), + 'ff' : ('っ', 'f'), + 'gg' : ('っ', 'g'), + 'hh' : ('っ', 'h'), + 'jj' : ('っ', 'j'), + 'kk' : ('っ', 'k'), + 'mm' : ('っ', 'm'), + 'pp' : ('っ', 'p'), + 'rr' : ('っ', 'r'), + 'ss' : ('っ', 's'), + 'tt' : ('っ', 't'), + 'vv' : ('っ', 'v'), + 'ww' : ('っ', 'w'), + 'xx' : ('っ', 'x'), + 'yy' : ('っ', 'y'), + 'zz' : ('っ', 'z'), +} + +# this is only used with romaji_typing_rule +romaji_correction_rule = { + 'nb' : ('ん', 'b'), + 'nc' : ('ん', 'c'), + 'nd' : ('ん', 'd'), + 'nf' : ('ん', 'f'), + 'ng' : ('ん', 'g'), + 'nh' : ('ん', 'h'), + 'nj' : ('ん', 'j'), + 'nk' : ('ん', 'k'), + 'nl' : ('ん', 'l'), + 'nm' : ('ん', 'm'), + 'np' : ('ん', 'p'), + 'nr' : ('ん', 'r'), + 'ns' : ('ん', 's'), + 'nt' : ('ん', 't'), + 'nv' : ('ん', 'v'), + 'nw' : ('ん', 'w'), + 'nx' : ('ん', 'x'), + 'nz' : ('ん', 'z'), + 'n\0' : ('ん', ''), + 'n,' : ('ん', ','), + 'n.' : ('ん', '.'), +} + +# EUC-JP and SJIS do not have the chars +romaji_utf8_rule = { + 'う゛' : ['ゔ'], +} + +# Hiragana normalization is needed for the personal dict. +romaji_normalize_rule = { + 'ヴ' : ['う゛'], +} + +# a port of 101kana.sty from scim-anthy +kana_typing_rule_static = { + # no modifiers keys + '1' : 'ぬ', + '2' : 'ふ', + '3' : 'あ', + '4' : 'う', + '5' : 'え', + '6' : 'お', + '7' : 'や', + '8' : 'ゆ', + '9' : 'よ', + '0' : 'わ', + '-' : 'ほ', + '^' : 'へ', + + 'q' : 'た', + 'w' : 'て', + 'e' : 'い', + 'r' : 'す', + 't' : 'か', + 'y' : 'ん', + 'u' : 'な', + 'i' : 'に', + 'o' : 'ら', + 'p' : 'せ', + '@' : '゛', + '[' : '゜', + + 'a' : 'ち', + 's' : 'と', + 'd' : 'し', + 'f' : 'は', + 'g' : 'き', + 'h' : 'く', + 'j' : 'ま', + 'k' : 'の', + 'l' : 'り', + ';' : 'れ', + ':' : 'け', + ']' : 'む', + + 'z' : 'つ', + 'x' : 'さ', + 'c' : 'そ', + 'v' : 'ひ', + 'b' : 'こ', + 'n' : 'み', + 'm' : 'も', + ',' : 'ね', + '.' : 'る', + '/' : 'め', + # u'\\' : u'ー', + '\\' : 'ろ', + + # shift modifiered keys + '!' : 'ぬ', + '"' : 'ふ', + '#' : 'ぁ', + '$' : 'ぅ', + '%' : 'ぇ', + '&' : 'ぉ', + '\'' : 'ゃ', + '(' : 'ゅ', + ')' : 'ょ', + '~' : 'を', + '=' : 'ほ', + '|' : 'ー', + + 'Q' : 'た', + 'W' : 'て', + 'E' : 'ぃ', + 'R' : 'す', + 'T' : 'ヵ', + 'Y' : 'ん', + 'U' : 'な', + 'I' : 'に', + 'O' : 'ら', + 'P' : 'せ', + '`' : '゛', + + '{' : '「', + + 'A' : 'ち', + 'S' : 'と', + 'D' : 'し', + 'F' : 'ゎ', + 'G' : 'き', + 'H' : 'く', + 'J' : 'ま', + 'K' : 'の', + 'L' : 'り', + '+' : 'れ', + '*' : 'ヶ', + + '}' : '」', + + 'Z' : 'っ', + 'X' : 'さ', + 'C' : 'そ', + 'V' : 'ゐ', + 'B' : 'こ', + 'M' : 'も', + 'N' : 'み', + '<' : '、', + '>' : '。', + + '?' : '・', + '_' : 'ろ', + + '¥' : 'ー', +} + +kana_voiced_consonant_no_rule = { + 'か' : 'が', + 'き' : 'ぎ', + 'く' : 'ぐ', + 'け' : 'げ', + 'こ' : 'ご', + 'さ' : 'ざ', + 'し' : 'じ', + 'す' : 'ず', + 'せ' : 'ぜ', + 'そ' : 'ぞ', + 'た' : 'だ', + 'ち' : 'ぢ', + 'つ' : 'づ', + 'て' : 'で', + 'と' : 'ど', + 'は' : 'ば', + 'ひ' : 'び', + 'ふ' : 'ぶ', + 'へ' : 'べ', + 'ほ' : 'ぼ', +} + +kana_semi_voiced_consonant_no_rule = { + 'は' : 'ぱ', + 'ひ' : 'ぴ', + 'ふ' : 'ぷ', + 'へ' : 'ぺ', + 'ほ' : 'ぽ', +} + +# Create the table dynamically with kana_voiced_consonant_no_rule +# +#kana_voiced_consonant_rule = { +# u'か@' : u'が', +# u'き@' : u'ぎ', +# u'く@' : u'ぐ', +# u'け@' : u'げ', +# u'こ@' : u'ご', +# u'さ@' : u'ざ', +# u'し@' : u'じ', +# u'す@' : u'ず', +# u'せ@' : u'ぜ', +# u'そ@' : u'ぞ', +# u'た@' : u'だ', +# u'ち@' : u'ぢ', +# u'つ@' : u'づ', +# u'て@' : u'で', +# u'と@' : u'ど', +# u'は@' : u'ば', +# u'ひ@' : u'び', +# u'ふ@' : u'ぶ', +# u'へ@' : u'べ', +# u'ほ@' : u'ぼ', +# u'か`' : u'が', +# u'き`' : u'ぎ', +# u'く`' : u'ぐ', +# u'け`' : u'げ', +# u'こ`' : u'ご', +# u'さ`' : u'ざ', +# u'し`' : u'じ', +# u'す`' : u'ず', +# u'せ`' : u'ぜ', +# u'そ`' : u'ぞ', +# u'た`' : u'だ', +# u'ち`' : u'ぢ', +# u'つ`' : u'づ', +# u'て`' : u'で', +# u'と`' : u'ど', +# u'は`' : u'ば', +# u'ひ`' : u'び', +# u'ふ`' : u'ぶ', +# u'へ`' : u'べ', +# u'ほ`' : u'ぼ', +# u'は[' : u'ぱ', +# u'ひ[' : u'ぴ', +# u'ふ[' : u'ぷ', +# u'へ[' : u'ぺ', +# u'ほ[' : u'ぽ', +#} +# +#kana_voiced_consonant_us_rule = { +# u'か[' : u'が', +# u'き[' : u'ぎ', +# u'く[' : u'ぐ', +# u'け[' : u'げ', +# u'こ[' : u'ご', +# u'さ[' : u'ざ', +# u'し[' : u'じ', +# u'す[' : u'ず', +# u'せ[' : u'ぜ', +# u'そ[' : u'ぞ', +# u'た[' : u'だ', +# u'ち[' : u'ぢ', +# u'つ[' : u'づ', +# u'て[' : u'で', +# u'と[' : u'ど', +# u'は[' : u'ば', +# u'ひ[' : u'び', +# u'ふ[' : u'ぶ', +# u'へ[' : u'べ', +# u'ほ[' : u'ぼ', +# u'は]' : u'ぱ', +# u'ひ]' : u'ぴ', +# u'ふ]' : u'ぷ', +# u'へ]' : u'ぺ', +# u'ほ]' : u'ぽ', +#} + +#hiragana, katakana, half_katakana +hiragana_katakana_table = { + 'あ' : ('ア', 'ア'), + 'い' : ('イ', 'イ'), + 'う' : ('ウ', 'ウ'), + 'え' : ('エ', 'エ'), + 'お' : ('オ', 'オ'), + 'か' : ('カ', 'カ'), + 'き' : ('キ', 'キ'), + 'く' : ('ク', 'ク'), + 'け' : ('ケ', 'ケ'), + 'こ' : ('コ', 'コ'), + 'が' : ('ガ', 'ガ'), + 'ぎ' : ('ギ', 'ギ'), + 'ぐ' : ('グ', 'グ'), + 'げ' : ('ゲ', 'ゲ'), + 'ご' : ('ゴ', 'ゴ'), + 'さ' : ('サ', 'サ'), + 'し' : ('シ', 'シ'), + 'す' : ('ス', 'ス'), + 'せ' : ('セ', 'セ'), + 'そ' : ('ソ', 'ソ'), + 'ざ' : ('ザ', 'ザ'), + 'じ' : ('ジ', 'ジ'), + 'ず' : ('ズ', 'ズ'), + 'ぜ' : ('ゼ', 'ゼ'), + 'ぞ' : ('ゾ', 'ゾ'), + 'た' : ('タ', 'タ'), + 'ち' : ('チ', 'チ'), + 'つ' : ('ツ', 'ツ'), + 'て' : ('テ', 'テ'), + 'と' : ('ト', 'ト'), + 'だ' : ('ダ', 'ダ'), + 'ぢ' : ('ヂ', 'ヂ'), + 'づ' : ('ヅ', 'ヅ'), + 'で' : ('デ', 'デ'), + 'ど' : ('ド', 'ド'), + 'な' : ('ナ', 'ナ'), + 'に' : ('ニ', 'ニ'), + 'ぬ' : ('ヌ', 'ヌ'), + 'ね' : ('ネ', 'ネ'), + 'の' : ('ノ', 'ノ'), + 'は' : ('ハ', 'ハ'), + 'ひ' : ('ヒ', 'ヒ'), + 'ふ' : ('フ', 'フ'), + 'へ' : ('ヘ', 'ヘ'), + 'ほ' : ('ホ', 'ホ'), + 'ば' : ('バ', 'バ'), + 'び' : ('ビ', 'ビ'), + 'ぶ' : ('ブ', 'ブ'), + 'べ' : ('ベ', 'ベ'), + 'ぼ' : ('ボ', 'ボ'), + 'ぱ' : ('パ', 'パ'), + 'ぴ' : ('ピ', 'ピ'), + 'ぷ' : ('プ', 'プ'), + 'ぺ' : ('ペ', 'ペ'), + 'ぽ' : ('ポ', 'ポ'), + 'ま' : ('マ', 'マ'), + 'み' : ('ミ', 'ミ'), + 'む' : ('ム', 'ム'), + 'め' : ('メ', 'メ'), + 'も' : ('モ', 'モ'), + 'や' : ('ヤ', 'ヤ'), + 'ゆ' : ('ユ', 'ユ'), + 'よ' : ('ヨ', 'ヨ'), + 'ら' : ('ラ', 'ラ'), + 'り' : ('リ', 'リ'), + 'る' : ('ル', 'ル'), + 'れ' : ('レ', 'レ'), + 'ろ' : ('ロ', 'ロ'), + 'わ' : ('ワ', 'ワ'), + 'を' : ('ヲ', 'ヲ'), + 'ん' : ('ン', 'ン'), + 'ぁ' : ('ァ', 'ァ'), + 'ぃ' : ('ィ', 'ィ'), + 'ぅ' : ('ゥ', 'ゥ'), + 'ぇ' : ('ェ', 'ェ'), + 'ぉ' : ('ォ', 'ォ'), + 'っ' : ('ッ', 'ッ'), + 'ゃ' : ('ャ', 'ャ'), + 'ゅ' : ('ュ', 'ュ'), + 'ょ' : ('ョ', 'ョ'), + 'ヵ' : ('ヵ', 'カ'), + 'ヶ' : ('ヶ', 'ケ'), + 'ゎ' : ('ヮ', 'ワ'), + 'ゐ' : ('ヰ', 'ィ'), + 'ゑ' : ('ヱ', 'ェ'), + 'ヴ' : ('ヴ', 'ヴ'), + + # symbols + 'ー' : ('ー', 'ー'), + '、' : ('、', '、'), + '。' : ('。', '。'), + '!' : ('!', '!'), + '\u201d' : ('\u201d', '"'), + '#' : ('#', '#'), + '$' : ('$', '$'), + '%' : ('%', '%'), + '&' : ('&', '&'), + '\u2019' : ('\u2019', '\''), + '(' : ('(', '('), + ')' : (')', ')'), + '\uff5e' : ('\uff5e', '~'), + '=' : ('=', '='), + '^' : ('^', '^'), + '\' : ('\', '\\'), + '|' : ('|', '|'), + '\u2018' : ('\u2018', '`'), + '@' : ('@', '@'), + '゛' : ('゛', '゙'), + '{' : ('{', '{'), + '゜' : ('゜', '゚'), + '「' : ('「', '「'), + '+' : ('+', '+'), + ';' : (';', ';'), + '*' : ('*', '*'), + ':' : (':', ':'), + '}' : ('}', '}'), + '」' : ('」', '」'), + '<' : ('<', '<'), + '>' : ('>', '>'), + '?' : ('?', '?'), + '・' : ('・', '・'), + '/' : ('/', '/'), + '_' : ('_', '_'), + '¥' : ('¥', '¥'), + + # numbers + '0': ('0', '0'), + '1': ('1', '1'), + '2': ('2', '2'), + '3': ('3', '3'), + '4': ('4', '4'), + '5': ('5', '5'), + '6': ('6', '6'), + '7': ('7', '7'), + '8': ('8', '8'), + '9': ('9', '9'), +} diff --git a/engine/python3/test.py b/engine/python3/test.py new file mode 100644 index 0000000..cd67997 --- /dev/null +++ b/engine/python3/test.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +import anthy +import sys + +ctx = anthy.anthy_context () +ctx._set_encoding (2) +if len(sys.argv) >= 2: + ctx.set_string (sys.argv[1]) +else: + ctx.set_string ("かまぁく") +conv_stat = anthy.anthy_conv_stat () +seg_stat = anthy.anthy_segment_stat () +ctx.get_stat (conv_stat) +for i in range (0, conv_stat.nr_segment): + ctx.get_segment_stat (i, seg_stat) + buf = " " + i = ctx.get_segment (i, 0, buf, 10) + print buf[:i] +# anthy.anthy_release_context (ctx) +ctx = None diff --git a/engine/python3/thumb.py b/engine/python3/thumb.py new file mode 100644 index 0000000..93a3967 --- /dev/null +++ b/engine/python3/thumb.py @@ -0,0 +1,657 @@ +# -*- coding: utf-8 -*- +# vim:set et sts=4 sw=4: +# +# ibus-anthy - The Anthy engine for IBus +# +# Copyright (c) 2007-2008 Peng Huang <shawn.p.huang@gmail.com> +# Copyright (c) 2009 Hideaki ABE <abe.sendai@gmail.com> +# Copyright (c) 2010-2014 Takao Fujiwara <takao.fujiwara1@gmail.com> +# Copyright (c) 2007-2014 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +__all__ = ( + 'ThumbShiftKeyboard', + 'ThumbShiftSegment', + ) + +from gi.repository import GLib +from gi.repository import IBus + +import segment + +_THUMB_BASIC_METHOD = 'base' + +_table_static = { + 'q': ['。', '', 'ぁ'], + 'w': ['か', 'が', 'え'], + 'e': ['た', 'だ', 'り'], + 'r': ['こ', 'ご', 'ゃ'], + 't': ['さ', 'ざ', 'れ'], + + 'y': ['ら', 'よ', 'ぱ'], + 'u': ['ち', 'に', 'ぢ'], + 'i': ['く', 'る', 'ぐ'], + 'o': ['つ', 'ま', 'づ'], + 'p': [',', 'ぇ', 'ぴ'], + '@': ['、', '', ''], + '[': ['゛', '゜', ''], + + 'a': ['う', '', 'を'], + 's': ['し', 'じ', 'あ'], + 'd': ['て', 'で', 'な'], + 'f': ['け', 'げ', 'ゅ'], + 'g': ['せ', 'ぜ', 'も'], + + 'h': ['は', 'み', 'ば'], + 'j': ['と', 'お', 'ど'], + 'k': ['き', 'の', 'ぎ'], + 'l': ['い', 'ょ', 'ぽ'], + ';': ['ん', 'っ', ''], + + 'z': ['.', '', 'ぅ'], + 'x': ['ひ', 'び', 'ー'], + 'c': ['す', 'ず', 'ろ'], + 'v': ['ふ', 'ぶ', 'や'], + 'b': ['へ', 'べ', 'ぃ'], + + 'n': ['め', 'ぬ', 'ぷ'], + 'm': ['そ', 'ゆ', 'ぞ'], + ',': ['ね', 'む', 'ぺ'], + '.': ['ほ', 'わ', 'ぼ'], + '/': ['・', 'ぉ', ''], + + '1': ['1', '', '?'], + '2': ['2', '', '/'], + '4': ['4', '', '「'], + '5': ['5', '', '」'], + + '6': ['6', '[', ''], + '7': ['7', ']', ''], + '8': ['8', '(', ''], + '9': ['9', ')', ''], + '\\': ['¥', '', ''], +} + +_nicola_j_table_static = { + ':': [':', '', ''], + '@': ['、', '', ''], + '[': ['゛', '゜', ''], + ']': ['」', '', ''], + '8': ['8', '(', ''], + '9': ['9', ')', ''], + '0': ['0', '', ''], +} + +_nicola_a_table_static = { + ':': [':', '', ''], + '@': ['@', '', ''], + '[': ['、', '', ''], + ']': ['゛', '゜', ''], + '8': ['8', '', ''], + '9': ['9', '(', ''], + '0': ['0', ')', ''], +} + +_nicola_f_table_static = { + ':': ['、', '', ''], + '@': ['@', '', ''], + '[': ['゛', '゜', ''], + ']': ['」', '', ''], + '8': ['8', '(', ''], + '9': ['9', ')', ''], + '0': ['0', '', ''], +} + +_kb231_j_fmv_table_static = { + '3': ['3', '', '~'], + '0': ['0', '『', ''], + '-': ['-', '』', ''], + '=': ['=', '', ''], +} + +_kb231_a_fmv_table_static = { + '3': ['3', '', '~'], + '0': ['0', ')', ''], + '-': ['-', '『', ''], + '=': ['=', '』', ''], +} + +_kb231_f_fmv_table_static = { + '3': ['3', '', '~'], + '0': ['0', '『', ''], + '-': ['-', '』', ''], + '=': ['=', '', ''], +} + +_kb611_j_fmv_table_static = { + '`': ['‘', '', ''], + '^': ['々', '£', ''], + ':': [':', '', ''], + '@': ['、', '¢', ''], + '[': ['゛', '゜', ''], + # keysyms are same and keycodes depend on the platforms. + #'¥': [u'¥', u'¬', u''], + '\\': ['¥', '¦', ''], +} + +_kb611_a_fmv_table_static = { + '`': ['々', '', '£'], + ':': [':', '', ''], + '@': ['@', '', ''], + '[': ['、', '¢', ''], + #'¥': [u'¥', u'¬', u''], + '\\': ['¥', '¦', ''], +} + +_kb611_f_fmv_table_static = { + '`': ['‘', '', ''], + '^': ['々', '£', ''], + ':': ['、', '¢', ''], + '@': ['@', '', ''], + '[': ['゛', '゜', ''], + #'¥': [u'¥', u'¬', u''], + '\\': ['¥', '¦', ''], +} + +_shift_table = { + 'H': 'ぱ', + 'X': 'ぴ', + 'V': 'ぷ', + 'B': 'ぺ', + '>': 'ぽ', +} + +table_static = {} +r_table_static = {} + +for k in list(_table_static.keys()): + table_static[ord(k)] = _table_static[k] + for c in _table_static[k]: + r_table_static[c] = k + +kana_voiced_consonant_rule = { + 'か゛' : 'が', + 'き゛' : 'ぎ', + 'く゛' : 'ぐ', + 'け゛' : 'げ', + 'こ゛' : 'ご', + 'さ゛' : 'ざ', + 'し゛' : 'じ', + 'す゛' : 'ず', + 'せ゛' : 'ぜ', + 'そ゛' : 'ぞ', + 'た゛' : 'だ', + 'ち゛' : 'ぢ', + 'つ゛' : 'づ', + 'て゛' : 'で', + 'と゛' : 'ど', + 'は゛' : 'ば', + 'ひ゛' : 'び', + 'ふ゛' : 'ぶ', + 'へ゛' : 'べ', + 'ほ゛' : 'ぼ', + 'は゜' : 'ぱ', + 'ひ゜' : 'ぴ', + 'ふ゜' : 'ぷ', + 'へ゜' : 'ぺ', + 'ほ゜' : 'ぽ', +} + +_UNFINISHED_HIRAGANA = set('かきくけこさしすせそたちつてとはひふへほ') + +class ThumbShiftKeyboard: + def __init__(self, prefs=None): + self.__prefs = prefs + self.__table = table_static + self.__r_table = r_table_static + self.__shift_table = {} + self.__ls = 0 + self.__rs = 0 + self.__t1 = 0 + self.__t2 = 0 + self.__layout = 0 + self.__fmv_extension = 2 + self.__handakuten = False + self.__thumb_typing_rule_section_base = None + self.__thumb_typing_rule_section = None + self.__init_thumb_typing_rule() + self.__init_layout_table() + if self.__prefs != None: + self.reset() + self.__reset_shift_table(False) + + def __init_thumb_typing_rule(self): + prefs = self.__prefs + if prefs == None: + self.__thumb_typing_rule_section = None + return + method = prefs.get_value('thumb_typing_rule', 'method') + if method == None: + method = _THUMB_BASIC_METHOD + self.__thumb_typing_rule_section_base = 'thumb_typing_rule' + self.__thumb_typing_rule_section = \ + self.__thumb_typing_rule_section_base + '/' + method + if self.__thumb_typing_rule_section not in prefs.sections(): + self.__thumb_typing_rule_section = None + + def __init_layout_table(self): + if self.__table != {}: + self.__table.clear() + if self.__r_table != {}: + self.__r_table.clear() + section_base = self.__thumb_typing_rule_section_base + section = self.__thumb_typing_rule_section + if section != None: + prefs = self.__prefs + for k in prefs.keys(section): + value = prefs.get_value(section, k) + ch = prefs.typing_from_config_key(k) + if ch == '': + continue + self.__set_bus_table(ch, value) + for k in prefs.get_value(section_base, 'newkeys'): + value = prefs.get_value_direct(section, k) + ch = prefs.typing_from_config_key(k) + if ch == '': + continue + self.__set_bus_table(ch, value) + else: + for k in list(_table.keys()): + self.__table[ord(k)] = _table_static[k] + for c in _table_static[k]: + self.__r_table[c] = k + + def __set_bus_table(self, key, value): + prefs = self.__prefs + if value == None or len(value) != 3: + return + if value[0] == '' and \ + value[1] == '' and value[2] == '': + return + self.__table[ord(key)] = value + for c in value: + self.__r_table[c] = key + + def __reset_layout_table(self, init, + j_table_label, j_table, + a_table_label, a_table, + f_table_label, f_table): + if init: + self.__init_layout_table() + method = None + sub_table = None + if self.__layout == 0: + method = j_table_label + sub_table = j_table + elif self.__layout == 1: + method = a_table_label + sub_table = a_table + elif self.__layout == 2: + method = f_table_label + sub_table = f_table + if method == None or sub_table == None: + return + section_base = self.__thumb_typing_rule_section_base + section = self.__thumb_typing_rule_section + sub_section = section_base + '/' + method + if section != None: + prefs = self.__prefs + for k in prefs.keys(sub_section): + value = prefs.get_value(sub_section, k) + ch = prefs.typing_from_config_key(k) + if ch == '': + continue + self.__set_bus_table(ch, value) + for k in prefs.get_value(section_base, method + '_newkeys'): + value = prefs.get_value_direct(sub_section, k) + ch = prefs.typing_from_config_key(k) + if ch == '': + continue + self.__set_bus_table(ch, value) + else: + for k in list(sub_table.keys()): + self.__table[ord(str(k))] = sub_table[k] + for c in sub_table[k]: + self.__r_table[c] = k + + def __reset_extension_table(self, init): + self.__reset_layout_table(init, + 'nicola_j_table', + _nicola_j_table_static, + 'nicola_a_table', + _nicola_a_table_static, + 'nicola_f_table', + _nicola_f_table_static) + if self.__fmv_extension == 0: + return + if self.__fmv_extension >= 1: + self.__reset_layout_table(False, + 'kb231_j_fmv_table', + _kb231_j_fmv_table_static, + 'kb231_a_fmv_table', + _kb231_a_fmv_table_static, + 'kb231_f_fmv_table', + _kb231_f_fmv_table_static) + if self.__fmv_extension >= 2: + self.__reset_layout_table(False, + 'kb611_j_fmv_table', + _kb611_j_fmv_table_static, + 'kb611_a_fmv_table', + _kb611_a_fmv_table_static, + 'kb611_f_fmv_table', + _kb611_f_fmv_table_static) + + def __reset_shift_table(self, init): + self.__reset_extension_table(init) + if self.__handakuten: + for k in list(_shift_table.keys()): + self.__shift_table[ord(k)] = _shift_table[k] + self.__r_table[_shift_table[k]] = k + elif self.__shift_table != {}: + for k in list(_shift_table.keys()): + if ord(k) in self.__shift_table: + del self.__shift_table[ord(k)] + if _shift_table[k] in self.__r_table: + del self.__r_table[_shift_table[k]] + + def __s_to_key_raw(self, s): + keyval = IBus.keyval_from_name(s.split('+')[-1]) + s = s.lower() + state = ('shift+' in s and IBus.ModifierType.SHIFT_MASK or 0) | ( + 'ctrl+' in s and IBus.ModifierType.CONTROL_MASK or 0) | ( + 'alt+' in s and IBus.ModifierType.MOD1_MASK or 0) + return (keyval, state) + + def __get_xkb_layout(self): + # Until Gdk.property_get is fixed + ''' + # Move importing Gdk into ThumbShiftKeyboard from the header + # because ibus-engine-anthy --xml does not requre to open X. + try: + from gi.repository import Gdk + get_default_root_window = Gdk.get_default_root_window + property_get = Gdk.property_get + intern = Gdk.Atom.intern + except ImportError: + get_default_root_window = lambda : None + property_get = lambda : None + intern = lambda : None + except RuntimeError: + # Do we support the engine without display? + print >> sys.stderr, "Gdk couldn't be initialized" + print >> sys.stderr, 'Could not open display' + get_default_root_window = lambda : None + property_get = lambda : None + intern = lambda : None + + root_window = get_default_root_window() + if not root_window: + return 0 + xkb_rules_names = intern('_XKB_RULES_NAMES', False) + xa_string = intern('STRING', False) + try: + prop = property_get(root_window, + xkb_rules_names, xa_string, + 0, 1024, 0)[3] + layout_list = prop.split('\0') + except TypeError: + import sys + print >> sys.stderr, \ + 'This problem is fixed in the latest gobject-introspection' + print >> sys.stderr, \ + 'https://bugzilla.gnome.org/show_bug.cgi?id=670509' + return 0 + layout = 0 + for data in layout_list: + if data == 'jp': + layout = 0 + elif data == 'us': + layout = 1 + elif data.find('japan:nicola_f_bs') >= 0: + layout = 2 + elif data.find('japan:') >= 0: + layout = 0 + return layout + ''' + + layout = 0 + argv = ['setxkbmap', '-query'] + (ret, std_out, std_error, exit_status) = \ + GLib.spawn_sync(None, argv, None, + GLib.SpawnFlags.SEARCH_PATH_FROM_ENVP, + None, None) + if not ret: + print(std_error, file=sys.stderr) + return layout + for line in std_out.split('\n'): + if line.startswith('layout:'): + data = line.split()[1] + if data == 'jp': + layout = 0 + elif data == 'us': + layout = 1 + elif line.startswith('options:'): + data = line.split()[1] + if data.find('japan:nicola_f_bs') >= 0: + layout = 2 + elif data.find('japan:') >= 0: + layout = 0 + return 0 + + def __reset_layout_and_handakuten(self): + mode = self.__prefs.get_value('thumb', 'keyboard_layout_mode') + layout = 0 + if mode == 1: + layout = self.__get_xkb_layout() + else: + layout = self.__prefs.get_value('thumb', 'keyboard_layout') + self.set_layout(layout) + + fmv_extension = self.__prefs.get_value('thumb', 'fmv_extension') + self.set_fmv_extension(fmv_extension) + handakuten = self.__prefs.get_value('thumb', 'handakuten') + self.set_handakuten(handakuten) + + def reset(self): + s = self.__prefs.get_value('thumb', 'ls') + ls, state = self.__s_to_key_raw(s) + if ls == 0xffffff: + ls = IBus.KEY_Muhenkan + self.set_ls(ls) + + s = self.__prefs.get_value('thumb', 'rs') + rs, state = self.__s_to_key_raw(s) + if rs == 0xffffff: + rs = IBus.KEY_Henkan + self.set_rs(rs) + + t1 = self.__prefs.get_value('thumb', 't1') + t2 = self.__prefs.get_value('thumb', 't2') + self.set_t1(t1) + self.set_t2(t2) + + GLib.idle_add(self.__reset_layout_and_handakuten, + priority = GLib.PRIORITY_LOW) + + def get_ls(self): + return self.__ls + + def set_ls(self, ls): + self.__ls = ls + + def get_rs(self): + return self.__rs + + def set_rs(self, rs): + self.__rs = rs + + def get_t1(self): + return self.__t1 + + def set_t1(self, t1): + self.__t1 = t1 + + def get_t2(self): + return self.__t2 + + def set_t2(self, t2): + self.__t2 = t2 + + def get_layout(self): + return self.__layout + + def set_layout(self, layout): + if self.__layout == layout: + return + self.__layout = layout + self.__reset_shift_table(True) + + def get_fmv_extension (self): + return self.__fmv_extension + + def set_fmv_extension (self, fmv_extension): + if self.__fmv_extension == fmv_extension: + return + self.__fmv_extension = fmv_extension + self.__reset_shift_table(True) + + def get_handakuten(self): + return self.__handakuten + + def set_handakuten(self, handakuten): + if self.__handakuten == handakuten: + return + self.__handakuten = handakuten + self.__reset_shift_table(True) + + def get_char(self, key, fallback=None): + return self.__table.get(key, fallback) + + def get_chars(self): + return list(self.__table.keys()) + + def get_r_char(self, key, fallback=None): + return self.__r_table.get(key, fallback) + + def get_r_chars(self): + return list(self.__r_table.keys()) + + def get_shift_char(self, key, fallback=None): + return self.__shift_table.get(key, fallback) + + def get_shift_chars(self): + return list(self.__shift_table.keys()) + + +class ThumbShiftSegment(segment.Segment): + _prefs = None + _thumb_typing_rule_section_base = None + _thumb_typing_rule_section = None + _r_table = {} + + def __init__(self, enchars='', jachars=''): + if not jachars: + if '!' <= enchars <= '~': + jachars = segment.unichar_half_to_full(enchars) + else: + jachars = enchars + enchars = self._r_table.get(jachars, '') + super(ThumbShiftSegment, self).__init__(enchars, jachars) + + @classmethod + def INIT_THUMB_TYPING_RULE(cls, prefs): + cls._prefs = prefs + if prefs == None: + cls._thumb_typing_rule_section = None + return + method = prefs.get_value('thumb_typing_rule', 'method') + if method == None: + method = _THUMB_BASIC_METHOD + cls._thumb_typing_rule_section_base = 'thumb_typing_rule' + cls._thumb_typing_rule_section = \ + cls._thumb_typing_rule_section_base + '/' + method + if cls._thumb_typing_rule_section not in prefs.sections(): + cls._thumb_typing_rule_section = None + cls._init_layout_table() + + @classmethod + def _init_layout_table(cls): + if cls._r_table != {}: + cls._r_table.clear() + section_base = cls._thumb_typing_rule_section_base + section = cls._thumb_typing_rule_section + if section != None: + prefs = cls._prefs + for k in prefs.keys(section): + value = prefs.get_value(section, k) + ch = prefs.typing_from_config_key(k) + if ch == '': + continue + cls._set_bus_table(ch, value) + for k in prefs.get_value(section_base, 'newkeys'): + value = prefs.get_value_direct(section, k) + ch = prefs.typing_from_config_key(k) + if ch == '': + continue + cls._set_bus_table(ch, value) + else: + for k in list(_table.keys()): + for c in _table_static[k]: + cls._r_table[c] = k + + @classmethod + def _set_bus_table(cls, key, value): + prefs = cls._prefs + if value == None or len(value) != 3: + return + if value[0] == '' and \ + value[1] == '' and value[2] == '': + return + for c in value: + cls._r_table[c] = key + + def is_finished(self): + return not (self._jachars in _UNFINISHED_HIRAGANA) + + def append(self, enchar): + if enchar == '\0' or enchar == '': + return [] + text = self._jachars + enchar + jachars = kana_voiced_consonant_rule.get(text, None) + if jachars: + self._enchars = self._enchars + self._r_table.get(enchar, '') + self._jachars = jachars + return [] + return [ThumbShiftSegment(enchar)] + + def prepend(self, enchar): + if enchar == '\0' or enchar == '': + return [] + if self._jachars == '': + if 0x21 <= enchars <= 0x7e: + self._enchars = enchar + self._jachars = segment.unichar_half_to_full(enchars) + else: + self._enchars = self._r_table.get(enchar, '') + self._jachars = enchar + return [] + return [ThumbShiftSegment(enchar)] + + def pop(self, index=-1): + self._enchars = '' + self._jachars = '' + return + |