diff --git a/SOURCES/add-a-gui-test.patch b/SOURCES/add-a-gui-test.patch deleted file mode 100644 index bab7113..0000000 --- a/SOURCES/add-a-gui-test.patch +++ /dev/null @@ -1,1816 +0,0 @@ -diff -Nru ibus-table-1.9.18/configure.ac ibus-table-1.9.18.new/configure.ac ---- ibus-table-1.9.18/configure.ac 2020-07-22 15:02:24.341755774 +0200 -+++ ibus-table-1.9.18.new/configure.ac 2020-07-22 15:03:04.097344610 +0200 -@@ -54,6 +54,15 @@ - AM_GNU_GETTEXT([external]) - AM_GNU_GETTEXT_VERSION(0.16.1) - -+AC_ARG_ENABLE([installed-tests], -+ [AS_HELP_STRING([--enable-installed-tests], -+ [Enable to install tests])], -+ [enable_installed_tests=$enableval], -+ [enable_installed_tests=no] -+) -+ -+AM_CONDITIONAL([ENABLE_INSTALLED_TESTS], [test x"$enable_installed_tests" = x"yes"]) -+ - # OUTPUT files - AC_CONFIG_FILES([po/Makefile.in - Makefile -diff -Nru ibus-table-1.9.18/tests/.gitignore ibus-table-1.9.18.new/tests/.gitignore ---- ibus-table-1.9.18/tests/.gitignore 2020-07-22 15:02:24.349755691 +0200 -+++ ibus-table-1.9.18.new/tests/.gitignore 2020-07-22 15:11:24.740159555 +0200 -@@ -1,5 +1,5 @@ - run_tests --run_tests.log --run_tests.trs --test-suite.log -+*.log -+*.trs -+*.tap - __pycache__/ -diff -Nru ibus-table-1.9.18/tests/Makefile.am ibus-table-1.9.18.new/tests/Makefile.am ---- ibus-table-1.9.18/tests/Makefile.am 2020-07-22 15:02:24.349755691 +0200 -+++ ibus-table-1.9.18.new/tests/Makefile.am 2020-07-22 15:04:28.298473415 +0200 -@@ -19,20 +19,58 @@ - # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - # - --TESTS = run_tests -+TESTS = \ -+ test_it.py \ -+ test_0_gtk.py \ -+ $(NULL) -+ -+check_SCRIPTS = run_tests -+ -+LOG_COMPILER = $(builddir)/run_tests -+ -+TESTS_ENVIRONMENT = \ -+ IBUS_TABLE_LOCATION=../ -+ -+test_meta_in = meta.test.in -+test_metas = -+ -+if ENABLE_INSTALLED_TESTS -+test_metas += $(patsubst %.py, %.test, $(TESTS)) -+test_source_DATA = $(test_metas) -+test_sourcedir = $(datadir)/installed-tests/ibus-table -+test_exec_SCRIPTS = \ -+ $(TESTS) \ -+ gtkcases.py \ -+ mock_engine.py \ -+ run_tests \ -+ $(NULL) -+test_execdir = $(libexecdir)/installed-tests/ibus-table -+ -+$(test_metas): $(test_meta_in) -+ @TEST_EXEC=`echo $@ | sed -e 's&\.test&\.py&'`; \ -+ sed -e "s&@TEST_EXECDIR@&$(test_execdir)&g" \ -+ -e "s&@TEST_EXEC@&$$TEST_EXEC&g" $< > $@.tmp; \ -+ mv $@.tmp $@; \ -+ $(NULL) -+endif - - run_tests: run_tests.in - sed -e 's&@PYTHON_BIN@&$(PYTHON)&g' \ -+ -e 's&@PKGDATADIR@&$(pkgdatadir)&g' \ - -e 's&@SRCDIR@&$(srcdir)&g' $< > $@ - chmod +x $@ - - EXTRA_DIST = \ -- run_tests.in \ -- test_it.py \ -+ $(test_meta_in) \ - __init__.py \ -+ gtkcases.py \ -+ mock_engine.py \ -+ run_tests.in \ -+ $(TESTS) \ - $(NULL) - - CLEANFILES = \ -+ $(test_metas) \ - run_tests \ - $(NULL) - -diff -Nru ibus-table-1.9.18/tests/gtkcases.py ibus-table-1.9.18.new/tests/gtkcases.py ---- ibus-table-1.9.18/tests/gtkcases.py 1970-01-01 01:00:00.000000000 +0100 -+++ ibus-table-1.9.18.new/tests/gtkcases.py 2020-07-22 15:03:04.097344610 +0200 -@@ -0,0 +1,38 @@ -+#!/usr/bin/python3 -+# -*- coding: utf-8 -*- -+ -+# 'init' has one array which is [keysym, keycode, modifier] and to be run -+# before the main tests. E.g. -+# Ctrl-space to enable Hiragana mode -+# -+# 'tests' cases are the main test cases. -+# 'preedit' case runs to create a preedit text. -+# 'lookup' case runs to update a lookup table. -+# 'commit' case runs to commit the preedit text. -+# 'result' case is the expected output. -+# 'preedit', 'lookup', 'commit' can choose the type of either 'string' or 'keys' -+# 'string' type is a string sequence which does not need modifiers -+ -+from gi import require_version as gi_require_version -+gi_require_version('IBus', '1.0') -+from gi.repository import IBus -+ -+TestCases = { -+ #'init': [IBus.KEY_j, 0, IBus.ModifierType.CONTROL_MASK], -+ 'tests': [ -+ {'preedit': {'string': 'a'}, -+ 'lookup': {'keys': [[IBus.KEY_Down, 0, 0]]}, -+ 'commit': {'keys': [[IBus.KEY_space, 0, 0]]}, -+ 'result': {'string': '区'} -+ }, -+ {'preedit': {'string': 'ijgl'}, -+ 'commit': {'keys': [[IBus.KEY_space, 0, 0]]}, -+ 'result': {'string': '漫画'} -+ }, -+ {'preedit': {'string': 'wgl'}, -+ 'lookup': {'keys': [[IBus.KEY_Down, 0, 0]]}, -+ 'commit': {'keys': [[IBus.KEY_space, 0, 0]]}, -+ 'result': {'string': '全国'} -+ }, -+ ] -+} -diff -Nru ibus-table-1.9.18/tests/meta.test.in ibus-table-1.9.18.new/tests/meta.test.in ---- ibus-table-1.9.18/tests/meta.test.in 1970-01-01 01:00:00.000000000 +0100 -+++ ibus-table-1.9.18.new/tests/meta.test.in 2020-07-22 15:03:04.097344610 +0200 -@@ -0,0 +1,4 @@ -+[Test] -+Type=session -+Exec=@TEST_EXECDIR@/run_tests @TEST_EXEC@ -+Output=TAP -diff -Nru ibus-table-1.9.18/tests/mock_engine.py ibus-table-1.9.18.new/tests/mock_engine.py ---- ibus-table-1.9.18/tests/mock_engine.py 1970-01-01 01:00:00.000000000 +0100 -+++ ibus-table-1.9.18.new/tests/mock_engine.py 2020-07-22 15:07:00.649895226 +0200 -@@ -0,0 +1,241 @@ -+# ibus-table - The Tables engine for IBus -+# -+# Copyright (c) 2018-2020 Mike FABIAN -+# -+# This library is free software; you can redistribute it and/or -+# modify it under the terms of the GNU Lesser General Public -+# License as published by the Free Software Foundation; either -+# version 2.1 of the License, or (at your option) any later version. -+# -+# This library is distributed in the hope that it will be useful, -+# but WITHOUT ANY WARRANTY; without even the implied warranty of -+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+# Lesser General Public License for more details. -+# -+# You should have received a copy of the GNU Lesser General Public -+# License along with this library; if not, write to the Free Software -+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -+# -+ -+''' -+Define some mock classes for the unittests. -+''' -+ -+from gi import require_version -+require_version('IBus', '1.0') -+from gi.repository import IBus -+ -+class MockEngine: -+ def __init__(self, engine_name = '', connection = None, object_path = ''): -+ self.mock_auxiliary_text = '' -+ self.mock_preedit_text = '' -+ self.mock_preedit_text_cursor_pos = 0 -+ self.mock_preedit_text_visible = True -+ self.mock_committed_text = '' -+ self.mock_committed_text_cursor_pos = 0 -+ self.client_capabilities = ( -+ IBus.Capabilite.PREEDIT_TEXT -+ | IBus.Capabilite.AUXILIARY_TEXT -+ | IBus.Capabilite.LOOKUP_TABLE -+ | IBus.Capabilite.FOCUS -+ | IBus.Capabilite.PROPERTY) -+ # There are lots of weird problems with surrounding text -+ # which makes this hard to test. Therefore this mock -+ # engine does not try to support surrounding text, i.e. -+ # we omit “| IBus.Capabilite.SURROUNDING_TEXT” here. -+ -+ def update_auxiliary_text(self, text, visible): -+ self.mock_auxiliary_text = text.text -+ -+ def hide_auxiliary_text(self): -+ pass -+ -+ def hide_preedit_text(self): -+ pass -+ -+ def commit_text(self, text): -+ self.mock_committed_text = ( -+ self.mock_committed_text[ -+ :self.mock_committed_text_cursor_pos] -+ + text.text -+ + self.mock_committed_text[ -+ self.mock_committed_text_cursor_pos:]) -+ self.mock_committed_text_cursor_pos += len(text.text) -+ -+ def forward_key_event(self, val, code, state): -+ if (val == IBus.KEY_Left -+ and self.mock_committed_text_cursor_pos > 0): -+ self.mock_committed_text_cursor_pos -= 1 -+ return -+ unicode = IBus.keyval_to_unicode(val) -+ if unicode: -+ self.mock_committed_text = ( -+ self.mock_committed_text[ -+ :self.mock_committed_text_cursor_pos] -+ + unicode -+ + self.mock_committed_text[ -+ self.mock_committed_text_cursor_pos:]) -+ self.mock_committed_text_cursor_pos += len(unicode) -+ -+ def update_lookup_table(self, table, visible): -+ pass -+ -+ def update_preedit_text(self, text, cursor_pos, visible): -+ self.mock_preedit_text = text.get_text() -+ self.mock_preedit_text_cursor_pos = cursor_pos -+ self.mock_preedit_text_visible = visible -+ -+ def register_properties(self, property_list): -+ pass -+ -+ def update_property(self, property): -+ pass -+ -+ def hide_lookup_table(self): -+ pass -+ -+class MockLookupTable: -+ def __init__(self, page_size = 9, cursor_pos = 0, cursor_visible = False, round = True): -+ self.clear() -+ self.mock_page_size = page_size -+ self.mock_cursor_pos = cursor_pos -+ self.mock_cursor_visible = cursor_visible -+ self.cursor_visible = cursor_visible -+ self.mock_round = round -+ self.mock_candidates = [] -+ self.mock_labels = [] -+ self.mock_page_number = 0 -+ -+ def clear(self): -+ self.mock_candidates = [] -+ self.mock_cursor_pos = 0 -+ -+ def set_page_size(self, size): -+ self.mock_page_size = size -+ -+ def get_page_size(self): -+ return self.mock_page_size -+ -+ def set_round(self, round): -+ self.mock_round = round -+ -+ def set_cursor_pos(self, pos): -+ self.mock_cursor_pos = pos -+ -+ def get_cursor_pos(self): -+ return self.mock_cursor_pos -+ -+ def get_cursor_in_page(self): -+ return (self.mock_cursor_pos -+ - self.mock_page_size * self.mock_page_number) -+ -+ def set_cursor_visible(self, visible): -+ self.mock_cursor_visible = visible -+ self.cursor_visible = visible -+ -+ def cursor_down(self): -+ if len(self.mock_candidates): -+ self.mock_cursor_pos += 1 -+ self.mock_cursor_pos %= len(self.mock_candidates) -+ -+ def cursor_up(self): -+ if len(self.mock_candidates): -+ if self.mock_cursor_pos > 0: -+ self.mock_cursor_pos -= 1 -+ else: -+ self.mock_cursor_pos = len(self.mock_candidates) - 1 -+ -+ def page_down(self): -+ if len(self.mock_candidates): -+ self.mock_page_number += 1 -+ self.mock_cursor_pos += self.mock_page_size -+ -+ def page_up(self): -+ if len(self.mock_candidates): -+ if self.mock_page_number > 0: -+ self.mock_page_number -= 1 -+ self.mock_cursor_pos -= self.mock_page_size -+ -+ def set_orientation(self, orientation): -+ self.mock_orientation = orientation -+ -+ def get_number_of_candidates(self): -+ return len(self.mock_candidates) -+ -+ def append_candidate(self, candidate): -+ self.mock_candidates.append(candidate.get_text()) -+ -+ def get_candidate(self, index): -+ return self.mock_candidates[index] -+ -+ def get_number_of_candidates(self): -+ return len(self.mock_candidates) -+ -+ def append_label(self, label): -+ self.mock_labels.append(label.get_text()) -+ -+class MockPropList: -+ def __init__(self, *args, **kwargs): -+ self._mock_proplist = [] -+ -+ def append(self, property): -+ self._mock_proplist.append(property) -+ -+ def get(self, index): -+ if index >= 0 and index < len(self._mock_proplist): -+ return self._mock_proplist[index] -+ else: -+ return None -+ -+ def update_property(self, property): -+ pass -+ -+class MockProperty: -+ def __init__(self, -+ key='', -+ prop_type=IBus.PropType.RADIO, -+ label=IBus.Text.new_from_string(''), -+ symbol=IBus.Text.new_from_string(''), -+ icon='', -+ tooltip=IBus.Text.new_from_string(''), -+ sensitive=True, -+ visible=True, -+ state=IBus.PropState.UNCHECKED, -+ sub_props=None): -+ self.mock_property_key = key -+ self.mock_property_prop_type = prop_type -+ self.mock_property_label = label.get_text() -+ self.mock_property_symbol = symbol.get_text() -+ self.mock_property_icon = icon -+ self.mock_property_tooltip = tooltip.get_text() -+ self.mock_property_sensitive = sensitive -+ self.mock_property_visible = visible -+ self.mock_property_state = state -+ self.mock_property_sub_props = sub_props -+ -+ def set_label(self, ibus_text): -+ self.mock_property_label = ibus_text.get_text() -+ -+ def set_symbol(self, ibus_text): -+ self.mock_property_symbol = ibus_text.get_text() -+ -+ def set_tooltip(self, ibus_text): -+ self.mock_property_tooltip = ibus_text.get_text() -+ -+ def set_icon(self, icon_path): -+ self.mock_property_icon = icon_path -+ -+ def set_sensitive(self, sensitive): -+ self.mock_property_sensitive = sensitive -+ -+ def set_visible(self, visible): -+ self.mock_property_visible = visible -+ -+ def set_state(self, state): -+ self.mock_property_state = state -+ -+ def set_sub_props(self, proplist): -+ self.mock_property_sub_props = proplist -+ -+ def get_key(self): -+ return self.mock_property_key -diff -Nru ibus-table-1.9.18/tests/run_tests.in ibus-table-1.9.18.new/tests/run_tests.in ---- ibus-table-1.9.18/tests/run_tests.in 2020-07-22 15:02:24.349755691 +0200 -+++ ibus-table-1.9.18.new/tests/run_tests.in 2020-07-22 15:04:42.505326247 +0200 -@@ -15,239 +15,47 @@ - # Lesser General Public License for more details. - # - # You should have received a copy of the GNU Lesser General Public --# License along with this library; if not, write to the Free Software --# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA --# -+# License along with this library. If not, see - --import os - import sys -+import os - import unittest - --from gi import require_version --require_version('IBus', '1.0') --from gi.repository import IBus -- --# -- Define some mock classes for the tests ---------------------------------- --class MockEngine: -- def __init__(self, engine_name = '', connection = None, object_path = ''): -- self.mock_auxiliary_text = '' -- self.mock_preedit_text = '' -- self.mock_preedit_text_cursor_pos = 0 -- self.mock_preedit_text_visible = True -- self.mock_committed_text = '' -- self.mock_committed_text_cursor_pos = 0 -- self.client_capabilities = ( -- IBus.Capabilite.PREEDIT_TEXT -- | IBus.Capabilite.AUXILIARY_TEXT -- | IBus.Capabilite.LOOKUP_TABLE -- | IBus.Capabilite.FOCUS -- | IBus.Capabilite.PROPERTY) -- # There are lots of weird problems with surrounding text -- # which makes this hard to test. Therefore this mock -- # engine does not try to support surrounding text, i.e. -- # we omit “| IBus.Capabilite.SURROUNDING_TEXT” here. -- -- def update_auxiliary_text(self, text, visible): -- self.mock_auxiliary_text = text.text -- -- def hide_auxiliary_text(self): -- pass -- -- def commit_text(self, text): -- self.mock_committed_text = ( -- self.mock_committed_text[ -- :self.mock_committed_text_cursor_pos] -- + text.text -- + self.mock_committed_text[ -- self.mock_committed_text_cursor_pos:]) -- self.mock_committed_text_cursor_pos += len(text.text) -- -- def forward_key_event(self, val, code, state): -- if (val == IBus.KEY_Left -- and self.mock_committed_text_cursor_pos > 0): -- self.mock_committed_text_cursor_pos -= 1 -- return -- unicode = IBus.keyval_to_unicode(val) -- if unicode: -- self.mock_committed_text = ( -- self.mock_committed_text[ -- :self.mock_committed_text_cursor_pos] -- + unicode -- + self.mock_committed_text[ -- self.mock_committed_text_cursor_pos:]) -- self.mock_committed_text_cursor_pos += len(unicode) -- -- def update_lookup_table(self, table, visible): -- pass -- -- def update_preedit_text(self, text, cursor_pos, visible): -- self.mock_preedit_text = text.get_text() -- self.mock_preedit_text_cursor_pos = cursor_pos -- self.mock_preedit_text_visible = visible -- -- def register_properties(self, property_list): -- pass -- -- def update_property(self, property): -- pass -- -- def hide_lookup_table(self): -- pass -- --class MockLookupTable: -- def __init__(self, page_size = 9, cursor_pos = 0, cursor_visible = False, round = True): -- self.clear() -- self.mock_page_size = page_size -- self.mock_cursor_pos = cursor_pos -- self.mock_cursor_visible = cursor_visible -- self.cursor_visible = cursor_visible -- self.mock_round = round -- self.mock_candidates = [] -- self.mock_labels = [] -- self.mock_page_number = 0 -- -- def clear(self): -- self.mock_candidates = [] -- self.mock_cursor_pos = 0 -- -- def set_page_size(self, size): -- self.mock_page_size = size -- -- def get_page_size(self): -- return self.mock_page_size -- -- def set_round(self, round): -- self.mock_round = round -- -- def set_cursor_pos(self, pos): -- self.mock_cursor_pos = pos -- -- def get_cursor_pos(self): -- return self.mock_cursor_pos -- -- def get_cursor_in_page(self): -- return (self.mock_cursor_pos -- - self.mock_page_size * self.mock_page_number) -- -- def set_cursor_visible(self, visible): -- self.mock_cursor_visible = visible -- self.cursor_visible = visible -- -- def cursor_down(self): -- if len(self.mock_candidates): -- self.mock_cursor_pos += 1 -- self.mock_cursor_pos %= len(self.mock_candidates) -- -- def cursor_up(self): -- if len(self.mock_candidates): -- if self.mock_cursor_pos > 0: -- self.mock_cursor_pos -= 1 -- else: -- self.mock_cursor_pos = len(self.mock_candidates) - 1 -- -- def page_down(self): -- if len(self.mock_candidates): -- self.mock_page_number += 1 -- self.mock_cursor_pos += self.mock_page_size -- -- def page_up(self): -- if len(self.mock_candidates): -- if self.mock_page_number > 0: -- self.mock_page_number -= 1 -- self.mock_cursor_pos -= self.mock_page_size -- -- def set_orientation(self, orientation): -- self.mock_orientation = orientation -- -- def get_number_of_candidates(self): -- return len(self.mock_candidates) -- -- def append_candidate(self, candidate): -- self.mock_candidates.append(candidate.get_text()) -- -- def get_candidate(self, index): -- return self.mock_candidates[index] -- -- def get_number_of_candidates(self): -- return len(self.mock_candidates) -- -- def append_label(self, label): -- self.mock_labels.append(label.get_text()) -- --class MockPropList: -- def __init__(self, *args, **kwargs): -- self._mock_proplist = [] -- -- def append(self, property): -- self._mock_proplist.append(property) -- -- def get(self, index): -- if index >= 0 and index < len(self._mock_proplist): -- return self._mock_proplist[index] -- else: -- return None -- -- def update_property(self, property): -- pass -- --class MockProperty: -- def __init__(self, -- key='', -- prop_type=IBus.PropType.RADIO, -- label=IBus.Text.new_from_string(''), -- symbol=IBus.Text.new_from_string(''), -- icon='', -- tooltip=IBus.Text.new_from_string(''), -- sensitive=True, -- visible=True, -- state=IBus.PropState.UNCHECKED, -- sub_props=None): -- self.mock_property_key = key -- self.mock_property_prop_type = prop_type -- self.mock_property_label = label.get_text() -- self.mock_property_symbol = symbol.get_text() -- self.mock_property_icon = icon -- self.mock_property_tooltip = tooltip.get_text() -- self.mock_property_sensitive = sensitive -- self.mock_property_visible = visible -- self.mock_property_state = state -- self.mock_property_sub_props = sub_props -- -- def set_label(self, ibus_text): -- self.mock_property_label = ibus_text.get_text() -- -- def set_symbol(self, ibus_text): -- self.mock_property_symbol = ibus_text.get_text() -- -- def set_tooltip(self, ibus_text): -- self.mock_property_tooltip = ibus_text.get_text() -- -- def set_sensitive(self, sensitive): -- self.mock_property_sensitive = sensitive -- -- def set_visible(self, visible): -- self.mock_property_visible = visible -- -- def set_state(self, state): -- self.mock_property_state = state -- -- def set_sub_props(self, proplist): -- self.mock_property_sub_props = proplist -- -- def get_key(self): -- return self.mock_property_key -- --# -- Monkey patch the environment with the mock classes ---------------------- --sys.modules["gi.repository.IBus"].Engine = MockEngine --sys.modules["gi.repository.IBus"].LookupTable = MockLookupTable --sys.modules["gi.repository.IBus"].Property = MockProperty --sys.modules["gi.repository.IBus"].PropList = MockPropList -+# pip3 install tap.py --user -+IMPORT_TAP_SUCCESSFUL = False -+try: -+ from tap import TAPTestRunner -+ IMPORT_TAP_SUCCESSFUL = True -+except (ImportError,): -+ pass -+ -+if 'IBUS_TABLE_LOCATION' in os.environ: -+ location_path = os.environ['IBUS_TABLE_LOCATION'] -+ if location_path != None and location_path != '': -+ engine_path = os.path.join(location_path, 'engine') -+ sys.path.append(engine_path) -+sys.path.append('@PKGDATADIR@/engine') - - # -- Load and run our unit tests --------------------------------------------- --os.environ['IBUS_TABLE_DEBUG_LEVEL'] = '255' -+pattern = 'test*.py' -+start_dir = os.path.dirname(__file__) -+if len(sys.argv) > 1: -+ pattern = sys.argv[-1] -+ dir = os.path.dirname(pattern) -+ pattern = os.path.basename(pattern) -+ if dir != '.': -+ start_dir = os.path.join(start_dir, dir) - loader = unittest.TestLoader() --suite = loader.discover(".") --runner = unittest.TextTestRunner(stream = sys.stderr, verbosity = 255) -+suite = loader.discover(start_dir=start_dir, pattern=pattern) -+ -+if IMPORT_TAP_SUCCESSFUL: -+ runner = TAPTestRunner(stream=sys.stderr, verbosity=255) -+ runner.set_outdir('.') -+ runner.set_format('Hi: {method_name} - {short_description}') -+ runner.set_combined(True) -+else: -+ runner = unittest.TextTestRunner(stream=sys.stderr, verbosity=255) -+ - result = runner.run(suite) - - if result.failures or result.errors: -diff -Nru ibus-table-1.9.18/tests/test_0_gtk.py ibus-table-1.9.18.new/tests/test_0_gtk.py ---- ibus-table-1.9.18/tests/test_0_gtk.py 1970-01-01 01:00:00.000000000 +0100 -+++ ibus-table-1.9.18.new/tests/test_0_gtk.py 2020-07-22 15:09:02.734630566 +0200 -@@ -0,0 +1,441 @@ -+#!/usr/bin/python3 -+# -*- coding: utf-8 -*- -+# -+# ibus-table - The Tables engine for IBus -+# -+# Copyright (c) 2020 Takao Fujiwara -+# Copyright (c) 2020 Mike FABIAN -+# -+# This library is free software; you can redistribute it and/or -+# modify it under the terms of the GNU Lesser General Public -+# License as published by the Free Software Foundation; either -+# version 2.1 of the License, or (at your option) any later version. -+# -+# This library is distributed in the hope that it will be useful, -+# but WITHOUT ANY WARRANTY; without even the implied warranty of -+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+# Lesser General Public License for more details. -+# -+# You should have received a copy of the GNU Lesser General Public -+# License along with this library. If not, see -+''' -+This file implements the test cases using GTK GUI -+''' -+# “Wrong continued indentation”: pylint: disable=bad-continuation -+# pylint: disable=attribute-defined-outside-init -+# pylint: disable=missing-function-docstring -+# pylint: disable=missing-class-docstring -+# pylint: disable=global-statement -+# pylint: disable=wrong-import-order -+# pylint: disable=wrong-import-position -+ -+import argparse -+import os -+import signal -+import sys -+import unittest -+ -+from gi import require_version as gi_require_version -+gi_require_version('GLib', '2.0') -+gi_require_version('Gdk', '3.0') -+gi_require_version('Gio', '2.0') -+gi_require_version('Gtk', '3.0') -+gi_require_version('IBus', '1.0') -+from gi.repository import GLib -+from gi.repository import Gdk -+from gi.repository import Gio -+from gi.repository import Gtk -+from gi.repository import IBus -+ -+# Get more verbose output in the test log: -+os.environ['IBUS_TABLE_DEBUG_LEVEL'] = '255' -+ -+sys.path.insert(0, "../engine") -+IMPORT_TABLE_SUCCESSFUL = False -+try: -+ import table -+ IMPORT_TABLE_SUCCESSFUL = True -+except (ImportError,): -+ pass -+IMPORT_TABSQLITEDB_SUCCESSFUL = False -+try: -+ import tabsqlitedb -+ IMPORT_TABSQLITEDB_SUCCESSFUL = True -+except (ImportError,): -+ pass -+# FIXME: -+#sys.path.pop(0) -+ -+DONE_EXIT = True -+ENGINE_NAME = 'wubi-jidian86' -+ -+from gtkcases import TestCases -+ -+# Need to flush the output against Gtk.main() -+def printflush(sentence): -+ try: -+ print(sentence, flush=True) -+ except IOError: -+ pass -+ -+def printerr(sentence): -+ try: -+ print(sentence, flush=True, file=sys.stderr) -+ except IOError: -+ pass -+ -+@unittest.skipUnless( -+ os.path.isfile( -+ os.path.join('/usr/share/ibus-table/tables', ENGINE_NAME + '.db')), -+ '%s.db is not installed.' % ENGINE_NAME + '.db') -+@unittest.skipUnless( -+ 'XDG_SESSION_TYPE' in os.environ -+ and os.environ['XDG_SESSION_TYPE'] in ('x11', 'wayland'), -+ 'XDG_SESSION_TYPE is neither "x11" nor "wayland".') -+@unittest.skipIf(Gdk.Display.open('') is None, 'Display cannot be opened.') -+class SimpleGtkTestCase(unittest.TestCase): -+ global DONE_EXIT -+ global ENGINE_NAME -+ ENGINE_PATH = '/com/redhat/IBus/engines/table/Test/Engine' -+ -+ @classmethod -+ def setUpClass(cls): -+ cls._flag = False -+ IBus.init() -+ cls._gsettings = Gio.Settings( -+ schema='org.freedesktop.ibus.engine.table', -+ path='/org/freedesktop/ibus/engine/table/%s/' % ENGINE_NAME) -+ cls._orig_chinesemode = cls._gsettings.get_int('chinesemode') -+ signums = [getattr(signal, s, None) for s in -+ 'SIGINT SIGTERM SIGHUP'.split()] -+ for signum in filter(None, signums): -+ original_handler = signal.getsignal(signum) -+ GLib.unix_signal_add(GLib.PRIORITY_HIGH, -+ signum, -+ cls.signal_handler, -+ (signum, original_handler)) -+ @classmethod -+ def tearDownClass(cls): -+ cls._gsettings.set_int('chinesemode', cls._orig_chinesemode) -+ -+ @classmethod -+ def signal_handler(cls, user_data): -+ (signum, original_handler) = user_data -+ cls.tearDownClass() -+ Gtk.main_quit() -+ signal.signal(signum, original_handler) -+ cls._flag = True -+ assert False, 'signal received: ' + str(signum) -+ -+ def setUp(self): -+ self.__id = 0 -+ self.__rerun = False -+ self.__test_index = 0 -+ self.__preedit_index = 0 -+ self.__lookup_index = 0 -+ self.__inserted_text = '' -+ self.__commit_done = False -+ self.__reset_coming = False -+ self._gsettings.set_int('chinesemode', 4) -+ -+ def register_ibus_engine(self): -+ self.__bus = IBus.Bus() -+ if not self.__bus.is_connected(): -+ self.fail('ibus-daemon is not running') -+ return False -+ self.__bus.get_connection().signal_subscribe( -+ 'org.freedesktop.DBus', -+ 'org.freedesktop.DBus', -+ 'NameOwnerChanged', -+ '/org/freedesktop/DBus', -+ None, -+ 0, -+ self.__bus_signal_cb, -+ self.__bus) -+ self.__factory = IBus.Factory( -+ object_path=IBus.PATH_FACTORY, -+ connection=self.__bus.get_connection()) -+ self.__factory.connect('create-engine', self.__create_engine_cb) -+ self.__component = IBus.Component( -+ name='org.freedesktop.IBus.Table.Test', -+ description='Test Table Component', -+ version='1.0', -+ license='GPL', -+ author=('Mike FABIAN , ' -+ + 'Caius "kaio" CHANCE '), -+ homepage='http://mike-fabian.github.io/ibus-table/', -+ command_line='', -+ textdomain='ibus-table') -+ desc = IBus.EngineDesc( -+ name=ENGINE_NAME, -+ longname='Test Table %s' % ENGINE_NAME, -+ description='Test Table Component', -+ language='t', -+ license='GPL', -+ author=('Mike FABIAN , ' -+ + 'Caius "kaio" CHANCE '), -+ icon='', -+ symbol='T') -+ self.__component.add_engine(desc) -+ self.__bus.register_component(self.__component) -+ self.__bus.request_name('org.freedesktop.IBus.Table.Test', 0) -+ return True -+ -+ def __bus_signal_cb(self, connection, sender_name, object_path, -+ interface_name, signal_name, parameters, -+ user_data): -+ if signal_name == 'NameOwnerChanged': -+ pass -+ if signal_name == 'UpdateLookupTable': -+ table = self.__engine._editor.get_lookup_table() -+ if table.get_number_of_candidates() == 0: -+ return -+ self.__lookup_test() -+ -+ def __create_engine_cb(self, factory, engine_name): -+ if engine_name != ENGINE_NAME: -+ return None -+ if (not IMPORT_TABLE_SUCCESSFUL -+ or not IMPORT_TABSQLITEDB_SUCCESSFUL): -+ with self.subTest(i='create-engine'): -+ self.fail('NG: ibus-table not installed?') -+ Gtk.main_quit() -+ return None -+ self.__id += 1 -+ object_path = '%s/%d' % (self.ENGINE_PATH, self.__id) -+ db_dir = '/usr/share/ibus-table/tables' -+ db_file = os.path.join(db_dir, engine_name + '.db') -+ database = tabsqlitedb.TabSqliteDb(filename=db_file, user_db=':memory:') -+ self.__engine = table.TabEngine( -+ self.__bus, -+ object_path, -+ database) -+ self.__engine.connect('focus-in', self.__engine_focus_in) -+ self.__engine.connect('focus-out', self.__engine_focus_out) -+ # FIXME: Need to connect 'reset' after TabEngine.clear_all_input_and_preedit() -+ # is called. -+ self.__engine.connect_after('reset', self.__engine_reset) -+ self.__bus.get_connection().signal_subscribe( -+ None, -+ IBus.INTERFACE_ENGINE, -+ 'UpdateLookupTable', -+ object_path, -+ None, -+ 0, -+ self.__bus_signal_cb, -+ self.__bus) -+ return self.__engine -+ -+ def __engine_focus_in(self, _engine): -+ if self.__test_index == len(TestCases['tests']): -+ if DONE_EXIT: -+ Gtk.main_quit() -+ return -+ # Workaround because focus-out resets the preedit text -+ # ibus_bus_set_global_engine() calls bus_input_context_set_engine() -+ # twice and it causes bus_engine_proxy_focus_out() -+ if self.__rerun: -+ self.__rerun = False -+ self.__main_test() -+ -+ def __engine_focus_out(self, _engine): -+ self.__rerun = True -+ self.__test_index = 0 -+ self.__entry.set_text('') -+ -+ def __engine_reset(self, _engine): -+ if self.__reset_coming: -+ self.__reset_coming = False -+ self.__main_test() -+ -+ def __entry_focus_in_event_cb(self, entry, event): -+ if self.__test_index == len(TestCases['tests']): -+ if DONE_EXIT: -+ Gtk.main_quit() -+ return False -+ self.__bus.set_global_engine_async(ENGINE_NAME, -+ -1, None, self.__set_engine_cb) -+ return False -+ -+ def __set_engine_cb(self, _object, res): -+ with self.subTest(i=self.__test_index): -+ if not self.__bus.set_global_engine_async_finish(res): -+ self.fail('set engine failed.') -+ return -+ # rerun always happen? -+ #self.__main_test() -+ -+ def __get_test_condition_length(self, tag): -+ tests = TestCases['tests'][self.__test_index] -+ try: -+ cases = tests[tag] -+ except KeyError: -+ return -1 -+ case_type = list(cases.keys())[0] -+ return len(cases[case_type]) -+ -+ def __entry_preedit_changed_cb(self, entry, preedit_str): -+ if len(preedit_str) == 0: -+ return -+ if self.__test_index == len(TestCases['tests']): -+ if DONE_EXIT: -+ Gtk.main_quit() -+ return -+ self.__preedit_index += 1 -+ if self.__preedit_index != self.__get_test_condition_length('preedit'): -+ return -+ if self.__get_test_condition_length('lookup') > 0: -+ return -+ self.__run_cases('commit') -+ -+ def __main_test(self): -+ self.__preedit_index = 0 -+ self.__lookup_index = 0 -+ self.__commit_done = False -+ self.__run_cases('preedit') -+ -+ def __lookup_test(self): -+ lookup_length = self.__get_test_condition_length('lookup') -+ # Need to return again even if all the lookup is finished -+ # until the final Engine.update_preedit() is called. -+ if self.__lookup_index > lookup_length: -+ return -+ self.__run_cases('lookup', -+ self.__lookup_index, -+ self.__lookup_index + 1) -+ if self.__lookup_index < lookup_length: -+ self.__lookup_index += 1 -+ return -+ self.__lookup_index += 1 -+ self.__run_cases('commit') -+ -+ def __run_cases(self, tag, start=-1, end=-1): -+ tests = TestCases['tests'][self.__test_index] -+ if tests is None: -+ return -+ try: -+ cases = tests[tag] -+ except KeyError: -+ return -+ case_type = list(cases.keys())[0] -+ i = 0 -+ if case_type == 'string': -+ printflush('test step: %s sequences: "%s"' -+ % (tag, str(cases['string']))) -+ for character in cases['string']: -+ if start >= 0 and i < start: -+ i += 1 -+ continue -+ if 0 <= end <= i: -+ break -+ self.__typing(ord(character), 0, 0) -+ i += 1 -+ if case_type == 'keys': -+ if start == -1 and end == -1: -+ printflush('test step: %s sequences: %s' -+ % (tag, str(cases['keys']))) -+ for key in cases['keys']: -+ if start >= 0 and i < start: -+ i += 1 -+ continue -+ if 0 <= end <= i: -+ break -+ if start != -1 or end != -1: -+ printflush('test step: %s sequences: [0x%X, 0x%X, 0x%X]' -+ % (tag, key[0], key[1], key[2])) -+ self.__typing(key[0], key[1], key[2]) -+ i += 1 -+ -+ def __typing(self, keyval, keycode, modifiers): -+ self.__engine.emit('process-key-event', keyval, keycode, modifiers) -+ modifiers |= IBus.ModifierType.RELEASE_MASK -+ self.__engine.emit('process-key-event', keyval, keycode, modifiers) -+ -+ def __buffer_inserted_text_cb(self, buffer, position, chars, nchars): -+ tests = TestCases['tests'][self.__test_index] -+ cases = tests['commit'] -+ case_type = list(cases.keys())[0] -+ if case_type == 'keys': -+ # space key is sent separatedly later -+ if cases['keys'][0] == [IBus.KEY_space, 0, 0]: -+ self.__inserted_text += chars -+ # FIXME: Return key emits 'reset' signal in GTK and it calls -+ # TableEngine.clear_all_input_and_preedit(). -+ elif cases['keys'][0] == [IBus.KEY_Return, 0, 0] or \ -+ cases['keys'][0] == [IBus.KEY_KP_Enter, 0, 0] or \ -+ cases['keys'][0] == [IBus.KEY_ISO_Enter, 0, 0] or \ -+ cases['keys'][0] == [IBus.KEY_Escape, 0, 0]: -+ self.__inserted_text = chars -+ self.__reset_coming = True -+ else: -+ self.__inserted_text = chars -+ cases = tests['result'] -+ if cases['string'] == self.__inserted_text: -+ printflush('OK: %d "%s"' -+ % (self.__test_index, self.__inserted_text)) -+ else: -+ if DONE_EXIT: -+ Gtk.main_quit() -+ with self.subTest(i=self.__test_index): -+ self.fail('NG: %d "%s" "%s"' -+ % (self.__test_index, str(cases['string']), -+ self.__inserted_text)) -+ self.__inserted_text = '' -+ self.__test_index += 1 -+ if self.__test_index == len(TestCases['tests']): -+ if DONE_EXIT: -+ Gtk.main_quit() -+ return -+ self.__commit_done = True -+ self.__entry.set_text('') -+ if not self.__reset_coming: -+ self.__main_test() -+ -+ def create_window(self): -+ window = Gtk.Window(type=Gtk.WindowType.TOPLEVEL) -+ self.__entry = entry = Gtk.Entry() -+ window.connect('destroy', Gtk.main_quit) -+ entry.connect('focus-in-event', self.__entry_focus_in_event_cb) -+ entry.connect('preedit-changed', self.__entry_preedit_changed_cb) -+ buffer = entry.get_buffer() -+ buffer.connect('inserted-text', self.__buffer_inserted_text_cb) -+ window.add(entry) -+ window.show_all() -+ -+ def main(self): # pylint: disable=no-self-use -+ # Some ATK relative warnings are called during launching GtkWindow. -+ flags = GLib.log_set_always_fatal(GLib.LogLevelFlags.LEVEL_CRITICAL) -+ Gtk.main() -+ GLib.log_set_always_fatal(flags) -+ -+ def test_typing(self): -+ if not self.register_ibus_engine(): -+ sys.exit(-1) -+ self.create_window() -+ self.main() -+ if self._flag: -+ self.fail('NG: signal failure') -+ -+def main(): -+ parser = argparse.ArgumentParser() -+ parser.add_argument('-k', '--keep', action='store_true', -+ help='keep this GtkWindow after test is done') -+ parser.add_argument('-F', '--unittest-failfast', action='store_true', -+ help='stop on first fail or error in unittest') -+ parser.add_argument('-H', '--unittest-help', action='store_true', -+ help='show unittest help message and exit') -+ args, unittest_args = parser.parse_known_args() -+ sys.argv[1:] = unittest_args -+ if args.keep: -+ global DONE_EXIT -+ DONE_EXIT = False -+ if args.unittest_failfast: -+ sys.argv.append('-f') -+ if args.unittest_help: -+ sys.argv.append('-h') -+ unittest.main() -+ -+ unittest.main() -+ -+if __name__ == '__main__': -+ main() -diff -Nru ibus-table-1.9.18/tests/test_it.py ibus-table-1.9.18.new/tests/test_it.py ---- ibus-table-1.9.18/tests/test_it.py 2020-07-22 15:02:24.350755681 +0200 -+++ ibus-table-1.9.18.new/tests/test_it.py 2020-07-22 15:05:43.128698254 +0200 -@@ -1,6 +1,5 @@ --# -*- coding: utf-8 -*- --# vim:et sts=4 sw=4 --# -+#!/usr/bin/python3 -+ - # ibus-table - The Tables engine for IBus - # - # Copyright (c) 2018 Mike FABIAN -@@ -26,19 +25,41 @@ - - import sys - import os --import unicodedata - import unittest --import subprocess -+import importlib -+import mock - - from gi import require_version - require_version('IBus', '1.0') - from gi.repository import IBus - -+# Get more verbose output in the test log: -+os.environ['IBUS_TABLE_DEBUG_LEVEL'] = '255' -+ -+# Monkey patch the environment with the mock classes: -+from mock_engine import MockEngine -+from mock_engine import MockLookupTable -+from mock_engine import MockProperty -+from mock_engine import MockPropList -+ - sys.path.insert(0, "../engine") --from table import * -+import table - import tabsqlitedb --import it_util --#sys.path.pop(0) -+import ibus_table_location -+sys.path.pop(0) -+ -+ENGINE_PATCHER = mock.patch.object( -+ IBus, 'Engine', new=MockEngine) -+LOOKUP_TABLE_PATCHER = mock.patch.object( -+ IBus, 'LookupTable', new=MockLookupTable) -+PROPERTY_PATCHER = mock.patch.object( -+ IBus, 'Property', new=MockProperty) -+PROP_LIST_PATCHER = mock.patch.object( -+ IBus, 'PropList', new=MockPropList) -+IBUS_ENGINE = IBus.Engine -+IBUS_LOOKUP_TABLE = IBus.LookupTable -+IBUS_PROPERTY = IBus.Property -+IBUS_PROP_LIST = IBus.PropList - - ENGINE = None - TABSQLITEDB = None -@@ -56,6 +77,8 @@ - ORIG_AUTOWILDCARD_MODE = None - ORIG_SINGLE_WILDCARD_CHAR = None - ORIG_MULTI_WILDCARD_CHAR = None -+ORIG_PINYIN_MODE = None -+ORIG_SUGGESTION_MODE = None - - def backup_original_settings(): - global ENGINE -@@ -73,6 +96,8 @@ - global ORIG_AUTOWILDCARD_MODE - global ORIG_SINGLE_WILDCARD_CHAR - global ORIG_MULTI_WILDCARD_CHAR -+ global ORIG_PINYIN_MODE -+ global ORIG_SUGGESTION_MODE - ORIG_INPUT_MODE = ENGINE.get_input_mode() - ORIG_CHINESE_MODE = ENGINE.get_chinese_mode() - ORIG_LETTER_WIDTH = ENGINE.get_letter_width() -@@ -87,6 +112,8 @@ - ORIG_AUTOWILDCARD_MODE = ENGINE.get_autowildcard_mode() - ORIG_SINGLE_WILDCARD_CHAR = ENGINE.get_single_wildcard_char() - ORIG_MULTI_WILDCARD_CHAR = ENGINE.get_multi_wildcard_char() -+ ORIG_PINYIN_MODE = ENGINE.get_pinyin_mode() -+ ORIG_SUGGESTION_MODE = ENGINE.get_suggestion_mode() - - def restore_original_settings(): - global ENGINE -@@ -104,6 +131,8 @@ - global ORIG_AUTOWILDCARD_MODE - global ORIG_SINGLE_WILDCARD_CHAR - global ORIG_MULTI_WILDCARD_CHAR -+ global ORIG_PINYIN_MODE -+ global ORIG_SUGGESTION_MODE - ENGINE.set_input_mode(ORIG_INPUT_MODE) - ENGINE.set_chinese_mode(ORIG_CHINESE_MODE) - ENGINE.set_letter_width(ORIG_LETTER_WIDTH[0], input_mode=0) -@@ -120,14 +149,16 @@ - ENGINE.set_autowildcard_mode(ORIG_AUTOWILDCARD_MODE) - ENGINE.set_single_wildcard_char(ORIG_SINGLE_WILDCARD_CHAR) - ENGINE.set_multi_wildcard_char(ORIG_MULTI_WILDCARD_CHAR) -+ ENGINE.set_pinyin_mode(ORIG_PINYIN_MODE) -+ ENGINE.set_suggestion_mode(ORIG_SUGGESTION_MODE) - - def set_default_settings(): - global ENGINE - global TABSQLITEDB - ENGINE.set_input_mode(mode=1) -- chinese_mode = 0 -+ chinese_mode = 4 - language_filter = TABSQLITEDB.ime_properties.get('language_filter') -- if language_filter in ['cm0', 'cm1', 'cm2', 'cm3', 'cm4']: -+ if language_filter in ('cm0', 'cm1', 'cm2', 'cm3', 'cm4'): - chinese_mode = int(language_filter[-1]) - ENGINE.set_chinese_mode(mode=chinese_mode) - -@@ -136,7 +167,7 @@ - 'def_full_width_letter') - if def_full_width_letter: - letter_width_mode = (def_full_width_letter.lower() == u'true') -- ENGINE.set_letter_width(mode=letter_width_mode, input_mode=0) -+ ENGINE.set_letter_width(mode=False, input_mode=0) - ENGINE.set_letter_width(mode=letter_width_mode, input_mode=1) - - punctuation_width_mode = False -@@ -144,7 +175,7 @@ - 'def_full_width_punct') - if def_full_width_punct: - punctuation_width_mode = (def_full_width_punct.lower() == u'true') -- ENGINE.set_punctuation_width(mode=punctuation_width_mode, input_mode=0) -+ ENGINE.set_punctuation_width(mode=False, input_mode=0) - ENGINE.set_punctuation_width(mode=punctuation_width_mode, input_mode=1) - - always_show_lookup_mode = True -@@ -161,7 +192,8 @@ - select_keys_csv = TABSQLITEDB.ime_properties.get('select_keys') - # select_keys_csv is something like: "1,2,3,4,5,6,7,8,9,0" - if select_keys_csv: -- ENGINE.set_page_size(len(select_keys_csv.split(","))) -+ page_size = len(select_keys_csv.split(",")) -+ ENGINE.set_page_size(page_size) - - onechar = False - ENGINE.set_onechar_mode(onechar) -@@ -223,28 +255,98 @@ - multi_wildcard_char = multi_wildcard_char[0] - ENGINE.set_multi_wildcard_char(multi_wildcard_char) - -+ ENGINE.set_pinyin_mode(False) -+ ENGINE.set_suggestion_mode(False) -+ - def set_up(engine_name): -+ ''' -+ Setup an ibus table engine -+ -+ :param engine_name: The name of the engine to setup -+ :type engine_name: String -+ :return: True if the engine could be setup successfully, False if not. -+ :rtype: Boolean -+ ''' -+ global ENGINE_PATCHER -+ global LOOKUP_TABLE_PATCHER -+ global PROPERTY_PATCHER -+ global PROP_LIST_PATCHER -+ global IBUS_ENGINE -+ global IBUS_LOOKUP_TABLE -+ global IBUS_PROPERTY -+ global IBUS_PROP_LIST - global TABSQLITEDB - global ENGINE -+ ENGINE_PATCHER.start() -+ LOOKUP_TABLE_PATCHER.start() -+ PROPERTY_PATCHER.start() -+ PROP_LIST_PATCHER.start() -+ assert IBus.Engine is not IBUS_ENGINE -+ assert IBus.Engine is MockEngine -+ assert IBus.LookupTable is not IBUS_LOOKUP_TABLE -+ assert IBus.LookupTable is MockLookupTable -+ assert IBus.Property is not IBUS_PROPERTY -+ assert IBus.Property is MockProperty -+ assert IBus.PropList is not IBUS_PROP_LIST -+ assert IBus.PropList is MockPropList -+ # Reload the table module so that the patches -+ # are applied to TabEngine: -+ sys.path.insert(0, '../engine') -+ importlib.reload(table) -+ sys.path.pop(0) - bus = IBus.Bus() - db_dir = '/usr/share/ibus-table/tables' - db_file = os.path.join(db_dir, engine_name + '.db') -- TABSQLITEDB = tabsqlitedb.tabsqlitedb( -+ if not os.path.isfile(db_file): -+ TABSQLITEDB = None -+ ENGINE = None -+ tear_down() -+ return False -+ TABSQLITEDB = tabsqlitedb.TabSqliteDb( - filename=db_file, user_db=':memory:') -- ENGINE = tabengine( -+ ENGINE = table.TabEngine( - bus, - '/com/redhat/IBus/engines/table/%s/engine/0' %engine_name, - TABSQLITEDB, -- unit_test = True) -+ unit_test=True) - backup_original_settings() - set_default_settings() -+ return True - - def tear_down(): -+ global ENGINE_PATCHER -+ global LOOKUP_TABLE_PATCHER -+ global PROPERTY_PATCHER -+ global PROP_LIST_PATCHER -+ global IBUS_ENGINE -+ global IBUS_LOOKUP_TABLE -+ global IBUS_PROPERTY -+ global IBUS_PROP_LIST -+ global TABSQLITEDB -+ global ENGINE -+ if ENGINE: - restore_original_settings() -+ TABSQLITEDB = None -+ ENGINE = None -+ # Remove the patches from the IBus stuff: -+ ENGINE_PATCHER.stop() -+ LOOKUP_TABLE_PATCHER.stop() -+ PROPERTY_PATCHER.stop() -+ PROP_LIST_PATCHER.stop() -+ assert IBus.Engine is IBUS_ENGINE -+ assert IBus.Engine is not MockEngine -+ assert IBus.LookupTable is IBUS_LOOKUP_TABLE -+ assert IBus.LookupTable is not MockLookupTable -+ assert IBus.Property is IBUS_PROPERTY -+ assert IBus.Property is not MockProperty -+ assert IBus.PropList is IBUS_PROP_LIST -+ assert IBus.PropList is not MockPropList - --class Wubi_Jidian86TestCase(unittest.TestCase): -+class WubiJidian86TestCase(unittest.TestCase): - def setUp(self): -- set_up('wubi-jidian86') -+ engine_name = 'wubi-jidian86' -+ if not set_up(engine_name): -+ self.skipTest('Could not setup “%s”, skipping test.' % engine_name) - - def tearDown(self): - tear_down() -@@ -257,7 +359,81 @@ - ENGINE.do_process_key_event(IBus.KEY_space, 0, 0) - self.assertEqual(ENGINE.mock_committed_text, '工') - -- def test_commit_to_preedit_and_switching_to_pinyin_and_defining_a_phrase(self): -+ def test_pinyin_mode(self): -+ # Pinyin mode is False by default: -+ self.assertEqual(ENGINE.get_pinyin_mode(), False) -+ ENGINE.do_process_key_event(IBus.KEY_a, 0, 0) -+ self.assertEqual(ENGINE.mock_preedit_text, '工') -+ ENGINE.do_process_key_event(IBus.KEY_space, 0, 0) -+ self.assertEqual(ENGINE.mock_preedit_text, '') -+ self.assertEqual(ENGINE.mock_committed_text, '工') -+ ENGINE.set_pinyin_mode(True) -+ ENGINE.do_process_key_event(IBus.KEY_a, 0, 0) -+ self.assertEqual(ENGINE.mock_preedit_text, '爱') -+ ENGINE.do_process_key_event(IBus.KEY_space, 0, 0) -+ self.assertEqual(ENGINE.mock_preedit_text, '') -+ self.assertEqual(ENGINE.mock_committed_text, '工爱') -+ ENGINE.set_pinyin_mode(False) -+ ENGINE.do_process_key_event(IBus.KEY_a, 0, 0) -+ self.assertEqual(ENGINE.mock_preedit_text, '工') -+ ENGINE.do_process_key_event(IBus.KEY_space, 0, 0) -+ self.assertEqual(ENGINE.mock_preedit_text, '') -+ self.assertEqual(ENGINE.mock_committed_text, '工爱工') -+ -+ def test_suggestion_mode(self): -+ if not ENGINE._ime_sg: -+ self.skipTest("This engine does not have a suggestion mode.") -+ # Suggestion mode is False by default: -+ self.assertEqual(ENGINE.get_suggestion_mode(), False) -+ self.assertEqual(ENGINE.get_pinyin_mode(), False) -+ ENGINE.do_process_key_event(IBus.KEY_a, 0, 0) -+ self.assertEqual(ENGINE.mock_preedit_text, '工') -+ ENGINE.do_process_key_event(IBus.KEY_space, 0, 0) -+ self.assertEqual(ENGINE.mock_preedit_text, '') -+ self.assertEqual(ENGINE.mock_committed_text, '工') -+ self.assertEqual(ENGINE._editor._lookup_table.mock_candidates, []) -+ ENGINE.set_suggestion_mode(True) -+ ENGINE.do_process_key_event(IBus.KEY_a, 0, 0) -+ self.assertEqual(ENGINE.mock_preedit_text, '工') -+ ENGINE.do_process_key_event(IBus.KEY_space, 0, 0) -+ self.assertEqual(ENGINE.mock_preedit_text, '') -+ self.assertEqual(ENGINE.mock_committed_text, '工工') -+ self.assertEqual(ENGINE._editor._lookup_table.mock_candidates, -+ ['工作人员 673 0', -+ '工作会议 310 0', -+ '工作报告 267 0', -+ '工人阶级 146 0', -+ '工作重点 78 0', -+ '工作小组 73 0', -+ '工业企业 71 0', -+ '工业大学 69 0', -+ '工作单位 61 0', -+ '工业生产 58 0']) -+ ENGINE.do_process_key_event(IBus.KEY_space, 0, 0) -+ self.assertEqual(ENGINE.mock_preedit_text, '') -+ self.assertEqual(ENGINE.mock_committed_text, '工工作人员') -+ ENGINE.set_pinyin_mode(True) -+ ENGINE.do_process_key_event(IBus.KEY_a, 0, 0) -+ self.assertEqual(ENGINE.mock_preedit_text, '爱') -+ ENGINE.do_process_key_event(IBus.KEY_space, 0, 0) -+ self.assertEqual(ENGINE.mock_preedit_text, '') -+ self.assertEqual(ENGINE.mock_committed_text, '工工作人员爱') -+ self.assertEqual(ENGINE._editor._lookup_table.mock_candidates, -+ ['爱因斯坦 1109 0', -+ '爱情故事 519 0', -+ '爱国主义 191 0', -+ '爱尔兰语 91 0', -+ '爱好和平 62 0', -+ '爱情小说 58 0', -+ '爱不释手 39 0', -+ '爱国热情 35 0', -+ '爱莫能助 34 0', -+ '爱理不理 32 0']) -+ ENGINE.do_process_key_event(IBus.KEY_space, 0, 0) -+ self.assertEqual(ENGINE.mock_preedit_text, '') -+ self.assertEqual(ENGINE.mock_committed_text, '工工作人员爱因斯坦') -+ -+ def test_commit_to_preedit_switching_to_pinyin_defining_a_phrase(self): - ENGINE.do_process_key_event(IBus.KEY_a, 0, 0) - # commit to preëdit needs a press and release of either - # the left or the right shift key: -@@ -324,7 +500,8 @@ - IBus.KEY_Shift_L, 0, - IBus.ModifierType.SHIFT_MASK | IBus.ModifierType.RELEASE_MASK) - self.assertEqual(ENGINE.mock_preedit_text, '工了你好以在') -- # Move right two characters in the preëdit (triggers a commit to preëdit): -+ # Move right two characters in the preëdit -+ # (triggers a commit to preëdit): - ENGINE.do_process_key_event(IBus.KEY_Right, 0, 0) - ENGINE.do_process_key_event(IBus.KEY_Right, 0, 0) - self.assertEqual(ENGINE.mock_auxiliary_text, 'd dhf dhfd\t#: abwd') -@@ -353,9 +530,88 @@ - ENGINE.do_process_key_event(IBus.KEY_space, 0, 0) - self.assertEqual(ENGINE.mock_committed_text, '工了你好以在工了你好以在') - -+ def test_chinese_mode(self): -+ ENGINE.set_chinese_mode(mode=0) # show simplified Chinese only -+ ENGINE.do_process_key_event(IBus.KEY_c, 0, 0) -+ self.assertEqual(ENGINE._editor._lookup_table.mock_candidates, -+ ['以 418261033 0', -+ '能 ex 1820000000 0', -+ '能 exx 1820000000 0', -+ '对 fy 1200000000 0', -+ '又 cc 729000000 0', -+ '又 ccc 729000000 0', -+ '通 ep 521000000 0', -+ '通 epk 521000000 0', -+ '台 kf 486000000 0', -+ '难忘 wyn 404000000 0']) -+ ENGINE.do_process_key_event(IBus.KEY_BackSpace, 0, 0) -+ self.assertEqual(ENGINE._editor._lookup_table.mock_candidates, []) -+ ENGINE.set_chinese_mode(mode=1) # show traditional Chinese only -+ ENGINE.do_process_key_event(IBus.KEY_c, 0, 0) -+ self.assertEqual(ENGINE._editor._lookup_table.mock_candidates, -+ ['以 418261033 0', -+ '能 ex 1820000000 0', -+ '能 exx 1820000000 0', -+ '又 cc 729000000 0', -+ '又 ccc 729000000 0', -+ '通 ep 521000000 0', -+ '通 epk 521000000 0', -+ '台 kf 486000000 0', -+ '能 e 306980312 0', -+ '能力 elt 274000000 0']) -+ ENGINE.do_process_key_event(IBus.KEY_BackSpace, 0, 0) -+ self.assertEqual(ENGINE._editor._lookup_table.mock_candidates, []) -+ ENGINE.set_chinese_mode(mode=2) # show simplified Chinese first -+ ENGINE.do_process_key_event(IBus.KEY_c, 0, 0) -+ self.assertEqual(ENGINE._editor._lookup_table.mock_candidates, -+ ['以 418261033 0', -+ '能 ex 1820000000 0', -+ '能 exx 1820000000 0', -+ '对 fy 1200000000 0', -+ '又 cc 729000000 0', -+ '又 ccc 729000000 0', -+ '通 ep 521000000 0', -+ '通 epk 521000000 0', -+ '台 kf 486000000 0', -+ '难忘 wyn 404000000 0']) -+ ENGINE.do_process_key_event(IBus.KEY_BackSpace, 0, 0) -+ self.assertEqual(ENGINE._editor._lookup_table.mock_candidates, []) -+ ENGINE.set_chinese_mode(mode=3) # show traditional Chinese first -+ ENGINE.do_process_key_event(IBus.KEY_c, 0, 0) -+ self.assertEqual(ENGINE._editor._lookup_table.mock_candidates, -+ ['以 418261033 0', -+ '能 ex 1820000000 0', -+ '能 exx 1820000000 0', -+ '又 cc 729000000 0', -+ '又 ccc 729000000 0', -+ '通 ep 521000000 0', -+ '通 epk 521000000 0', -+ '台 kf 486000000 0', -+ '能 e 306980312 0', -+ '能力 elt 274000000 0']) -+ ENGINE.do_process_key_event(IBus.KEY_BackSpace, 0, 0) -+ self.assertEqual(ENGINE._editor._lookup_table.mock_candidates, []) -+ ENGINE.set_chinese_mode(mode=4) # show all characters -+ ENGINE.do_process_key_event(IBus.KEY_c, 0, 0) -+ self.assertEqual(ENGINE._editor._lookup_table.mock_candidates, -+ ['以 418261033 0', -+ '能 ex 1820000000 0', -+ '能 exx 1820000000 0', -+ '对 fy 1200000000 0', -+ '又 cc 729000000 0', -+ '又 ccc 729000000 0', -+ '通 ep 521000000 0', -+ '通 epk 521000000 0', -+ '台 kf 486000000 0', -+ '难忘 wyn 404000000 0']) -+ ENGINE.do_process_key_event(IBus.KEY_BackSpace, 0, 0) -+ self.assertEqual(ENGINE._editor._lookup_table.mock_candidates, []) -+ - class Stroke5TestCase(unittest.TestCase): - def setUp(self): -- set_up('stroke5') -+ engine_name = 'stroke5' -+ if not set_up(engine_name): -+ self.skipTest('Could not setup “%s”, skipping test.' % engine_name) - - def tearDown(self): - tear_down() -@@ -372,9 +628,56 @@ - ENGINE.do_process_key_event(IBus.KEY_space, 0, 0) - self.assertEqual(ENGINE.mock_committed_text, '的') - -+class TelexTestCase(unittest.TestCase): -+ def setUp(self): -+ engine_name = 'telex' -+ if not set_up(engine_name): -+ self.skipTest('Could not setup “%s”, skipping test.' % engine_name) -+ -+ def tearDown(self): -+ tear_down() -+ -+ def test_dummy(self): -+ self.assertEqual(True, True) -+ -+ def test_telex(self): -+ ENGINE.do_process_key_event(IBus.KEY_o, 0, 0) -+ self.assertEqual(ENGINE.mock_preedit_text, 'o') -+ self.assertEqual(ENGINE.mock_committed_text, '') -+ ENGINE.do_process_key_event(IBus.KEY_backslash, 0, 0) -+ self.assertEqual(ENGINE.mock_preedit_text, '') -+ self.assertEqual(ENGINE.mock_committed_text, 'o') -+ ENGINE.do_process_key_event(IBus.KEY_o, 0, 0) -+ ENGINE.do_process_key_event(IBus.KEY_f, 0, 0) -+ self.assertEqual(ENGINE.mock_preedit_text, '') -+ self.assertEqual(ENGINE.mock_committed_text, 'oò') -+ ENGINE.do_process_key_event(IBus.KEY_o, 0, 0) -+ ENGINE.do_process_key_event(IBus.KEY_o, 0, 0) -+ self.assertEqual(ENGINE.mock_preedit_text, 'ô') -+ self.assertEqual(ENGINE.mock_committed_text, 'oò') -+ ENGINE.do_process_key_event(IBus.KEY_backslash, 0, 0) -+ self.assertEqual(ENGINE.mock_preedit_text, '') -+ self.assertEqual(ENGINE.mock_committed_text, 'oòô') -+ ENGINE.do_process_key_event(IBus.KEY_o, 0, 0) -+ ENGINE.do_process_key_event(IBus.KEY_o, 0, 0) -+ self.assertEqual(ENGINE.mock_preedit_text, 'ô') -+ self.assertEqual(ENGINE.mock_committed_text, 'oòô') -+ ENGINE.do_process_key_event(IBus.KEY_backslash, 0, 0) -+ self.assertEqual(ENGINE.mock_preedit_text, '') -+ self.assertEqual(ENGINE.mock_committed_text, 'oòôô') -+ ENGINE.do_process_key_event(IBus.KEY_o, 0, 0) -+ ENGINE.do_process_key_event(IBus.KEY_o, 0, 0) -+ self.assertEqual(ENGINE.mock_preedit_text, 'ô') -+ self.assertEqual(ENGINE.mock_committed_text, 'oòôô') -+ ENGINE.do_process_key_event(IBus.KEY_j, 0, 0) -+ self.assertEqual(ENGINE.mock_preedit_text, '') -+ self.assertEqual(ENGINE.mock_committed_text, 'oòôôộ') -+ - class TranslitTestCase(unittest.TestCase): - def setUp(self): -- set_up('translit') -+ engine_name ='translit' -+ if not set_up(engine_name): -+ self.skipTest('Could not setup “%s”, skipping test.' % engine_name) - - def tearDown(self): - tear_down() -@@ -401,3 +704,215 @@ - self.assertEqual(ENGINE.mock_preedit_text, 'с') - ENGINE.do_process_key_event(IBus.KEY_space, 0, 0) - self.assertEqual(ENGINE.mock_committed_text, 'шщс ') -+ -+ def test_sh_multiple_match_slavic(self): -+ ENGINE.do_process_key_event(IBus.KEY_scaron, 0, 0) -+ self.assertEqual(ENGINE.mock_preedit_text, 'ш') -+ self.assertEqual(ENGINE.mock_committed_text, '') -+ ENGINE.do_process_key_event(IBus.KEY_h, 0, 0) -+ self.assertEqual(ENGINE.mock_preedit_text, '') -+ self.assertEqual(ENGINE.mock_committed_text, 'щ') -+ ENGINE.do_process_key_event(IBus.KEY_scaron, 0, 0) -+ self.assertEqual(ENGINE.mock_preedit_text, 'ш') -+ self.assertEqual(ENGINE.mock_committed_text, 'щ') -+ ENGINE.do_process_key_event(IBus.KEY_ccaron, 0, 0) -+ self.assertEqual(ENGINE.mock_preedit_text, '') -+ self.assertEqual(ENGINE.mock_committed_text, 'щщ') -+ -+class Cangjie5TestCase(unittest.TestCase): -+ def setUp(self): -+ engine_name = 'cangjie5' -+ if not set_up(engine_name): -+ self.skipTest('Could not setup “%s”, skipping test.' % engine_name) -+ -+ def tearDown(self): -+ tear_down() -+ -+ def test_dummy(self): -+ self.assertEqual(True, True) -+ -+ def test_single_char_commit_with_space(self): -+ ENGINE.do_process_key_event(IBus.KEY_a, 0, 0) -+ ENGINE.do_process_key_event(IBus.KEY_space, 0, 0) -+ self.assertEqual(ENGINE.mock_committed_text, '日') -+ -+ def test_type_one_char_and_check_auxiliary(self): -+ ENGINE.do_process_key_event(IBus.KEY_d, 0, 0) -+ self.assertEqual(ENGINE.mock_preedit_text, '木') -+ self.assertEqual(ENGINE._editor._lookup_table.mock_candidates[8], -+ '林 木 1000 0') -+ ENGINE.do_process_key_event(IBus.KEY_v, 0, 0) -+ ENGINE.do_process_key_event(IBus.KEY_i, 0, 0) -+ ENGINE.do_process_key_event(IBus.KEY_i, 0, 0) -+ self.assertEqual(ENGINE.mock_preedit_text, '機') -+ self.assertEqual(ENGINE.mock_auxiliary_text, '木女戈戈 (1 / 1)') -+ self.assertEqual(ENGINE._editor._lookup_table.mock_candidates, -+ ['機 1000 0']) -+ ENGINE.do_process_key_event(IBus.KEY_space, 0, 0) -+ self.assertEqual(ENGINE.mock_committed_text, '機') -+ -+class IpaXSampaTestCase(unittest.TestCase): -+ def setUp(self): -+ engine_name = 'ipa-x-sampa' -+ if not set_up(engine_name): -+ self.skipTest('Could not setup “%s”, skipping test.' % engine_name) -+ -+ def tearDown(self): -+ tear_down() -+ -+ def test_dummy(self): -+ self.assertEqual(True, True) -+ -+ def test_single_char_commit_with_space(self): -+ ENGINE.do_process_key_event(IBus.KEY_at, 0, 0) -+ ENGINE.do_process_key_event(IBus.KEY_space, 0, 0) -+ self.assertEqual(ENGINE.mock_committed_text, 'ə ') -+ -+ def test_single_char_commit_with_f3(self): -+ ENGINE.do_process_key_event(IBus.KEY_at, 0, 0) -+ self.assertEqual(ENGINE._editor._lookup_table.mock_candidates, -+ ['ə 0 0', 'ɘ \\ 0 0', 'ɚ ` 0 0']) -+ ENGINE.do_process_key_event(IBus.KEY_F3, 0, 0) -+ self.assertEqual(ENGINE.mock_committed_text, 'ɚ') -+ -+class LatexTestCase(unittest.TestCase): -+ def setUp(self): -+ engine_name = 'latex' -+ if not set_up(engine_name): -+ self.skipTest('Could not setup “%s”, skipping test.' % engine_name) -+ -+ def tearDown(self): -+ tear_down() -+ -+ def test_dummy(self): -+ self.assertEqual(True, True) -+ -+ def test_single_char_commit_with_space(self): -+ ENGINE.do_process_key_event(IBus.KEY_backslash, 0, 0) -+ ENGINE.do_process_key_event(IBus.KEY_a, 0, 0) -+ ENGINE.do_process_key_event(IBus.KEY_l, 0, 0) -+ ENGINE.do_process_key_event(IBus.KEY_p, 0, 0) -+ ENGINE.do_process_key_event(IBus.KEY_h, 0, 0) -+ ENGINE.do_process_key_event(IBus.KEY_a, 0, 0) -+ ENGINE.do_process_key_event(IBus.KEY_space, 0, 0) -+ self.assertEqual(ENGINE.mock_committed_text, 'α') -+ -+ def test_single_char_commit_with_space_fraktur(self): -+ # needs ibus-table-others-1.3.10 which adds -+ # most of Unicode 9.0 block Mathematical Alphanumeric Symbols -+ ENGINE.do_process_key_event(IBus.KEY_backslash, 0, 0) -+ ENGINE.do_process_key_event(IBus.KEY_m, 0, 0) -+ ENGINE.do_process_key_event(IBus.KEY_a, 0, 0) -+ ENGINE.do_process_key_event(IBus.KEY_t, 0, 0) -+ ENGINE.do_process_key_event(IBus.KEY_h, 0, 0) -+ ENGINE.do_process_key_event(IBus.KEY_f, 0, 0) -+ ENGINE.do_process_key_event(IBus.KEY_r, 0, 0) -+ ENGINE.do_process_key_event(IBus.KEY_a, 0, 0) -+ ENGINE.do_process_key_event(IBus.KEY_k, 0, 0) -+ ENGINE.do_process_key_event(IBus.KEY_F, 0, 0) -+ ENGINE.do_process_key_event(IBus.KEY_space, 0, 0) -+ self.assertEqual(ENGINE.mock_committed_text, '𝔉') -+ -+ def test_single_char_commit_with_f3(self): -+ ENGINE.do_process_key_event(IBus.KEY_backslash, 0, 0) -+ ENGINE.do_process_key_event(IBus.KEY_b, 0, 0) -+ # Lookup table shows only the first page, subsequent -+ # pages are added on demand as a speed optimization: -+ self.assertEqual(ENGINE._editor._lookup_table.mock_candidates, -+ ['¯ ar 0 0', -+ '⊥ ot 0 0', -+ 'β eta 0 0', -+ 'ℶ eth 0 0', -+ '⋂ igcap 0 0', -+ '⋃ igcup 0 0', -+ '⋁ igvee 0 0', -+ '⋈ owtie 0 0', -+ '⊡ oxdot 0 0']) -+ ENGINE.do_process_key_event(IBus.KEY_F3, 0, 0) -+ self.assertEqual(ENGINE.mock_committed_text, 'β') -+ ENGINE.do_process_key_event(IBus.KEY_backslash, 0, 0) -+ ENGINE.do_process_key_event(IBus.KEY_b, 0, 0) -+ ENGINE.do_process_key_event(IBus.KEY_Page_Down, 0, 0) -+ self.assertEqual(ENGINE._editor._lookup_table.mock_candidates, -+ ['β eta 0 1', # user freq for β increased to 1 -+ '¯ ar 0 0', -+ '⊥ ot 0 0', -+ 'ℶ eth 0 0', -+ '⋂ igcap 0 0', -+ '⋃ igcup 0 0', -+ '⋁ igvee 0 0', -+ '⋈ owtie 0 0', -+ '⊡ oxdot 0 0', -+ '• ullet 0 0', -+ '∙ ullet 0 0', -+ '≏ umpeq 0 0', -+ '∽ acksim 0 0', -+ '∵ ecause 0 0', -+ '≬ etween 0 0', -+ '⊞ oxplus 0 0', -+ '⊼ arwedge 0 0', -+ '⋀ igwedge 0 0']) -+ self.assertEqual(ENGINE._editor._lookup_table.get_cursor_pos(), 9) -+ ENGINE.do_process_key_event(IBus.KEY_Down, 0, 0) -+ ENGINE.do_process_key_event(IBus.KEY_Down, 0, 0) -+ ENGINE.do_process_key_event(IBus.KEY_Down, 0, 0) -+ ENGINE.do_process_key_event(IBus.KEY_Down, 0, 0) -+ ENGINE.do_process_key_event(IBus.KEY_Down, 0, 0) -+ ENGINE.do_process_key_event(IBus.KEY_Down, 0, 0) -+ self.assertEqual(ENGINE._editor._lookup_table.get_cursor_pos(), 15) -+ self.assertEqual(ENGINE._editor._lookup_table.mock_candidates[0:18], -+ ['β eta 0 1', # user freq for β increased to 1 -+ '¯ ar 0 0', -+ '⊥ ot 0 0', -+ 'ℶ eth 0 0', -+ '⋂ igcap 0 0', -+ '⋃ igcup 0 0', -+ '⋁ igvee 0 0', -+ '⋈ owtie 0 0', -+ '⊡ oxdot 0 0', -+ '• ullet 0 0', -+ '∙ ullet 0 0', -+ '≏ umpeq 0 0', -+ '∽ acksim 0 0', -+ '∵ ecause 0 0', -+ '≬ etween 0 0', -+ '⊞ oxplus 0 0', -+ '⊼ arwedge 0 0', -+ '⋀ igwedge 0 0']) -+ ENGINE.do_process_key_event(IBus.KEY_space, 0, 0) -+ self.assertEqual(ENGINE.mock_committed_text, 'β⊞') -+ ENGINE.do_process_key_event(IBus.KEY_backslash, 0, 0) -+ ENGINE.do_process_key_event(IBus.KEY_b, 0, 0) -+ ENGINE.do_process_key_event(IBus.KEY_Page_Down, 0, 0) -+ self.assertEqual(ENGINE._editor._lookup_table.mock_candidates, -+ ['β eta 0 1', # user freq for β increased to 1 -+ '⊞ oxplus 0 1', # user freq for ⊞ increased to 1 -+ '¯ ar 0 0', -+ '⊥ ot 0 0', -+ 'ℶ eth 0 0', -+ '⋂ igcap 0 0', -+ '⋃ igcup 0 0', -+ '⋁ igvee 0 0', -+ '⋈ owtie 0 0', -+ '⊡ oxdot 0 0', -+ '• ullet 0 0', -+ '∙ ullet 0 0', -+ '≏ umpeq 0 0', -+ '∽ acksim 0 0', -+ '∵ ecause 0 0', -+ '≬ etween 0 0', -+ '⊼ arwedge 0 0', -+ '⋀ igwedge 0 0']) -+ self.assertEqual(ENGINE._editor._lookup_table.get_cursor_pos(), 9) -+ ENGINE.do_process_key_event(IBus.KEY_Down, 0, 0) -+ ENGINE.do_process_key_event(IBus.KEY_Down, 0, 0) -+ ENGINE.do_process_key_event(IBus.KEY_Down, 0, 0) -+ ENGINE.do_process_key_event(IBus.KEY_Down, 0, 0) -+ ENGINE.do_process_key_event(IBus.KEY_Down, 0, 0) -+ ENGINE.do_process_key_event(IBus.KEY_Down, 0, 0) -+ self.assertEqual(ENGINE._editor._lookup_table.get_cursor_pos(), 15) -+ ENGINE.do_process_key_event(IBus.KEY_space, 0, 0) -+ self.assertEqual(ENGINE.mock_committed_text, 'β⊞≬') -+ -+if __name__ == '__main__': -+ unittest.main() diff --git a/SOURCES/add-a-test-suite.patch b/SOURCES/add-a-test-suite.patch deleted file mode 100644 index 382c7ff..0000000 --- a/SOURCES/add-a-test-suite.patch +++ /dev/null @@ -1,6974 +0,0 @@ -diff -Nru ibus-table-1.9.18.orig/Makefile.am ibus-table-1.9.18/Makefile.am ---- ibus-table-1.9.18.orig/Makefile.am 2020-07-22 11:52:11.640532230 +0200 -+++ ibus-table-1.9.18/Makefile.am 2020-07-22 14:43:51.905260956 +0200 -@@ -30,6 +30,7 @@ - data \ - po \ - setup \ -+ tests \ - $(NULL) - - ACLOCAL_AMFLAGS = -I m4 -diff -Nru ibus-table-1.9.18.orig/Makefile.in ibus-table-1.9.18/Makefile.in ---- ibus-table-1.9.18.orig/Makefile.in 2017-08-02 11:32:47.000000000 +0200 -+++ ibus-table-1.9.18/Makefile.in 2020-07-22 16:15:15.492860836 +0200 -@@ -1,7 +1,7 @@ --# Makefile.in generated by automake 1.15 from Makefile.am. -+# Makefile.in generated by automake 1.16.1 from Makefile.am. - # @configure_input@ - --# Copyright (C) 1994-2014 Free Software Foundation, Inc. -+# Copyright (C) 1994-2018 Free Software Foundation, Inc. - - # This Makefile.in is free software; the Free Software Foundation - # gives unlimited permission to copy and/or distribute it, -@@ -168,7 +168,7 @@ - $(RECURSIVE_CLEAN_TARGETS) \ - $(am__extra_recursive_targets) - AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ -- cscope distdir dist dist-all distcheck -+ cscope distdir distdir-am dist dist-all distcheck - am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) - # Read a list of newline-separated strings from the standard input, - # and print each of them once, without duplicates. Input order is -@@ -193,7 +193,7 @@ - am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/ibus-table.pc.in \ - $(srcdir)/ibus-table.spec.in ABOUT-NLS AUTHORS COPYING \ - ChangeLog INSTALL NEWS README compile config.guess \ -- config.rpath config.sub install-sh missing -+ config.rpath config.sub install-sh missing py-compile - DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) - distdir = $(PACKAGE)-$(VERSION) - top_distdir = $(distdir) -@@ -392,6 +392,7 @@ - data \ - po \ - setup \ -+ tests \ - $(NULL) - - ACLOCAL_AMFLAGS = -I m4 -@@ -457,8 +458,8 @@ - echo ' $(SHELL) ./config.status'; \ - $(SHELL) ./config.status;; \ - *) \ -- echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe)'; \ -- cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \ -+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__maybe_remake_depfiles)'; \ -+ cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__maybe_remake_depfiles);; \ - esac; - - $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) -@@ -622,7 +623,10 @@ - -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags - -rm -f cscope.out cscope.in.out cscope.po.out cscope.files - --distdir: $(DISTFILES) -+distdir: $(BUILT_SOURCES) -+ $(MAKE) $(AM_MAKEFLAGS) distdir-am -+ -+distdir-am: $(DISTFILES) - $(am__remove_distdir) - test -d "$(distdir)" || mkdir "$(distdir)" - @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ -diff -Nru ibus-table-1.9.18.orig/configure.ac ibus-table-1.9.18/configure.ac -diff -Nru ibus-table-1.9.18.orig/tests/Makefile.in ibus-table-1.9.18/tests/Makefile.in ---- ibus-table-1.9.18.orig/tests/Makefile.in 1970-01-01 01:00:00.000000000 +0100 -+++ ibus-table-1.9.18/tests/Makefile.in 2020-07-22 16:28:37.394963243 +0200 -@@ -0,0 +1,853 @@ -+# Makefile.in generated by automake 1.16.1 from Makefile.am. -+# @configure_input@ -+ -+# Copyright (C) 1994-2018 Free Software Foundation, Inc. -+ -+# This Makefile.in is free software; the Free Software Foundation -+# gives unlimited permission to copy and/or distribute it, -+# with or without modifications, as long as this notice is preserved. -+ -+# This program is distributed in the hope that it will be useful, -+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without -+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A -+# PARTICULAR PURPOSE. -+ -+@SET_MAKE@ -+ -+# vim:set noet ts=4 -+# -+# ibus-table - The Tables engine for IBus -+# -+# Copyright (c) 2018 Mike FABIAN -+# -+# This library is free software; you can redistribute it and/or -+# modify it under the terms of the GNU Lesser General Public -+# License as published by the Free Software Foundation; either -+# version 2.1 of the License, or (at your option) any later version. -+# -+# This library is distributed in the hope that it will be useful, -+# but WITHOUT ANY WARRANTY; without even the implied warranty of -+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+# Lesser General Public License for more details. -+# -+# You should have received a copy of the GNU Lesser General Public -+# License along with this library; if not, write to the Free Software -+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -+# -+VPATH = @srcdir@ -+am__is_gnu_make = { \ -+ if test -z '$(MAKELEVEL)'; then \ -+ false; \ -+ elif test -n '$(MAKE_HOST)'; then \ -+ true; \ -+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ -+ true; \ -+ else \ -+ false; \ -+ fi; \ -+} -+am__make_running_with_option = \ -+ case $${target_option-} in \ -+ ?) ;; \ -+ *) echo "am__make_running_with_option: internal error: invalid" \ -+ "target option '$${target_option-}' specified" >&2; \ -+ exit 1;; \ -+ esac; \ -+ has_opt=no; \ -+ sane_makeflags=$$MAKEFLAGS; \ -+ if $(am__is_gnu_make); then \ -+ sane_makeflags=$$MFLAGS; \ -+ else \ -+ case $$MAKEFLAGS in \ -+ *\\[\ \ ]*) \ -+ bs=\\; \ -+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ -+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ -+ esac; \ -+ fi; \ -+ skip_next=no; \ -+ strip_trailopt () \ -+ { \ -+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ -+ }; \ -+ for flg in $$sane_makeflags; do \ -+ test $$skip_next = yes && { skip_next=no; continue; }; \ -+ case $$flg in \ -+ *=*|--*) continue;; \ -+ -*I) strip_trailopt 'I'; skip_next=yes;; \ -+ -*I?*) strip_trailopt 'I';; \ -+ -*O) strip_trailopt 'O'; skip_next=yes;; \ -+ -*O?*) strip_trailopt 'O';; \ -+ -*l) strip_trailopt 'l'; skip_next=yes;; \ -+ -*l?*) strip_trailopt 'l';; \ -+ -[dEDm]) skip_next=yes;; \ -+ -[JT]) skip_next=yes;; \ -+ esac; \ -+ case $$flg in \ -+ *$$target_option*) has_opt=yes; break;; \ -+ esac; \ -+ done; \ -+ test $$has_opt = yes -+am__make_dryrun = (target_option=n; $(am__make_running_with_option)) -+am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) -+pkgdatadir = $(datadir)/@PACKAGE@ -+pkgincludedir = $(includedir)/@PACKAGE@ -+pkglibdir = $(libdir)/@PACKAGE@ -+pkglibexecdir = $(libexecdir)/@PACKAGE@ -+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd -+install_sh_DATA = $(install_sh) -c -m 644 -+install_sh_PROGRAM = $(install_sh) -c -+install_sh_SCRIPT = $(install_sh) -c -+INSTALL_HEADER = $(INSTALL_DATA) -+transform = $(program_transform_name) -+NORMAL_INSTALL = : -+PRE_INSTALL = : -+POST_INSTALL = : -+NORMAL_UNINSTALL = : -+PRE_UNINSTALL = : -+POST_UNINSTALL = : -+build_triplet = @build@ -+host_triplet = @host@ -+subdir = tests -+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 -+am__aclocal_m4_deps = $(top_srcdir)/m4/gettext.m4 \ -+ $(top_srcdir)/m4/iconv.m4 $(top_srcdir)/m4/lib-ld.m4 \ -+ $(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \ -+ $(top_srcdir)/m4/nls.m4 $(top_srcdir)/m4/po.m4 \ -+ $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/configure.ac -+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ -+ $(ACLOCAL_M4) -+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) -+mkinstalldirs = $(install_sh) -d -+CONFIG_CLEAN_FILES = -+CONFIG_CLEAN_VPATH_FILES = -+AM_V_P = $(am__v_P_@AM_V@) -+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) -+am__v_P_0 = false -+am__v_P_1 = : -+AM_V_GEN = $(am__v_GEN_@AM_V@) -+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) -+am__v_GEN_0 = @echo " GEN " $@; -+am__v_GEN_1 = -+AM_V_at = $(am__v_at_@AM_V@) -+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) -+am__v_at_0 = @ -+am__v_at_1 = -+SOURCES = -+DIST_SOURCES = -+am__can_run_installinfo = \ -+ case $$AM_UPDATE_INFO_DIR in \ -+ n|no|NO) false;; \ -+ *) (install-info --version) >/dev/null 2>&1;; \ -+ esac -+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) -+am__tty_colors_dummy = \ -+ mgn= red= grn= lgn= blu= brg= std=; \ -+ am__color_tests=no -+am__tty_colors = { \ -+ $(am__tty_colors_dummy); \ -+ if test "X$(AM_COLOR_TESTS)" = Xno; then \ -+ am__color_tests=no; \ -+ elif test "X$(AM_COLOR_TESTS)" = Xalways; then \ -+ am__color_tests=yes; \ -+ elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \ -+ am__color_tests=yes; \ -+ fi; \ -+ if test $$am__color_tests = yes; then \ -+ red=''; \ -+ grn=''; \ -+ lgn=''; \ -+ blu=''; \ -+ mgn=''; \ -+ brg=''; \ -+ std=''; \ -+ fi; \ -+} -+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; -+am__vpath_adj = case $$p in \ -+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ -+ *) f=$$p;; \ -+ esac; -+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; -+am__install_max = 40 -+am__nobase_strip_setup = \ -+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` -+am__nobase_strip = \ -+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" -+am__nobase_list = $(am__nobase_strip_setup); \ -+ for p in $$list; do echo "$$p $$p"; done | \ -+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ -+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ -+ if (++n[$$2] == $(am__install_max)) \ -+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ -+ END { for (dir in files) print dir, files[dir] }' -+am__base_list = \ -+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ -+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' -+am__uninstall_files_from_dir = { \ -+ test -z "$$files" \ -+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ -+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ -+ $(am__cd) "$$dir" && rm -f $$files; }; \ -+ } -+am__recheck_rx = ^[ ]*:recheck:[ ]* -+am__global_test_result_rx = ^[ ]*:global-test-result:[ ]* -+am__copy_in_global_log_rx = ^[ ]*:copy-in-global-log:[ ]* -+# A command that, given a newline-separated list of test names on the -+# standard input, print the name of the tests that are to be re-run -+# upon "make recheck". -+am__list_recheck_tests = $(AWK) '{ \ -+ recheck = 1; \ -+ while ((rc = (getline line < ($$0 ".trs"))) != 0) \ -+ { \ -+ if (rc < 0) \ -+ { \ -+ if ((getline line2 < ($$0 ".log")) < 0) \ -+ recheck = 0; \ -+ break; \ -+ } \ -+ else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \ -+ { \ -+ recheck = 0; \ -+ break; \ -+ } \ -+ else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \ -+ { \ -+ break; \ -+ } \ -+ }; \ -+ if (recheck) \ -+ print $$0; \ -+ close ($$0 ".trs"); \ -+ close ($$0 ".log"); \ -+}' -+# A command that, given a newline-separated list of test names on the -+# standard input, create the global log from their .trs and .log files. -+am__create_global_log = $(AWK) ' \ -+function fatal(msg) \ -+{ \ -+ print "fatal: making $@: " msg | "cat >&2"; \ -+ exit 1; \ -+} \ -+function rst_section(header) \ -+{ \ -+ print header; \ -+ len = length(header); \ -+ for (i = 1; i <= len; i = i + 1) \ -+ printf "="; \ -+ printf "\n\n"; \ -+} \ -+{ \ -+ copy_in_global_log = 1; \ -+ global_test_result = "RUN"; \ -+ while ((rc = (getline line < ($$0 ".trs"))) != 0) \ -+ { \ -+ if (rc < 0) \ -+ fatal("failed to read from " $$0 ".trs"); \ -+ if (line ~ /$(am__global_test_result_rx)/) \ -+ { \ -+ sub("$(am__global_test_result_rx)", "", line); \ -+ sub("[ ]*$$", "", line); \ -+ global_test_result = line; \ -+ } \ -+ else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \ -+ copy_in_global_log = 0; \ -+ }; \ -+ if (copy_in_global_log) \ -+ { \ -+ rst_section(global_test_result ": " $$0); \ -+ while ((rc = (getline line < ($$0 ".log"))) != 0) \ -+ { \ -+ if (rc < 0) \ -+ fatal("failed to read from " $$0 ".log"); \ -+ print line; \ -+ }; \ -+ printf "\n"; \ -+ }; \ -+ close ($$0 ".trs"); \ -+ close ($$0 ".log"); \ -+}' -+# Restructured Text title. -+am__rst_title = { sed 's/.*/ & /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; } -+# Solaris 10 'make', and several other traditional 'make' implementations, -+# pass "-e" to $(SHELL), and POSIX 2008 even requires this. Work around it -+# by disabling -e (using the XSI extension "set +e") if it's set. -+am__sh_e_setup = case $$- in *e*) set +e;; esac -+# Default flags passed to test drivers. -+am__common_driver_flags = \ -+ --color-tests "$$am__color_tests" \ -+ --enable-hard-errors "$$am__enable_hard_errors" \ -+ --expect-failure "$$am__expect_failure" -+# To be inserted before the command running the test. Creates the -+# directory for the log if needed. Stores in $dir the directory -+# containing $f, in $tst the test, in $log the log. Executes the -+# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and -+# passes TESTS_ENVIRONMENT. Set up options for the wrapper that -+# will run the test scripts (or their associated LOG_COMPILER, if -+# thy have one). -+am__check_pre = \ -+$(am__sh_e_setup); \ -+$(am__vpath_adj_setup) $(am__vpath_adj) \ -+$(am__tty_colors); \ -+srcdir=$(srcdir); export srcdir; \ -+case "$@" in \ -+ */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;; \ -+ *) am__odir=.;; \ -+esac; \ -+test "x$$am__odir" = x"." || test -d "$$am__odir" \ -+ || $(MKDIR_P) "$$am__odir" || exit $$?; \ -+if test -f "./$$f"; then dir=./; \ -+elif test -f "$$f"; then dir=; \ -+else dir="$(srcdir)/"; fi; \ -+tst=$$dir$$f; log='$@'; \ -+if test -n '$(DISABLE_HARD_ERRORS)'; then \ -+ am__enable_hard_errors=no; \ -+else \ -+ am__enable_hard_errors=yes; \ -+fi; \ -+case " $(XFAIL_TESTS) " in \ -+ *[\ \ ]$$f[\ \ ]* | *[\ \ ]$$dir$$f[\ \ ]*) \ -+ am__expect_failure=yes;; \ -+ *) \ -+ am__expect_failure=no;; \ -+esac; \ -+$(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT) -+# A shell command to get the names of the tests scripts with any registered -+# extension removed (i.e., equivalently, the names of the test logs, with -+# the '.log' extension removed). The result is saved in the shell variable -+# '$bases'. This honors runtime overriding of TESTS and TEST_LOGS. Sadly, -+# we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)", -+# since that might cause problem with VPATH rewrites for suffix-less tests. -+# See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'. -+am__set_TESTS_bases = \ -+ bases='$(TEST_LOGS)'; \ -+ bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \ -+ bases=`echo $$bases` -+RECHECK_LOGS = $(TEST_LOGS) -+AM_RECURSIVE_TARGETS = check recheck -+TEST_SUITE_LOG = test-suite.log -+TEST_EXTENSIONS = @EXEEXT@ .test -+LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver -+LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS) -+am__set_b = \ -+ case '$@' in \ -+ */*) \ -+ case '$*' in \ -+ */*) b='$*';; \ -+ *) b=`echo '$@' | sed 's/\.log$$//'`; \ -+ esac;; \ -+ *) \ -+ b='$*';; \ -+ esac -+am__test_logs1 = $(TESTS:=.log) -+am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log) -+TEST_LOGS = $(am__test_logs2:.test.log=.log) -+TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver -+TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \ -+ $(TEST_LOG_FLAGS) -+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/test-driver -+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) -+ACLOCAL = @ACLOCAL@ -+AMTAR = @AMTAR@ -+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ -+AUTOCONF = @AUTOCONF@ -+AUTOHEADER = @AUTOHEADER@ -+AUTOMAKE = @AUTOMAKE@ -+AWK = @AWK@ -+CC = @CC@ -+CCDEPMODE = @CCDEPMODE@ -+CFLAGS = @CFLAGS@ -+CPPFLAGS = @CPPFLAGS@ -+CYGPATH_W = @CYGPATH_W@ -+DEFS = @DEFS@ -+DEPDIR = @DEPDIR@ -+ECHO_C = @ECHO_C@ -+ECHO_N = @ECHO_N@ -+ECHO_T = @ECHO_T@ -+EXEEXT = @EXEEXT@ -+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@ -+GMSGFMT = @GMSGFMT@ -+GMSGFMT_015 = @GMSGFMT_015@ -+IBUS_CFLAGS = @IBUS_CFLAGS@ -+IBUS_LIBS = @IBUS_LIBS@ -+INSTALL = @INSTALL@ -+INSTALL_DATA = @INSTALL_DATA@ -+INSTALL_PROGRAM = @INSTALL_PROGRAM@ -+INSTALL_SCRIPT = @INSTALL_SCRIPT@ -+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ -+INTLLIBS = @INTLLIBS@ -+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@ -+LDFLAGS = @LDFLAGS@ -+LIBICONV = @LIBICONV@ -+LIBINTL = @LIBINTL@ -+LIBOBJS = @LIBOBJS@ -+LIBS = @LIBS@ -+LTLIBICONV = @LTLIBICONV@ -+LTLIBINTL = @LTLIBINTL@ -+LTLIBOBJS = @LTLIBOBJS@ -+MAINT = @MAINT@ -+MAKEINFO = @MAKEINFO@ -+MKDIR_P = @MKDIR_P@ -+MSGFMT = @MSGFMT@ -+MSGFMT_015 = @MSGFMT_015@ -+MSGMERGE = @MSGMERGE@ -+OBJEXT = @OBJEXT@ -+PACKAGE = @PACKAGE@ -+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ -+PACKAGE_NAME = @PACKAGE_NAME@ -+PACKAGE_STRING = @PACKAGE_STRING@ -+PACKAGE_TARNAME = @PACKAGE_TARNAME@ -+PACKAGE_URL = @PACKAGE_URL@ -+PACKAGE_VERSION = @PACKAGE_VERSION@ -+PATH_SEPARATOR = @PATH_SEPARATOR@ -+PKG_CONFIG = @PKG_CONFIG@ -+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ -+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ -+POSUB = @POSUB@ -+PYTHON = @PYTHON@ -+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ -+PYTHON_PLATFORM = @PYTHON_PLATFORM@ -+PYTHON_PREFIX = @PYTHON_PREFIX@ -+PYTHON_VERSION = @PYTHON_VERSION@ -+SET_MAKE = @SET_MAKE@ -+SHELL = @SHELL@ -+STRIP = @STRIP@ -+USE_NLS = @USE_NLS@ -+VERSION = @VERSION@ -+XGETTEXT = @XGETTEXT@ -+XGETTEXT_015 = @XGETTEXT_015@ -+abs_builddir = @abs_builddir@ -+abs_srcdir = @abs_srcdir@ -+abs_top_builddir = @abs_top_builddir@ -+abs_top_srcdir = @abs_top_srcdir@ -+ac_ct_CC = @ac_ct_CC@ -+am__include = @am__include@ -+am__leading_dot = @am__leading_dot@ -+am__quote = @am__quote@ -+am__tar = @am__tar@ -+am__untar = @am__untar@ -+bindir = @bindir@ -+build = @build@ -+build_alias = @build_alias@ -+build_cpu = @build_cpu@ -+build_os = @build_os@ -+build_vendor = @build_vendor@ -+builddir = @builddir@ -+datadir = @datadir@ -+datarootdir = @datarootdir@ -+docdir = @docdir@ -+dvidir = @dvidir@ -+exec_prefix = @exec_prefix@ -+host = @host@ -+host_alias = @host_alias@ -+host_cpu = @host_cpu@ -+host_os = @host_os@ -+host_vendor = @host_vendor@ -+htmldir = @htmldir@ -+includedir = @includedir@ -+infodir = @infodir@ -+install_sh = @install_sh@ -+libdir = @libdir@ -+libexecdir = @libexecdir@ -+localedir = @localedir@ -+localstatedir = @localstatedir@ -+mandir = @mandir@ -+mkdir_p = @mkdir_p@ -+oldincludedir = @oldincludedir@ -+pdfdir = @pdfdir@ -+pkgpyexecdir = @pkgpyexecdir@ -+pkgpythondir = @pkgpythondir@ -+prefix = @prefix@ -+program_transform_name = @program_transform_name@ -+psdir = @psdir@ -+pyexecdir = @pyexecdir@ -+pythondir = @pythondir@ -+sbindir = @sbindir@ -+sharedstatedir = @sharedstatedir@ -+srcdir = @srcdir@ -+sysconfdir = @sysconfdir@ -+target_alias = @target_alias@ -+top_build_prefix = @top_build_prefix@ -+top_builddir = @top_builddir@ -+top_srcdir = @top_srcdir@ -+TESTS = run_tests -+EXTRA_DIST = \ -+ run_tests.in \ -+ test_it.py \ -+ __init__.py \ -+ $(NULL) -+ -+CLEANFILES = \ -+ run_tests \ -+ $(NULL) -+ -+MAINTAINERCLEANFILES = \ -+ Makefile.in \ -+ $(NULL) -+ -+all: all-am -+ -+.SUFFIXES: -+.SUFFIXES: .log .test .test$(EXEEXT) .trs -+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) -+ @for dep in $?; do \ -+ case '$(am__configure_deps)' in \ -+ *$$dep*) \ -+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ -+ && { if test -f $@; then exit 0; else break; fi; }; \ -+ exit 1;; \ -+ esac; \ -+ done; \ -+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu tests/Makefile'; \ -+ $(am__cd) $(top_srcdir) && \ -+ $(AUTOMAKE) --gnu tests/Makefile -+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status -+ @case '$?' in \ -+ *config.status*) \ -+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ -+ *) \ -+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ -+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ -+ esac; -+ -+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) -+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh -+ -+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) -+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh -+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) -+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh -+$(am__aclocal_m4_deps): -+tags TAGS: -+ -+ctags CTAGS: -+ -+cscope cscopelist: -+ -+ -+# Recover from deleted '.trs' file; this should ensure that -+# "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create -+# both 'foo.log' and 'foo.trs'. Break the recipe in two subshells -+# to avoid problems with "make -n". -+.log.trs: -+ rm -f $< $@ -+ $(MAKE) $(AM_MAKEFLAGS) $< -+ -+# Leading 'am--fnord' is there to ensure the list of targets does not -+# expand to empty, as could happen e.g. with make check TESTS=''. -+am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck) -+am--force-recheck: -+ @: -+ -+$(TEST_SUITE_LOG): $(TEST_LOGS) -+ @$(am__set_TESTS_bases); \ -+ am__f_ok () { test -f "$$1" && test -r "$$1"; }; \ -+ redo_bases=`for i in $$bases; do \ -+ am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \ -+ done`; \ -+ if test -n "$$redo_bases"; then \ -+ redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \ -+ redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \ -+ if $(am__make_dryrun); then :; else \ -+ rm -f $$redo_logs && rm -f $$redo_results || exit 1; \ -+ fi; \ -+ fi; \ -+ if test -n "$$am__remaking_logs"; then \ -+ echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \ -+ "recursion detected" >&2; \ -+ elif test -n "$$redo_logs"; then \ -+ am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \ -+ fi; \ -+ if $(am__make_dryrun); then :; else \ -+ st=0; \ -+ errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \ -+ for i in $$redo_bases; do \ -+ test -f $$i.trs && test -r $$i.trs \ -+ || { echo "$$errmsg $$i.trs" >&2; st=1; }; \ -+ test -f $$i.log && test -r $$i.log \ -+ || { echo "$$errmsg $$i.log" >&2; st=1; }; \ -+ done; \ -+ test $$st -eq 0 || exit 1; \ -+ fi -+ @$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \ -+ ws='[ ]'; \ -+ results=`for b in $$bases; do echo $$b.trs; done`; \ -+ test -n "$$results" || results=/dev/null; \ -+ all=` grep "^$$ws*:test-result:" $$results | wc -l`; \ -+ pass=` grep "^$$ws*:test-result:$$ws*PASS" $$results | wc -l`; \ -+ fail=` grep "^$$ws*:test-result:$$ws*FAIL" $$results | wc -l`; \ -+ skip=` grep "^$$ws*:test-result:$$ws*SKIP" $$results | wc -l`; \ -+ xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \ -+ xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \ -+ error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \ -+ if test `expr $$fail + $$xpass + $$error` -eq 0; then \ -+ success=true; \ -+ else \ -+ success=false; \ -+ fi; \ -+ br='==================='; br=$$br$$br$$br$$br; \ -+ result_count () \ -+ { \ -+ if test x"$$1" = x"--maybe-color"; then \ -+ maybe_colorize=yes; \ -+ elif test x"$$1" = x"--no-color"; then \ -+ maybe_colorize=no; \ -+ else \ -+ echo "$@: invalid 'result_count' usage" >&2; exit 4; \ -+ fi; \ -+ shift; \ -+ desc=$$1 count=$$2; \ -+ if test $$maybe_colorize = yes && test $$count -gt 0; then \ -+ color_start=$$3 color_end=$$std; \ -+ else \ -+ color_start= color_end=; \ -+ fi; \ -+ echo "$${color_start}# $$desc $$count$${color_end}"; \ -+ }; \ -+ create_testsuite_report () \ -+ { \ -+ result_count $$1 "TOTAL:" $$all "$$brg"; \ -+ result_count $$1 "PASS: " $$pass "$$grn"; \ -+ result_count $$1 "SKIP: " $$skip "$$blu"; \ -+ result_count $$1 "XFAIL:" $$xfail "$$lgn"; \ -+ result_count $$1 "FAIL: " $$fail "$$red"; \ -+ result_count $$1 "XPASS:" $$xpass "$$red"; \ -+ result_count $$1 "ERROR:" $$error "$$mgn"; \ -+ }; \ -+ { \ -+ echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" | \ -+ $(am__rst_title); \ -+ create_testsuite_report --no-color; \ -+ echo; \ -+ echo ".. contents:: :depth: 2"; \ -+ echo; \ -+ for b in $$bases; do echo $$b; done \ -+ | $(am__create_global_log); \ -+ } >$(TEST_SUITE_LOG).tmp || exit 1; \ -+ mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG); \ -+ if $$success; then \ -+ col="$$grn"; \ -+ else \ -+ col="$$red"; \ -+ test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG); \ -+ fi; \ -+ echo "$${col}$$br$${std}"; \ -+ echo "$${col}Testsuite summary for $(PACKAGE_STRING)$${std}"; \ -+ echo "$${col}$$br$${std}"; \ -+ create_testsuite_report --maybe-color; \ -+ echo "$$col$$br$$std"; \ -+ if $$success; then :; else \ -+ echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}"; \ -+ if test -n "$(PACKAGE_BUGREPORT)"; then \ -+ echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}"; \ -+ fi; \ -+ echo "$$col$$br$$std"; \ -+ fi; \ -+ $$success || exit 1 -+ -+check-TESTS: -+ @list='$(RECHECK_LOGS)'; test -z "$$list" || rm -f $$list -+ @list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list -+ @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) -+ @set +e; $(am__set_TESTS_bases); \ -+ log_list=`for i in $$bases; do echo $$i.log; done`; \ -+ trs_list=`for i in $$bases; do echo $$i.trs; done`; \ -+ log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \ -+ $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \ -+ exit $$?; -+recheck: all -+ @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) -+ @set +e; $(am__set_TESTS_bases); \ -+ bases=`for i in $$bases; do echo $$i; done \ -+ | $(am__list_recheck_tests)` || exit 1; \ -+ log_list=`for i in $$bases; do echo $$i.log; done`; \ -+ log_list=`echo $$log_list`; \ -+ $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \ -+ am__force_recheck=am--force-recheck \ -+ TEST_LOGS="$$log_list"; \ -+ exit $$? -+run_tests.log: run_tests -+ @p='run_tests'; \ -+ b='run_tests'; \ -+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ -+ --log-file $$b.log --trs-file $$b.trs \ -+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ -+ "$$tst" $(AM_TESTS_FD_REDIRECT) -+.test.log: -+ @p='$<'; \ -+ $(am__set_b); \ -+ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ -+ --log-file $$b.log --trs-file $$b.trs \ -+ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ -+ "$$tst" $(AM_TESTS_FD_REDIRECT) -+@am__EXEEXT_TRUE@.test$(EXEEXT).log: -+@am__EXEEXT_TRUE@ @p='$<'; \ -+@am__EXEEXT_TRUE@ $(am__set_b); \ -+@am__EXEEXT_TRUE@ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ -+@am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \ -+@am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ -+@am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT) -+ -+distdir: $(BUILT_SOURCES) -+ $(MAKE) $(AM_MAKEFLAGS) distdir-am -+ -+distdir-am: $(DISTFILES) -+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ -+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ -+ list='$(DISTFILES)'; \ -+ dist_files=`for file in $$list; do echo $$file; done | \ -+ sed -e "s|^$$srcdirstrip/||;t" \ -+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ -+ case $$dist_files in \ -+ */*) $(MKDIR_P) `echo "$$dist_files" | \ -+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ -+ sort -u` ;; \ -+ esac; \ -+ for file in $$dist_files; do \ -+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ -+ if test -d $$d/$$file; then \ -+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ -+ if test -d "$(distdir)/$$file"; then \ -+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ -+ fi; \ -+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ -+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ -+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ -+ fi; \ -+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ -+ else \ -+ test -f "$(distdir)/$$file" \ -+ || cp -p $$d/$$file "$(distdir)/$$file" \ -+ || exit 1; \ -+ fi; \ -+ done -+check-am: all-am -+ $(MAKE) $(AM_MAKEFLAGS) check-TESTS -+check: check-am -+all-am: Makefile -+installdirs: -+install: install-am -+install-exec: install-exec-am -+install-data: install-data-am -+uninstall: uninstall-am -+ -+install-am: all-am -+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am -+ -+installcheck: installcheck-am -+install-strip: -+ if test -z '$(STRIP)'; then \ -+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ -+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ -+ install; \ -+ else \ -+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ -+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ -+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ -+ fi -+mostlyclean-generic: -+ -test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS) -+ -test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs) -+ -test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) -+ -+clean-generic: -+ -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) -+ -+distclean-generic: -+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) -+ -+maintainer-clean-generic: -+ @echo "This command is intended for maintainers to use" -+ @echo "it deletes files that may require special tools to rebuild." -+ -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) -+clean: clean-am -+ -+clean-am: clean-generic mostlyclean-am -+ -+distclean: distclean-am -+ -rm -f Makefile -+distclean-am: clean-am distclean-generic -+ -+dvi: dvi-am -+ -+dvi-am: -+ -+html: html-am -+ -+html-am: -+ -+info: info-am -+ -+info-am: -+ -+install-data-am: -+ -+install-dvi: install-dvi-am -+ -+install-dvi-am: -+ -+install-exec-am: -+ -+install-html: install-html-am -+ -+install-html-am: -+ -+install-info: install-info-am -+ -+install-info-am: -+ -+install-man: -+ -+install-pdf: install-pdf-am -+ -+install-pdf-am: -+ -+install-ps: install-ps-am -+ -+install-ps-am: -+ -+installcheck-am: -+ -+maintainer-clean: maintainer-clean-am -+ -rm -f Makefile -+maintainer-clean-am: distclean-am maintainer-clean-generic -+ -+mostlyclean: mostlyclean-am -+ -+mostlyclean-am: mostlyclean-generic -+ -+pdf: pdf-am -+ -+pdf-am: -+ -+ps: ps-am -+ -+ps-am: -+ -+uninstall-am: -+ -+.MAKE: check-am install-am install-strip -+ -+.PHONY: all all-am check check-TESTS check-am clean clean-generic \ -+ cscopelist-am ctags-am distclean distclean-generic distdir dvi \ -+ dvi-am html html-am info info-am install install-am \ -+ install-data install-data-am install-dvi install-dvi-am \ -+ install-exec install-exec-am install-html install-html-am \ -+ install-info install-info-am install-man install-pdf \ -+ install-pdf-am install-ps install-ps-am install-strip \ -+ installcheck installcheck-am installdirs maintainer-clean \ -+ maintainer-clean-generic mostlyclean mostlyclean-generic pdf \ -+ pdf-am ps ps-am recheck tags-am uninstall uninstall-am -+ -+.PRECIOUS: Makefile -+ -+ -+run_tests: run_tests.in -+ sed -e 's&@PYTHON_BIN@&$(PYTHON)&g' \ -+ -e 's&@SRCDIR@&$(srcdir)&g' $< > $@ -+ chmod +x $@ -+ -+# Tell versions [3.59,3.63) of GNU make to not export all variables. -+# Otherwise a system limit (for SysV at least) may be exceeded. -+.NOEXPORT: ---- ibus-table-1.9.18.orig/configure.ac 2020-07-22 11:52:11.639532241 +0200 -+++ ibus-table-1.9.18/configure.ac 2020-07-22 14:43:51.905260956 +0200 -@@ -68,6 +68,7 @@ - setup/Makefile - setup/ibus-setup-table - setup/version.py -+ tests/Makefile - ibus-table.spec - ibus-table.pc] - ) -diff -Nru ibus-table-1.9.18.orig/engine/Makefile.am ibus-table-1.9.18/engine/Makefile.am ---- ibus-table-1.9.18.orig/engine/Makefile.am 2020-07-22 11:52:11.650532123 +0200 -+++ ibus-table-1.9.18/engine/Makefile.am 2020-07-22 14:43:51.906260946 +0200 -@@ -32,6 +32,7 @@ - table.py \ - tabcreatedb.py \ - tabsqlitedb.py \ -+ it_util.py \ - $(NULL) - engine_table_DATA = \ - $(NULL) -diff -Nru ibus-table-1.9.18.orig/engine/it_util.py ibus-table-1.9.18/engine/it_util.py ---- ibus-table-1.9.18.orig/engine/it_util.py 1970-01-01 01:00:00.000000000 +0100 -+++ ibus-table-1.9.18/engine/it_util.py 2020-07-22 14:43:51.906260946 +0200 -@@ -0,0 +1,63 @@ -+# -*- coding: utf-8 -*- -+# vim:et sts=4 sw=4 -+# -+# ibus-table - The Tables engine for IBus -+# -+# Copyright (c) 2008-2009 Yu Yuwei -+# Copyright (c) 2009-2014 Caius "kaio" CHANCE -+# Copyright (c) 2012-2015 Mike FABIAN -+# -+# This library is free software; you can redistribute it and/or -+# modify it under the terms of the GNU Lesser General Public -+# License as published by the Free Software Foundation; either -+# version 2.1 of the License, or (at your option) any later version. -+# -+# This library is distributed in the hope that it will be useful, -+# but WITHOUT ANY WARRANTY; without even the implied warranty of -+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+# Lesser General Public License for more details. -+# -+# You should have received a copy of the GNU Lesser General Public -+# License along with this library; if not, write to the Free Software -+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -+# -+''' -+Utility functions used in ibus-table -+''' -+ -+import sys -+import re -+import string -+ -+def config_section_normalize(section): -+ '''Replaces “_:” with “-” in the dconf section and converts to lower case -+ -+ :param section: The name of the dconf section -+ :type section: string -+ :rtype: string -+ -+ To make the comparison of the dconf sections work correctly. -+ -+ I avoid using .lower() here because it is locale dependent, when -+ using .lower() this would not achieve the desired effect of -+ comparing the dconf sections case insentively in some locales, it -+ would fail for example if Turkish locale (tr_TR.UTF-8) is set. -+ -+ Examples: -+ -+ >>> config_section_normalize('Foo_bAr:Baz') -+ 'foo-bar-baz' -+ ''' -+ return re.sub(r'[_:]', r'-', section).translate( -+ bytes.maketrans( -+ bytes(string.ascii_uppercase.encode('ascii')), -+ bytes(string.ascii_lowercase.encode('ascii')))) -+ -+ -+if __name__ == "__main__": -+ import doctest -+ (FAILED, ATTEMPTED) = doctest.testmod() -+ if FAILED: -+ sys.exit(1) -+ else: -+ sys.exit(0) -diff -Nru ibus-table-1.9.18.orig/engine/table.py ibus-table-1.9.18/engine/table.py ---- ibus-table-1.9.18.orig/engine/table.py 2020-07-22 11:52:11.650532123 +0200 -+++ ibus-table-1.9.18/engine/table.py 2020-07-22 14:43:51.907260935 +0200 -@@ -37,6 +37,7 @@ - import re - from gi.repository import GObject - import time -+import it_util - - debug_level = int(0) - -@@ -215,8 +216,10 @@ - max_key_length, database): - self.db = database - self._config = config -- engine_name = os.path.basename(self.db.filename).replace('.db', '') -- self._config_section = "engine/Table/%s" % engine_name.replace(' ','_') -+ engine_name = os.path.basename( -+ self.db.filename).replace('.db', '').replace(' ','_') -+ self._config_section = it_util.config_section_normalize( -+ "engine/Table/%s" % engine_name) - self._max_key_length = int(max_key_length) - self._max_key_length_pinyin = 7 - self._valid_input_chars = valid_input_chars -@@ -320,7 +323,7 @@ - self._config_section, - "ChineseMode")) - if self._chinese_mode == None: -- self._chinese_mode = self.get_chinese_mode() -+ self._chinese_mode = self.get_default_chinese_mode() - elif debug_level > 1: - sys.stderr.write( - "Chinese mode found in user config, mode=%s\n" -@@ -342,7 +345,7 @@ - def get_new_lookup_table( - self, page_size=10, - select_keys=[49, 50, 51, 52, 53, 54, 55, 56, 57, 48], -- orientation=True): -+ orientation=IBus.Orientation.VERTICAL): - ''' - [49, 50, 51, 52, 53, 54, 55, 56, 57, 48] are the key codes - for the characters ['1', '2', '3', '4', '5', '6', '7', '8', '0'] -@@ -351,7 +354,7 @@ - page_size = 1 - if page_size > len(select_keys): - page_size = len(select_keys) -- lookup_table = IBus.LookupTable.new( -+ lookup_table = IBus.LookupTable( - page_size=page_size, - cursor_pos=0, - cursor_visible=True, -@@ -371,7 +374,7 @@ - """ - return self._select_keys - -- def get_chinese_mode (self): -+ def get_default_chinese_mode (self): - ''' - Use db value or LC_CTYPE in your box to determine the _chinese_mode - ''' -@@ -380,7 +383,7 @@ - if __db_chinese_mode >= 0: - if debug_level > 1: - sys.stderr.write( -- "get_chinese_mode(): " -+ "get_default_chinese_mode(): " - + "default Chinese mode found in database, mode=%s\n" - %__db_chinese_mode) - return __db_chinese_mode -@@ -390,19 +393,19 @@ - __lc = os.environ['LC_ALL'].split('.')[0].lower() - if debug_level > 1: - sys.stderr.write( -- "get_chinese_mode(): __lc=%s found in LC_ALL\n" -+ "get_default_chinese_mode(): __lc=%s found in LC_ALL\n" - % __lc) - elif 'LC_CTYPE' in os.environ: - __lc = os.environ['LC_CTYPE'].split('.')[0].lower() - if debug_level > 1: - sys.stderr.write( -- "get_chinese_mode(): __lc=%s found in LC_CTYPE\n" -+ "get_default_chinese_mode(): __lc=%s found in LC_CTYPE\n" - % __lc) - else: - __lc = os.environ['LANG'].split('.')[0].lower() - if debug_level > 1: - sys.stderr.write( -- "get_chinese_mode(): __lc=%s found in LANG\n" -+ "get_default_chinese_mode(): __lc=%s found in LANG\n" - % __lc) - - if '_cn' in __lc or '_sg' in __lc: -@@ -419,14 +422,14 @@ - # variant: - if debug_level > 1: - sys.stderr.write( -- "get_chinese_mode(): last fallback, " -+ "get_default_chinese_mode(): last fallback, " - + "database is Chinese but we don’t know " - + "which variant.\n") - return 4 # show all Chinese characters - else: - if debug_level > 1: - sys.stderr.write( -- "get_chinese_mode(): last fallback, " -+ "get_default_chinese_mode(): last fallback, " - + "database is not Chinese, returning -1.\n") - return -1 - except: -@@ -1202,7 +1205,7 @@ - class tabengine (IBus.Engine): - '''The IM Engine for Tables''' - -- def __init__(self, bus, obj_path, db ): -+ def __init__(self, bus, obj_path, db, unit_test=False): - super(tabengine, self).__init__(connection=bus.get_connection(), - object_path=obj_path) - global debug_level -@@ -1210,6 +1213,7 @@ - debug_level = int(os.getenv('IBUS_TABLE_DEBUG_LEVEL')) - except (TypeError, ValueError): - debug_level = int(0) -+ self._unit_test = unit_test - self._input_purpose = 0 - self._has_input_purpose = False - if hasattr(IBus, 'InputPurpose'): -@@ -1224,12 +1228,16 @@ - os.path.sep, 'icons', os.path.sep) - # name for config section - self._engine_name = os.path.basename( -- self.db.filename).replace('.db', '') -- self._config_section = ( -- "engine/Table/%s" % self._engine_name.replace(' ','_')) -+ self.db.filename).replace('.db', '').replace(' ','_') -+ self._config_section = it_util.config_section_normalize( -+ "engine/Table/%s" % self._engine_name) -+ if debug_level > 1: -+ sys.stderr.write( -+ 'tabengine.__init__() self._config_section = %s\n' -+ % self._config_section) - - # config module -- self._config = self._bus.get_config () -+ self._config = self._bus.get_config() - self._config.connect ("value-changed", self.config_value_changed_cb) - - # self._ime_py: Indicates whether this table supports pinyin mode -@@ -1699,7 +1707,7 @@ - self._save_user_count = 0 - super(tabengine, self).destroy() - -- def set_input_mode(self, mode=0): -+ def set_input_mode(self, mode=1): - if mode == self._input_mode: - return - self._input_mode = mode -@@ -1722,6 +1730,9 @@ - self._full_width_punct[self._input_mode]) - self.reset() - -+ def get_input_mode(self): -+ return self._input_mode -+ - def set_pinyin_mode(self, mode=False): - if mode == self._editor._py_mode: - return -@@ -1741,76 +1752,302 @@ - self._input_mode) - self._update_ui() - -- def set_onechar_mode(self, mode=False): -+ def set_onechar_mode(self, mode=False, update_dconf=True): - if mode == self._editor._onechar: - return - self._editor._onechar = mode - self._init_or_update_property_menu( - self.onechar_mode_menu, mode) -- self._config.set_value( -- self._config_section, -- "OneChar", -- GLib.Variant.new_boolean(mode)) -+ self.db.reset_phrases_cache() -+ if update_dconf: -+ self._config.set_value( -+ self._config_section, -+ "OneChar", -+ GLib.Variant.new_boolean(mode)) -+ -+ def get_onechar_mode(self): -+ return self._editor._onechar - -- def set_autocommit_mode(self, mode=False): -+ def set_autocommit_mode(self, mode=False, update_dconf=True): - if mode == self._auto_commit: - return - self._auto_commit = mode - self._init_or_update_property_menu( - self.autocommit_mode_menu, mode) -- self._config.set_value( -- self._config_section, -- "AutoCommit", -- GLib.Variant.new_boolean(mode)) -+ if update_dconf: -+ self._config.set_value( -+ self._config_section, -+ "AutoCommit", -+ GLib.Variant.new_boolean(mode)) - -- def set_letter_width(self, mode=False, input_mode=0): -- if mode == self._full_width_letter[input_mode]: -+ def get_autocommit_mode(self): -+ return self._auto_commit -+ -+ def set_autoselect_mode(self, mode=False, update_dconf=True): -+ if mode == self._auto_select: - return -- self._full_width_letter[input_mode] = mode -- self._editor._full_width_letter[input_mode] = mode -- if input_mode == self._input_mode: -- self._init_or_update_property_menu( -- self.letter_width_menu, mode) -- if input_mode: -+ self._auto_select = mode -+ self._editor._auto_select = mode -+ if update_dconf: - self._config.set_value( - self._config_section, -- "TabDefFullWidthLetter", -+ "AutoSelect", - GLib.Variant.new_boolean(mode)) -- else: -+ -+ def get_autoselect_mode(self): -+ return self._auto_select -+ -+ def set_autowildcard_mode(self, mode=False, update_dconf=True): -+ if mode == self._auto_wildcard: -+ return -+ self._auto_wildcard = mode -+ self._editor._auto_wildcard = mode -+ self.db.reset_phrases_cache() -+ if update_dconf: - self._config.set_value( - self._config_section, -- "EnDefFullWidthLetter", -+ "AutoWildcard", - GLib.Variant.new_boolean(mode)) - -- def set_punctuation_width(self, mode=False, input_mode=0): -- if mode == self._full_width_punct[input_mode]: -+ def get_autowildcard_mode(self): -+ return self._auto_wildcard -+ -+ def set_single_wildcard_char(self, char=u'', update_dconf=True): -+ if char == self._single_wildcard_char: - return -- self._full_width_punct[input_mode] = mode -- self._editor._full_width_punct[input_mode] = mode -- if input_mode == self._input_mode: -- self._init_or_update_property_menu( -- self.punctuation_width_menu, mode) -- if input_mode: -+ self._single_wildcard_char = char -+ self._editor._single_wildcard_char = char -+ self.db.reset_phrases_cache() -+ if update_dconf: -+ self._config.set_value( -+ self._config_section, -+ "singlewildcardchar", -+ GLib.Variant.new_string(char)) -+ -+ def get_single_wildcard_char(self): -+ return self._single_wildcard_char -+ -+ def set_multi_wildcard_char(self, char=u'', update_dconf=True): -+ if char == self._multi_wildcard_char: -+ return -+ self._multi_wildcard_char = char -+ self._editor._multi_wildcard_char = char -+ self.db.reset_phrases_cache() -+ if update_dconf: -+ self._config.set_value( -+ self._config_section, -+ "multiwildcardchar", -+ GLib.Variant.new_string(char)) -+ -+ def get_multi_wildcard_char(self): -+ return self._multi_wildcard_char -+ -+ def set_space_key_behavior_mode(self, mode=False, update_dconf=True): -+ '''Sets the behaviour of the space key -+ -+ :param mode: How the space key should behave -+ :type mode: Boolean -+ True: space is used as a page down key -+ and not as a commit key. -+ False: space is used as a commit key -+ and not used as a page down key -+ :param update_dconf: Whether to write the change to dconf. -+ Set this to False if this method is -+ called because the dconf key changed -+ to avoid endless loops when the dconf -+ key is changed twice in a short time. -+ :type update_dconf: boolean -+ ''' -+ if debug_level > 1: -+ sys.stderr.write( -+ "set_space_key_behavior_mode(%s)\n" -+ %mode) -+ if mode == True: -+ # space is used as a page down key and not as a commit key: -+ if IBus.KEY_space not in self._page_down_keys: -+ self._page_down_keys.append(IBus.KEY_space) -+ if IBus.KEY_space in self._commit_keys: -+ self._commit_keys.remove(IBus.KEY_space) -+ if mode == False: -+ # space is used as a commit key and not used as a page down key: -+ if IBus.KEY_space in self._page_down_keys: -+ self._page_down_keys.remove(IBus.KEY_space) -+ if IBus.KEY_space not in self._commit_keys: -+ self._commit_keys.append(IBus.KEY_space) -+ if debug_level > 1: -+ sys.stderr.write( -+ 'set_space_key_behavior_mode(): self._page_down_keys=%s\n' -+ % repr(self._page_down_keys)) -+ sys.stderr.write( -+ 'set_space_key_behavior_mode(): self._commit_keys=%s\n' -+ % repr(self._commit_keys)) -+ if update_dconf: - self._config.set_value( - self._config_section, -- "TabDefFullWidthPunct", -+ "spacekeybehavior", - GLib.Variant.new_boolean(mode)) -- else: -+ -+ def get_space_key_behavior_mode(self): -+ mode = False -+ if IBus.KEY_space in self._page_down_keys: -+ mode = True -+ if IBus.KEY_space in self._commit_keys: -+ # commit key behaviour overrides the page down behaviour -+ mode = False -+ return mode -+ -+ def set_always_show_lookup(self, mode=False, update_dconf=True): -+ if mode == self._always_show_lookup: -+ return -+ self._always_show_lookup = mode -+ if update_dconf: - self._config.set_value( - self._config_section, -- "EnDefFullWidthPunct", -+ "AlwaysShowLookup", - GLib.Variant.new_boolean(mode)) - -- def set_chinese_mode(self, mode=0): -+ def get_always_show_lookup(self): -+ return self._always_show_lookup -+ -+ def set_lookup_table_orientation(self, orientation, update_dconf=True): -+ '''Sets the orientation of the lookup table -+ -+ :param orientation: The orientation of the lookup table -+ :type mode: integer >= 0 and <= 2 -+ IBUS_ORIENTATION_HORIZONTAL = 0, -+ IBUS_ORIENTATION_VERTICAL = 1, -+ IBUS_ORIENTATION_SYSTEM = 2. -+ :param update_dconf: Whether to write the change to dconf. -+ Set this to False if this method is -+ called because the dconf key changed -+ to avoid endless loops when the dconf -+ key is changed twice in a short time. -+ :type update_dconf: boolean -+ ''' -+ if debug_level > 1: -+ sys.stderr.write( -+ "set_lookup_table_orientation(%s)\n" -+ %orientation) -+ if orientation == self._editor._orientation: -+ return -+ if orientation >= 0 and orientation <= 2: -+ self._editor._orientation = orientation -+ self._editor._lookup_table.set_orientation(orientation) -+ if update_dconf: -+ self._config.set_value( -+ self._config_section, -+ 'lookuptableorientation', -+ GLib.Variant.new_int32(orientation)) -+ -+ def get_lookup_table_orientation(self): -+ '''Returns the current orientation of the lookup table -+ -+ :rtype: integer -+ ''' -+ return self._editor._orientation -+ -+ def set_page_size(self, page_size, update_dconf=True): -+ '''Sets the page size of the lookup table -+ -+ :param orientation: The orientation of the lookup table -+ :type mode: integer >= 1 and <= number of select keys -+ :param update_dconf: Whether to write the change to dconf. -+ Set this to False if this method is -+ called because the dconf key changed -+ to avoid endless loops when the dconf -+ key is changed twice in a short time. -+ :type update_dconf: boolean -+ ''' -+ if debug_level > 1: -+ sys.stderr.write( -+ "set_page_size(%s)\n" -+ %page_size) -+ if page_size == self._editor._page_size: -+ return -+ if value > len(self._editor._select_keys): -+ value = len(self._editor._select_keys) -+ if value < 1: -+ value = 1 -+ self._editor._page_size = value -+ self._editor._lookup_table = self._editor.get_new_lookup_table( -+ page_size = self._editor._page_size, -+ select_keys = self._editor._select_keys, -+ orientation = self._editor._orientation) -+ self.reset() -+ if update_dconf: -+ self._config.set_value( -+ self._config_section, -+ 'lookuptablepagesize', -+ GLib.Variant.new_int32(value)) -+ -+ def get_page_size(self): -+ '''Returns the current page size of the lookup table -+ -+ :rtype: integer -+ ''' -+ return self._editor._page_size -+ -+ def set_letter_width(self, mode=False, input_mode=0, update_dconf=True): -+ if mode == self._full_width_letter[input_mode]: -+ return -+ self._full_width_letter[input_mode] = mode -+ self._editor._full_width_letter[input_mode] = mode -+ if input_mode == self._input_mode: -+ self._init_or_update_property_menu( -+ self.letter_width_menu, mode) -+ if update_dconf: -+ if input_mode: -+ self._config.set_value( -+ self._config_section, -+ "TabDefFullWidthLetter", -+ GLib.Variant.new_boolean(mode)) -+ else: -+ self._config.set_value( -+ self._config_section, -+ "EnDefFullWidthLetter", -+ GLib.Variant.new_boolean(mode)) -+ -+ def get_letter_width(self): -+ return self._full_width_letter -+ -+ def set_punctuation_width(self, mode=False, input_mode=0, update_dconf=True): -+ if mode == self._full_width_punct[input_mode]: -+ return -+ self._full_width_punct[input_mode] = mode -+ self._editor._full_width_punct[input_mode] = mode -+ if input_mode == self._input_mode: -+ self._init_or_update_property_menu( -+ self.punctuation_width_menu, mode) -+ if update_dconf: -+ if input_mode: -+ self._config.set_value( -+ self._config_section, -+ "TabDefFullWidthPunct", -+ GLib.Variant.new_boolean(mode)) -+ else: -+ self._config.set_value( -+ self._config_section, -+ "EnDefFullWidthPunct", -+ GLib.Variant.new_boolean(mode)) -+ -+ def get_punctuation_width(self): -+ return self._full_width_punct -+ -+ def set_chinese_mode(self, mode=0, update_dconf=True): - if mode == self._editor._chinese_mode: - return - self._editor._chinese_mode = mode -+ self.db.reset_phrases_cache() - self._init_or_update_property_menu( - self.chinese_mode_menu, mode) -- self._config.set_value( -- self._config_section, -- "ChineseMode", -- GLib.Variant.new_int32(mode)) -+ if update_dconf: -+ self._config.set_value( -+ self._config_section, -+ "ChineseMode", -+ GLib.Variant.new_int32(mode)) -+ -+ def get_chinese_mode(self): -+ return self._editor._chinese_mode - - def _init_or_update_property_menu(self, menu, current_mode=0): - key = menu['key'] -@@ -2233,6 +2470,44 @@ - return True - return False - -+ def _return_false(self, keyval, keycode, state): -+ '''A replacement for “return False” in do_process_key_event() -+ -+ do_process_key_event should return “True” if a key event has -+ been handled completely. It should return “False” if the key -+ event should be passed to the application. -+ -+ But just doing “return False” doesn’t work well when trying to -+ do the unit tests. The MockEngine class in the unit tests -+ cannot get that return value. Therefore, it cannot do the -+ necessary updates to the self._mock_committed_text etc. which -+ prevents proper testing of the effects of such keys passed to -+ the application. Instead of “return False”, one can also use -+ self.forward_key_event(keyval, keycode, keystate) to pass the -+ key to the application. And this works fine with the unit -+ tests because a forward_key_event function is implemented in -+ MockEngine as well which then gets the key and can test its -+ effects. -+ -+ Unfortunately, “forward_key_event()” does not work in Qt5 -+ applications because the ibus module in Qt5 does not implement -+ “forward_key_event()”. Therefore, always using -+ “forward_key_event()” instead of “return False” in -+ “do_process_key_event()” would break ibus-typing-booster -+ completely for all Qt5 applictions. -+ -+ To work around this problem and make unit testing possible -+ without breaking Qt5 applications, we use this helper function -+ which uses “forward_key_event()” when unit testing and “return -+ False” during normal usage. -+ -+ ''' -+ if self._unit_test: -+ self.forward_key_event(keyval, keycode, state) -+ return True -+ else: -+ return False -+ - def do_process_key_event(self, keyval, keycode, state): - '''Process Key Events - Key Events include Key Press and Key Release, -@@ -2243,9 +2518,13 @@ - if (self._has_input_purpose - and self._input_purpose - in [IBus.InputPurpose.PASSWORD, IBus.InputPurpose.PIN]): -- return False -+ return self._return_false(keyval, keycode, state) - - key = KeyEvent(keyval, keycode, state) -+ if debug_level > 1: -+ sys.stderr.write( -+ "process_key_event() " -+ "KeyEvent object: %s" % key) - - result = self._process_key_event (key) - self._prev_key = key -@@ -2308,13 +2587,13 @@ - def _english_mode_process_key_event(self, key): - # Ignore key release events - if key.state & IBus.ModifierType.RELEASE_MASK: -- return False -+ return self._return_false(key.val, key.code, key.state) - if key.val >= 128: -- return False -+ return self._return_false(key.val, key.code, key.state) - # we ignore all hotkeys here - if (key.state - & (IBus.ModifierType.CONTROL_MASK|IBus.ModifierType.MOD1_MASK)): -- return False -+ return self._return_false(key.val, key.code, key.state) - keychar = IBus.keyval_to_unicode(key.val) - if type(keychar) != type(u''): - keychar = keychar.decode('UTF-8') -@@ -2323,7 +2602,7 @@ - else: - trans_char = self.cond_letter_translate(keychar) - if trans_char == keychar: -- return False -+ return self._return_false(key.val, key.code, key.state) - self.commit_string(trans_char) - return True - -@@ -2387,7 +2666,7 @@ - # (Must be below all self._match_hotkey() callse - # because these match on a release event). - if key.state & IBus.ModifierType.RELEASE_MASK: -- return False -+ return self._return_false(key.val, key.code, key.state) - - keychar = IBus.keyval_to_unicode(key.val) - if type(keychar) != type(u''): -@@ -2419,7 +2698,7 @@ - trans_char = self.cond_letter_translate(keychar) - if trans_char == keychar: - self._prev_char = trans_char -- return False -+ return self._return_false(key.val, key.code, key.state) - else: - self.commit_string(trans_char) - return True -@@ -2441,12 +2720,12 @@ - # input but it ends up here. If it is leading input - # (i.e. the preëdit is empty) we should always pass - # IBus.KEY_KP_Enter to the application: -- return False -+ return self._return_false(key.val, key.code, key.state) - if self._auto_select: - self._editor.commit_to_preedit() - commit_string = self._editor.get_preedit_string_complete() - self.commit_string(commit_string) -- return False -+ return self._return_false(key.val, key.code, key.state) - else: - commit_string = self._editor.get_preedit_tabkeys_complete() - self.commit_string(commit_string) -@@ -2474,18 +2753,18 @@ - # to “шшш”. - self._editor.commit_to_preedit() - self.commit_string(self._editor.get_preedit_string_complete()) -- return False -+ return self._return_false(key.val, key.code, key.state) - - if key.val in (IBus.KEY_Down, IBus.KEY_KP_Down) : - if not self._editor.get_preedit_string_complete(): -- return False -+ return self._return_false(key.val, key.code, key.state) - res = self._editor.cursor_down() - self._update_ui() - return res - - if key.val in (IBus.KEY_Up, IBus.KEY_KP_Up): - if not self._editor.get_preedit_string_complete(): -- return False -+ return self._return_false(key.val, key.code, key.state) - res = self._editor.cursor_up() - self._update_ui() - return res -@@ -2493,7 +2772,7 @@ - if (key.val in (IBus.KEY_Left, IBus.KEY_KP_Left) - and key.state & IBus.ModifierType.CONTROL_MASK): - if not self._editor.get_preedit_string_complete(): -- return False -+ return self._return_false(key.val, key.code, key.state) - self._editor.control_arrow_left() - self._update_ui() - return True -@@ -2501,21 +2780,21 @@ - if (key.val in (IBus.KEY_Right, IBus.KEY_KP_Right) - and key.state & IBus.ModifierType.CONTROL_MASK): - if not self._editor.get_preedit_string_complete(): -- return False -+ return self._return_false(key.val, key.code, key.state) - self._editor.control_arrow_right() - self._update_ui() - return True - - if key.val in (IBus.KEY_Left, IBus.KEY_KP_Left): - if not self._editor.get_preedit_string_complete(): -- return False -+ return self._return_false(key.val, key.code, key.state) - self._editor.arrow_left() - self._update_ui() - return True - - if key.val in (IBus.KEY_Right, IBus.KEY_KP_Right): - if not self._editor.get_preedit_string_complete(): -- return False -+ return self._return_false(key.val, key.code, key.state) - self._editor.arrow_right() - self._update_ui() - return True -@@ -2523,14 +2802,14 @@ - if (key.val == IBus.KEY_BackSpace - and key.state & IBus.ModifierType.CONTROL_MASK): - if not self._editor.get_preedit_string_complete(): -- return False -+ return self._return_false(key.val, key.code, key.state) - self._editor.remove_preedit_before_cursor() - self._update_ui() - return True - - if key.val == IBus.KEY_BackSpace: - if not self._editor.get_preedit_string_complete(): -- return False -+ return self._return_false(key.val, key.code, key.state) - self._editor.remove_char() - self._update_ui() - return True -@@ -2538,14 +2817,14 @@ - if (key.val == IBus.KEY_Delete - and key.state & IBus.ModifierType.CONTROL_MASK): - if not self._editor.get_preedit_string_complete(): -- return False -+ return self._return_false(key.val, key.code, key.state) - self._editor.remove_preedit_after_cursor() - self._update_ui() - return True - - if key.val == IBus.KEY_Delete: - if not self._editor.get_preedit_string_complete(): -- return False -+ return self._return_false(key.val, key.code, key.state) - self._editor.delete() - self._update_ui() - return True -@@ -2567,10 +2846,10 @@ - # now we ignore all other hotkeys - if (key.state - & (IBus.ModifierType.CONTROL_MASK|IBus.ModifierType.MOD1_MASK)): -- return False -+ return self._return_false(key.val, key.code, key.state) - - if key.state & IBus.ModifierType.MOD1_MASK: -- return False -+ return self._return_false(key.val, key.code, key.state) - - # Section to handle valid input characters: - # -@@ -2731,7 +3010,7 @@ - # - # returned no result. So whatever this was, we cannot handle it, - # just pass it through to the application by returning “False”. -- return False -+ return self._return_false(key.val, key.code, key.state) - - def do_focus_in (self): - if debug_level > 1: -@@ -2802,92 +3081,47 @@ - self.set_input_mode(value) - return - if name == u'autoselect': -- self._editor._auto_select = value -- self._auto_select = value -+ self.set_autoselect_mode(value, update_dconf=False) - return - if name == u'autocommit': -- self.set_autocommit_mode(value) -+ self.set_autocommit_mode(value, update_dconf=False) - return - if name == u'chinesemode': -- self.set_chinese_mode(value) -- self.db.reset_phrases_cache() -+ self.set_chinese_mode(value, update_dconf=False) - return - if name == u'endeffullwidthletter': -- self.set_letter_width(value, input_mode=0) -+ self.set_letter_width(value, input_mode=0, update_dconf=False) - return - if name == u'endeffullwidthpunct': -- self.set_punctuation_width(value, input_mode=0) -+ self.set_punctuation_width(value, input_mode=0, update_dconf=False) - return - if name == u'lookuptableorientation': -- self._editor._orientation = value -- self._editor._lookup_table.set_orientation(value) -+ self.set_lookup_table_orientation(value, update_dconf=False) - return - if name == u'lookuptablepagesize': -- if value > len(self._editor._select_keys): -- value = len(self._editor._select_keys) -- self._config.set_value( -- self._config_section, -- 'lookuptablepagesize', -- GLib.Variant.new_int32(value)) -- if value < 1: -- value = 1 -- self._config.set_value( -- self._config_section, -- 'lookuptablepagesize', -- GLib.Variant.new_int32(value)) -- self._editor._page_size = value -- self._editor._lookup_table = self._editor.get_new_lookup_table( -- page_size = self._editor._page_size, -- select_keys = self._editor._select_keys, -- orientation = self._editor._orientation) -- self.reset() -- return -- if name == u'lookuptableselectkeys': -- self._editor.set_select_keys(value) -+ self.set_page_size(value, update_dconf=False) - return - if name == u'onechar': -- self.set_onechar_mode(value) -- self.db.reset_phrases_cache() -+ self.set_onechar_mode(value, update_dconf=False) - return - if name == u'tabdeffullwidthletter': -- self.set_letter_width(value, input_mode=1) -+ self.set_letter_width(value, input_mode=1, update_dconf=False) - return - if name == u'tabdeffullwidthpunct': -- self.set_punctuation_width(value, input_mode=1) -+ self.set_punctuation_width(value, input_mode=1, update_dconf=False) - return - if name == u'alwaysshowlookup': -- self._always_show_lookup = value -+ self.set_always_show_lookup(value, update_dconf=False) - return - if name == u'spacekeybehavior': -- if value == True: -- # space is used as a page down key and not as a commit key: -- if IBus.KEY_space not in self._page_down_keys: -- self._page_down_keys.append(IBus.KEY_space) -- if IBus.KEY_space in self._commit_keys: -- self._commit_keys.remove(IBus.KEY_space) -- if value == False: -- # space is used as a commit key and not used as a page down key: -- if IBus.KEY_space in self._page_down_keys: -- self._page_down_keys.remove(IBus.KEY_space) -- if IBus.KEY_space not in self._commit_keys: -- self._commit_keys.append(IBus.KEY_space) -- if debug_level > 1: -- sys.stderr.write( -- "self._page_down_keys=%s\n" -- % repr(self._page_down_keys)) -+ self.set_space_key_behavior_mode(value, update_dconf=False) - return - if name == u'singlewildcardchar': -- self._single_wildcard_char = value -- self._editor._single_wildcard_char = value -- self.db.reset_phrases_cache() -+ self.set_single_wildcard_char(value, update_dconf=False) - return - if name == u'multiwildcardchar': -- self._multi_wildcard_char = value -- self._editor._multi_wildcard_char = value -- self.db.reset_phrases_cache() -+ self.set_multi_wildcard_char(value, update_dconf=False) - return - if name == u'autowildcard': -- self._auto_wildcard = value -- self._editor._auto_wildcard = value -- self.db.reset_phrases_cache() -+ self.set_autowildcard_mode(value, update_dconf=False) - return -diff -Nru ibus-table-1.9.18.orig/engine/table.py.orig ibus-table-1.9.18/engine/table.py.orig ---- ibus-table-1.9.18.orig/engine/table.py.orig 1970-01-01 01:00:00.000000000 +0100 -+++ ibus-table-1.9.18/engine/table.py.orig 2020-07-22 14:43:13.607657038 +0200 -@@ -0,0 +1,2890 @@ -+# -*- coding: utf-8 -*- -+# vim:et sts=4 sw=4 -+# -+# ibus-table - The Tables engine for IBus -+# -+# Copyright (c) 2008-2009 Yu Yuwei -+# Copyright (c) 2009-2014 Caius "kaio" CHANCE -+# Copyright (c) 2012-2015 Mike FABIAN -+# -+# This library is free software; you can redistribute it and/or -+# modify it under the terms of the GNU Lesser General Public -+# License as published by the Free Software Foundation; either -+# version 2.1 of the License, or (at your option) any later version. -+# -+# This library is distributed in the hope that it will be useful, -+# but WITHOUT ANY WARRANTY; without even the implied warranty of -+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+# Lesser General Public License for more details. -+# -+# You should have received a copy of the GNU Lesser General Public -+# License along with this library; if not, write to the Free Software -+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -+# -+ -+__all__ = ( -+ "tabengine", -+) -+ -+import sys -+import os -+import string -+from gi import require_version -+require_version('IBus', '1.0') -+from gi.repository import IBus -+from gi.repository import GLib -+#import tabsqlitedb -+import re -+from gi.repository import GObject -+import time -+ -+debug_level = int(0) -+ -+from gettext import dgettext -+_ = lambda a : dgettext ("ibus-table", a) -+N_ = lambda a : a -+ -+ -+def ascii_ispunct(character): -+ ''' -+ Use our own function instead of ascii.ispunct() -+ from “from curses import ascii” because the behaviour -+ of the latter is kind of weird. In Python 3.3.2 it does -+ for example: -+ -+ >>> from curses import ascii -+ >>> ascii.ispunct('.') -+ True -+ >>> ascii.ispunct(u'.') -+ True -+ >>> ascii.ispunct('a') -+ False -+ >>> ascii.ispunct(u'a') -+ False -+ >>> -+ >>> ascii.ispunct(u'あ') -+ True -+ >>> ascii.ispunct('あ') -+ True -+ >>> -+ -+ あ isn’t punctuation. ascii.ispunct() only really works -+ in the ascii range, it returns weird results when used -+ over the whole unicode range. Maybe we should better use -+ unicodedata.category(), which works fine to figure out -+ what is punctuation for all of unicode. But at the moment -+ I am only porting from Python2 to Python3 and just want to -+ preserve the original behaviour for the moment. -+ ''' -+ if character in '''!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~''': -+ return True -+ else: -+ return False -+ -+def variant_to_value(variant): -+ if type(variant) != GLib.Variant: -+ return variant -+ type_string = variant.get_type_string() -+ if type_string == 's': -+ return variant.get_string() -+ elif type_string == 'i': -+ return variant.get_int32() -+ elif type_string == 'b': -+ return variant.get_boolean() -+ elif type_string == 'as': -+ # In the latest pygobject3 3.3.4 or later, g_variant_dup_strv -+ # returns the allocated strv but in the previous release, -+ # it returned the tuple of (strv, length) -+ if type(GLib.Variant.new_strv([]).dup_strv()) == tuple: -+ return variant.dup_strv()[0] -+ else: -+ return variant.dup_strv() -+ else: -+ print('error: unknown variant type: %s' %type_string) -+ return variant -+ -+def argb(a, r, g, b): -+ return (((a & 0xff)<<24) -+ + ((r & 0xff) << 16) -+ + ((g & 0xff) << 8) -+ + (b & 0xff)) -+ -+def rgb(r, g, b): -+ return argb(255, r, g, b) -+ -+__half_full_table = [ -+ (0x0020, 0x3000, 1), -+ (0x0021, 0xFF01, 0x5E), -+ (0x00A2, 0xFFE0, 2), -+ (0x00A5, 0xFFE5, 1), -+ (0x00A6, 0xFFE4, 1), -+ (0x00AC, 0xFFE2, 1), -+ (0x00AF, 0xFFE3, 1), -+ (0x20A9, 0xFFE6, 1), -+ (0xFF61, 0x3002, 1), -+ (0xFF62, 0x300C, 2), -+ (0xFF64, 0x3001, 1), -+ (0xFF65, 0x30FB, 1), -+ (0xFF66, 0x30F2, 1), -+ (0xFF67, 0x30A1, 1), -+ (0xFF68, 0x30A3, 1), -+ (0xFF69, 0x30A5, 1), -+ (0xFF6A, 0x30A7, 1), -+ (0xFF6B, 0x30A9, 1), -+ (0xFF6C, 0x30E3, 1), -+ (0xFF6D, 0x30E5, 1), -+ (0xFF6E, 0x30E7, 1), -+ (0xFF6F, 0x30C3, 1), -+ (0xFF70, 0x30FC, 1), -+ (0xFF71, 0x30A2, 1), -+ (0xFF72, 0x30A4, 1), -+ (0xFF73, 0x30A6, 1), -+ (0xFF74, 0x30A8, 1), -+ (0xFF75, 0x30AA, 2), -+ (0xFF77, 0x30AD, 1), -+ (0xFF78, 0x30AF, 1), -+ (0xFF79, 0x30B1, 1), -+ (0xFF7A, 0x30B3, 1), -+ (0xFF7B, 0x30B5, 1), -+ (0xFF7C, 0x30B7, 1), -+ (0xFF7D, 0x30B9, 1), -+ (0xFF7E, 0x30BB, 1), -+ (0xFF7F, 0x30BD, 1), -+ (0xFF80, 0x30BF, 1), -+ (0xFF81, 0x30C1, 1), -+ (0xFF82, 0x30C4, 1), -+ (0xFF83, 0x30C6, 1), -+ (0xFF84, 0x30C8, 1), -+ (0xFF85, 0x30CA, 6), -+ (0xFF8B, 0x30D2, 1), -+ (0xFF8C, 0x30D5, 1), -+ (0xFF8D, 0x30D8, 1), -+ (0xFF8E, 0x30DB, 1), -+ (0xFF8F, 0x30DE, 5), -+ (0xFF94, 0x30E4, 1), -+ (0xFF95, 0x30E6, 1), -+ (0xFF96, 0x30E8, 6), -+ (0xFF9C, 0x30EF, 1), -+ (0xFF9D, 0x30F3, 1), -+ (0xFFA0, 0x3164, 1), -+ (0xFFA1, 0x3131, 30), -+ (0xFFC2, 0x314F, 6), -+ (0xFFCA, 0x3155, 6), -+ (0xFFD2, 0x315B, 9), -+ (0xFFE9, 0x2190, 4), -+ (0xFFED, 0x25A0, 1), -+ (0xFFEE, 0x25CB, 1)] -+ -+def unichar_half_to_full(c): -+ code = ord(c) -+ for half, full, size in __half_full_table: -+ if code >= half and code < half + size: -+ if sys.version_info >= (3, 0, 0): -+ return chr(full + code - half) -+ else: -+ return unichr(full + code - half) -+ return c -+ -+def unichar_full_to_half(c): -+ code = ord(c) -+ for half, full, size in __half_full_table: -+ if code >= full and code < full + size: -+ if sys.version_info >= (3, 0, 0): -+ return chr(half + code - full) -+ else: -+ return unichr(half + code - full) -+ return c -+ -+SAVE_USER_COUNT_MAX = 16 -+SAVE_USER_TIMEOUT = 30 # in seconds -+ -+class KeyEvent: -+ def __init__(self, keyval, keycode, state): -+ self.val = keyval -+ self.code = keycode -+ self.state = state -+ def __str__(self): -+ return "%s 0x%08x" % (IBus.keyval_name(self.val), self.state) -+ -+ -+class editor(object): -+ '''Hold user inputs chars and preedit string''' -+ def __init__ (self, config, valid_input_chars, pinyin_valid_input_chars, -+ single_wildcard_char, multi_wildcard_char, -+ auto_wildcard, full_width_letter, full_width_punct, -+ max_key_length, database): -+ self.db = database -+ self._config = config -+ engine_name = os.path.basename(self.db.filename).replace('.db', '') -+ self._config_section = "engine/Table/%s" % engine_name.replace(' ','_') -+ self._max_key_length = int(max_key_length) -+ self._max_key_length_pinyin = 7 -+ self._valid_input_chars = valid_input_chars -+ self._pinyin_valid_input_chars = pinyin_valid_input_chars -+ self._single_wildcard_char = single_wildcard_char -+ self._multi_wildcard_char = multi_wildcard_char -+ self._auto_wildcard = auto_wildcard -+ self._full_width_letter = full_width_letter -+ self._full_width_punct = full_width_punct -+ # -+ # The values below will be reset in -+ # self.clear_input_not_committed_to_preedit() -+ self._chars_valid = u'' # valid user input in table mode -+ self._chars_invalid = u'' # invalid user input in table mode -+ self._chars_valid_update_candidates_last = u'' -+ self._chars_invalid_update_candidates_last = u'' -+ # self._candidates holds the “best” candidates matching the user input -+ # [(tabkeys, phrase, freq, user_freq), ...] -+ self._candidates = [] -+ self._candidates_previous = [] -+ -+ # self._u_chars: holds the user input of the phrases which -+ # have been automatically committed to preedit (but not yet -+ # “really” committed). -+ self._u_chars = [] -+ # self._strings: holds the phrases which have been -+ # automatically committed to preedit (but not yet “really” -+ # committed). -+ # -+ # self._u_chars and self._strings should always have the same -+ # length, if I understand it correctly. -+ # -+ # Example when using the wubi-jidian86 table: -+ # -+ # self._u_chars = ['gaaa', 'gggg', 'ihty'] -+ # self._strings = ['形式', '王', '小'] -+ # -+ # I.e. after typing 'gaaa', '形式' is in the preedit and -+ # both self._u_chars and self._strings are empty. When typing -+ # another 'g', the maximum key length of the wubi table (which is 4) -+ # is exceeded and '形式' is automatically committed to the preedit -+ # (but not yet “really” committed, i.e. not yet committed into -+ # the application). The key 'gaaa' and the matching phrase '形式' -+ # are stored in self._u_chars and self._strings respectively -+ # and 'gaaa' is removed from self._chars_valid. Now self._chars_valid -+ # contains only the 'g' which starts a new search for candidates ... -+ # When removing the 'g' with backspace, the 'gaaa' is moved -+ # back from self._u_chars into self._chars_valid again and -+ # the same candidate list is shown as before the last 'g' had -+ # been entered. -+ self._strings = [] -+ # self._cursor_precommit: The cursor -+ # position inthe array of strings which have already been -+ # committed to preëdit but not yet “really” committed. -+ self._cursor_precommit = 0 -+ -+ self._prompt_characters = eval( -+ self.db.ime_properties.get('char_prompts')) -+ -+ select_keys_csv = variant_to_value(self._config.get_value( -+ self._config_section, -+ "LookupTableSelectKeys")) -+ if select_keys_csv == None: -+ select_keys_csv = self.db.get_select_keys() -+ if select_keys_csv == None: -+ select_keys_csv = '1,2,3,4,5,6,7,8,9' -+ self._select_keys = [ -+ IBus.keyval_from_name(y) -+ for y in [x.strip() for x in select_keys_csv.split(",")]] -+ self._page_size = variant_to_value(self._config.get_value( -+ self._config_section, -+ "lookuptablepagesize")) -+ if self._page_size == None or self._page_size > len(self._select_keys): -+ self._page_size = len(self._select_keys) -+ self._orientation = variant_to_value(self._config.get_value( -+ self._config_section, -+ "LookupTableOrientation")) -+ if self._orientation == None: -+ self._orientation = self.db.get_orientation() -+ self._lookup_table = self.get_new_lookup_table( -+ page_size = self._page_size, -+ select_keys = self._select_keys, -+ orientation = self._orientation) -+ # self._py_mode: whether in pinyin mode -+ self._py_mode = False -+ # self._onechar: whether we only select single character -+ self._onechar = variant_to_value(self._config.get_value( -+ self._config_section, -+ "OneChar")) -+ if self._onechar == None: -+ self._onechar = False -+ # self._chinese_mode: the candidate filter mode, -+ # 0 means to show simplified Chinese only -+ # 1 means to show traditional Chinese only -+ # 2 means to show all characters but show simplified Chinese first -+ # 3 means to show all characters but show traditional Chinese first -+ # 4 means to show all characters -+ # we use LC_CTYPE or LANG to determine which one to use if -+ # no default comes from the config. -+ self._chinese_mode = variant_to_value(self._config.get_value( -+ self._config_section, -+ "ChineseMode")) -+ if self._chinese_mode == None: -+ self._chinese_mode = self.get_chinese_mode() -+ elif debug_level > 1: -+ sys.stderr.write( -+ "Chinese mode found in user config, mode=%s\n" -+ % self._chinese_mode) -+ -+ # If auto select is true, then the first candidate phrase will -+ # be selected automatically during typing. Auto select is true -+ # by default for the stroke5 table for example. -+ self._auto_select = variant_to_value(self._config.get_value( -+ self._config_section, -+ "AutoSelect")) -+ if self._auto_select == None: -+ if self.db.ime_properties.get('auto_select') != None: -+ self._auto_select = self.db.ime_properties.get( -+ 'auto_select').lower() == u'true' -+ else: -+ self._auto_select = False -+ -+ def get_new_lookup_table( -+ self, page_size=10, -+ select_keys=[49, 50, 51, 52, 53, 54, 55, 56, 57, 48], -+ orientation=True): -+ ''' -+ [49, 50, 51, 52, 53, 54, 55, 56, 57, 48] are the key codes -+ for the characters ['1', '2', '3', '4', '5', '6', '7', '8', '0'] -+ ''' -+ if page_size < 1: -+ page_size = 1 -+ if page_size > len(select_keys): -+ page_size = len(select_keys) -+ lookup_table = IBus.LookupTable.new( -+ page_size=page_size, -+ cursor_pos=0, -+ cursor_visible=True, -+ round=True) -+ for keycode in select_keys: -+ lookup_table.append_label( -+ IBus.Text.new_from_string("%s." %IBus.keyval_name(keycode))) -+ lookup_table.set_orientation(orientation) -+ return lookup_table -+ -+ def get_select_keys(self): -+ """ -+ Returns the list of key codes for the select keys. -+ For example, if the select keys are ["1", "2", ...] the -+ key codes are [49, 50, ...]. If the select keys are -+ ["F1", "F2", ...] the key codes are [65470, 65471, ...] -+ """ -+ return self._select_keys -+ -+ def get_chinese_mode (self): -+ ''' -+ Use db value or LC_CTYPE in your box to determine the _chinese_mode -+ ''' -+ # use db value, if applicable -+ __db_chinese_mode = self.db.get_chinese_mode() -+ if __db_chinese_mode >= 0: -+ if debug_level > 1: -+ sys.stderr.write( -+ "get_chinese_mode(): " -+ + "default Chinese mode found in database, mode=%s\n" -+ %__db_chinese_mode) -+ return __db_chinese_mode -+ # otherwise -+ try: -+ if 'LC_ALL' in os.environ: -+ __lc = os.environ['LC_ALL'].split('.')[0].lower() -+ if debug_level > 1: -+ sys.stderr.write( -+ "get_chinese_mode(): __lc=%s found in LC_ALL\n" -+ % __lc) -+ elif 'LC_CTYPE' in os.environ: -+ __lc = os.environ['LC_CTYPE'].split('.')[0].lower() -+ if debug_level > 1: -+ sys.stderr.write( -+ "get_chinese_mode(): __lc=%s found in LC_CTYPE\n" -+ % __lc) -+ else: -+ __lc = os.environ['LANG'].split('.')[0].lower() -+ if debug_level > 1: -+ sys.stderr.write( -+ "get_chinese_mode(): __lc=%s found in LANG\n" -+ % __lc) -+ -+ if '_cn' in __lc or '_sg' in __lc: -+ # CN and SG should prefer traditional Chinese by default -+ return 2 # show simplified Chinese first -+ elif '_hk' in __lc or '_tw' in __lc or '_mo' in __lc: -+ # HK, TW, and MO should prefer traditional Chinese by default -+ return 3 # show traditional Chinese first -+ else: -+ if self.db._is_chinese: -+ # This table is used for Chinese, but we don’t -+ # know for which variant. Therefore, better show -+ # all Chinese characters and don’t prefer any -+ # variant: -+ if debug_level > 1: -+ sys.stderr.write( -+ "get_chinese_mode(): last fallback, " -+ + "database is Chinese but we don’t know " -+ + "which variant.\n") -+ return 4 # show all Chinese characters -+ else: -+ if debug_level > 1: -+ sys.stderr.write( -+ "get_chinese_mode(): last fallback, " -+ + "database is not Chinese, returning -1.\n") -+ return -1 -+ except: -+ import traceback -+ traceback.print_exc() -+ return -1 -+ -+ def clear_all_input_and_preedit(self): -+ ''' -+ Clear all input, whether committed to preëdit or not. -+ ''' -+ if debug_level > 1: -+ sys.stderr.write("clear_all_input_and_preedit()\n") -+ self.clear_input_not_committed_to_preedit() -+ self._u_chars = [] -+ self._strings = [] -+ self._cursor_precommit = 0 -+ self.update_candidates() -+ -+ def is_empty(self): -+ return u'' == self._chars_valid + self._chars_invalid -+ -+ def clear_input_not_committed_to_preedit(self): -+ ''' -+ Clear the input which has not yet been committed to preëdit. -+ ''' -+ if debug_level > 1: -+ sys.stderr.write("clear_input_not_committed_to_preedit()\n") -+ self._chars_valid = u'' -+ self._chars_invalid = u'' -+ self._chars_valid_update_candidates_last = u'' -+ self._chars_invalid_update_candidates_last = u'' -+ self._lookup_table.clear() -+ self._lookup_table.set_cursor_visible(True) -+ self._candidates = [] -+ self._candidates_previous = [] -+ -+ def add_input(self, c): -+ ''' -+ Add input character and update candidates. -+ -+ Returns “True” if candidates were found, “False” if not. -+ ''' -+ if (self._chars_invalid -+ or (not self._py_mode -+ and (c not in -+ self._valid_input_chars -+ + self._single_wildcard_char -+ + self._multi_wildcard_char)) -+ or (self._py_mode -+ and (c not in -+ self._pinyin_valid_input_chars -+ + self._single_wildcard_char -+ + self._multi_wildcard_char))): -+ self._chars_invalid += c -+ else: -+ self._chars_valid += c -+ res = self.update_candidates() -+ return res -+ -+ def pop_input(self): -+ '''remove and display last input char held''' -+ _c = '' -+ if self._chars_invalid: -+ _c = self._chars_invalid[-1] -+ self._chars_invalid = self._chars_invalid[:-1] -+ elif self._chars_valid: -+ _c = self._chars_valid[-1] -+ self._chars_valid = self._chars_valid[:-1] -+ if (not self._chars_valid) and self._u_chars: -+ self._chars_valid = self._u_chars.pop( -+ self._cursor_precommit - 1) -+ self._strings.pop(self._cursor_precommit - 1) -+ self._cursor_precommit -= 1 -+ self.update_candidates () -+ return _c -+ -+ def get_input_chars (self): -+ '''get characters held, valid and invalid''' -+ return self._chars_valid + self._chars_invalid -+ -+ def split_strings_committed_to_preedit(self, index, index_in_phrase): -+ head = self._strings[index][:index_in_phrase] -+ tail = self._strings[index][index_in_phrase:] -+ self._u_chars.pop(index) -+ self._strings.pop(index) -+ self._u_chars.insert(index, self.db.parse_phrase(head)) -+ self._strings.insert(index, head) -+ self._u_chars.insert(index+1, self.db.parse_phrase(tail)) -+ self._strings.insert(index+1, tail) -+ -+ def remove_preedit_before_cursor(self): -+ '''Remove preëdit left of cursor''' -+ if self._chars_invalid: -+ return -+ if self.get_input_chars(): -+ self.commit_to_preedit() -+ if not self._strings: -+ return -+ if self._cursor_precommit <= 0: -+ return -+ self._u_chars = self._u_chars[self._cursor_precommit:] -+ self._strings = self._strings[self._cursor_precommit:] -+ self._cursor_precommit = 0 -+ -+ def remove_preedit_after_cursor(self): -+ '''Remove preëdit right of cursor''' -+ if self._chars_invalid: -+ return -+ if self.get_input_chars(): -+ self.commit_to_preedit() -+ if not self._strings: -+ return -+ if self._cursor_precommit >= len(self._strings): -+ return -+ self._u_chars = self._u_chars[:self._cursor_precommit] -+ self._strings = self._strings[:self._cursor_precommit] -+ self._cursor_precommit = len(self._strings) -+ -+ def remove_preedit_character_before_cursor(self): -+ '''Remove character before cursor in strings comitted to preëdit''' -+ if self._chars_invalid: -+ return -+ if self.get_input_chars(): -+ self.commit_to_preedit() -+ if not self._strings: -+ return -+ if self._cursor_precommit < 1: -+ return -+ self._cursor_precommit -= 1 -+ self._chars_valid = self._u_chars.pop(self._cursor_precommit) -+ self._strings.pop(self._cursor_precommit) -+ self.update_candidates() -+ -+ def remove_preedit_character_after_cursor (self): -+ '''Remove character after cursor in strings committed to preëdit''' -+ if self._chars_invalid: -+ return -+ if self.get_input_chars(): -+ self.commit_to_preedit() -+ if not self._strings: -+ return -+ if self._cursor_precommit > len(self._strings) - 1: -+ return -+ self._u_chars.pop(self._cursor_precommit) -+ self._strings.pop(self._cursor_precommit) -+ -+ def get_preedit_tabkeys_parts(self): -+ '''Returns the tabkeys which were used to type the parts -+ of the preëdit string. -+ -+ Such as “(left_of_current_edit, current_edit, right_of_current_edit)” -+ -+ “left_of_current_edit” and “right_of_current_edit” are -+ strings of tabkeys which have been typed to get the phrases -+ which have already been committed to preëdit, but not -+ “really” committed yet. “current_edit” is the string of -+ tabkeys of the part of the preëdit string which is not -+ committed at all. -+ -+ For example, the return value could look like: -+ -+ (('gggg', 'aahw'), 'adwu', ('ijgl', 'jbus')) -+ -+ See also get_preedit_string_parts() which might return something -+ like -+ -+ (('王', '工具'), '其', ('漫画', '最新')) -+ -+ when the wubi-jidian86 table is used. -+ ''' -+ left_of_current_edit = () -+ current_edit = u'' -+ right_of_current_edit = () -+ if self.get_input_chars(): -+ current_edit = self.get_input_chars() -+ if self._u_chars: -+ left_of_current_edit = tuple( -+ self._u_chars[:self._cursor_precommit]) -+ right_of_current_edit = tuple( -+ self._u_chars[self._cursor_precommit:]) -+ return (left_of_current_edit, current_edit, right_of_current_edit) -+ -+ def get_preedit_tabkeys_complete(self): -+ '''Returns the tabkeys which belong to the parts of the preëdit -+ string as a single string -+ ''' -+ (left_tabkeys, -+ current_tabkeys, -+ right_tabkeys) = self.get_preedit_tabkeys_parts() -+ return (u''.join(left_tabkeys) -+ + current_tabkeys -+ + u''.join(right_tabkeys)) -+ -+ def get_preedit_string_parts(self): -+ '''Returns the phrases which are parts of the preëdit string. -+ -+ Such as “(left_of_current_edit, current_edit, right_of_current_edit)” -+ -+ “left_of_current_edit” and “right_of_current_edit” are -+ tuples of strings which have already been committed to preëdit, but not -+ “really” committed yet. “current_edit” is the phrase in the part of the -+ preëdit string which is not yet committed at all. -+ -+ For example, the return value could look like: -+ -+ (('王', '工具'), '其', ('漫画', '最新')) -+ -+ See also get_preedit_tabkeys_parts() which might return something -+ like -+ -+ (('gggg', 'aahw'), 'adwu', ('ijgl', 'jbus')) -+ -+ when the wubi-jidian86 table is used. -+ ''' -+ left_of_current_edit = () -+ current_edit = u'' -+ right_of_current_edit = () -+ if self._candidates: -+ current_edit = self._candidates[ -+ int(self._lookup_table.get_cursor_pos())][1] -+ elif self.get_input_chars(): -+ current_edit = self.get_input_chars() -+ if self._strings: -+ left_of_current_edit = tuple( -+ self._strings[:self._cursor_precommit]) -+ right_of_current_edit = tuple( -+ self._strings[self._cursor_precommit:]) -+ return (left_of_current_edit, current_edit, right_of_current_edit) -+ -+ def get_preedit_string_complete(self): -+ '''Returns the phrases which are parts of the preëdit string as a -+ single string -+ -+ ''' -+ (left_strings, -+ current_string, -+ right_strings) = self.get_preedit_string_parts() -+ return u''.join(left_strings) + current_string + u''.join(right_strings) -+ -+ def get_caret (self): -+ '''Get caret position in preëdit string''' -+ caret = 0 -+ if self._cursor_precommit and self._strings: -+ for x in self._strings[:self._cursor_precommit]: -+ caret += len(x) -+ if self._candidates: -+ caret += len( -+ self._candidates[int(self._lookup_table.get_cursor_pos())][1]) -+ else: -+ caret += len(self.get_input_chars()) -+ return caret -+ -+ def arrow_left(self): -+ '''Move cursor left in the preëdit string.''' -+ if self._chars_invalid: -+ return -+ if self.get_input_chars(): -+ self.commit_to_preedit() -+ if not self._strings: -+ return -+ if self._cursor_precommit <= 0: -+ return -+ if len(self._strings[self._cursor_precommit-1]) <= 1: -+ self._cursor_precommit -= 1 -+ else: -+ self.split_strings_committed_to_preedit( -+ self._cursor_precommit-1, -1) -+ self.update_candidates() -+ -+ def arrow_right(self): -+ '''Move cursor right in the preëdit string.''' -+ if self._chars_invalid: -+ return -+ if self.get_input_chars(): -+ self.commit_to_preedit() -+ if not self._strings: -+ return -+ if self._cursor_precommit >= len(self._strings): -+ return -+ self._cursor_precommit += 1 -+ if len(self._strings[self._cursor_precommit-1]) > 1: -+ self.split_strings_committed_to_preedit(self._cursor_precommit-1, 1) -+ self.update_candidates() -+ -+ def control_arrow_left(self): -+ '''Move cursor to the beginning of the preëdit string.''' -+ if self._chars_invalid: -+ return -+ if self.get_input_chars(): -+ self.commit_to_preedit() -+ if not self._strings: -+ return -+ self._cursor_precommit = 0 -+ self.update_candidates () -+ -+ def control_arrow_right(self): -+ '''Move cursor to the end of the preëdit string''' -+ if self._chars_invalid: -+ return -+ if self.get_input_chars(): -+ self.commit_to_preedit() -+ if not self._strings: -+ return -+ self._cursor_precommit = len(self._strings) -+ self.update_candidates () -+ -+ def append_candidate_to_lookup_table( -+ self, tabkeys=u'', phrase=u'', freq=0, user_freq=0): -+ '''append candidate to lookup_table''' -+ if debug_level > 1: -+ sys.stderr.write( -+ "append_candidate() " -+ + "tabkeys=%(t)s phrase=%(p)s freq=%(f)s user_freq=%(u)s\n" -+ % {'t': tabkeys, 'p': phrase, 'f': freq, 'u': user_freq}) -+ if not tabkeys or not phrase: -+ return -+ regexp = self._chars_valid -+ if self._multi_wildcard_char: -+ regexp = regexp.replace( -+ self._multi_wildcard_char, '_multi_wildchard_char_') -+ if self._single_wildcard_char: -+ regexp = regexp.replace( -+ self._single_wildcard_char, '_single_wildchard_char_') -+ regexp = re.escape(regexp) -+ regexp = regexp.replace('_multi_wildchard_char_', '.*') -+ regexp = regexp.replace('_single_wildchard_char_', '.?') -+ match = re.match(r'^'+regexp, tabkeys) -+ if match: -+ remaining_tabkeys = tabkeys[match.end():] -+ else: -+ # This should never happen! For the candidates -+ # added to the lookup table here, a match has -+ # been found for self._chars_valid in the database. -+ # In that case, the above regular expression should -+ # match as well. -+ remaining_tabkeys = tabkeys -+ if debug_level > 1: -+ sys.stderr.write( -+ "append_candidate() " -+ + "remaining_tabkeys=%(remaining_tabkeys)s " -+ + "self._chars_valid=%(chars_valid)s phrase=%(phrase)s\n" -+ % {'remaining_tabkeys': remaining_tabkeys, -+ 'chars_valid': self._chars_valid, -+ 'phrase': phrase}) -+ table_code = u'' -+ if self.db._is_chinese and self._py_mode: -+ # restore tune symbol -+ remaining_tabkeys = remaining_tabkeys.replace( -+ '!','↑1').replace( -+ '@','↑2').replace( -+ '#','↑3').replace( -+ '$','↑4').replace( -+ '%','↑5') -+ # If in pinyin mode, phrase can only be one character. -+ # When using pinyin mode for a table like Wubi or Cangjie, -+ # the reason is probably because one does not know the -+ # Wubi or Cangjie code. So get that code from the table -+ # and display it as well to help the user learn that code. -+ # The Wubi tables contain several codes for the same -+ # character, therefore self.db.find_zi_code(phrase) may -+ # return a list. The last code in that list is the full -+ # table code for that characters, other entries in that -+ # list are shorter substrings of the full table code which -+ # are not interesting to display. Therefore, we use only -+ # the last element of the list of table codes. -+ possible_table_codes = self.db.find_zi_code(phrase) -+ if possible_table_codes: -+ table_code = possible_table_codes[-1] -+ table_code_new = u'' -+ for char in table_code: -+ if char in self._prompt_characters: -+ table_code_new += self._prompt_characters[char] -+ else: -+ table_code_new += char -+ table_code = table_code_new -+ if not self._py_mode: -+ remaining_tabkeys_new = u'' -+ for char in remaining_tabkeys: -+ if char in self._prompt_characters: -+ remaining_tabkeys_new += self._prompt_characters[char] -+ else: -+ remaining_tabkeys_new += char -+ remaining_tabkeys = remaining_tabkeys_new -+ candidate_text = phrase + u' ' + remaining_tabkeys -+ if table_code: -+ candidate_text = candidate_text + u' ' + table_code -+ attrs = IBus.AttrList () -+ attrs.append(IBus.attr_foreground_new( -+ rgb(0x19,0x73,0xa2), 0, len(candidate_text))) -+ if not self._py_mode and freq < 0: -+ # this is a user defined phrase: -+ attrs.append( -+ IBus.attr_foreground_new(rgb(0x77,0x00,0xc3), 0, len(phrase))) -+ elif not self._py_mode and user_freq > 0: -+ # this is a system phrase which has already been used by the user: -+ attrs.append(IBus.attr_foreground_new( -+ rgb(0x00,0x00,0x00), 0, len(phrase))) -+ else: -+ # this is a system phrase that has not been used yet: -+ attrs.append(IBus.attr_foreground_new( -+ rgb(0x00,0x00,0x00), 0, len(phrase))) -+ if debug_level > 0: -+ debug_text = u' ' + str(freq) + u' ' + str(user_freq) -+ candidate_text += debug_text -+ attrs.append(IBus.attr_foreground_new( -+ rgb(0x00,0xff,0x00), -+ len(candidate_text) - len(debug_text), -+ len(candidate_text))) -+ text = IBus.Text.new_from_string(candidate_text) -+ 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._lookup_table.append_candidate (text) -+ self._lookup_table.set_cursor_visible(True) -+ -+ def update_candidates (self): -+ ''' -+ Searches for candidates and updates the lookuptable. -+ -+ Returns “True” if candidates were found and “False” if not. -+ ''' -+ if debug_level > 1: -+ sys.stderr.write( -+ "update_candidates() " -+ + "self._chars_valid=%(chars_valid)s " -+ + "self._chars_invalid=%(chars_invalid)s " -+ + "self._chars_valid_update_candidates_last=%(chars_last)s " -+ + "self._candidates=%(candidates)s " -+ + "self.db.startchars=%(start)s " -+ + "self._strings=%(strings)s\n" -+ % {'chars_valid': self._chars_valid, -+ 'chars_invalid': self._chars_invalid, -+ 'chars_last': self._chars_valid_update_candidates_last, -+ 'candidates': self._candidates, -+ 'start': self.db.startchars, -+ 'strings': self._strings}) -+ if (self._chars_valid == self._chars_valid_update_candidates_last -+ and -+ self._chars_invalid == self._chars_invalid_update_candidates_last): -+ # The input did not change since we came here last, do -+ # nothing and leave candidates and lookup table unchanged: -+ if self._candidates: -+ return True -+ else: -+ return False -+ self._chars_valid_update_candidates_last = self._chars_valid -+ self._chars_invalid_update_candidates_last = self._chars_invalid -+ self._lookup_table.clear() -+ self._lookup_table.set_cursor_visible(True) -+ if self._chars_invalid or not self._chars_valid: -+ self._candidates = [] -+ self._candidates_previous = self._candidates -+ return False -+ if self._py_mode and self.db._is_chinese: -+ self._candidates = self.db.select_chinese_characters_by_pinyin( -+ tabkeys=self._chars_valid, -+ chinese_mode=self._chinese_mode, -+ single_wildcard_char=self._single_wildcard_char, -+ multi_wildcard_char=self._multi_wildcard_char) -+ else: -+ self._candidates = self.db.select_words( -+ tabkeys=self._chars_valid, -+ onechar=self._onechar, -+ chinese_mode=self._chinese_mode, -+ single_wildcard_char=self._single_wildcard_char, -+ multi_wildcard_char=self._multi_wildcard_char, -+ auto_wildcard=self._auto_wildcard) -+ # If only a wildcard character has been typed, insert a -+ # special candidate at the first position for the wildcard -+ # character itself. For example, if “?” is used as a -+ # wildcard character and this is the only character typed, add -+ # a candidate ('?', '?', 0, 1000000000) in halfwidth mode or a -+ # candidate ('?', '?', 0, 1000000000) in fullwidth mode. -+ # This is needed to make it possible to input the wildcard -+ # characters themselves, if “?” acted only as a wildcard -+ # it would be impossible to input a fullwidth question mark. -+ if (self._chars_valid -+ in [self._single_wildcard_char, self._multi_wildcard_char]): -+ wildcard_key = self._chars_valid -+ wildcard_phrase = self._chars_valid -+ if ascii_ispunct(wildcard_key): -+ if self._full_width_punct[1]: -+ wildcard_phrase = unichar_half_to_full(wildcard_phrase) -+ else: -+ wildcard_phrase = unichar_full_to_half(wildcard_phrase) -+ else: -+ if self._full_width_letter[1]: -+ wildcard_phrase = unichar_half_to_full(wildcard_phrase) -+ else: -+ wildcard_phrase = unichar_full_to_half(wildcard_phrase) -+ self._candidates.insert( -+ 0, (wildcard_key, wildcard_phrase, 0, 1000000000)) -+ if self._candidates: -+ self.fill_lookup_table() -+ self._candidates_previous = self._candidates -+ return True -+ # There are only valid and no invalid input characters but no -+ # matching candidates could be found from the databases. The -+ # last of self._chars_valid must have caused this. That -+ # character is valid in the sense that it is listed in -+ # self._valid_input_chars, it is only invalid in the sense -+ # that after adding this character, no candidates could be -+ # found anymore. Add this character to self._chars_invalid -+ # and remove it from self._chars_valid. -+ self._chars_invalid += self._chars_valid[-1] -+ self._chars_valid = self._chars_valid[:-1] -+ self._chars_valid_update_candidates_last = self._chars_valid -+ self._chars_invalid_update_candidates_last = self._chars_invalid -+ return False -+ -+ def commit_to_preedit(self): -+ '''Add selected phrase in lookup table to preëdit string''' -+ if not self._chars_valid: -+ return False -+ if self._candidates: -+ self._u_chars.insert(self._cursor_precommit, -+ self._candidates[self.get_cursor_pos()][0]) -+ self._strings.insert(self._cursor_precommit, -+ self._candidates[self.get_cursor_pos()][1]) -+ self._cursor_precommit += 1 -+ self.clear_input_not_committed_to_preedit() -+ self.update_candidates() -+ return True -+ -+ def commit_to_preedit_current_page(self, index): -+ ''' -+ Commits the candidate at position “index” in the current -+ page of the lookup table to the preëdit. Does not yet “really” -+ commit the candidate, only to the preëdit. -+ ''' -+ cursor_pos = self._lookup_table.get_cursor_pos() -+ cursor_in_page = self._lookup_table.get_cursor_in_page() -+ current_page_start = cursor_pos - cursor_in_page -+ real_index = current_page_start + index -+ if real_index >= len(self._candidates): -+ # the index given is out of range we do not commit anything -+ return False -+ self._lookup_table.set_cursor_pos(real_index) -+ return self.commit_to_preedit() -+ -+ def get_aux_strings (self): -+ '''Get aux strings''' -+ input_chars = self.get_input_chars () -+ if input_chars: -+ aux_string = input_chars -+ if debug_level > 0 and self._u_chars: -+ (tabkeys_left, -+ tabkeys_current, -+ tabkeys_right) = self.get_preedit_tabkeys_parts() -+ (strings_left, -+ string_current, -+ strings_right) = self.get_preedit_string_parts() -+ aux_string = u'' -+ for i in range(0, len(strings_left)): -+ aux_string += ( -+ u'(' -+ + tabkeys_left[i] + u' '+ strings_left[i] -+ + u') ') -+ aux_string += input_chars -+ for i in range(0, len(strings_right)): -+ aux_string += ( -+ u' (' -+ + tabkeys_right[i]+u' '+strings_right[i] -+ + u')') -+ if self._py_mode: -+ aux_string = aux_string.replace( -+ '!','1').replace( -+ '@','2').replace( -+ '#','3').replace( -+ '$','4').replace( -+ '%','5') -+ else: -+ aux_string_new = u'' -+ for char in aux_string: -+ if char in self._prompt_characters: -+ aux_string_new += self._prompt_characters[char] -+ else: -+ aux_string_new += char -+ aux_string = aux_string_new -+ return aux_string -+ -+ # There are no input strings at the moment. But there could -+ # be stuff committed to the preëdit. If there is something -+ # committed to the preëdit, show some information in the -+ # auxiliary text. -+ # -+ # For the character at the position of the cursor in the -+ # preëdit, show a list of possible input key sequences which -+ # could be used to type that character at the left side of the -+ # auxiliary text. -+ # -+ # If the preëdit is longer than one character, show the input -+ # key sequence which will be defined for the complete current -+ # contents of the preëdit, if the preëdit is committed. -+ aux_string = u'' -+ if self._strings: -+ if self._cursor_precommit >= len(self._strings): -+ char = self._strings[-1][0] -+ else: -+ char = self._strings[self._cursor_precommit][0] -+ aux_string = u' '.join(self.db.find_zi_code(char)) -+ cstr = u''.join(self._strings) -+ if self.db.user_can_define_phrase: -+ if len(cstr) > 1: -+ aux_string += (u'\t#: ' + self.db.parse_phrase(cstr)) -+ aux_string_new = u'' -+ for char in aux_string: -+ if char in self._prompt_characters: -+ aux_string_new += self._prompt_characters[char] -+ else: -+ aux_string_new += char -+ return aux_string_new -+ -+ def fill_lookup_table(self): -+ '''Fill more entries to self._lookup_table if needed. -+ -+ If the cursor in _lookup_table moved beyond current length, -+ add more entries from _candidiate[0] to _lookup_table.''' -+ -+ looklen = self._lookup_table.get_number_of_candidates() -+ psize = self._lookup_table.get_page_size() -+ if (self._lookup_table.get_cursor_pos() + psize >= looklen and -+ looklen < len(self._candidates)): -+ endpos = looklen + psize -+ batch = self._candidates[looklen:endpos] -+ for x in batch: -+ self.append_candidate_to_lookup_table( -+ tabkeys=x[0], phrase=x[1], freq=x[2], user_freq=x[3]) -+ -+ def cursor_down(self): -+ '''Process Arrow Down Key Event -+ Move Lookup Table cursor down''' -+ self.fill_lookup_table() -+ -+ res = self._lookup_table.cursor_down() -+ self.update_candidates () -+ if not res and self._candidates: -+ return True -+ return res -+ -+ def cursor_up(self): -+ '''Process Arrow Up Key Event -+ Move Lookup Table cursor up''' -+ res = self._lookup_table.cursor_up() -+ self.update_candidates () -+ if not res and self._candidates: -+ return True -+ return res -+ -+ def page_down(self): -+ '''Process Page Down Key Event -+ Move Lookup Table page down''' -+ self.fill_lookup_table() -+ res = self._lookup_table.page_down() -+ self.update_candidates () -+ if not res and self._candidates: -+ return True -+ return res -+ -+ def page_up(self): -+ '''Process Page Up Key Event -+ move Lookup Table page up''' -+ res = self._lookup_table.page_up() -+ self.update_candidates () -+ if not res and self._candidates: -+ return True -+ return res -+ -+ def select_key(self, keycode): -+ ''' -+ Commit a candidate which was selected by typing a selection key -+ from the lookup table to the preedit. Does not yet “really” -+ commit the candidate, only to the preedit. -+ ''' -+ if keycode not in self._select_keys: -+ return False -+ return self.commit_to_preedit_current_page( -+ self._select_keys.index(keycode)) -+ -+ def remove_candidate_from_user_database(self, keycode): -+ '''Remove a candidate displayed in the lookup table from the user -+ database. -+ -+ The candidate indicated by the selection key with the key code -+ “keycode” is removed, if possible. If it is not in the user -+ database at all, nothing happens. -+ -+ If this is a candidate which is also in the system database, -+ removing it from the user database only means that its user -+ frequency data is reset. It might still appear in subsequent -+ matches but with much lower priority. -+ -+ If this is a candidate which is user defined and not in the system -+ database, it will not match at all anymore after removing it. -+ -+ ''' -+ if keycode not in self._select_keys: -+ return False -+ index = self._select_keys.index(keycode) -+ cursor_pos = self._lookup_table.get_cursor_pos() -+ cursor_in_page = self._lookup_table.get_cursor_in_page() -+ current_page_start = cursor_pos - cursor_in_page -+ real_index = current_page_start + index -+ if len(self._candidates) > real_index: # this index is valid -+ candidate = self._candidates[real_index] -+ self.db.remove_phrase( -+ tabkeys=candidate[0], phrase=candidate[1], commit=True) -+ # call update_candidates() to get a new SQL query. The -+ # input has not really changed, therefore we must clear -+ # the remembered list of characters to -+ # force update_candidates() to really do something and not -+ # return immediately: -+ self._chars_valid_update_candidates_last = u'' -+ self._chars_invalid_update_candidates_last = u'' -+ self.update_candidates() -+ return True -+ else: -+ return False -+ -+ def get_cursor_pos (self): -+ '''get lookup table cursor position''' -+ return self._lookup_table.get_cursor_pos() -+ -+ def get_lookup_table (self): -+ '''Get lookup table''' -+ return self._lookup_table -+ -+ def remove_char(self): -+ '''Process remove_char Key Event''' -+ if debug_level > 1: -+ sys.stderr.write("remove_char()\n") -+ if self.get_input_chars(): -+ self.pop_input () -+ return -+ self.remove_preedit_character_before_cursor() -+ -+ def delete(self): -+ '''Process delete Key Event''' -+ if self.get_input_chars(): -+ return -+ self.remove_preedit_character_after_cursor() -+ -+ def cycle_next_cand(self): -+ '''Cycle cursor to next candidate in the page.''' -+ total = len(self._candidates) -+ -+ if total > 0: -+ page_size = self._lookup_table.get_page_size() -+ pos = self._lookup_table.get_cursor_pos() -+ page = int(pos/page_size) -+ pos += 1 -+ if pos >= (page+1)*page_size or pos >= total: -+ pos = page*page_size -+ res = self._lookup_table.set_cursor_pos(pos) -+ return True -+ else: -+ return False -+ -+ def one_candidate (self): -+ '''Return true if there is only one candidate''' -+ return len(self._candidates) == 1 -+ -+ -+######################## -+### Engine Class ##### -+#################### -+class tabengine (IBus.Engine): -+ '''The IM Engine for Tables''' -+ -+ def __init__(self, bus, obj_path, db ): -+ super(tabengine, self).__init__(connection=bus.get_connection(), -+ object_path=obj_path) -+ global debug_level -+ try: -+ debug_level = int(os.getenv('IBUS_TABLE_DEBUG_LEVEL')) -+ except (TypeError, ValueError): -+ debug_level = int(0) -+ self._input_purpose = 0 -+ self._has_input_purpose = False -+ if hasattr(IBus, 'InputPurpose'): -+ self._has_input_purpose = True -+ self._bus = bus -+ # this is the backend sql db we need for our IME -+ # we receive this db from IMEngineFactory -+ #self.db = tabsqlitedb.tabsqlitedb( name = dbname ) -+ self.db = db -+ self._setup_pid = 0 -+ self._icon_dir = '%s%s%s%s' % (os.getenv('IBUS_TABLE_LOCATION'), -+ os.path.sep, 'icons', os.path.sep) -+ # name for config section -+ self._engine_name = os.path.basename( -+ self.db.filename).replace('.db', '') -+ self._config_section = ( -+ "engine/Table/%s" % self._engine_name.replace(' ','_')) -+ -+ # config module -+ self._config = self._bus.get_config () -+ self._config.connect ("value-changed", self.config_value_changed_cb) -+ -+ # self._ime_py: Indicates whether this table supports pinyin mode -+ self._ime_py = self.db.ime_properties.get('pinyin_mode') -+ if self._ime_py: -+ if self._ime_py.lower() == u'true': -+ self._ime_py = True -+ else: -+ self._ime_py = False -+ else: -+ print('We could not find "pinyin_mode" entry in database, ' -+ + 'is it an outdated database?') -+ self._ime_py = False -+ -+ self._symbol = self.db.ime_properties.get('symbol') -+ if self._symbol == None or self._symbol == u'': -+ self._symbol = self.db.ime_properties.get('status_prompt') -+ if self._symbol == None: -+ self._symbol = u'' -+ # some Chinese tables have “STATUS_PROMPT = CN” replace it -+ # with the shorter and nicer “中”: -+ if self._symbol == u'CN': -+ self._symbol = u'中' -+ # workaround for the translit and translit-ua tables which -+ # have 2 character symbols. '☑' + self._symbol then is -+ # 3 characters and currently gnome-shell ignores symbols longer -+ # than 3 characters: -+ if self._symbol == u'Ya': -+ self._symbol = u'Я' -+ if self._symbol == u'Yi': -+ self._symbol = u'Ї' -+ # now we check and update the valid input characters -+ self._valid_input_chars = self.db.ime_properties.get( -+ 'valid_input_chars') -+ self._pinyin_valid_input_chars = u'abcdefghijklmnopqrstuvwxyz!@#$%' -+ -+ self._single_wildcard_char = variant_to_value(self._config.get_value( -+ self._config_section, -+ "singlewildcardchar")) -+ if self._single_wildcard_char == None: -+ self._single_wildcard_char = self.db.ime_properties.get( -+ 'single_wildcard_char') -+ if self._single_wildcard_char == None: -+ self._single_wildcard_char = u'' -+ if len(self._single_wildcard_char) > 1: -+ self._single_wildcard_char = self._single_wildcard_char[0] -+ -+ self._multi_wildcard_char = variant_to_value(self._config.get_value( -+ self._config_section, -+ "multiwildcardchar")) -+ if self._multi_wildcard_char == None: -+ self._multi_wildcard_char = self.db.ime_properties.get( -+ 'multi_wildcard_char') -+ if self._multi_wildcard_char == None: -+ self._multi_wildcard_char = u'' -+ if len(self._multi_wildcard_char) > 1: -+ self._multi_wildcard_char = self._multi_wildcard_char[0] -+ -+ self._auto_wildcard = variant_to_value(self._config.get_value( -+ self._config_section, -+ "autowildcard")) -+ if self._auto_wildcard == None: -+ self._auto_wildcard = self.db.ime_properties.get('auto_wildcard') -+ if self._auto_wildcard and self._auto_wildcard.lower() == u'false': -+ self._auto_wildcard = False -+ else: -+ self._auto_wildcard = True -+ -+ self._max_key_length = int(self.db.ime_properties.get('max_key_length')) -+ self._max_key_length_pinyin = 7 -+ -+ self._page_up_keys = [ -+ IBus.KEY_Page_Up, -+ IBus.KEY_KP_Page_Up, -+ IBus.KEY_minus -+ ] -+ self._page_down_keys = [ -+ IBus.KEY_Page_Down, -+ IBus.KEY_KP_Page_Down, -+ IBus.KEY_equal -+ ] -+ # If page up or page down keys are defined in the database, -+ # use the values from the database instead of the above -+ # hardcoded defaults: -+ page_up_keys_csv = self.db.ime_properties.get('page_up_keys') -+ page_down_keys_csv = self.db.ime_properties.get('page_down_keys') -+ if page_up_keys_csv: -+ self._page_up_keys = [ -+ IBus.keyval_from_name(x) -+ for x in page_up_keys_csv.split(',')] -+ if page_down_keys_csv: -+ self._page_down_keys = [ -+ IBus.keyval_from_name(x) -+ for x in page_down_keys_csv.split(',')] -+ # Remove keys from the page up/down keys if they are needed -+ # for input (for example, '=' or '-' could well be needed for -+ # input. Input is more important): -+ for character in ( -+ self._valid_input_chars -+ + self._single_wildcard_char -+ + self._multi_wildcard_char): -+ keyval = IBus.unicode_to_keyval(character) -+ if keyval in self._page_up_keys: -+ self._page_up_keys.remove(keyval) -+ if keyval in self._page_down_keys: -+ self._page_down_keys.remove(keyval) -+ self._commit_keys = [IBus.KEY_space] -+ # If commit keys are are defined in the database, use the -+ # value from the database instead of the above hardcoded -+ # default: -+ commit_keys_csv = self.db.ime_properties.get('commit_keys') -+ if commit_keys_csv: -+ self._commit_keys = [ -+ IBus.keyval_from_name(x) -+ for x in commit_keys_csv.split(',')] -+ # If commit keys conflict with page up/down keys, remove them -+ # from the page up/down keys (They cannot really be used for -+ # both at the same time. Theoretically, keys from the page -+ # up/down keys could still be used to commit when the number -+ # of candidates is 0 because then there is nothing to -+ # page. But that would be only confusing): -+ for keyval in self._commit_keys: -+ if keyval in self._page_up_keys: -+ self._page_up_keys.remove(keyval) -+ if keyval in self._page_down_keys: -+ self._page_down_keys.remove(keyval) -+ # Finally, check the user setting, i.e. the config value -+ # “spacekeybehavior” and let the user have the last word -+ # how to use the space key: -+ spacekeybehavior = variant_to_value(self._config.get_value( -+ self._config_section, -+ "spacekeybehavior")) -+ if spacekeybehavior == True: -+ # space is used as a page down key and not as a commit key: -+ if IBus.KEY_space not in self._page_down_keys: -+ self._page_down_keys.append(IBus.KEY_space) -+ if IBus.KEY_space in self._commit_keys: -+ self._commit_keys.remove(IBus.KEY_space) -+ if spacekeybehavior == False: -+ # space is used as a commit key and not used as a page down key: -+ if IBus.KEY_space in self._page_down_keys: -+ self._page_down_keys.remove(IBus.KEY_space) -+ if IBus.KEY_space not in self._commit_keys: -+ self._commit_keys.append(IBus.KEY_space) -+ if debug_level > 1: -+ sys.stderr.write( -+ "self._page_down_keys=%s\n" %repr(self._page_down_keys)) -+ sys.stderr.write( -+ "self._commit_keys=%s\n" %repr(self._commit_keys)) -+ -+ # 0 = Direct input, i.e. table input OFF (aka “English input mode”), -+ # most characters are just passed through to the application -+ # (but some fullwidth ↔ halfwidth conversion may be done even -+ # in this mode, depending on the settings) -+ # 1 = Table input ON (aka “Table input mode”, “Chinese mode”) -+ self._input_mode = variant_to_value(self._config.get_value( -+ self._config_section, -+ "inputmode")) -+ if self._input_mode == None: -+ self._input_mode = 1 -+ -+ # self._prev_key: hold the key event last time. -+ self._prev_key = None -+ self._prev_char = None -+ self._double_quotation_state = False -+ self._single_quotation_state = False -+ -+ self._full_width_letter = [ -+ variant_to_value(self._config.get_value( -+ self._config_section, -+ "EnDefFullWidthLetter")), -+ variant_to_value(self._config.get_value( -+ self._config_section, -+ "TabDefFullWidthLetter")) -+ ] -+ if self._full_width_letter[0] == None: -+ self._full_width_letter[0] = False -+ if self._full_width_letter[1] == None: -+ self._full_width_letter[1] = self.db.ime_properties.get( -+ 'def_full_width_letter').lower() == u'true' -+ self._full_width_punct = [ -+ variant_to_value(self._config.get_value( -+ self._config_section, -+ "EnDefFullWidthPunct")), -+ variant_to_value(self._config.get_value( -+ self._config_section, -+ "TabDefFullWidthPunct")) -+ ] -+ if self._full_width_punct[0] == None: -+ self._full_width_punct[0] = False -+ if self._full_width_punct[1] == None: -+ self._full_width_punct[1] = self.db.ime_properties.get( -+ 'def_full_width_punct').lower() == u'true' -+ -+ self._auto_commit = variant_to_value(self._config.get_value( -+ self._config_section, -+ "AutoCommit")) -+ if self._auto_commit == None: -+ self._auto_commit = self.db.ime_properties.get( -+ 'auto_commit').lower() == u'true' -+ -+ # If auto select is true, then the first candidate phrase will -+ # be selected automatically during typing. Auto select is true -+ # by default for the stroke5 table for example. -+ self._auto_select = variant_to_value(self._config.get_value( -+ self._config_section, -+ "AutoSelect")) -+ if self._auto_select == None: -+ if self.db.ime_properties.get('auto_select') != None: -+ self._auto_select = self.db.ime_properties.get( -+ 'auto_select').lower() == u'true' -+ else: -+ self._auto_select = False -+ -+ self._always_show_lookup = variant_to_value(self._config.get_value( -+ self._config_section, -+ "AlwaysShowLookup")) -+ if self._always_show_lookup == None: -+ if self.db.ime_properties.get('always_show_lookup') != None: -+ self._always_show_lookup = self.db.ime_properties.get( -+ 'always_show_lookup').lower() == u'true' -+ else: -+ self._always_show_lookup = True -+ -+ self._editor = editor(self._config, -+ self._valid_input_chars, -+ self._pinyin_valid_input_chars, -+ self._single_wildcard_char, -+ self._multi_wildcard_char, -+ self._auto_wildcard, -+ self._full_width_letter, -+ self._full_width_punct, -+ self._max_key_length, -+ self.db) -+ -+ self.chinese_mode_properties = { -+ 'ChineseMode.Simplified': { -+ # show simplified Chinese only -+ 'number': 0, -+ 'symbol': '簡', -+ 'icon': 'sc-mode.svg', -+ 'label': _('Simplified Chinese'), -+ 'tooltip': -+ _('Switch to “Simplified Chinese only”.')}, -+ 'ChineseMode.Traditional': { -+ # show traditional Chinese only -+ 'number': 1, -+ 'symbol': '繁', -+ 'icon': 'tc-mode.svg', -+ 'label': _('Traditional Chinese'), -+ 'tooltip': -+ _('Switch to “Traditional Chinese only”.')}, -+ 'ChineseMode.SimplifiedFirst': { -+ # show all but simplified first -+ 'number': 2, -+ 'symbol': '簡/大', -+ 'icon': 'scb-mode.svg', -+ 'label': _('Simplified Chinese first'), -+ 'tooltip': -+ _('Switch to “Simplified Chinese before traditional”.')}, -+ 'ChineseMode.TraditionalFirst': { -+ # show all but traditional first -+ 'number': 3, -+ 'symbol': '繁/大', -+ 'icon': 'tcb-mode.svg', -+ 'label': _('Traditional Chinese first'), -+ 'tooltip': -+ _('Switch to “Traditional Chinese before simplified”.')}, -+ 'ChineseMode.All': { -+ # show all Chinese characters, no particular order -+ 'number': 4, -+ 'symbol': '大', -+ 'icon': 'cb-mode.svg', -+ 'label': _('All Chinese characters'), -+ 'tooltip': _('Switch to “All Chinese characters”.')} -+ } -+ self.chinese_mode_menu = { -+ 'key': 'ChineseMode', -+ 'label': _('Chinese mode'), -+ 'tooltip': _('Switch Chinese mode'), -+ 'shortcut_hint': '(Ctrl-;)', -+ 'sub_properties': self.chinese_mode_properties -+ } -+ if self.db._is_chinese: -+ self.input_mode_properties = { -+ 'InputMode.Direct': { -+ 'number': 0, -+ 'symbol': '英', -+ 'icon': 'english.svg', -+ 'label': _('English'), -+ 'tooltip': _('Switch to English input')}, -+ 'InputMode.Table': { -+ 'number': 1, -+ 'symbol': '中', -+ 'symbol_table': '中', -+ 'symbol_pinyin': '拼音', -+ 'icon': 'chinese.svg', -+ 'label': _('Chinese'), -+ 'tooltip': _('Switch to Chinese input')} -+ } -+ else: -+ self.input_mode_properties = { -+ 'InputMode.Direct': { -+ 'number': 0, -+ 'symbol': '☐' + self._symbol, -+ 'icon': 'english.svg', -+ 'label': _('Direct'), -+ 'tooltip': _('Switch to direct input')}, -+ 'InputMode.Table': { -+ 'number': 1, -+ 'symbol': '☑' + self._symbol, -+ 'icon': 'ibus-table.svg', -+ 'label': _('Table'), -+ 'tooltip': _('Switch to table input')} -+ } -+ # The symbol of the property “InputMode” is displayed -+ # in the input method indicator of the Gnome3 panel. -+ # This depends on the property name “InputMode” and -+ # is case sensitive! -+ self.input_mode_menu = { -+ 'key': 'InputMode', -+ 'label': _('Input mode'), -+ 'tooltip': _('Switch Input mode'), -+ 'shortcut_hint': '(Left Shift)', -+ 'sub_properties': self.input_mode_properties -+ } -+ self.letter_width_properties = { -+ 'LetterWidth.Half': { -+ 'number': 0, -+ 'symbol': '◑', -+ 'icon': 'half-letter.svg', -+ 'label': _('Half'), -+ 'tooltip': _('Switch to halfwidth letters')}, -+ 'LetterWidth.Full': { -+ 'number': 1, -+ 'symbol': '●', -+ 'icon': 'full-letter.svg', -+ 'label': _('Full'), -+ 'tooltip': _('Switch to fullwidth letters')} -+ } -+ self.letter_width_menu = { -+ 'key': 'LetterWidth', -+ 'label': _('Letter width'), -+ 'tooltip': _('Switch letter width'), -+ 'shortcut_hint': '(Shift-Space)', -+ 'sub_properties': self.letter_width_properties -+ } -+ self.punctuation_width_properties = { -+ 'PunctuationWidth.Half': { -+ 'number': 0, -+ 'symbol': ',.', -+ 'icon': 'half-punct.svg', -+ 'label': _('Half'), -+ 'tooltip': _('Switch to halfwidth punctuation')}, -+ 'PunctuationWidth.Full': { -+ 'number': 1, -+ 'symbol': '、。', -+ 'icon': 'full-punct.svg', -+ 'label': _('Full'), -+ 'tooltip': _('Switch to fullwidth punctuation')} -+ } -+ self.punctuation_width_menu = { -+ 'key': 'PunctuationWidth', -+ 'label': _('Punctuation width'), -+ 'tooltip': _('Switch punctuation width'), -+ 'shortcut_hint': '(Ctrl-.)', -+ 'sub_properties': self.punctuation_width_properties -+ } -+ self.pinyin_mode_properties = { -+ 'PinyinMode.Table': { -+ 'number': 0, -+ 'symbol': '☐ 拼音', -+ 'icon': 'tab-mode.svg', -+ 'label': _('Table'), -+ 'tooltip': _('Switch to table mode')}, -+ 'PinyinMode.Pinyin': { -+ 'number': 1, -+ 'symbol': '☑ 拼音', -+ 'icon': 'py-mode.svg', -+ 'label': _('Pinyin'), -+ 'tooltip': _('Switch to pinyin mode')} -+ } -+ self.pinyin_mode_menu = { -+ 'key': 'PinyinMode', -+ 'label': _('Pinyin mode'), -+ 'tooltip': _('Switch pinyin mode'), -+ 'shortcut_hint': '(Right Shift)', -+ 'sub_properties': self.pinyin_mode_properties -+ } -+ self.onechar_mode_properties = { -+ 'OneCharMode.Phrase': { -+ 'number': 0, -+ 'symbol': '☐ 1', -+ 'icon': 'phrase.svg', -+ 'label': _('Multiple character match'), -+ 'tooltip': _('Switch to matching multiple characters at once')}, -+ 'OneCharMode.OneChar': { -+ 'number': 1, -+ 'symbol': '☑ 1', -+ 'icon': 'onechar.svg', -+ 'label': _('Single character match'), -+ 'tooltip': _('Switch to matching only single characters')} -+ } -+ self.onechar_mode_menu = { -+ 'key': 'OneCharMode', -+ 'label': _('Onechar mode'), -+ 'tooltip': _('Switch onechar mode'), -+ 'shortcut_hint': '(Ctrl-,)', -+ 'sub_properties': self.onechar_mode_properties -+ } -+ self.autocommit_mode_properties = { -+ 'AutoCommitMode.Direct': { -+ 'number': 0, -+ 'symbol': '☐ ↑', -+ 'icon': 'ncommit.svg', -+ 'label': _('Normal'), -+ 'tooltip': -+ _('Switch to normal commit mode ' -+ + '(automatic commits go into the preedit ' -+ + 'instead of into the application. ' -+ + 'This enables automatic definitions of new shortcuts)')}, -+ 'AutoCommitMode.Normal': { -+ 'number': 1, -+ 'symbol': '☑ ↑', -+ 'icon': 'acommit.svg', -+ 'label': _('Direct'), -+ 'tooltip': -+ _('Switch to direct commit mode ' -+ + '(automatic commits go directly into the application)')} -+ } -+ self.autocommit_mode_menu = { -+ 'key': 'AutoCommitMode', -+ 'label': _('Auto commit mode'), -+ 'tooltip': _('Switch autocommit mode'), -+ 'shortcut_hint': '(Ctrl-/)', -+ 'sub_properties': self.autocommit_mode_properties -+ } -+ self._prop_dict = {} -+ self._init_properties() -+ -+ self._on = False -+ self._save_user_count = 0 -+ self._save_user_start = time.time() -+ -+ self._save_user_count_max = SAVE_USER_COUNT_MAX -+ self._save_user_timeout = SAVE_USER_TIMEOUT -+ self.reset() -+ -+ self.sync_timeout_id = GObject.timeout_add_seconds(1, -+ self._sync_user_db) -+ -+ def reset(self): -+ self._editor.clear_all_input_and_preedit() -+ self._double_quotation_state = False -+ self._single_quotation_state = False -+ self._prev_key = None -+ self._update_ui() -+ -+ def do_destroy(self): -+ if self.sync_timeout_id > 0: -+ GObject.source_remove(self.sync_timeout_id) -+ self.sync_timeout_id = 0 -+ self.reset () -+ self.do_focus_out () -+ if self._save_user_count > 0: -+ self.db.sync_usrdb() -+ self._save_user_count = 0 -+ super(tabengine, self).destroy() -+ -+ def set_input_mode(self, mode=0): -+ if mode == self._input_mode: -+ return -+ self._input_mode = mode -+ # Not saved to config on purpose. In the setup tool one -+ # can select whether “Table input” or “Direct input” should -+ # be the default when the input method starts. But when -+ # changing this input mode using the property menu, -+ # the change is not remembered. -+ self._init_or_update_property_menu( -+ self.input_mode_menu, -+ self._input_mode) -+ # Letter width and punctuation width depend on the input mode. -+ # Therefore, the properties for letter width and punctuation -+ # width need to be updated here: -+ self._init_or_update_property_menu( -+ self.letter_width_menu, -+ self._full_width_letter[self._input_mode]) -+ self._init_or_update_property_menu( -+ self.punctuation_width_menu, -+ self._full_width_punct[self._input_mode]) -+ self.reset() -+ -+ def set_pinyin_mode(self, mode=False): -+ if mode == self._editor._py_mode: -+ return -+ # The pinyin mode is never saved to config on purpose -+ self._editor.commit_to_preedit() -+ self._editor._py_mode = mode -+ self._init_or_update_property_menu( -+ self.pinyin_mode_menu, mode) -+ if mode: -+ self.input_mode_properties['InputMode.Table']['symbol'] = ( -+ self.input_mode_properties['InputMode.Table']['symbol_pinyin']) -+ else: -+ self.input_mode_properties['InputMode.Table']['symbol'] = ( -+ self.input_mode_properties['InputMode.Table']['symbol_table']) -+ self._init_or_update_property_menu( -+ self.input_mode_menu, -+ self._input_mode) -+ self._update_ui() -+ -+ def set_onechar_mode(self, mode=False): -+ if mode == self._editor._onechar: -+ return -+ self._editor._onechar = mode -+ self._init_or_update_property_menu( -+ self.onechar_mode_menu, mode) -+ self._config.set_value( -+ self._config_section, -+ "OneChar", -+ GLib.Variant.new_boolean(mode)) -+ -+ def set_autocommit_mode(self, mode=False): -+ if mode == self._auto_commit: -+ return -+ self._auto_commit = mode -+ self._init_or_update_property_menu( -+ self.autocommit_mode_menu, mode) -+ self._config.set_value( -+ self._config_section, -+ "AutoCommit", -+ GLib.Variant.new_boolean(mode)) -+ -+ def set_letter_width(self, mode=False, input_mode=0): -+ if mode == self._full_width_letter[input_mode]: -+ return -+ self._full_width_letter[input_mode] = mode -+ self._editor._full_width_letter[input_mode] = mode -+ if input_mode == self._input_mode: -+ self._init_or_update_property_menu( -+ self.letter_width_menu, mode) -+ if input_mode: -+ self._config.set_value( -+ self._config_section, -+ "TabDefFullWidthLetter", -+ GLib.Variant.new_boolean(mode)) -+ else: -+ self._config.set_value( -+ self._config_section, -+ "EnDefFullWidthLetter", -+ GLib.Variant.new_boolean(mode)) -+ -+ def set_punctuation_width(self, mode=False, input_mode=0): -+ if mode == self._full_width_punct[input_mode]: -+ return -+ self._full_width_punct[input_mode] = mode -+ self._editor._full_width_punct[input_mode] = mode -+ if input_mode == self._input_mode: -+ self._init_or_update_property_menu( -+ self.punctuation_width_menu, mode) -+ if input_mode: -+ self._config.set_value( -+ self._config_section, -+ "TabDefFullWidthPunct", -+ GLib.Variant.new_boolean(mode)) -+ else: -+ self._config.set_value( -+ self._config_section, -+ "EnDefFullWidthPunct", -+ GLib.Variant.new_boolean(mode)) -+ -+ def set_chinese_mode(self, mode=0): -+ if mode == self._editor._chinese_mode: -+ return -+ self._editor._chinese_mode = mode -+ self._init_or_update_property_menu( -+ self.chinese_mode_menu, mode) -+ self._config.set_value( -+ self._config_section, -+ "ChineseMode", -+ GLib.Variant.new_int32(mode)) -+ -+ def _init_or_update_property_menu(self, menu, current_mode=0): -+ key = menu['key'] -+ if key in self._prop_dict: -+ update_prop = True -+ else: -+ update_prop = False -+ sub_properties = menu['sub_properties'] -+ for prop in sub_properties: -+ if sub_properties[prop]['number'] == int(current_mode): -+ symbol = sub_properties[prop]['symbol'] -+ icon = sub_properties[prop]['icon'] -+ label = '%(label)s (%(symbol)s) %(shortcut_hint)s' % { -+ 'label': menu['label'], -+ 'symbol': symbol, -+ 'shortcut_hint': menu['shortcut_hint']} -+ tooltip = '%(tooltip)s\n%(shortcut_hint)s' % { -+ 'tooltip': menu['tooltip'], -+ 'shortcut_hint': menu['shortcut_hint']} -+ self._prop_dict[key] = IBus.Property( -+ key=key, -+ prop_type=IBus.PropType.MENU, -+ label=IBus.Text.new_from_string(label), -+ symbol=IBus.Text.new_from_string(symbol), -+ icon=os.path.join(self._icon_dir, icon), -+ tooltip=IBus.Text.new_from_string(tooltip), -+ sensitive=True, -+ visible=True, -+ state=IBus.PropState.UNCHECKED, -+ sub_props=None) -+ self._prop_dict[key].set_sub_props( -+ self._init_sub_properties( -+ sub_properties, current_mode=current_mode)) -+ if update_prop: -+ self.properties.update_property(self._prop_dict[key]) -+ self.update_property(self._prop_dict[key]) -+ else: -+ self.properties.append(self._prop_dict[key]) -+ -+ def _init_sub_properties(self, modes, current_mode=0): -+ sub_props = IBus.PropList() -+ for mode in sorted(modes, key=lambda x: (modes[x]['number'])): -+ sub_props.append(IBus.Property( -+ key=mode, -+ prop_type=IBus.PropType.RADIO, -+ label=IBus.Text.new_from_string(modes[mode]['label']), -+ icon=os.path.join(modes[mode]['icon']), -+ tooltip=IBus.Text.new_from_string(modes[mode]['tooltip']), -+ sensitive=True, -+ visible=True, -+ state=IBus.PropState.UNCHECKED, -+ sub_props=None)) -+ i = 0 -+ while sub_props.get(i) != None: -+ prop = sub_props.get(i) -+ key = prop.get_key() -+ self._prop_dict[key] = prop -+ if modes[key]['number'] == int(current_mode): -+ prop.set_state(IBus.PropState.CHECKED) -+ else: -+ prop.set_state(IBus.PropState.UNCHECKED) -+ self.update_property(prop) # important! -+ i += 1 -+ return sub_props -+ -+ def _init_properties(self): -+ self._prop_dict = {} -+ self.properties = IBus.PropList() -+ -+ self._init_or_update_property_menu( -+ self.input_mode_menu, -+ self._input_mode) -+ -+ if self.db._is_chinese and self._editor._chinese_mode != -1: -+ self._init_or_update_property_menu( -+ self.chinese_mode_menu, -+ self._editor._chinese_mode) -+ -+ if self.db._is_cjk: -+ self._init_or_update_property_menu( -+ self.letter_width_menu, -+ self._full_width_letter[self._input_mode]) -+ self._init_or_update_property_menu( -+ self.punctuation_width_menu, -+ self._full_width_punct[self._input_mode]) -+ -+ if self._ime_py: -+ self._init_or_update_property_menu( -+ self.pinyin_mode_menu, -+ self._editor._py_mode) -+ -+ if self.db._is_cjk: -+ self._init_or_update_property_menu( -+ self.onechar_mode_menu, -+ self._editor._onechar) -+ -+ if self.db.user_can_define_phrase and self.db.rules: -+ self._init_or_update_property_menu( -+ self.autocommit_mode_menu, -+ self._auto_commit) -+ -+ self._setup_property = IBus.Property( -+ key = u'setup', -+ label = IBus.Text.new_from_string(_('Setup')), -+ icon = 'gtk-preferences', -+ tooltip = IBus.Text.new_from_string(_('Configure ibus-table “%(engine-name)s”') %{ -+ 'engine-name': self._engine_name}), -+ sensitive = True, -+ visible = True) -+ self.properties.append(self._setup_property) -+ -+ self.register_properties(self.properties) -+ -+ def do_property_activate( -+ self, property, prop_state = IBus.PropState.UNCHECKED): -+ ''' -+ Handle clicks on properties -+ ''' -+ if debug_level > 1: -+ sys.stderr.write( -+ "do_property_activate() property=%(p)s prop_state=%(ps)s\n" -+ % {'p': property, 'ps': prop_state}) -+ if property == "setup": -+ self._start_setup() -+ return -+ if prop_state != IBus.PropState.CHECKED: -+ # If the mouse just hovered over a menu button and -+ # no sub-menu entry was clicked, there is nothing to do: -+ return -+ if property.startswith(self.input_mode_menu['key']+'.'): -+ self.set_input_mode( -+ self.input_mode_properties[property]['number']) -+ return -+ if (property.startswith(self.pinyin_mode_menu['key']+'.') -+ and self._ime_py): -+ self.set_pinyin_mode( -+ bool(self.pinyin_mode_properties[property]['number'])) -+ return -+ if (property.startswith(self.onechar_mode_menu['key']+'.') -+ and self.db._is_cjk): -+ self.set_onechar_mode( -+ bool(self.onechar_mode_properties[property]['number'])) -+ return -+ if (property.startswith(self.autocommit_mode_menu['key']+'.') -+ and self.db.user_can_define_phrase and self.db.rules): -+ self.set_autocommit_mode( -+ bool(self.autocommit_mode_properties[property]['number'])) -+ return -+ if (property.startswith(self.letter_width_menu['key']+'.') -+ and self.db._is_cjk): -+ self.set_letter_width( -+ bool(self.letter_width_properties[property]['number']), -+ input_mode=self._input_mode) -+ return -+ if (property.startswith(self.punctuation_width_menu['key']+'.') -+ and self.db._is_cjk): -+ self.set_punctuation_width( -+ bool(self.punctuation_width_properties[property]['number']), -+ input_mode=self._input_mode) -+ return -+ if (property.startswith(self.chinese_mode_menu['key']+'.') -+ and self.db._is_chinese -+ and self._editor._chinese_mode != -1): -+ self.set_chinese_mode( -+ self.chinese_mode_properties[property]['number']) -+ return -+ -+ def _start_setup(self): -+ if self._setup_pid != 0: -+ pid, state = os.waitpid(self._setup_pid, os.P_NOWAIT) -+ if pid != self._setup_pid: -+ # If the last setup tool started from here is still -+ # running the pid returned by the above os.waitpid() -+ # is 0. In that case just return, don’t start a -+ # second setup tool. -+ return -+ self._setup_pid = 0 -+ setup_cmd = os.path.join( -+ os.getenv('IBUS_TABLE_LIB_LOCATION'), -+ 'ibus-setup-table') -+ self._setup_pid = os.spawnl( -+ os.P_NOWAIT, -+ setup_cmd, -+ 'ibus-setup-table', -+ '--engine-name table:%s' %self._engine_name) -+ -+ def _update_preedit(self): -+ '''Update Preedit String in UI''' -+ preedit_string_parts = self._editor.get_preedit_string_parts() -+ left_of_current_edit = u''.join(preedit_string_parts[0]) -+ current_edit = preedit_string_parts[1] -+ right_of_current_edit = u''.join(preedit_string_parts[2]) -+ if not self._editor._py_mode: -+ current_edit_new = u'' -+ for char in current_edit: -+ if char in self._editor._prompt_characters: -+ current_edit_new += self._editor._prompt_characters[char] -+ else: -+ current_edit_new += char -+ current_edit = current_edit_new -+ preedit_string_complete = ( -+ left_of_current_edit + current_edit + right_of_current_edit) -+ if not preedit_string_complete: -+ super(tabengine, self).update_preedit_text( -+ IBus.Text.new_from_string(u''), 0, False) -+ return -+ color_left = rgb(0xf9, 0x0f, 0x0f) # bright red -+ color_right = rgb(0x1e, 0xdc, 0x1a) # light green -+ color_invalid = rgb(0xff, 0x00, 0xff) # magenta -+ attrs = IBus.AttrList() -+ attrs.append( -+ IBus.attr_foreground_new( -+ color_left, -+ 0, -+ len(left_of_current_edit))) -+ attrs.append( -+ IBus.attr_foreground_new( -+ color_right, -+ len(left_of_current_edit) + len(current_edit), -+ len(preedit_string_complete))) -+ if self._editor._chars_invalid: -+ attrs.append( -+ IBus.attr_foreground_new( -+ color_invalid, -+ len(left_of_current_edit) + len(current_edit) -+ - len(self._editor._chars_invalid), -+ len(left_of_current_edit) + len(current_edit) -+ )) -+ attrs.append( -+ IBus.attr_underline_new( -+ IBus.AttrUnderline.SINGLE, -+ 0, -+ len(preedit_string_complete))) -+ text = IBus.Text.new_from_string(preedit_string_complete) -+ 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 -+ super(tabengine, self).update_preedit_text( -+ text, self._editor.get_caret(), True) -+ -+ def _update_aux (self): -+ '''Update Aux String in UI''' -+ aux_string = self._editor.get_aux_strings() -+ if len(self._editor._candidates) > 0: -+ aux_string += u' (%d / %d)' % ( -+ self._editor._lookup_table.get_cursor_pos() +1, -+ self._editor._lookup_table.get_number_of_candidates()) -+ if aux_string: -+ attrs = IBus.AttrList() -+ attrs.append(IBus.attr_foreground_new( -+ rgb(0x95,0x15,0xb5),0, len(aux_string))) -+ text = IBus.Text.new_from_string(aux_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 -+ visible = True -+ if not aux_string or not self._always_show_lookup: -+ visible = False -+ super(tabengine, self).update_auxiliary_text(text, visible) -+ else: -+ self.hide_auxiliary_text() -+ -+ def _update_lookup_table (self): -+ '''Update Lookup Table in UI''' -+ if len(self._editor._candidates) == 0: -+ # Also make sure to hide lookup table if there are -+ # no candidates to display. On f17, this makes no -+ # difference but gnome-shell in f18 will display -+ # an empty suggestion popup if the number of candidates -+ # is zero! -+ self.hide_lookup_table() -+ return -+ if self._editor.is_empty (): -+ self.hide_lookup_table() -+ return -+ if not self._always_show_lookup: -+ self.hide_lookup_table() -+ return -+ self.update_lookup_table(self._editor.get_lookup_table(), True) -+ -+ def _update_ui (self): -+ '''Update User Interface''' -+ self._update_lookup_table () -+ self._update_preedit () -+ self._update_aux () -+ -+ def _check_phrase (self, tabkeys=u'', phrase=u''): -+ """Check the given phrase and update save user db info""" -+ if not tabkeys or not phrase: -+ return -+ self.db.check_phrase(tabkeys=tabkeys, phrase=phrase) -+ -+ if self._save_user_count <= 0: -+ self._save_user_start = time.time() -+ self._save_user_count += 1 -+ -+ def _sync_user_db(self): -+ """Save user db to disk""" -+ if self._save_user_count >= 0: -+ now = time.time() -+ time_delta = now - self._save_user_start -+ if (self._save_user_count > self._save_user_count_max or -+ time_delta >= self._save_user_timeout): -+ self.db.sync_usrdb() -+ self._save_user_count = 0 -+ self._save_user_start = now -+ return True -+ -+ def commit_string (self, phrase, tabkeys=u''): -+ if debug_level > 1: -+ sys.stderr.write("commit_string() phrase=%(p)s\n" -+ %{'p': phrase}) -+ self._editor.clear_all_input_and_preedit() -+ self._update_ui() -+ super(tabengine, self).commit_text(IBus.Text.new_from_string(phrase)) -+ if len(phrase) > 0: -+ self._prev_char = phrase[-1] -+ else: -+ self._prev_char = None -+ self._check_phrase(tabkeys=tabkeys, phrase=phrase) -+ -+ def commit_everything_unless_invalid(self): -+ ''' -+ Commits the current input to the preëdit and then -+ commits the preëdit to the application unless there are -+ invalid input characters. -+ -+ Returns “True” if something was committed, “False” if not. -+ ''' -+ if debug_level > 1: -+ sys.stderr.write("commit_everything_unless_invalid()\n") -+ if self._editor._chars_invalid: -+ return False -+ if not self._editor.is_empty(): -+ self._editor.commit_to_preedit() -+ self.commit_string(self._editor.get_preedit_string_complete(), -+ tabkeys=self._editor.get_preedit_tabkeys_complete()) -+ return True -+ -+ def _convert_to_full_width(self, c): -+ '''Convert half width character to full width''' -+ -+ # This function handles punctuation that does not comply to the -+ # Unicode conversion formula in unichar_half_to_full(c). -+ # For ".", "\"", "'"; there are even variations under specific -+ # cases. This function should be more abstracted by extracting -+ # that to another handling function later on. -+ special_punct_dict = {u"<": u"《", # 《 U+300A LEFT DOUBLE ANGLE BRACKET -+ u">": u"》", # 》 U+300B RIGHT DOUBLE ANGLE BRACKET -+ u"[": u"「", # 「 U+300C LEFT CORNER BRACKET -+ u"]": u"」", # 」U+300D RIGHT CORNER BRACKET -+ u"{": u"『", # 『 U+300E LEFT WHITE CORNER BRACKET -+ u"}": u"』", # 』U+300F RIGHT WHITE CORNER BRACKET -+ u"\\": u"、", # 、 U+3001 IDEOGRAPHIC COMMA -+ u"^": u"……", # … U+2026 HORIZONTAL ELLIPSIS -+ u"_": u"——", # — U+2014 EM DASH -+ u"$": u"¥" # ¥ U+FFE5 FULLWIDTH YEN SIGN -+ } -+ -+ # special puncts w/o further conditions -+ if c in special_punct_dict.keys(): -+ if c in [u"\\", u"^", u"_", u"$"]: -+ return special_punct_dict[c] -+ elif self._input_mode: -+ return special_punct_dict[c] -+ -+ # special puncts w/ further conditions -+ if c == u".": -+ if (self._prev_char -+ and self._prev_char.isdigit() -+ and self._prev_key -+ and chr(self._prev_key.val) == self._prev_char): -+ return u"." -+ else: -+ return u"。" # 。U+3002 IDEOGRAPHIC FULL STOP -+ elif c == u"\"": -+ self._double_quotation_state = not self._double_quotation_state -+ if self._double_quotation_state: -+ return u"“" # “ U+201C LEFT DOUBLE QUOTATION MARK -+ else: -+ return u"”" # ” U+201D RIGHT DOUBLE QUOTATION MARK -+ elif c == u"'": -+ self._single_quotation_state = not self._single_quotation_state -+ if self._single_quotation_state: -+ return u"‘" # ‘ U+2018 LEFT SINGLE QUOTATION MARK -+ else: -+ return u"’" # ’ U+2019 RIGHT SINGLE QUOTATION MARK -+ -+ return unichar_half_to_full(c) -+ -+ def _match_hotkey (self, key, keyval, state): -+ -+ # Match only when keys are released -+ state = state | IBus.ModifierType.RELEASE_MASK -+ if key.val == keyval and (key.state & state) == state: -+ # If it is a key release event, the previous key -+ # must have been the same key pressed down. -+ if (self._prev_key -+ and key.val == self._prev_key.val): -+ return True -+ -+ return False -+ -+ def do_candidate_clicked(self, index, button, state): -+ if self._editor.commit_to_preedit_current_page(index): -+ # commits to preëdit -+ self.commit_string( -+ self._editor.get_preedit_string_complete(), -+ tabkeys=self._editor.get_preedit_tabkeys_complete()) -+ return True -+ return False -+ -+ def do_process_key_event(self, keyval, keycode, state): -+ '''Process Key Events -+ Key Events include Key Press and Key Release, -+ modifier means Key Pressed -+ ''' -+ if debug_level > 1: -+ sys.stderr.write("do_process_key_event()\n") -+ if (self._has_input_purpose -+ and self._input_purpose -+ in [IBus.InputPurpose.PASSWORD, IBus.InputPurpose.PIN]): -+ return False -+ -+ key = KeyEvent(keyval, keycode, state) -+ -+ result = self._process_key_event (key) -+ self._prev_key = key -+ return result -+ -+ def _process_key_event (self, key): -+ '''Internal method to process key event''' -+ # Match mode switch hotkey -+ if (self._editor.is_empty() -+ and (self._match_hotkey( -+ key, IBus.KEY_Shift_L, -+ IBus.ModifierType.SHIFT_MASK))): -+ self.set_input_mode(int(not self._input_mode)) -+ return True -+ -+ # Match fullwidth/halfwidth letter mode switch hotkey -+ if self.db._is_cjk: -+ if (key.val == IBus.KEY_space -+ and key.state & IBus.ModifierType.SHIFT_MASK -+ and not key.state & IBus.ModifierType.RELEASE_MASK): -+ # Ignore when Shift+Space was pressed, the key release -+ # event will toggle the fullwidth/halfwidth letter mode, we -+ # don’t want to insert an extra space on the key press -+ # event. -+ return True -+ if (self._match_hotkey( -+ key, IBus.KEY_space, -+ IBus.ModifierType.SHIFT_MASK)): -+ self.set_letter_width( -+ not self._full_width_letter[self._input_mode], -+ input_mode = self._input_mode) -+ return True -+ -+ # Match full half punct mode switch hotkey -+ if (self._match_hotkey( -+ key, IBus.KEY_period, -+ IBus.ModifierType.CONTROL_MASK) and self.db._is_cjk): -+ self.set_punctuation_width( -+ not self._full_width_punct[self._input_mode], -+ input_mode = self._input_mode) -+ return True -+ -+ if self._input_mode: -+ return self._table_mode_process_key_event (key) -+ else: -+ return self._english_mode_process_key_event (key) -+ -+ def cond_letter_translate(self, char): -+ if self._full_width_letter[self._input_mode] and self.db._is_cjk: -+ return self._convert_to_full_width(char) -+ else: -+ return char -+ -+ def cond_punct_translate(self, char): -+ if self._full_width_punct[self._input_mode] and self.db._is_cjk: -+ return self._convert_to_full_width(char) -+ else: -+ return char -+ -+ def _english_mode_process_key_event(self, key): -+ # Ignore key release events -+ if key.state & IBus.ModifierType.RELEASE_MASK: -+ return False -+ if key.val >= 128: -+ return False -+ # we ignore all hotkeys here -+ if (key.state -+ & (IBus.ModifierType.CONTROL_MASK|IBus.ModifierType.MOD1_MASK)): -+ return False -+ keychar = IBus.keyval_to_unicode(key.val) -+ if type(keychar) != type(u''): -+ keychar = keychar.decode('UTF-8') -+ if ascii_ispunct(keychar): -+ trans_char = self.cond_punct_translate(keychar) -+ else: -+ trans_char = self.cond_letter_translate(keychar) -+ if trans_char == keychar: -+ return False -+ self.commit_string(trans_char) -+ return True -+ -+ def _table_mode_process_key_event(self, key): -+ if debug_level > 0: -+ sys.stderr.write('_table_mode_process_key_event() ') -+ sys.stderr.write('repr(key)=%(key)s\n' %{'key': key}) -+ # Change pinyin mode -+ # (change only if the editor is empty. When the editor -+ # is not empty, the right shift key should commit to preëdit -+ # and not change the pinyin mode). -+ if (self._ime_py -+ and self._editor.is_empty() -+ and self._match_hotkey( -+ key, IBus.KEY_Shift_R, -+ IBus.ModifierType.SHIFT_MASK)): -+ self.set_pinyin_mode(not self._editor._py_mode) -+ return True -+ # process commit to preedit -+ if (self._match_hotkey( -+ key, IBus.KEY_Shift_R, -+ IBus.ModifierType.SHIFT_MASK) -+ or self._match_hotkey( -+ key, IBus.KEY_Shift_L, -+ IBus.ModifierType.SHIFT_MASK)): -+ res = self._editor.commit_to_preedit() -+ self._update_ui() -+ return res -+ -+ # Left ALT key to cycle candidates in the current page. -+ if (self._match_hotkey( -+ key, IBus.KEY_Alt_L, -+ IBus.ModifierType.MOD1_MASK)): -+ res = self._editor.cycle_next_cand() -+ self._update_ui() -+ return res -+ -+ # Match single char mode switch hotkey -+ if (self._match_hotkey( -+ key, IBus.KEY_comma, -+ IBus.ModifierType.CONTROL_MASK) and self.db._is_cjk): -+ self.set_onechar_mode(not self._editor._onechar) -+ return True -+ -+ # Match direct commit mode switch hotkey -+ if (self._match_hotkey( -+ key, IBus.KEY_slash, -+ IBus.ModifierType.CONTROL_MASK) -+ and self.db.user_can_define_phrase and self.db.rules): -+ self.set_autocommit_mode(not self._auto_commit) -+ return True -+ -+ # Match Chinese mode shift -+ if (self._match_hotkey( -+ key, IBus.KEY_semicolon, -+ IBus.ModifierType.CONTROL_MASK) and self.db._is_chinese): -+ self.set_chinese_mode((self._editor._chinese_mode+1) % 5) -+ return True -+ -+ # Ignore key release events -+ # (Must be below all self._match_hotkey() callse -+ # because these match on a release event). -+ if key.state & IBus.ModifierType.RELEASE_MASK: -+ return False -+ -+ keychar = IBus.keyval_to_unicode(key.val) -+ if type(keychar) != type(u''): -+ keychar = keychar.decode('UTF-8') -+ -+ # Section to handle leading invalid input: -+ # -+ # This is the first character typed, if it is invalid -+ # input, handle it immediately here, if it is valid, continue. -+ if (self._editor.is_empty() -+ and not self._editor.get_preedit_string_complete()): -+ if ((keychar not in ( -+ self._valid_input_chars -+ + self._single_wildcard_char -+ + self._multi_wildcard_char) -+ or (self.db.startchars and keychar not in self.db.startchars)) -+ and (not key.state & -+ (IBus.ModifierType.MOD1_MASK | -+ IBus.ModifierType.CONTROL_MASK))): -+ if debug_level > 0: -+ sys.stderr.write( -+ '_table_mode_process_key_event() ' -+ + 'leading invalid input: ' -+ + 'repr(keychar)=%(keychar)s\n' -+ % {'keychar': keychar}) -+ if ascii_ispunct(keychar): -+ trans_char = self.cond_punct_translate(keychar) -+ else: -+ trans_char = self.cond_letter_translate(keychar) -+ if trans_char == keychar: -+ self._prev_char = trans_char -+ return False -+ else: -+ self.commit_string(trans_char) -+ return True -+ -+ if key.val == IBus.KEY_Escape: -+ self.reset() -+ self._update_ui() -+ return True -+ -+ if key.val in (IBus.KEY_Return, IBus.KEY_KP_Enter): -+ if (self._editor.is_empty() -+ and not self._editor.get_preedit_string_complete()): -+ # When IBus.KEY_Return is typed, -+ # IBus.keyval_to_unicode(key.val) returns a non-empty -+ # string. But when IBus.KEY_KP_Enter is typed it -+ # returns an empty string. Therefore, when typing -+ # IBus.KEY_KP_Enter as leading input, the key is not -+ # handled by the section to handle leading invalid -+ # input but it ends up here. If it is leading input -+ # (i.e. the preëdit is empty) we should always pass -+ # IBus.KEY_KP_Enter to the application: -+ return False -+ if self._auto_select: -+ self._editor.commit_to_preedit() -+ commit_string = self._editor.get_preedit_string_complete() -+ self.commit_string(commit_string) -+ return False -+ else: -+ commit_string = self._editor.get_preedit_tabkeys_complete() -+ self.commit_string(commit_string) -+ return True -+ -+ if key.val in (IBus.KEY_Tab, IBus.KEY_KP_Tab) and self._auto_select: -+ # Used for example for the Russian transliteration method -+ # “translit”, which uses “auto select”. If for example -+ # a file with the name “шшш” exists and one types in -+ # a bash shell: -+ # -+ # “ls sh” -+ # -+ # the “sh” is converted to “ш” and one sees -+ # -+ # “ls ш” -+ # -+ # in the shell where the “ш” is still in preëdit -+ # because “shh” would be converted to “щ”, i.e. there -+ # is more than one candidate and the input method is still -+ # waiting whether one more “h” will be typed or not. But -+ # if the next character typed is a Tab, the preëdit is -+ # committed here and “False” is returned to pass the Tab -+ # character through to the bash to complete the file name -+ # to “шшш”. -+ self._editor.commit_to_preedit() -+ self.commit_string(self._editor.get_preedit_string_complete()) -+ return False -+ -+ if key.val in (IBus.KEY_Down, IBus.KEY_KP_Down) : -+ if not self._editor.get_preedit_string_complete(): -+ return False -+ res = self._editor.cursor_down() -+ self._update_ui() -+ return res -+ -+ if key.val in (IBus.KEY_Up, IBus.KEY_KP_Up): -+ if not self._editor.get_preedit_string_complete(): -+ return False -+ res = self._editor.cursor_up() -+ self._update_ui() -+ return res -+ -+ if (key.val in (IBus.KEY_Left, IBus.KEY_KP_Left) -+ and key.state & IBus.ModifierType.CONTROL_MASK): -+ if not self._editor.get_preedit_string_complete(): -+ return False -+ self._editor.control_arrow_left() -+ self._update_ui() -+ return True -+ -+ if (key.val in (IBus.KEY_Right, IBus.KEY_KP_Right) -+ and key.state & IBus.ModifierType.CONTROL_MASK): -+ if not self._editor.get_preedit_string_complete(): -+ return False -+ self._editor.control_arrow_right() -+ self._update_ui() -+ return True -+ -+ if key.val in (IBus.KEY_Left, IBus.KEY_KP_Left): -+ if not self._editor.get_preedit_string_complete(): -+ return False -+ self._editor.arrow_left() -+ self._update_ui() -+ return True -+ -+ if key.val in (IBus.KEY_Right, IBus.KEY_KP_Right): -+ if not self._editor.get_preedit_string_complete(): -+ return False -+ self._editor.arrow_right() -+ self._update_ui() -+ return True -+ -+ if (key.val == IBus.KEY_BackSpace -+ and key.state & IBus.ModifierType.CONTROL_MASK): -+ if not self._editor.get_preedit_string_complete(): -+ return False -+ self._editor.remove_preedit_before_cursor() -+ self._update_ui() -+ return True -+ -+ if key.val == IBus.KEY_BackSpace: -+ if not self._editor.get_preedit_string_complete(): -+ return False -+ self._editor.remove_char() -+ self._update_ui() -+ return True -+ -+ if (key.val == IBus.KEY_Delete -+ and key.state & IBus.ModifierType.CONTROL_MASK): -+ if not self._editor.get_preedit_string_complete(): -+ return False -+ self._editor.remove_preedit_after_cursor() -+ self._update_ui() -+ return True -+ -+ if key.val == IBus.KEY_Delete: -+ if not self._editor.get_preedit_string_complete(): -+ return False -+ self._editor.delete() -+ self._update_ui() -+ return True -+ -+ if (key.val in self._editor.get_select_keys() -+ and self._editor._candidates -+ and key.state & IBus.ModifierType.CONTROL_MASK): -+ res = self._editor.select_key(key.val) -+ self._update_ui() -+ return res -+ -+ if (key.val in self._editor.get_select_keys() -+ and self._editor._candidates -+ and key.state & IBus.ModifierType.MOD1_MASK): -+ res = self._editor.remove_candidate_from_user_database(key.val) -+ self._update_ui() -+ return res -+ -+ # now we ignore all other hotkeys -+ if (key.state -+ & (IBus.ModifierType.CONTROL_MASK|IBus.ModifierType.MOD1_MASK)): -+ return False -+ -+ if key.state & IBus.ModifierType.MOD1_MASK: -+ return False -+ -+ # Section to handle valid input characters: -+ # -+ # All keys which could possibly conflict with the valid input -+ # characters should be checked below this section. These are -+ # SELECT_KEYS, PAGE_UP_KEYS, PAGE_DOWN_KEYS, and COMMIT_KEYS. -+ # -+ # For example, consider a table has -+ # -+ # SELECT_KEYS = 1,2,3,4,5,6,7,8,9,0 -+ # -+ # and -+ # -+ # VALID_INPUT_CHARS = 0123456789abcdef -+ # -+ # (Currently the cns11643 table has this, for example) -+ # -+ # Then the digit “1” could be interpreted either as an input -+ # character or as a select key but of course not both. If the -+ # meaning as a select key or page down key were preferred, -+ # this would make some input impossible which probably makes -+ # the whole input method useless. If the meaning as an input -+ # character is preferred, this makes selection using that key -+ # impossible. Making selection by key impossible is not nice -+ # either, but it is not a complete show stopper as there are -+ # still other possibilities to select, for example using the -+ # arrow-up/arrow-down keys or click with the mouse. -+ # -+ # Of course one should maybe consider fixing the conflict -+ # between the keys by using different SELECT_KEYS and/or -+ # PAGE_UP_KEYS/PAGE_DOWN_KEYS in that table ... -+ if (keychar -+ and (keychar in (self._valid_input_chars -+ + self._single_wildcard_char -+ + self._multi_wildcard_char) -+ or (self._editor._py_mode -+ and keychar in (self._pinyin_valid_input_chars -+ + self._single_wildcard_char -+ + self._multi_wildcard_char)))): -+ if debug_level > 0: -+ sys.stderr.write( -+ '_table_mode_process_key_event() valid input: ' -+ + 'repr(keychar)=%(keychar)s\n' -+ % {'keychar': keychar}) -+ if self._editor._py_mode: -+ if ((len(self._editor._chars_valid) -+ == self._max_key_length_pinyin) -+ or (len(self._editor._chars_valid) > 1 -+ and self._editor._chars_valid[-1] in '!@#$%')): -+ if self._auto_commit: -+ self.commit_everything_unless_invalid() -+ else: -+ self._editor.commit_to_preedit() -+ else: -+ if ((len(self._editor._chars_valid) -+ == self._max_key_length) -+ or (len(self._editor._chars_valid) -+ in self.db.possible_tabkeys_lengths)): -+ if self._auto_commit: -+ self.commit_everything_unless_invalid() -+ else: -+ self._editor.commit_to_preedit() -+ res = self._editor.add_input(keychar) -+ if not res: -+ if self._auto_select and self._editor._candidates_previous: -+ # Used for example for the Russian transliteration method -+ # “translit”, which uses “auto select”. -+ # The “translit” table contains: -+ # -+ # sh ш -+ # shh щ -+ # -+ # so typing “sh” matches “ш” and “щ”. The -+ # candidate with the shortest key sequence comes -+ # first in the lookup table, therefore “sh ш” -+ # is shown in the preëdit (The other candidate, -+ # “shh щ” comes second in the lookup table and -+ # could be selected using arrow-down. But -+ # “translit” hides the lookup table by default). -+ # -+ # Now, when after typing “sh” one types “s”, -+ # the key “shs” has no match, so add_input('s') -+ # returns “False” and we end up here. We pop the -+ # last character “s” which caused the match to -+ # fail, commit first of the previous candidates, -+ # i.e. “sh ш” and feed the “s” into the -+ # key event handler again. -+ self._editor.pop_input() -+ self.commit_everything_unless_invalid() -+ return self._table_mode_process_key_event(key) -+ self.commit_everything_unless_invalid() -+ self._update_ui() -+ return True -+ else: -+ if (self._auto_commit and self._editor.one_candidate() -+ and -+ (self._editor._chars_valid -+ == self._editor._candidates[0][0])): -+ self.commit_everything_unless_invalid() -+ self._update_ui() -+ return True -+ -+ if key.val in self._commit_keys: -+ if self.commit_everything_unless_invalid(): -+ if self._editor._auto_select: -+ self.commit_string(u' ') -+ return True -+ -+ if key.val in self._page_down_keys and self._editor._candidates: -+ res = self._editor.page_down() -+ self._update_ui() -+ return res -+ -+ if key.val in self._page_up_keys and self._editor._candidates: -+ res = self._editor.page_up() -+ self._update_ui() -+ return res -+ -+ if (key.val in self._editor.get_select_keys() -+ and self._editor._candidates): -+ if self._editor.select_key(key.val): # commits to preëdit -+ self.commit_string( -+ self._editor.get_preedit_string_complete(), -+ tabkeys=self._editor.get_preedit_tabkeys_complete()) -+ return True -+ -+ # Section to handle trailing invalid input: -+ # -+ # If the key has still not been handled when this point is -+ # reached, it cannot be a valid input character. Neither can -+ # it be a select key nor a page-up/page-down key. Adding this -+ # key to the tabkeys and search for matching candidates in the -+ # table would thus be pointless. -+ # -+ # So we commit all pending input immediately and then commit -+ # this invalid input character as well, possibly converted to -+ # fullwidth or halfwidth. -+ if keychar: -+ if debug_level > 0: -+ sys.stderr.write( -+ '_table_mode_process_key_event() trailing invalid input: ' -+ + 'repr(keychar)=%(keychar)s\n' -+ % {'keychar': keychar}) -+ if not self._editor._candidates: -+ self.commit_string(self._editor.get_preedit_tabkeys_complete()) -+ else: -+ self._editor.commit_to_preedit() -+ self.commit_string(self._editor.get_preedit_string_complete()) -+ if ascii_ispunct(keychar): -+ self.commit_string(self.cond_punct_translate(keychar)) -+ else: -+ self.commit_string(self.cond_letter_translate(keychar)) -+ return True -+ -+ # What kind of key was this?? -+ # -+ # keychar = IBus.keyval_to_unicode(key.val) -+ # -+ # returned no result. So whatever this was, we cannot handle it, -+ # just pass it through to the application by returning “False”. -+ return False -+ -+ def do_focus_in (self): -+ if debug_level > 1: -+ sys.stderr.write("do_focus_in()") -+ if self._on: -+ self.register_properties(self.properties) -+ self._init_or_update_property_menu( -+ self.input_mode_menu, -+ self._input_mode) -+ self._update_ui () -+ -+ def do_focus_out (self): -+ if self._has_input_purpose: -+ self._input_purpose = 0 -+ self._editor.clear_all_input_and_preedit() -+ -+ def do_set_content_type(self, purpose, hints): -+ if self._has_input_purpose: -+ self._input_purpose = purpose -+ -+ def do_enable (self): -+ self._on = True -+ self.do_focus_in() -+ -+ def do_disable (self): -+ self._on = False -+ -+ def do_page_up (self): -+ if self._editor.page_up (): -+ self._update_ui () -+ return True -+ return False -+ -+ def do_page_down (self): -+ if self._editor.page_down (): -+ self._update_ui () -+ return True -+ return False -+ -+ def config_section_normalize(self, section): -+ # This function replaces _: with - in the dconf -+ # section and converts to lower case to make -+ # the comparison of the dconf sections work correctly. -+ # I avoid using .lower() here because it is locale dependent, -+ # when using .lower() this would not achieve the desired -+ # effect of comparing the dconf sections case insentively -+ # in some locales, it would fail for example if Turkish -+ # locale (tr_TR.UTF-8) is set. -+ if sys.version_info >= (3, 0, 0): # Python3 -+ return re.sub(r'[_:]', r'-', section).translate( -+ ''.maketrans( -+ string.ascii_uppercase, -+ string.ascii_lowercase)) -+ else: # Python2 -+ return re.sub(r'[_:]', r'-', section).translate( -+ string.maketrans( -+ string.ascii_uppercase, -+ string.ascii_lowercase).decode('ISO-8859-1')) -+ -+ def config_value_changed_cb(self, config, section, name, value): -+ if (self.config_section_normalize(self._config_section) -+ != self.config_section_normalize(section)): -+ return -+ value = variant_to_value(value) -+ print('config value %(n)s for engine %(en)s changed to %(value)s' -+ % {'n': name, 'en': self._engine_name, 'value': value}) -+ if name == u'inputmode': -+ self.set_input_mode(value) -+ return -+ if name == u'autoselect': -+ self._editor._auto_select = value -+ self._auto_select = value -+ return -+ if name == u'autocommit': -+ self.set_autocommit_mode(value) -+ return -+ if name == u'chinesemode': -+ self.set_chinese_mode(value) -+ self.db.reset_phrases_cache() -+ return -+ if name == u'endeffullwidthletter': -+ self.set_letter_width(value, input_mode=0) -+ return -+ if name == u'endeffullwidthpunct': -+ self.set_punctuation_width(value, input_mode=0) -+ return -+ if name == u'lookuptableorientation': -+ self._editor._orientation = value -+ self._editor._lookup_table.set_orientation(value) -+ return -+ if name == u'lookuptablepagesize': -+ if value > len(self._editor._select_keys): -+ value = len(self._editor._select_keys) -+ self._config.set_value( -+ self._config_section, -+ 'lookuptablepagesize', -+ GLib.Variant.new_int32(value)) -+ if value < 1: -+ value = 1 -+ self._config.set_value( -+ self._config_section, -+ 'lookuptablepagesize', -+ GLib.Variant.new_int32(value)) -+ self._editor._page_size = value -+ self._editor._lookup_table = self._editor.get_new_lookup_table( -+ page_size = self._editor._page_size, -+ select_keys = self._editor._select_keys, -+ orientation = self._editor._orientation) -+ self.reset() -+ return -+ if name == u'onechar': -+ self.set_onechar_mode(value) -+ self.db.reset_phrases_cache() -+ return -+ if name == u'tabdeffullwidthletter': -+ self.set_letter_width(value, input_mode=1) -+ return -+ if name == u'tabdeffullwidthpunct': -+ self.set_punctuation_width(value, input_mode=1) -+ return -+ if name == u'alwaysshowlookup': -+ self._always_show_lookup = value -+ return -+ if name == u'spacekeybehavior': -+ if value == True: -+ # space is used as a page down key and not as a commit key: -+ if IBus.KEY_space not in self._page_down_keys: -+ self._page_down_keys.append(IBus.KEY_space) -+ if IBus.KEY_space in self._commit_keys: -+ self._commit_keys.remove(IBus.KEY_space) -+ if value == False: -+ # space is used as a commit key and not used as a page down key: -+ if IBus.KEY_space in self._page_down_keys: -+ self._page_down_keys.remove(IBus.KEY_space) -+ if IBus.KEY_space not in self._commit_keys: -+ self._commit_keys.append(IBus.KEY_space) -+ if debug_level > 1: -+ sys.stderr.write( -+ "self._page_down_keys=%s\n" -+ % repr(self._page_down_keys)) -+ return -+ if name == u'singlewildcardchar': -+ self._single_wildcard_char = value -+ self._editor._single_wildcard_char = value -+ self.db.reset_phrases_cache() -+ return -+ if name == u'multiwildcardchar': -+ self._multi_wildcard_char = value -+ self._editor._multi_wildcard_char = value -+ self.db.reset_phrases_cache() -+ return -+ if name == u'autowildcard': -+ self._auto_wildcard = value -+ self._editor._auto_wildcard = value -+ self.db.reset_phrases_cache() -+ return -diff -Nru ibus-table-1.9.18.orig/engine/tabsqlitedb.py ibus-table-1.9.18/engine/tabsqlitedb.py ---- ibus-table-1.9.18.orig/engine/tabsqlitedb.py 2020-07-22 11:52:11.651532112 +0200 -+++ ibus-table-1.9.18/engine/tabsqlitedb.py 2020-07-22 14:43:51.907260935 +0200 -@@ -1047,6 +1047,8 @@ - traceback.print_exc () - - def init_user_db(self, db_file): -+ if db_file == ':memory:': -+ return - if not path.exists(db_file): - db = sqlite3.connect(db_file) - # 20000 pages should be enough to cache the whole database -diff -Nru ibus-table-1.9.18.orig/engine/tabsqlitedb.py.orig ibus-table-1.9.18/engine/tabsqlitedb.py.orig ---- ibus-table-1.9.18.orig/engine/tabsqlitedb.py.orig 1970-01-01 01:00:00.000000000 +0100 -+++ ibus-table-1.9.18/engine/tabsqlitedb.py.orig 2020-07-22 11:52:11.651532112 +0200 -@@ -0,0 +1,1433 @@ -+# -*- coding: utf-8 -*- -+# vim:et sts=4 sw=4 -+# -+# ibus-table - The Tables engine for IBus -+# -+# Copyright (c) 2008-2009 Yu Yuwei -+# Copyright (c) 2009-2014 Caius "kaio" CHANCE -+# Copyright (c) 2012-2015 Mike FABIAN -+# -+# This library is free software; you can redistribute it and/or -+# modify it under the terms of the GNU Lesser General Public -+# License as published by the Free Software Foundation; either -+# version 2.1 of the License, or (at your option) any later version. -+# -+# This library is distributed in the hope that it will be useful, -+# but WITHOUT ANY WARRANTY; without even the implied warranty of -+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+# Lesser General Public License for more details. -+# -+# You should have received a copy of the GNU Lesser General Public -+# License along with this library; if not, write to the Free Software -+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -+# -+ -+import sys -+if sys.version_info < (3, 0, 0): -+ reload (sys) -+ sys.setdefaultencoding('utf-8') -+import os -+import os.path as path -+import shutil -+import sqlite3 -+import uuid -+import time -+import re -+import chinese_variants -+ -+debug_level = int(0) -+ -+database_version = '1.00' -+ -+patt_r = re.compile(r'c([ea])(\d):(.*)') -+patt_p = re.compile(r'p(-{0,1}\d)(-{0,1}\d)') -+ -+chinese_nocheck_chars = 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"ㄚㄛㄜㄝㄞㄟㄠㄡㄢㄣㄤㄥㄦ" -+ -+class ImeProperties: -+ def __init__(self, db=None, default_properties={}): -+ ''' -+ “db” is the handle of the sqlite3 database file obtained by -+ sqlite3.connect(). -+ ''' -+ if not db: -+ return None -+ self.ime_property_cache = default_properties -+ sqlstr = 'SELECT attr, val FROM main.ime;' -+ try: -+ results = db.execute(sqlstr).fetchall() -+ except: -+ import traceback -+ traceback.print_exc() -+ for result in results: -+ self.ime_property_cache[result[0]] = result[1] -+ -+ def get(self, key): -+ if key in self.ime_property_cache: -+ return self.ime_property_cache[key] -+ else: -+ return None -+ -+class tabsqlitedb: -+ '''Phrase database for tables -+ -+ The phrases table in the database has columns with the names: -+ -+ “id”, “tabkeys”, “phrase”, “freq”, “user_freq” -+ -+ There are 2 databases, sysdb, userdb. -+ -+ sysdb: System database for the input method, for example something -+ like /usr/share/ibus-table/tables/wubi-jidian86.db -+ “user_freq” is always 0 in a system database. “freq” -+ is some number in a system database indicating a frequency -+ of use of that phrase relative to the other phrases in that -+ database. -+ -+ user_db: Database on disk where the phrases used or defined by the -+ user are stored. “user_freq” is a counter which counts how -+ many times that combination of “tabkeys” and “phrase” has -+ been used. “freq” is equal to 0 for all combinations of -+ “tabkeys” and “phrase” where an entry for that phrase is -+ already in the system database which starts with the same -+ “tabkeys”. -+ For combinations of “tabkeys” and “phrase” which do not exist -+ at all in the system database, “freq” is equal to -1 to -+ indidated that this is a user defined phrase. -+ ''' -+ def __init__( -+ self, filename = None, user_db = None, create_database = False): -+ global debug_level -+ try: -+ debug_level = int(os.getenv('IBUS_TABLE_DEBUG_LEVEL')) -+ except (TypeError, ValueError): -+ debug_level = int(0) -+ self.old_phrases = [] -+ self.filename = filename -+ self._user_db = user_db -+ self.reset_phrases_cache() -+ -+ if create_database or os.path.isfile(self.filename): -+ self.db = sqlite3.connect(self.filename) -+ else: -+ print('Cannot open database file %s' %self.filename) -+ try: -+ self.db.execute('PRAGMA encoding = "UTF-8";') -+ self.db.execute('PRAGMA case_sensitive_like = true;') -+ self.db.execute('PRAGMA page_size = 4096;') -+ # 20000 pages should be enough to cache the whole database -+ self.db.execute('PRAGMA cache_size = 20000;') -+ self.db.execute('PRAGMA temp_store = MEMORY;') -+ self.db.execute('PRAGMA journal_size_limit = 1000000;') -+ self.db.execute('PRAGMA synchronous = NORMAL;') -+ except: -+ import traceback -+ traceback.print_exc() -+ print('Error while initializing database.') -+ # create IME property table -+ self.db.executescript( -+ 'CREATE TABLE IF NOT EXISTS main.ime (attr TEXT, val TEXT);') -+ # Initalize missing attributes in the ime table with some -+ # default values, they should be updated using the attributes -+ # found in the source when creating a system database with -+ # tabcreatedb.py -+ self._default_ime_attributes = { -+ 'name':'', -+ 'name.zh_cn':'', -+ 'name.zh_hk':'', -+ 'name.zh_tw':'', -+ 'author':'somebody', -+ 'uuid':'%s' % uuid.uuid4(), -+ 'serial_number':'%s' % time.strftime('%Y%m%d'), -+ 'icon':'ibus-table.svg', -+ 'license':'LGPL', -+ 'languages':'', -+ 'language_filter':'', -+ 'valid_input_chars':'abcdefghijklmnopqrstuvwxyz', -+ 'max_key_length':'4', -+ 'commit_keys':'space', -+ # 'forward_keys':'Return', -+ 'select_keys':'1,2,3,4,5,6,7,8,9,0', -+ 'page_up_keys':'Page_Up,minus', -+ 'page_down_keys':'Page_Down,equal', -+ 'status_prompt':'', -+ 'def_full_width_punct':'true', -+ 'def_full_width_letter':'false', -+ 'user_can_define_phrase':'false', -+ 'pinyin_mode':'false', -+ 'dynamic_adjust':'false', -+ 'auto_select':'false', -+ 'auto_commit':'false', -+ # 'no_check_chars':u'', -+ 'description':'A IME under IBus Table', -+ 'layout':'us', -+ 'symbol':'', -+ 'rules':'', -+ 'least_commit_length':'0', -+ 'start_chars':'', -+ 'orientation':'true', -+ 'always_show_lookup':'true', -+ 'char_prompts':'{}' -+ # we use this entry for those IME, which don't -+ # have rules to build up phrase, but still need -+ # auto commit to preedit -+ } -+ if create_database: -+ select_sqlstr = ''' -+ SELECT val FROM main.ime WHERE attr = :attr;''' -+ insert_sqlstr = ''' -+ INSERT INTO main.ime (attr, val) VALUES (:attr, :val);''' -+ for attr in self._default_ime_attributes: -+ sqlargs = { -+ 'attr': attr, -+ 'val': self._default_ime_attributes[attr] -+ } -+ if not self.db.execute(select_sqlstr, sqlargs).fetchall(): -+ self.db.execute(insert_sqlstr, sqlargs) -+ self.ime_properties = ImeProperties( -+ db=self.db, -+ default_properties=self._default_ime_attributes) -+ # shared variables in this class: -+ self._mlen = int(self.ime_properties.get("max_key_length")) -+ self._is_chinese = self.is_chinese() -+ self._is_cjk = self.is_cjk() -+ self.user_can_define_phrase = self.ime_properties.get( -+ 'user_can_define_phrase') -+ if self.user_can_define_phrase: -+ if self.user_can_define_phrase.lower() == u'true' : -+ self.user_can_define_phrase = True -+ else: -+ self.user_can_define_phrase = False -+ else: -+ print( -+ 'Could not find "user_can_define_phrase" entry from database, ' -+ + 'is it an outdated database?') -+ self.user_can_define_phrase = False -+ -+ self.dynamic_adjust = self.ime_properties.get('dynamic_adjust') -+ if self.dynamic_adjust: -+ if self.dynamic_adjust.lower() == u'true' : -+ self.dynamic_adjust = True -+ else: -+ self.dynamic_adjust = False -+ else: -+ print( -+ 'Could not find "dynamic_adjust" entry from database, ' -+ + 'is it an outdated database?') -+ self.dynamic_adjust = False -+ -+ self.rules = self.get_rules () -+ self.possible_tabkeys_lengths = self.get_possible_tabkeys_lengths() -+ self.startchars = self.get_start_chars () -+ -+ if not user_db or create_database: -+ # No user database requested or we are -+ # just creating the system database and -+ # we do not need a user database for that -+ return -+ -+ if user_db != ":memory:": -+ # Do not move this import to the beginning of this script! -+ # If for example the home directory is not writeable, -+ # ibus_table_location.py would fail because it cannot -+ # create some directories. -+ # -+ # But for tabcreatedb.py, no such directories are needed, -+ # tabcreatedb.py should not fail just because -+ # ibus_table_location.py cannot create some directories. -+ # -+ # “HOME=/foobar ibus-table-createdb” should not fail if -+ # “/foobar” is not writeable. -+ import ibus_table_location -+ tables_path = path.join(ibus_table_location.data_home(), "tables") -+ if not path.isdir(tables_path): -+ old_tables_path = os.path.join( -+ os.getenv('HOME'), '.ibus/tables') -+ if path.isdir(old_tables_path): -+ if os.access(os.path.join( -+ old_tables_path, 'debug.log'), os.F_OK): -+ os.unlink(os.path.join(old_tables_path, 'debug.log')) -+ if os.access(os.path.join( -+ old_tables_path, 'setup-debug.log'), os.F_OK): -+ os.unlink(os.path.join( -+ old_tables_path, 'setup-debug.log')) -+ shutil.copytree(old_tables_path, tables_path) -+ shutil.rmtree(old_tables_path) -+ os.symlink(tables_path, old_tables_path) -+ else: -+ os.makedirs(tables_path) -+ user_db = path.join(tables_path, user_db) -+ if not path.exists(user_db): -+ sys.stderr.write( -+ 'The user database %(udb)s does not exist yet.\n' -+ % {'udb': user_db}) -+ else: -+ try: -+ desc = self.get_database_desc(user_db) -+ phrase_table_column_names = [ -+ 'id', 'tabkeys', 'phrase','freq','user_freq'] -+ if (desc == None -+ or desc["version"] != database_version -+ or (self.get_number_of_columns_of_phrase_table(user_db) -+ != len(phrase_table_column_names))): -+ sys.stderr.write( -+ 'The user database %s seems to be incompatible.\n' -+ % user_db) -+ if desc == None: -+ sys.stderr.write( -+ 'There is no version information in ' -+ + 'the database.\n') -+ self.old_phrases = self.extract_user_phrases( -+ user_db, old_database_version = '0.0') -+ elif desc["version"] != database_version: -+ sys.stderr.write( -+ 'The version of the database does not match ' -+ + '(too old or too new?).\n' -+ 'ibus-table wants version=%s\n' -+ % database_version -+ + 'But the database actually has version=%s\n' -+ % desc['version']) -+ self.old_phrases = self.extract_user_phrases( -+ user_db, old_database_version = desc['version']) -+ elif (self.get_number_of_columns_of_phrase_table( -+ user_db) -+ != len(phrase_table_column_names)): -+ sys.stderr.write( -+ 'The number of columns of the database ' -+ + 'does not match.\n' -+ + 'ibus-table expects %s columns.\n' -+ % len(phrase_table_column_names) -+ + 'But the database actually has %s columns.\n' -+ % self.get_number_of_columns_of_phrase_table( -+ user_db) -+ + 'But the versions of the databases are ' -+ + 'identical.\n' -+ + 'This should never happen!\n') -+ self.old_phrases = None -+ from time import strftime -+ timestamp = strftime('-%Y-%m-%d_%H:%M:%S') -+ sys.stderr.write( -+ 'Renaming the incompatible database to "%s".\n' -+ % user_db+timestamp) -+ if os.path.exists(user_db): -+ os.rename(user_db, user_db+timestamp) -+ if os.path.exists(user_db+'-shm'): -+ os.rename(user_db+'-shm', user_db+'-shm'+timestamp) -+ if os.path.exists(user_db+'-wal'): -+ os.rename(user_db+'-wal', user_db+'-wal'+timestamp) -+ sys.stderr.write( -+ 'Creating a new, empty database "s".\n' -+ % user_db) -+ self.init_user_db(user_db) -+ sys.stderr.write( -+ 'If user phrases were successfully recovered from ' -+ + 'the old,\n' -+ + 'incompatible database, they will be used to ' -+ + 'initialize the new database.\n') -+ else: -+ sys.stderr.write( -+ 'Compatible database %s found.\n' % user_db) -+ except: -+ import traceback -+ traceback.print_exc() -+ -+ # open user phrase database -+ try: -+ sys.stderr.write( -+ 'Connect to the database %(name)s.\n' %{'name': user_db}) -+ self.db.executescript(''' -+ ATTACH DATABASE "%s" AS user_db; -+ PRAGMA user_db.encoding = "UTF-8"; -+ PRAGMA user_db.case_sensitive_like = true; -+ PRAGMA user_db.page_size = 4096; -+ PRAGMA user_db.cache_size = 20000; -+ PRAGMA user_db.temp_store = MEMORY; -+ PRAGMA user_db.journal_mode = WAL; -+ PRAGMA user_db.journal_size_limit = 1000000; -+ PRAGMA user_db.synchronous = NORMAL; -+ ''' % user_db) -+ except: -+ sys.stderr.write('Could not open the database %s.\n' % user_db) -+ from time import strftime -+ timestamp = strftime('-%Y-%m-%d_%H:%M:%S') -+ sys.stderr.write('Renaming the incompatible database to "%s".\n' -+ % user_db+timestamp) -+ if os.path.exists(user_db): -+ os.rename(user_db, user_db+timestamp) -+ if os.path.exists(user_db+'-shm'): -+ os.rename(user_db+'-shm', user_db+'-shm'+timestamp) -+ if os.path.exists(user_db+'-wal'): -+ os.rename(user_db+'-wal', user_db+'-wal'+timestamp) -+ sys.stderr.write('Creating a new, empty database "%s".\n' -+ % user_db) -+ self.init_user_db(user_db) -+ self.db.executescript(''' -+ ATTACH DATABASE "%s" AS user_db; -+ PRAGMA user_db.encoding = "UTF-8"; -+ PRAGMA user_db.case_sensitive_like = true; -+ PRAGMA user_db.page_size = 4096; -+ PRAGMA user_db.cache_size = 20000; -+ PRAGMA user_db.temp_store = MEMORY; -+ PRAGMA user_db.journal_mode = WAL; -+ PRAGMA user_db.journal_size_limit = 1000000; -+ PRAGMA user_db.synchronous = NORMAL; -+ ''' % user_db) -+ self.create_tables("user_db") -+ if self.old_phrases: -+ sqlargs = [] -+ for x in self.old_phrases: -+ sqlargs.append( -+ {'tabkeys': x[0], -+ 'phrase': x[1], -+ 'freq': x[2], -+ 'user_freq': x[3]}) -+ sqlstr = ''' -+ INSERT INTO user_db.phrases (tabkeys, phrase, freq, user_freq) -+ VALUES (:tabkeys, :phrase, :freq, :user_freq) -+ ''' -+ try: -+ self.db.executemany(sqlstr, sqlargs) -+ except: -+ import traceback -+ traceback.print_exec() -+ self.db.commit () -+ self.db.execute('PRAGMA wal_checkpoint;') -+ -+ # try create all tables in user database -+ self.create_indexes ("user_db", commit=False) -+ self.generate_userdb_desc () -+ -+ def update_phrase( -+ self, tabkeys=u'', phrase=u'', -+ user_freq=0, database='user_db', commit=True): -+ '''update phrase freqs''' -+ if debug_level > 1: -+ sys.stderr.write( -+ 'update_phrase() tabkeys=%(t)s phrase=%(p)s ' -+ % {'t': tabkeys, 'p': phrase} -+ + 'user_freq=%(u)s database=%(d)s\n' -+ % {'u': user_freq, 'd': database}) -+ if not tabkeys or not phrase: -+ return -+ sqlstr = ''' -+ UPDATE %s.phrases SET user_freq = :user_freq -+ WHERE tabkeys = :tabkeys AND phrase = :phrase -+ ;''' % database -+ sqlargs = {'user_freq': user_freq, 'tabkeys': tabkeys, 'phrase': phrase} -+ try: -+ self.db.execute(sqlstr, sqlargs) -+ if commit: -+ self.db.commit() -+ self.invalidate_phrases_cache(tabkeys) -+ except: -+ import traceback -+ traceback.print_exc() -+ -+ def sync_usrdb (self): -+ ''' -+ Trigger a checkpoint operation. -+ ''' -+ if self._user_db is None: -+ return -+ self.db.commit() -+ self.db.execute('PRAGMA wal_checkpoint;') -+ -+ def reset_phrases_cache (self): -+ self._phrases_cache = {} -+ -+ def invalidate_phrases_cache (self, tabkeys=u''): -+ for i in range(1, self._mlen + 1): -+ if self._phrases_cache.get(tabkeys[0:i]): -+ self._phrases_cache.pop(tabkeys[0:i]) -+ -+ def is_chinese (self): -+ __lang = self.ime_properties.get('languages') -+ if __lang: -+ __langs = __lang.split(',') -+ for _l in __langs: -+ if _l.lower().find('zh') != -1: -+ return True -+ return False -+ -+ def is_cjk(self): -+ languages = self.ime_properties.get('languages') -+ if languages: -+ languages = languages.split(',') -+ for language in languages: -+ for lang in ['zh', 'ja', 'ko']: -+ if language.strip().startswith(lang): -+ return True -+ return False -+ -+ def get_chinese_mode (self): -+ try: -+ __dict = {'cm0':0, 'cm1':1, 'cm2':2, 'cm3':3, 'cm4':4} -+ __filt = self.ime_properties.get('language_filter') -+ return __dict[__filt] -+ except: -+ return -1 -+ -+ def get_select_keys (self): -+ ret = self.ime_properties.get("select_keys") -+ if ret: -+ return ret -+ return "1,2,3,4,5,6,7,8,9,0" -+ -+ def get_orientation (self): -+ try: -+ return int(self.ime_properties.get('orientation')) -+ except: -+ return 1 -+ -+ def create_tables (self, database): -+ '''Create tables that contain all phrase''' -+ if database == 'main': -+ sqlstr = ''' -+ CREATE TABLE IF NOT EXISTS %s.goucima -+ (zi TEXT PRIMARY KEY, goucima TEXT); -+ ''' % database -+ self.db.execute (sqlstr) -+ sqlstr = ''' -+ CREATE TABLE IF NOT EXISTS %s.pinyin -+ (pinyin TEXT, zi TEXT, freq INTEGER); -+ ''' % database -+ self.db.execute(sqlstr) -+ -+ sqlstr = ''' -+ CREATE TABLE IF NOT EXISTS %s.phrases -+ (id INTEGER PRIMARY KEY, tabkeys TEXT, phrase TEXT, -+ freq INTEGER, user_freq INTEGER); -+ ''' % database -+ self.db.execute (sqlstr) -+ self.db.commit() -+ -+ def update_ime (self, attrs): -+ '''Update or insert attributes in ime table, attrs is a iterable object -+ Like [(attr,val), (attr,val), ...] -+ -+ This is called only by tabcreatedb.py. -+ ''' -+ select_sqlstr = 'SELECT val from main.ime WHERE attr = :attr' -+ update_sqlstr = 'UPDATE main.ime SET val = :val WHERE attr = :attr;' -+ insert_sqlstr = 'INSERT INTO main.ime (attr, val) VALUES (:attr, :val);' -+ for attr, val in attrs: -+ sqlargs = {'attr': attr, 'val': val} -+ if self.db.execute(select_sqlstr, sqlargs).fetchall(): -+ self.db.execute(update_sqlstr, sqlargs) -+ else: -+ self.db.execute(insert_sqlstr, sqlargs) -+ self.db.commit() -+ # update ime properties cache: -+ self.ime_properties = ImeProperties( -+ db=self.db, -+ default_properties=self._default_ime_attributes) -+ # The self variables used by tabcreatedb.py need to be updated now: -+ self._mlen = int(self.ime_properties.get('max_key_length')) -+ self._is_chinese = self.is_chinese() -+ self.user_can_define_phrase = self.ime_properties.get( -+ 'user_can_define_phrase') -+ if self.user_can_define_phrase: -+ if self.user_can_define_phrase.lower() == u'true' : -+ self.user_can_define_phrase = True -+ else: -+ self.user_can_define_phrase = False -+ else: -+ print( -+ 'Could not find "user_can_define_phrase" entry from database, ' -+ + 'is it a outdated database?') -+ self.user_can_define_phrase = False -+ self.rules = self.get_rules() -+ -+ def get_rules (self): -+ '''Get phrase construct rules''' -+ rules = {} -+ if self.user_can_define_phrase: -+ try: -+ _rules = self.ime_properties.get('rules') -+ if _rules: -+ _rules = _rules.strip().split(';') -+ for rule in _rules: -+ res = patt_r.match (rule) -+ if res: -+ cms = [] -+ if res.group(1) == 'a': -+ rules['above'] = int(res.group(2)) -+ _cms = res.group(3).split('+') -+ if len(_cms) > self._mlen: -+ print('rule: "%s" over max key length' %rule) -+ break -+ for _cm in _cms: -+ cm_res = patt_p.match(_cm) -+ cms.append((int(cm_res.group(1)), -+ int(cm_res.group(2)))) -+ rules[int(res.group(2))]=cms -+ else: -+ print('not a legal rule: "%s"' %rule) -+ except Exception: -+ import traceback -+ traceback.print_exc () -+ return rules -+ else: -+ return "" -+ -+ def get_possible_tabkeys_lengths(self): -+ '''Return a list of the possible lengths for tabkeys in this table. -+ -+ Example: -+ -+ If the table source has rules like: -+ -+ RULES = ce2:p11+p12+p21+p22;ce3:p11+p21+p22+p31;ca4:p11+p21+p31+p41 -+ -+ self._rules will be set to -+ -+ self._rules={2: [(1, 1), (1, 2), (2, 1), (2, 2)], 3: [(1, 1), (1, 2), (2, 1), (3, 1)], 4: [(1, 1), (2, 1), (3, 1), (-1, 1)], 'above': 4} -+ -+ and then this function returns “[4, 4, 4]” -+ -+ Or, if the table source has no RULES but LEAST_COMMIT_LENGTH=2 -+ and MAX_KEY_LENGTH = 4, then it returns “[2, 3, 4]” -+ -+ I cannot find any tables which use LEAST_COMMIT_LENGTH though. -+ ''' -+ if self.rules: -+ max_len = self.rules["above"] -+ return [len(self.rules[x]) for x in range(2, max_len+1)][:] -+ else: -+ try: -+ least_commit_len = int( -+ self.ime_properties.get('least_commit_length')) -+ except: -+ least_commit_len = 0 -+ if least_commit_len > 0: -+ return list(range(least_commit_len, self._mlen + 1)) -+ else: -+ return [] -+ -+ def get_start_chars (self): -+ '''return possible start chars of IME''' -+ return self.ime_properties.get('start_chars') -+ -+ def get_no_check_chars (self): -+ '''Get the characters which engine should not change freq''' -+ _chars = self.ime_properties.get('no_check_chars') -+ if type(_chars) != type(u''): -+ _chars = _chars.decode('utf-8') -+ return _chars -+ -+ def add_phrases (self, phrases, database = 'main'): -+ '''Add many phrases to database fast. Used by tabcreatedb.py when -+ creating the system database from scratch. -+ -+ “phrases” is a iterable object which looks like: -+ -+ [(tabkeys, phrase, freq ,user_freq), (tabkeys, phrase, freq, user_freq), ...] -+ -+ This function does not check whether phrases are already -+ there. As this function is only used while creating the -+ system database, it is not really necessary to check whether -+ phrases are already there because the database is initially -+ empty anyway. And the caller should take care that the -+ “phrases” argument does not contain duplicates. -+ -+ ''' -+ if debug_level > 1: -+ sys.stderr.write("add_phrases() len(phrases)=%s\n" -+ %len(phrases)) -+ insert_sqlstr = ''' -+ INSERT INTO %(database)s.phrases -+ (tabkeys, phrase, freq, user_freq) -+ VALUES (:tabkeys, :phrase, :freq, :user_freq); -+ ''' % {'database': database} -+ insert_sqlargs = [] -+ for (tabkeys, phrase, freq, user_freq) in phrases: -+ insert_sqlargs.append({ -+ 'tabkeys': tabkeys, -+ 'phrase': phrase, -+ 'freq': freq, -+ 'user_freq': user_freq}) -+ self.invalidate_phrases_cache(tabkeys) -+ self.db.executemany(insert_sqlstr, insert_sqlargs) -+ self.db.commit() -+ self.db.execute('PRAGMA wal_checkpoint;') -+ -+ def add_phrase( -+ self, tabkeys=u'', phrase=u'', freq=0, user_freq=0, -+ database='main',commit=True): -+ '''Add phrase to database, phrase is a object of -+ (tabkeys, phrase, freq ,user_freq) -+ ''' -+ if debug_level > 1: -+ sys.stderr.write( -+ 'add_phrase tabkeys=%(t)s phrase=%(p)s ' -+ % {'t': tabkeys, 'p': phrase} -+ + 'freq=%(f)s user_freq=%(u)s\n' -+ % {'f': freq, 'u': user_freq}) -+ if not tabkeys or not phrase: -+ return -+ select_sqlstr = ''' -+ SELECT * FROM %(database)s.phrases -+ WHERE tabkeys = :tabkeys AND phrase = :phrase; -+ ''' % {'database': database} -+ select_sqlargs = {'tabkeys': tabkeys, 'phrase': phrase} -+ results = self.db.execute(select_sqlstr, select_sqlargs).fetchall() -+ if results: -+ # there is already such a phrase, i.e. add_phrase was called -+ # in error, do nothing to avoid duplicate entries. -+ if debug_level > 1: -+ sys.stderr.write( -+ 'add_phrase() ' -+ + 'select_sqlstr=%(sql)s select_sqlargs=%(arg)s ' -+ % {'sql': select_sqlstr, 'arg': select_sqlargs} -+ + 'already there!: results=%(r)s \n' -+ % {'r': results}) -+ return -+ -+ insert_sqlstr = ''' -+ INSERT INTO %(database)s.phrases -+ (tabkeys, phrase, freq, user_freq) -+ VALUES (:tabkeys, :phrase, :freq, :user_freq); -+ ''' % {'database': database} -+ insert_sqlargs = { -+ 'tabkeys': tabkeys, -+ 'phrase': phrase, -+ 'freq': freq, -+ 'user_freq': user_freq} -+ if debug_level > 1: -+ sys.stderr.write( -+ 'add_phrase() insert_sqlstr=%(sql)s insert_sqlargs=%(arg)s\n' -+ % {'sql': insert_sqlstr, 'arg': insert_sqlargs}) -+ try: -+ self.db.execute (insert_sqlstr, insert_sqlargs) -+ if commit: -+ self.db.commit() -+ self.invalidate_phrases_cache(tabkeys) -+ except: -+ import traceback -+ traceback.print_exc() -+ -+ def add_goucima (self, goucimas): -+ '''Add goucima into database, goucimas is iterable object -+ Like goucimas = [(zi,goucima), (zi,goucima), ...] -+ ''' -+ sqlstr = ''' -+ INSERT INTO main.goucima (zi, goucima) VALUES (:zi, :goucima); -+ ''' -+ sqlargs = [] -+ for zi, goucima in goucimas: -+ sqlargs.append({'zi': zi, 'goucima': goucima}) -+ try: -+ self.db.commit() -+ self.db.executemany(sqlstr, sqlargs) -+ self.db.commit() -+ self.db.execute('PRAGMA wal_checkpoint;') -+ except: -+ import traceback -+ traceback.print_exc() -+ -+ def add_pinyin (self, pinyins, database = 'main'): -+ '''Add pinyin to database, pinyins is a iterable object -+ Like: [(zi,pinyin, freq), (zi, pinyin, freq), ...] -+ ''' -+ sqlstr = ''' -+ INSERT INTO %s.pinyin (pinyin, zi, freq) VALUES (:pinyin, :zi, :freq); -+ ''' % database -+ count = 0 -+ for pinyin, zi, freq in pinyins: -+ count += 1 -+ pinyin = pinyin.replace( -+ '1','!').replace( -+ '2','@').replace( -+ '3','#').replace( -+ '4','$').replace( -+ '5','%') -+ try: -+ self.db.execute( -+ sqlstr, {'pinyin': pinyin, 'zi': zi, 'freq': freq}) -+ except Exception: -+ sys.stderr.write( -+ 'Error when inserting into pinyin table. ' -+ + 'count=%(c)s pinyin=%(p)s zi=%(z)s freq=%(f)s\n' -+ % {'c': count, 'p': pinyin, 'z': zi, 'f': freq}) -+ import traceback -+ traceback.print_exc() -+ self.db.commit() -+ -+ def optimize_database (self, database='main'): -+ sqlstr = ''' -+ CREATE TABLE tmp AS SELECT * FROM %(database)s.phrases; -+ DELETE FROM %(database)s.phrases; -+ INSERT INTO %(database)s.phrases SELECT * FROM tmp ORDER BY -+ tabkeys ASC, phrase ASC, user_freq DESC, freq DESC, id ASC; -+ DROP TABLE tmp; -+ CREATE TABLE tmp AS SELECT * FROM %(database)s.goucima; -+ DELETE FROM %(database)s.goucima; -+ INSERT INTO %(database)s.goucima SELECT * FROM tmp ORDER BY zi, goucima; -+ DROP TABLE tmp; -+ CREATE TABLE tmp AS SELECT * FROM %(database)s.pinyin; -+ DELETE FROM %(database)s.pinyin; -+ INSERT INTO %(database)s.pinyin SELECT * FROM tmp ORDER BY pinyin ASC, freq DESC; -+ DROP TABLE tmp; -+ ''' % {'database':database} -+ self.db.executescript (sqlstr) -+ self.db.executescript ("VACUUM;") -+ self.db.commit() -+ -+ def drop_indexes(self, database): -+ '''Drop the indexes in the database to reduce its size -+ -+ We do not use any indexes at the moment, therefore this -+ function does nothing. -+ ''' -+ if debug_level > 1: -+ sys.stderr.write("drop_indexes()\n") -+ return -+ -+ def create_indexes(self, database, commit=True): -+ '''Create indexes for the database. -+ -+ We do not use any indexes at the moment, therefore -+ this function does nothing. We used indexes before, -+ but benchmarking showed that none of them was really -+ speeding anything up, therefore we deleted all of them -+ to get much smaller databases (about half the size). -+ -+ If some index turns out to be very useful in future, it could -+ be created here (and dropped in “drop_indexes()”). -+ ''' -+ if debug_level > 1: -+ sys.stderr.write("create_indexes()\n") -+ return -+ -+ def big5_code(self, phrase): -+ try: -+ big5 = phrase.encode('Big5') -+ except: -+ big5 = b'\xff\xff' # higher than any Big5 code -+ return big5 -+ -+ def best_candidates( -+ self, typed_tabkeys=u'', candidates=[], chinese_mode=-1): -+ ''' -+ “candidates” is an array containing something like: -+ [(tabkeys, phrase, freq, user_freq), ...] -+ -+ “typed_tabkeys” is key sequence the user really typed, which -+ maybe only the beginning part of the “tabkeys” in a matched -+ candidate. -+ ''' -+ maximum_number_of_candidates = 100 -+ engine_name = os.path.basename(self.filename).replace('.db', '') -+ if engine_name in [ -+ 'cangjie3', 'cangjie5', 'cangjie-big', -+ 'quick-classic', 'quick3', 'quick5']: -+ code_point_function = self.big5_code -+ else: -+ code_point_function = lambda x: (1) -+ if chinese_mode in (2, 3) and self._is_chinese: -+ if chinese_mode == 2: -+ bitmask = (1 << 0) # used in simplified Chinese -+ else: -+ bitmask = (1 << 1) # used in traditional Chinese -+ return sorted(candidates, -+ key=lambda x: ( -+ - int( -+ typed_tabkeys == x[0] -+ ), # exact matches first! -+ -1*x[3], # user_freq descending -+ # Prefer characters used in the -+ # desired Chinese variant: -+ -(bitmask -+ & chinese_variants.detect_chinese_category( -+ x[1])), -+ -1*x[2], # freq descending -+ len(x[0]), # len(tabkeys) ascending -+ x[0], # tabkeys alphabetical -+ code_point_function(x[1][0]), -+ # Unicode codepoint of first character of phrase: -+ ord(x[1][0]) -+ ))[:maximum_number_of_candidates] -+ return sorted(candidates, -+ key=lambda x: ( -+ - int( -+ typed_tabkeys == x[0] -+ ), # exact matches first! -+ -1*x[3], # user_freq descending -+ -1*x[2], # freq descending -+ len(x[0]), # len(tabkeys) ascending -+ x[0], # tabkeys alphabetical -+ code_point_function(x[1][0]), -+ # Unicode codepoint of first character of phrase: -+ ord(x[1][0]) -+ ))[:maximum_number_of_candidates] -+ -+ def select_words( -+ self, tabkeys=u'', onechar=False, chinese_mode=-1, -+ single_wildcard_char=u'', multi_wildcard_char=u'', -+ auto_wildcard=False): -+ ''' -+ Get matching phrases for tabkeys from the database. -+ ''' -+ if not tabkeys: -+ return [] -+ # query phrases cache first -+ best = self._phrases_cache.get(tabkeys) -+ if best: -+ return best -+ one_char_condition = '' -+ if onechar: -+ # for some users really like to select only single characters -+ one_char_condition = ' AND length(phrase)=1 ' -+ -+ if self.user_can_define_phrase or self.dynamic_adjust: -+ sqlstr = ''' -+ SELECT tabkeys, phrase, freq, user_freq FROM -+ ( -+ SELECT tabkeys, phrase, freq, user_freq FROM main.phrases -+ WHERE tabkeys LIKE :tabkeys ESCAPE :escapechar %(one_char_condition)s -+ UNION ALL -+ SELECT tabkeys, phrase, freq, user_freq FROM user_db.phrases -+ WHERE tabkeys LIKE :tabkeys ESCAPE :escapechar %(one_char_condition)s -+ ) -+ ''' % {'one_char_condition': one_char_condition} -+ else: -+ sqlstr = ''' -+ SELECT tabkeys, phrase, freq, user_freq FROM main.phrases -+ WHERE tabkeys LIKE :tabkeys ESCAPE :escapechar %(one_char_condition)s -+ ''' % {'one_char_condition': one_char_condition} -+ escapechar = '☺' -+ for c in '!@#': -+ if c not in [single_wildcard_char, multi_wildcard_char]: -+ escapechar = c -+ tabkeys_for_like = tabkeys -+ tabkeys_for_like = tabkeys_for_like.replace( -+ escapechar, escapechar+escapechar) -+ if '%' not in [single_wildcard_char, multi_wildcard_char]: -+ tabkeys_for_like = tabkeys_for_like.replace('%', escapechar+'%') -+ if '_' not in [single_wildcard_char, multi_wildcard_char]: -+ tabkeys_for_like = tabkeys_for_like.replace('_', escapechar+'_') -+ if single_wildcard_char: -+ tabkeys_for_like = tabkeys_for_like.replace( -+ single_wildcard_char, '_') -+ if multi_wildcard_char: -+ tabkeys_for_like = tabkeys_for_like.replace( -+ multi_wildcard_char, '%%') -+ if auto_wildcard: -+ tabkeys_for_like += '%%' -+ sqlargs = {'tabkeys': tabkeys_for_like, 'escapechar': escapechar} -+ unfiltered_results = self.db.execute(sqlstr, sqlargs).fetchall() -+ bitmask = None -+ if chinese_mode == 0: -+ bitmask = (1 << 0) # simplified only -+ elif chinese_mode == 1: -+ bitmask = (1 << 1) # traditional only -+ if not bitmask: -+ results = unfiltered_results -+ else: -+ results = [] -+ for result in unfiltered_results: -+ if (bitmask -+ & chinese_variants.detect_chinese_category(result[1])): -+ results.append(result) -+ # merge matches from the system database and from the user -+ # database to avoid duplicates in the candidate list for -+ # example, if we have the result ('aaaa', '工', 551000000, 0) -+ # from the system database and ('aaaa', '工', 0, 5) from the -+ # user database, these should be merged into one match -+ # ('aaaa', '工', 551000000, 5). -+ phrase_frequencies = {} -+ for result in results: -+ key = (result[0], result[1]) -+ if key not in phrase_frequencies: -+ phrase_frequencies[key] = result -+ else: -+ phrase_frequencies.update([( -+ key, -+ key + -+ ( -+ max(result[2], phrase_frequencies[key][2]), -+ max(result[3], phrase_frequencies[key][3])) -+ )]) -+ best = self.best_candidates( -+ typed_tabkeys=tabkeys, -+ candidates=phrase_frequencies.values(), -+ chinese_mode=chinese_mode) -+ if debug_level > 1: -+ sys.stderr.write("select_words() best=%s\n" %repr(best)) -+ self._phrases_cache[tabkeys] = best -+ return best -+ -+ def select_chinese_characters_by_pinyin( -+ self, tabkeys=u'', chinese_mode=-1, single_wildcard_char=u'', -+ multi_wildcard_char=u''): -+ ''' -+ Get Chinese characters matching the pinyin given by tabkeys -+ from the database. -+ ''' -+ if not tabkeys: -+ return [] -+ sqlstr = ''' -+ SELECT pinyin, zi, freq FROM main.pinyin WHERE pinyin LIKE :tabkeys -+ ORDER BY freq DESC, pinyin ASC -+ ;''' -+ tabkeys_for_like = tabkeys -+ if single_wildcard_char: -+ tabkeys_for_like = tabkeys_for_like.replace( -+ single_wildcard_char, '_') -+ if multi_wildcard_char: -+ tabkeys_for_like = tabkeys_for_like.replace( -+ multi_wildcard_char, '%%') -+ tabkeys_for_like += '%%' -+ sqlargs = {'tabkeys': tabkeys_for_like} -+ results = self.db.execute(sqlstr, sqlargs).fetchall() -+ # now convert the results into a list of candidates in the format -+ # which was returned before I simplified the pinyin database table. -+ bitmask = None -+ if chinese_mode == 0: -+ bitmask = (1 << 0) # simplified only -+ elif chinese_mode == 1: -+ bitmask = (1 << 1) # traditional only -+ phrase_frequencies = [] -+ for (pinyin, zi, freq) in results: -+ if not bitmask: -+ phrase_frequencies.append(tuple([pinyin, zi, freq, 0])) -+ else: -+ if bitmask & chinese_variants.detect_chinese_category(zi): -+ phrase_frequencies.append(tuple([pinyin, zi, freq, 0])) -+ return self.best_candidates( -+ typed_tabkeys=tabkeys, -+ candidates=phrase_frequencies, -+ chinese_mode=chinese_mode) -+ -+ def generate_userdb_desc (self): -+ try: -+ sqlstring = ( -+ 'CREATE TABLE IF NOT EXISTS user_db.desc ' -+ + '(name PRIMARY KEY, value);') -+ self.db.executescript (sqlstring) -+ sqlstring = 'INSERT OR IGNORE INTO user_db.desc VALUES (?, ?);' -+ self.db.execute (sqlstring, ('version', database_version)) -+ sqlstring = ( -+ 'INSERT OR IGNORE INTO user_db.desc ' -+ + 'VALUES (?, DATETIME("now", "localtime"));') -+ self.db.execute (sqlstring, ("create-time", )) -+ self.db.commit () -+ except: -+ import traceback -+ traceback.print_exc () -+ -+ def init_user_db(self, db_file): -+ if not path.exists(db_file): -+ db = sqlite3.connect(db_file) -+ # 20000 pages should be enough to cache the whole database -+ db.executescript(''' -+ PRAGMA encoding = "UTF-8"; -+ PRAGMA case_sensitive_like = true; -+ PRAGMA page_size = 4096; -+ PRAGMA cache_size = 20000; -+ PRAGMA temp_store = MEMORY; -+ PRAGMA journal_mode = WAL; -+ PRAGMA journal_size_limit = 1000000; -+ PRAGMA synchronous = NORMAL; -+ ''') -+ db.commit() -+ -+ def get_database_desc (self, db_file): -+ if not path.exists (db_file): -+ return None -+ try: -+ db = sqlite3.connect (db_file) -+ desc = {} -+ for row in db.execute ("SELECT * FROM desc;").fetchall(): -+ desc [row[0]] = row[1] -+ db.close() -+ return desc -+ except: -+ return None -+ -+ def get_number_of_columns_of_phrase_table(self, db_file): -+ ''' -+ Get the number of columns in the 'phrases' table in -+ the database in db_file. -+ -+ Determines the number of columns by parsing this: -+ -+ sqlite> select sql from sqlite_master where name='phrases'; -+ CREATE TABLE phrases -+ (id INTEGER PRIMARY KEY, tabkeys TEXT, phrase TEXT, -+ freq INTEGER, user_freq INTEGER) -+ sqlite> -+ -+ This result could be on a single line, as above, or on multiple -+ lines. -+ ''' -+ if not path.exists (db_file): -+ return 0 -+ try: -+ db = sqlite3.connect (db_file) -+ tp_res = db.execute( -+ "select sql from sqlite_master where name='phrases';" -+ ).fetchall() -+ # Remove possible line breaks from the string where we -+ # want to match: -+ string = ' '.join(tp_res[0][0].splitlines()) -+ res = re.match(r'.*\((.*)\)', string) -+ if res: -+ tp = res.group(1).split(',') -+ return len(tp) -+ else: -+ return 0 -+ except: -+ return 0 -+ -+ def get_goucima (self, zi): -+ '''Get goucima of given character''' -+ if not zi: -+ return u'' -+ sqlstr = 'SELECT goucima FROM main.goucima WHERE zi = :zi;' -+ results = self.db.execute(sqlstr, {'zi': zi}).fetchall() -+ if results: -+ goucima = results[0][0] -+ else: -+ goucima = u'' -+ if debug_level > 1: -+ sys.stderr.write("get_goucima() goucima=%s\n" %goucima) -+ return goucima -+ -+ def parse_phrase (self, phrase): -+ '''Parse phrase to get its table code -+ -+ Example: -+ -+ Let’s assume we use wubi-jidian86. The rules in the source of -+ that table are: -+ -+ RULES = ce2:p11+p12+p21+p22;ce3:p11+p21+p31+p32;ca4:p11+p21+p31+p-11 -+ -+ “ce2” is a rule for phrases of length 2, “ce3” is a rule -+ for phrases of length 3, “ca4” is a rule for phrases of -+ length 4 *and* for all phrases with a length greater then -+ 4. “pnm” in such a rule means to use the n-th character of -+ the phrase and take the m-th character of the table code of -+ that character. I.e. “p-11” is the first character of the -+ table code of the last character in the phrase. -+ -+ Let’s assume the phrase is “天下大事”. The goucima (構詞碼 -+ = “word formation keys”) for these 4 characters are: -+ -+ character goucima -+ 天 gdi -+ 下 ghi -+ 大 dddd -+ 事 gkvh -+ -+ (If no special goucima are defined by the user, the longest -+ encoding for a single character in a table is the goucima for -+ that character). -+ -+ The length of the phrase “天下大事” is 4 characters, -+ therefore the rule ca4:p11+p21+p31+p-11 applies, i.e. the -+ table code for “天下大事” is calculated by using the first, -+ second, third and last character of the phrase and taking the -+ first character of the goucima for each of these. Therefore, -+ the table code for “天下大事” is “ggdg”. -+ -+ ''' -+ if debug_level > 1: -+ sys.stderr.write( -+ 'parse_phrase() phrase=%(p)s rules%(r)s\n' -+ % {'p': phrase, 'r': self.rules}) -+ if type(phrase) != type(u''): -+ phrase = phrase.decode('UTF-8') -+ # Shouldn’t this function try first whether the system database -+ # already has an entry for this phrase and if yes return it -+ # instead of constructing a new entry according to the rules? -+ # And construct a new entry only when no entry already exists -+ # in the system database?? -+ if len(phrase) == 0: -+ return u'' -+ if len(phrase) == 1: -+ return self.get_goucima(phrase) -+ if not self.rules: -+ return u'' -+ if len(phrase) in self.rules: -+ rule = self.rules[len(phrase)] -+ elif len(phrase) > self.rules['above']: -+ rule = self.rules[self.rules['above']] -+ else: -+ sys.stderr.write( -+ 'No rule for this phrase length. phrase=%(p)s rules=%(r)s\n' -+ %{'p': phrase, 'r': self.rules}) -+ return u'' -+ if len(rule) > self._mlen: -+ sys.stderr.write( -+ 'Rule exceeds maximum key length. rule=%(r)s self._mlen=%(m)s\n' -+ %{'r': rule, 'm': self._mlen}) -+ return u'' -+ tabkeys = u'' -+ for (zi, ma) in rule: -+ if zi > 0: -+ zi -= 1 -+ if ma > 0: -+ ma -= 1 -+ tabkey = self.get_goucima(phrase[zi])[ma] -+ if not tabkey: -+ return u'' -+ tabkeys += tabkey -+ if debug_level > 1: -+ sys.stderr.write("parse_phrase() tabkeys=%s\n" %tabkeys) -+ return tabkeys -+ -+ def is_in_system_database(self, tabkeys=u'', phrase=u''): -+ ''' -+ Checks whether “phrase” can be matched in the system database -+ with a key sequence *starting* with “tabkeys”. -+ ''' -+ if debug_level > 1: -+ sys.stderr.write( -+ 'is_in_system_database() tabkeys=%(t)s phrase=%(p)s\n' -+ % {'t': tabkeys, 'p': phrase}) -+ if not tabkeys or not phrase: -+ return False -+ sqlstr = ''' -+ SELECT * FROM main.phrases -+ WHERE tabkeys LIKE :tabkeys AND phrase = :phrase; -+ ''' -+ sqlargs = {'tabkeys': tabkeys+'%%', 'phrase': phrase} -+ results = self.db.execute(sqlstr, sqlargs).fetchall() -+ if debug_level > 1: -+ sys.stderr.write( -+ 'is_in_system_database() tabkeys=%(t)s phrase=%(p)s ' -+ % {'t': tabkeys, 'p': phrase} -+ + 'results=%(r)s\n' -+ % {'r': results}) -+ if results: -+ return True -+ else: -+ return False -+ -+ def user_frequency(self, tabkeys=u'', phrase=u''): -+ if debug_level > 1: -+ sys.stderr.write( -+ 'user_frequency() tabkeys=%(t)s phrase=%(p)s\n' -+ % {'t': tabkeys, 'p': phrase}) -+ if not tabkeys or not phrase: -+ return 0 -+ sqlstr = ''' -+ SELECT sum(user_freq) FROM user_db.phrases -+ WHERE tabkeys = :tabkeys AND phrase = :phrase GROUP BY tabkeys, phrase; -+ ''' -+ sqlargs = {'tabkeys': tabkeys, 'phrase': phrase} -+ result = self.db.execute(sqlstr, sqlargs).fetchall() -+ if debug_level > 1: -+ sys.stderr.write("user_frequency() result=%s\n" %result) -+ if result: -+ return result[0][0] -+ else: -+ return 0 -+ -+ def check_phrase(self, tabkeys=u'', phrase=u''): -+ '''Adjust user_freq in user database if necessary. -+ -+ Also, if the phrase is not in the system database, and it is a -+ Chinese table, and defining user phrases is allowed, add it as -+ a user defined phrase to the user database if it is not yet -+ there. -+ ''' -+ if debug_level > 1: -+ sys.stderr.write( -+ 'check_phrase_internal() tabkey=%(t)s phrase=%(p)s\n' -+ % {'t': tabkeys, 'p': phrase}) -+ if type(phrase) != type(u''): -+ phrase = phrase.decode('utf8') -+ if type(tabkeys) != type(u''): -+ tabkeys = tabkeys.decode('utf8') -+ if not tabkeys or not phrase: -+ return -+ if self._is_chinese and phrase in chinese_nocheck_chars: -+ return -+ if not self.dynamic_adjust: -+ if not self.user_can_define_phrase or not self.is_chinese: -+ return -+ tabkeys = self.parse_phrase(phrase) -+ if not tabkeys: -+ # no tabkeys could be constructed from the rules in the table -+ return -+ if self.is_in_system_database(tabkeys=tabkeys, phrase=phrase): -+ # if it is in the system database, it does not need to -+ # be defined -+ return -+ if self.user_frequency(tabkeys=tabkeys, phrase=phrase) > 0: -+ # if it is in the user database, it has been defined before -+ return -+ # add this user defined phrase to the user database: -+ self.add_phrase( -+ tabkeys=tabkeys, phrase=phrase, freq=-1, user_freq=1, -+ database='user_db') -+ else: -+ if self.is_in_system_database(tabkeys=tabkeys, phrase=phrase): -+ user_freq = self.user_frequency(tabkeys=tabkeys, phrase=phrase) -+ if user_freq > 0: -+ self.update_phrase( -+ tabkeys=tabkeys, phrase=phrase, user_freq=user_freq+1) -+ else: -+ self.add_phrase( -+ tabkeys=tabkeys, phrase=phrase, freq=0, user_freq=1, -+ database='user_db') -+ else: -+ if not self.user_can_define_phrase or not self.is_chinese: -+ return -+ tabkeys = self.parse_phrase(phrase) -+ if not tabkeys: -+ # no tabkeys could be constructed from the rules -+ # in the table -+ return -+ user_freq = self.user_frequency(tabkeys=tabkeys, phrase=phrase) -+ if user_freq > 0: -+ self.update_phrase( -+ tabkeys=tabkeys, phrase=phrase, user_freq=user_freq+1) -+ else: -+ self.add_phrase( -+ tabkeys=tabkeys, phrase=phrase, freq=-1, user_freq=1, -+ database='user_db') -+ -+ def find_zi_code (self, phrase): -+ ''' -+ Return the list of possible tabkeys for a phrase. -+ -+ For example, if “phrase” is “你” and the table is wubi-jidian.86.txt, -+ the result will be ['wq', 'wqi', 'wqiy'] because that table -+ contains the following 3 lines matching that phrase exactly: -+ -+ wq 你 597727619 -+ wqi 你 1490000000 -+ wqiy 你 1490000000 -+ ''' -+ if type(phrase) != type(u''): -+ phrase = phrase.decode('utf8') -+ sqlstr = ''' -+ SELECT tabkeys FROM main.phrases WHERE phrase = :phrase -+ ORDER by length(tabkeys) ASC; -+ ''' -+ sqlargs = {'phrase': phrase} -+ results = self.db.execute(sqlstr, sqlargs).fetchall() -+ list_of_possible_tabkeys = [x[0] for x in results] -+ return list_of_possible_tabkeys -+ -+ def remove_phrase ( -+ self, tabkeys=u'', phrase=u'', database='user_db', commit=True): -+ '''Remove phrase from database -+ ''' -+ if not phrase: -+ return -+ if tabkeys: -+ delete_sqlstr = ''' -+ DELETE FROM %(database)s.phrases -+ WHERE tabkeys = :tabkeys AND phrase = :phrase; -+ ''' % {'database': database} -+ else: -+ delete_sqlstr = ''' -+ DELETE FROM %(database)s.phrases -+ WHERE phrase = :phrase; -+ ''' % {'database': database} -+ delete_sqlargs = {'tabkeys': tabkeys, 'phrase': phrase} -+ self.db.execute(delete_sqlstr, delete_sqlargs) -+ if commit: -+ self.db.commit() -+ self.invalidate_phrases_cache(tabkeys) -+ -+ def extract_user_phrases( -+ self, database_file='', old_database_version='0.0'): -+ '''extract user phrases from database''' -+ sys.stderr.write( -+ 'Trying to recover the phrases from the old, ' -+ + 'incompatible database.\n') -+ try: -+ db = sqlite3.connect(database_file) -+ db.execute('PRAGMA wal_checkpoint;') -+ if old_database_version >= '1.00': -+ phrases = db.execute( -+ ''' -+ SELECT tabkeys, phrase, freq, sum(user_freq) FROM phrases -+ GROUP BY tabkeys, phrase, freq; -+ ''' -+ ).fetchall() -+ db.close() -+ phrases = sorted( -+ phrases, key=lambda x: (x[0], x[1], x[2], x[3])) -+ sys.stderr.write( -+ 'Recovered phrases from the old database: phrases=%s\n' -+ % repr(phrases)) -+ return phrases[:] -+ else: -+ # database is very old, it may still use many columns -+ # of type INTEGER for the tabkeys. Therefore, ignore -+ # the tabkeys in the database and try to get them -+ # from the system database instead. -+ phrases = [] -+ results = db.execute( -+ 'SELECT phrase, sum(user_freq) ' -+ + 'FROM phrases GROUP BY phrase;' -+ ).fetchall() -+ for result in results: -+ sqlstr = ''' -+ SELECT tabkeys FROM main.phrases WHERE phrase = :phrase -+ ORDER BY length(tabkeys) DESC; -+ ''' -+ sqlargs = {'phrase': result[0]} -+ tabkeys_results = self.db.execute( -+ sqlstr, sqlargs).fetchall() -+ if tabkeys_results: -+ phrases.append( -+ (tabkeys_results[0][0], result[0], 0, result[1])) -+ else: -+ # No tabkeys for that phrase could not be -+ # found in the system database. Try to get -+ # tabkeys by calling self.parse_phrase(), that -+ # might return something if the table has -+ # rules to construct user defined phrases: -+ tabkeys = self.parse_phrase(result[0]) -+ if tabkeys: -+ # for user defined phrases, the “freq” column is -1: -+ phrases.append((tabkeys, result[0], -1, result[1])) -+ db.close() -+ phrases = sorted( -+ phrases, key=lambda x: (x[0], x[1], x[2], x[3])) -+ sys.stderr.write( -+ 'Recovered phrases from the very old database: phrases=%s\n' -+ % repr(phrases)) -+ return phrases[:] -+ except: -+ import traceback -+ traceback.print_exc() -+ return [] -diff -Nru ibus-table-1.9.18.orig/setup/main.py ibus-table-1.9.18/setup/main.py ---- ibus-table-1.9.18.orig/setup/main.py 2020-07-22 11:52:11.654532080 +0200 -+++ ibus-table-1.9.18/setup/main.py 2020-07-22 14:43:51.908260925 +0200 -@@ -62,7 +62,7 @@ - "endeffullwidthletter": False, - "endeffullwidthpunct": False, - "alwaysshowlookup": True, -- "lookuptableorientation": True, -+ "lookuptableorientation": 1, # 0 = horizontal, 1 = vertical, 2 = system - "lookuptablepagesize": 6, - "onechar": False, - "autoselect": False, -@@ -224,12 +224,8 @@ - and type(auto_commit) == type(u'') - and auto_commit.lower() in [u'true', u'false']): - OPTION_DEFAULTS['autocommit'] = auto_commit.lower() == u'true' -- orientation = self.tabsqlitedb.ime_properties.get('orientation') -- if (orientation -- and type(orientation) == type(u'') -- and orientation.lower() in [u'true', u'false']): -- OPTION_DEFAULTS['lookuptableorientation'] = ( -- orientation.lower() == u'true') -+ orientation = self.tabsqlitedb.get_orientation() -+ OPTION_DEFAULTS['lookuptableorientation'] = orientation - # if space is a page down key, set the option - # “spacekeybehavior” to “True”: - page_down_keys_csv = self.tabsqlitedb.ime_properties.get( -@@ -258,14 +254,16 @@ - single_wildcard_char = self.tabsqlitedb.ime_properties.get( - 'single_wildcard_char') - if (single_wildcard_char -- and type(single_wildcard_char) == type(u'') -- and len(single_wildcard_char) == 1): -+ and type(single_wildcard_char) == type(u'')): -+ if len(single_wildcard_char) > 1: -+ single_wildcard_char = single_wildcard_char[0] - OPTION_DEFAULTS['singlewildcardchar'] = single_wildcard_char - multi_wildcard_char = self.tabsqlitedb.ime_properties.get( - 'multi_wildcard_char') - if (multi_wildcard_char -- and type(multi_wildcard_char) == type(u'') -- and len(multi_wildcard_char) == 1): -+ and type(multi_wildcard_char) == type(u'')): -+ if len(multi_wildcard_char) > 1: -+ multi_wildcard_char = multi_wildcard_char[0] - OPTION_DEFAULTS['multiwildcardchar'] = multi_wildcard_char - - def __restore_defaults(self): -diff -Nru ibus-table-1.9.18.orig/tests/.gitignore ibus-table-1.9.18/tests/.gitignore ---- ibus-table-1.9.18.orig/tests/.gitignore 1970-01-01 01:00:00.000000000 +0100 -+++ ibus-table-1.9.18/tests/.gitignore 2020-07-22 14:43:51.908260925 +0200 -@@ -0,0 +1,5 @@ -+run_tests -+run_tests.log -+run_tests.trs -+test-suite.log -+__pycache__/ -diff -Nru ibus-table-1.9.18.orig/tests/Makefile.am ibus-table-1.9.18/tests/Makefile.am ---- ibus-table-1.9.18.orig/tests/Makefile.am 1970-01-01 01:00:00.000000000 +0100 -+++ ibus-table-1.9.18/tests/Makefile.am 2020-07-22 14:43:51.908260925 +0200 -@@ -0,0 +1,41 @@ -+# vim:set noet ts=4 -+# -+# ibus-table - The Tables engine for IBus -+# -+# Copyright (c) 2018 Mike FABIAN -+# -+# This library is free software; you can redistribute it and/or -+# modify it under the terms of the GNU Lesser General Public -+# License as published by the Free Software Foundation; either -+# version 2.1 of the License, or (at your option) any later version. -+# -+# This library is distributed in the hope that it will be useful, -+# but WITHOUT ANY WARRANTY; without even the implied warranty of -+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+# Lesser General Public License for more details. -+# -+# You should have received a copy of the GNU Lesser General Public -+# License along with this library; if not, write to the Free Software -+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -+# -+ -+TESTS = run_tests -+ -+run_tests: run_tests.in -+ sed -e 's&@PYTHON_BIN@&$(PYTHON)&g' \ -+ -e 's&@SRCDIR@&$(srcdir)&g' $< > $@ -+ chmod +x $@ -+ -+EXTRA_DIST = \ -+ run_tests.in \ -+ test_it.py \ -+ __init__.py \ -+ $(NULL) -+ -+CLEANFILES = \ -+ run_tests \ -+ $(NULL) -+ -+MAINTAINERCLEANFILES = \ -+ Makefile.in \ -+ $(NULL) -diff -Nru ibus-table-1.9.18.orig/tests/run_tests.in ibus-table-1.9.18/tests/run_tests.in ---- ibus-table-1.9.18.orig/tests/run_tests.in 1970-01-01 01:00:00.000000000 +0100 -+++ ibus-table-1.9.18/tests/run_tests.in 2020-07-22 14:43:51.908260925 +0200 -@@ -0,0 +1,254 @@ -+#!/usr/bin/python3 -+ -+# ibus-table - The Tables engine for IBus -+# -+# Copyright (c) 2018 Mike FABIAN -+# -+# This library is free software; you can redistribute it and/or -+# modify it under the terms of the GNU Lesser General Public -+# License as published by the Free Software Foundation; either -+# version 2.1 of the License, or (at your option) any later version. -+# -+# This library is distributed in the hope that it will be useful, -+# but WITHOUT ANY WARRANTY; without even the implied warranty of -+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+# Lesser General Public License for more details. -+# -+# You should have received a copy of the GNU Lesser General Public -+# License along with this library; if not, write to the Free Software -+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -+# -+ -+import os -+import sys -+import unittest -+ -+from gi import require_version -+require_version('IBus', '1.0') -+from gi.repository import IBus -+ -+# -- Define some mock classes for the tests ---------------------------------- -+class MockEngine: -+ def __init__(self, engine_name = '', connection = None, object_path = ''): -+ self.mock_auxiliary_text = '' -+ self.mock_preedit_text = '' -+ self.mock_preedit_text_cursor_pos = 0 -+ self.mock_preedit_text_visible = True -+ self.mock_committed_text = '' -+ self.mock_committed_text_cursor_pos = 0 -+ self.client_capabilities = ( -+ IBus.Capabilite.PREEDIT_TEXT -+ | IBus.Capabilite.AUXILIARY_TEXT -+ | IBus.Capabilite.LOOKUP_TABLE -+ | IBus.Capabilite.FOCUS -+ | IBus.Capabilite.PROPERTY) -+ # There are lots of weird problems with surrounding text -+ # which makes this hard to test. Therefore this mock -+ # engine does not try to support surrounding text, i.e. -+ # we omit “| IBus.Capabilite.SURROUNDING_TEXT” here. -+ -+ def update_auxiliary_text(self, text, visible): -+ self.mock_auxiliary_text = text.text -+ -+ def hide_auxiliary_text(self): -+ pass -+ -+ def commit_text(self, text): -+ self.mock_committed_text = ( -+ self.mock_committed_text[ -+ :self.mock_committed_text_cursor_pos] -+ + text.text -+ + self.mock_committed_text[ -+ self.mock_committed_text_cursor_pos:]) -+ self.mock_committed_text_cursor_pos += len(text.text) -+ -+ def forward_key_event(self, val, code, state): -+ if (val == IBus.KEY_Left -+ and self.mock_committed_text_cursor_pos > 0): -+ self.mock_committed_text_cursor_pos -= 1 -+ return -+ unicode = IBus.keyval_to_unicode(val) -+ if unicode: -+ self.mock_committed_text = ( -+ self.mock_committed_text[ -+ :self.mock_committed_text_cursor_pos] -+ + unicode -+ + self.mock_committed_text[ -+ self.mock_committed_text_cursor_pos:]) -+ self.mock_committed_text_cursor_pos += len(unicode) -+ -+ def update_lookup_table(self, table, visible): -+ pass -+ -+ def update_preedit_text(self, text, cursor_pos, visible): -+ self.mock_preedit_text = text.get_text() -+ self.mock_preedit_text_cursor_pos = cursor_pos -+ self.mock_preedit_text_visible = visible -+ -+ def register_properties(self, property_list): -+ pass -+ -+ def update_property(self, property): -+ pass -+ -+ def hide_lookup_table(self): -+ pass -+ -+class MockLookupTable: -+ def __init__(self, page_size = 9, cursor_pos = 0, cursor_visible = False, round = True): -+ self.clear() -+ self.mock_page_size = page_size -+ self.mock_cursor_pos = cursor_pos -+ self.mock_cursor_visible = cursor_visible -+ self.cursor_visible = cursor_visible -+ self.mock_round = round -+ self.mock_candidates = [] -+ self.mock_labels = [] -+ self.mock_page_number = 0 -+ -+ def clear(self): -+ self.mock_candidates = [] -+ self.mock_cursor_pos = 0 -+ -+ def set_page_size(self, size): -+ self.mock_page_size = size -+ -+ def get_page_size(self): -+ return self.mock_page_size -+ -+ def set_round(self, round): -+ self.mock_round = round -+ -+ def set_cursor_pos(self, pos): -+ self.mock_cursor_pos = pos -+ -+ def get_cursor_pos(self): -+ return self.mock_cursor_pos -+ -+ def get_cursor_in_page(self): -+ return (self.mock_cursor_pos -+ - self.mock_page_size * self.mock_page_number) -+ -+ def set_cursor_visible(self, visible): -+ self.mock_cursor_visible = visible -+ self.cursor_visible = visible -+ -+ def cursor_down(self): -+ if len(self.mock_candidates): -+ self.mock_cursor_pos += 1 -+ self.mock_cursor_pos %= len(self.mock_candidates) -+ -+ def cursor_up(self): -+ if len(self.mock_candidates): -+ if self.mock_cursor_pos > 0: -+ self.mock_cursor_pos -= 1 -+ else: -+ self.mock_cursor_pos = len(self.mock_candidates) - 1 -+ -+ def page_down(self): -+ if len(self.mock_candidates): -+ self.mock_page_number += 1 -+ self.mock_cursor_pos += self.mock_page_size -+ -+ def page_up(self): -+ if len(self.mock_candidates): -+ if self.mock_page_number > 0: -+ self.mock_page_number -= 1 -+ self.mock_cursor_pos -= self.mock_page_size -+ -+ def set_orientation(self, orientation): -+ self.mock_orientation = orientation -+ -+ def get_number_of_candidates(self): -+ return len(self.mock_candidates) -+ -+ def append_candidate(self, candidate): -+ self.mock_candidates.append(candidate.get_text()) -+ -+ def get_candidate(self, index): -+ return self.mock_candidates[index] -+ -+ def get_number_of_candidates(self): -+ return len(self.mock_candidates) -+ -+ def append_label(self, label): -+ self.mock_labels.append(label.get_text()) -+ -+class MockPropList: -+ def __init__(self, *args, **kwargs): -+ self._mock_proplist = [] -+ -+ def append(self, property): -+ self._mock_proplist.append(property) -+ -+ def get(self, index): -+ if index >= 0 and index < len(self._mock_proplist): -+ return self._mock_proplist[index] -+ else: -+ return None -+ -+ def update_property(self, property): -+ pass -+ -+class MockProperty: -+ def __init__(self, -+ key='', -+ prop_type=IBus.PropType.RADIO, -+ label=IBus.Text.new_from_string(''), -+ symbol=IBus.Text.new_from_string(''), -+ icon='', -+ tooltip=IBus.Text.new_from_string(''), -+ sensitive=True, -+ visible=True, -+ state=IBus.PropState.UNCHECKED, -+ sub_props=None): -+ self.mock_property_key = key -+ self.mock_property_prop_type = prop_type -+ self.mock_property_label = label.get_text() -+ self.mock_property_symbol = symbol.get_text() -+ self.mock_property_icon = icon -+ self.mock_property_tooltip = tooltip.get_text() -+ self.mock_property_sensitive = sensitive -+ self.mock_property_visible = visible -+ self.mock_property_state = state -+ self.mock_property_sub_props = sub_props -+ -+ def set_label(self, ibus_text): -+ self.mock_property_label = ibus_text.get_text() -+ -+ def set_symbol(self, ibus_text): -+ self.mock_property_symbol = ibus_text.get_text() -+ -+ def set_tooltip(self, ibus_text): -+ self.mock_property_tooltip = ibus_text.get_text() -+ -+ def set_sensitive(self, sensitive): -+ self.mock_property_sensitive = sensitive -+ -+ def set_visible(self, visible): -+ self.mock_property_visible = visible -+ -+ def set_state(self, state): -+ self.mock_property_state = state -+ -+ def set_sub_props(self, proplist): -+ self.mock_property_sub_props = proplist -+ -+ def get_key(self): -+ return self.mock_property_key -+ -+# -- Monkey patch the environment with the mock classes ---------------------- -+sys.modules["gi.repository.IBus"].Engine = MockEngine -+sys.modules["gi.repository.IBus"].LookupTable = MockLookupTable -+sys.modules["gi.repository.IBus"].Property = MockProperty -+sys.modules["gi.repository.IBus"].PropList = MockPropList -+ -+# -- Load and run our unit tests --------------------------------------------- -+os.environ['IBUS_TABLE_DEBUG_LEVEL'] = '255' -+loader = unittest.TestLoader() -+suite = loader.discover(".") -+runner = unittest.TextTestRunner(stream = sys.stderr, verbosity = 255) -+result = runner.run(suite) -+ -+if result.failures or result.errors: -+ sys.exit(1) -diff -Nru ibus-table-1.9.18.orig/tests/test_it.py ibus-table-1.9.18/tests/test_it.py ---- ibus-table-1.9.18.orig/tests/test_it.py 1970-01-01 01:00:00.000000000 +0100 -+++ ibus-table-1.9.18/tests/test_it.py 2020-07-22 14:43:51.908260925 +0200 -@@ -0,0 +1,403 @@ -+# -*- coding: utf-8 -*- -+# vim:et sts=4 sw=4 -+# -+# ibus-table - The Tables engine for IBus -+# -+# Copyright (c) 2018 Mike FABIAN -+# -+# This library is free software; you can redistribute it and/or -+# modify it under the terms of the GNU Lesser General Public -+# License as published by the Free Software Foundation; either -+# version 2.1 of the License, or (at your option) any later version. -+# -+# This library is distributed in the hope that it will be useful, -+# but WITHOUT ANY WARRANTY; without even the implied warranty of -+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+# Lesser General Public License for more details. -+# -+# You should have received a copy of the GNU Lesser General Public -+# License along with this library; if not, write to the Free Software -+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -+# -+ -+''' -+This file implements the test cases for the unit tests of ibus-table -+''' -+ -+import sys -+import os -+import unicodedata -+import unittest -+import subprocess -+ -+from gi import require_version -+require_version('IBus', '1.0') -+from gi.repository import IBus -+ -+sys.path.insert(0, "../engine") -+from table import * -+import tabsqlitedb -+import it_util -+#sys.path.pop(0) -+ -+ENGINE = None -+TABSQLITEDB = None -+ORIG_INPUT_MODE = None -+ORIG_CHINESE_MODE = None -+ORIG_LETTER_WIDTH = None -+ORIG_PUNCTUATION_WIDTH = None -+ORIG_ALWAYS_SHOW_LOOKUP = None -+ORIG_LOOKUP_TABLE_ORIENTATION = None -+ORIG_PAGE_SIZE = None -+ORIG_ONECHAR_MODE = None -+ORIG_AUTOSELECT_MODE = None -+ORIG_AUTOCOMMIT_MODE = None -+ORIG_SPACE_KEY_BEHAVIOR_MODE = None -+ORIG_AUTOWILDCARD_MODE = None -+ORIG_SINGLE_WILDCARD_CHAR = None -+ORIG_MULTI_WILDCARD_CHAR = None -+ -+def backup_original_settings(): -+ global ENGINE -+ global ORIG_INPUT_MODE -+ global ORIG_CHINESE_MODE -+ global ORIG_LETTER_WIDTH -+ global ORIG_PUNCTUATION_WIDTH -+ global ORIG_ALWAYS_SHOW_LOOKUP -+ global ORIG_LOOKUP_TABLE_ORIENTATION -+ global ORIG_PAGE_SIZE -+ global ORIG_ONECHAR_MODE -+ global ORIG_AUTOSELECT_MODE -+ global ORIG_AUTOCOMMIT_MODE -+ global ORIG_SPACE_KEY_BEHAVIOR_MODE -+ global ORIG_AUTOWILDCARD_MODE -+ global ORIG_SINGLE_WILDCARD_CHAR -+ global ORIG_MULTI_WILDCARD_CHAR -+ ORIG_INPUT_MODE = ENGINE.get_input_mode() -+ ORIG_CHINESE_MODE = ENGINE.get_chinese_mode() -+ ORIG_LETTER_WIDTH = ENGINE.get_letter_width() -+ ORIG_PUNCTUATION_WIDTH = ENGINE.get_punctuation_width() -+ ORIG_ALWAYS_SHOW_LOOKUP = ENGINE.get_always_show_lookup() -+ ORIG_LOOKUP_TABLE_ORIENTATION = ENGINE.get_lookup_table_orientation() -+ ORIG_PAGE_SIZE = ENGINE.get_page_size() -+ ORIG_ONECHAR_MODE = ENGINE.get_onechar_mode() -+ ORIG_AUTOSELECT_MODE = ENGINE.get_autoselect_mode() -+ ORIG_AUTOCOMMIT_MODE = ENGINE.get_autocommit_mode() -+ ORIG_SPACE_KEY_BEHAVIOR_MODE = ENGINE.get_space_key_behavior_mode() -+ ORIG_AUTOWILDCARD_MODE = ENGINE.get_autowildcard_mode() -+ ORIG_SINGLE_WILDCARD_CHAR = ENGINE.get_single_wildcard_char() -+ ORIG_MULTI_WILDCARD_CHAR = ENGINE.get_multi_wildcard_char() -+ -+def restore_original_settings(): -+ global ENGINE -+ global ORIG_INPUT_MODE -+ global ORIG_CHINESE_MODE -+ global ORIG_LETTER_WIDTH -+ global ORIG_PUNCTUATION_WIDTH -+ global ORIG_ALWAYS_SHOW_LOOKUP -+ global ORIG_LOOKUP_TABLE_ORIENTATION -+ global ORIG_PAGE_SIZE -+ global ORIG_ONECHAR_MODE -+ global ORIG_AUTOSELECT_MODE -+ global ORIG_AUTOCOMMIT_MODE -+ global ORIG_SPACE_KEY_BEHAVIOR_MODE -+ global ORIG_AUTOWILDCARD_MODE -+ global ORIG_SINGLE_WILDCARD_CHAR -+ global ORIG_MULTI_WILDCARD_CHAR -+ ENGINE.set_input_mode(ORIG_INPUT_MODE) -+ ENGINE.set_chinese_mode(ORIG_CHINESE_MODE) -+ ENGINE.set_letter_width(ORIG_LETTER_WIDTH[0], input_mode=0) -+ ENGINE.set_letter_width(ORIG_LETTER_WIDTH[1], input_mode=1) -+ ENGINE.set_punctuation_width(ORIG_PUNCTUATION_WIDTH[0], input_mode=0) -+ ENGINE.set_punctuation_width(ORIG_PUNCTUATION_WIDTH[1], input_mode=1) -+ ENGINE.set_always_show_lookup(ORIG_ALWAYS_SHOW_LOOKUP) -+ ENGINE.set_lookup_table_orientation(ORIG_LOOKUP_TABLE_ORIENTATION) -+ ENGINE.set_page_size(ORIG_PAGE_SIZE) -+ ENGINE.set_onechar_mode(ORIG_ONECHAR_MODE) -+ ENGINE.set_autoselect_mode(ORIG_AUTOSELECT_MODE) -+ ENGINE.set_autocommit_mode(ORIG_AUTOCOMMIT_MODE) -+ ENGINE.set_space_key_behavior_mode(ORIG_SPACE_KEY_BEHAVIOR_MODE) -+ ENGINE.set_autowildcard_mode(ORIG_AUTOWILDCARD_MODE) -+ ENGINE.set_single_wildcard_char(ORIG_SINGLE_WILDCARD_CHAR) -+ ENGINE.set_multi_wildcard_char(ORIG_MULTI_WILDCARD_CHAR) -+ -+def set_default_settings(): -+ global ENGINE -+ global TABSQLITEDB -+ ENGINE.set_input_mode(mode=1) -+ chinese_mode = 0 -+ language_filter = TABSQLITEDB.ime_properties.get('language_filter') -+ if language_filter in ['cm0', 'cm1', 'cm2', 'cm3', 'cm4']: -+ chinese_mode = int(language_filter[-1]) -+ ENGINE.set_chinese_mode(mode=chinese_mode) -+ -+ letter_width_mode = False -+ def_full_width_letter = TABSQLITEDB.ime_properties.get( -+ 'def_full_width_letter') -+ if def_full_width_letter: -+ letter_width_mode = (def_full_width_letter.lower() == u'true') -+ ENGINE.set_letter_width(mode=letter_width_mode, input_mode=0) -+ ENGINE.set_letter_width(mode=letter_width_mode, input_mode=1) -+ -+ punctuation_width_mode = False -+ def_full_width_punct = TABSQLITEDB.ime_properties.get( -+ 'def_full_width_punct') -+ if def_full_width_punct: -+ punctuation_width_mode = (def_full_width_punct.lower() == u'true') -+ ENGINE.set_punctuation_width(mode=punctuation_width_mode, input_mode=0) -+ ENGINE.set_punctuation_width(mode=punctuation_width_mode, input_mode=1) -+ -+ always_show_lookup_mode = True -+ always_show_lookup = TABSQLITEDB.ime_properties.get( -+ 'always_show_lookup') -+ if always_show_lookup: -+ always_show_lookup_mode = (always_show_lookup.lower() == u'true') -+ ENGINE.set_always_show_lookup(always_show_lookup_mode) -+ -+ orientation = TABSQLITEDB.get_orientation() -+ ENGINE.set_lookup_table_orientation(orientation) -+ -+ page_size = 6 -+ select_keys_csv = TABSQLITEDB.ime_properties.get('select_keys') -+ # select_keys_csv is something like: "1,2,3,4,5,6,7,8,9,0" -+ if select_keys_csv: -+ ENGINE.set_page_size(len(select_keys_csv.split(","))) -+ -+ onechar = False -+ ENGINE.set_onechar_mode(onechar) -+ -+ auto_select_mode = False -+ auto_select = TABSQLITEDB.ime_properties.get('auto_select') -+ if auto_select: -+ auto_select_mode = (auto_select.lower() == u'true') -+ ENGINE.set_autoselect_mode(auto_select_mode) -+ -+ auto_commit_mode = False -+ auto_commit = TABSQLITEDB.ime_properties.get('auto_commit') -+ if auto_commit: -+ auto_commit_mode = (auto_commit.lower() == u'true') -+ ENGINE.set_autocommit_mode(auto_commit_mode) -+ -+ space_key_behavior_mode = False -+ # if space is a page down key, set the option -+ # “spacekeybehavior” to “True”: -+ page_down_keys_csv = TABSQLITEDB.ime_properties.get( -+ 'page_down_keys') -+ if page_down_keys_csv: -+ page_down_keys = [ -+ IBus.keyval_from_name(x) -+ for x in page_down_keys_csv.split(',')] -+ if IBus.KEY_space in page_down_keys: -+ space_key_behavior_mode = True -+ # if space is a commit key, set the option -+ # “spacekeybehavior” to “False” (overrides if space is -+ # also a page down key): -+ commit_keys_csv = TABSQLITEDB.ime_properties.get('commit_keys') -+ if commit_keys_csv: -+ commit_keys = [ -+ IBus.keyval_from_name(x) -+ for x in commit_keys_csv.split(',')] -+ if IBus.KEY_space in commit_keys: -+ space_key_behavior_mode = False -+ ENGINE.set_space_key_behavior_mode(space_key_behavior_mode) -+ -+ auto_wildcard_mode = True -+ auto_wildcard = TABSQLITEDB.ime_properties.get('auto_wildcard') -+ if auto_wildcard: -+ auto_wildcard_mode = (auto_wildcard.lower() == u'true') -+ ENGINE.set_autowildcard_mode(auto_wildcard_mode) -+ -+ single_wildcard_char = TABSQLITEDB.ime_properties.get( -+ 'single_wildcard_char') -+ if not single_wildcard_char: -+ single_wildcard_char = u'' -+ if len(single_wildcard_char) > 1: -+ single_wildcard_char = single_wildcard_char[0] -+ ENGINE.set_single_wildcard_char(single_wildcard_char) -+ -+ multi_wildcard_char = TABSQLITEDB.ime_properties.get( -+ 'multi_wildcard_char') -+ if not multi_wildcard_char: -+ multi_wildcard_char = u'' -+ if len(multi_wildcard_char) > 1: -+ multi_wildcard_char = multi_wildcard_char[0] -+ ENGINE.set_multi_wildcard_char(multi_wildcard_char) -+ -+def set_up(engine_name): -+ global TABSQLITEDB -+ global ENGINE -+ bus = IBus.Bus() -+ db_dir = '/usr/share/ibus-table/tables' -+ db_file = os.path.join(db_dir, engine_name + '.db') -+ TABSQLITEDB = tabsqlitedb.tabsqlitedb( -+ filename=db_file, user_db=':memory:') -+ ENGINE = tabengine( -+ bus, -+ '/com/redhat/IBus/engines/table/%s/engine/0' %engine_name, -+ TABSQLITEDB, -+ unit_test = True) -+ backup_original_settings() -+ set_default_settings() -+ -+def tear_down(): -+ restore_original_settings() -+ -+class Wubi_Jidian86TestCase(unittest.TestCase): -+ def setUp(self): -+ set_up('wubi-jidian86') -+ -+ def tearDown(self): -+ tear_down() -+ -+ def test_dummy(self): -+ self.assertEqual(True, True) -+ -+ def test_single_char_commit_with_space(self): -+ ENGINE.do_process_key_event(IBus.KEY_a, 0, 0) -+ ENGINE.do_process_key_event(IBus.KEY_space, 0, 0) -+ self.assertEqual(ENGINE.mock_committed_text, '工') -+ -+ def test_commit_to_preedit_and_switching_to_pinyin_and_defining_a_phrase(self): -+ ENGINE.do_process_key_event(IBus.KEY_a, 0, 0) -+ # commit to preëdit needs a press and release of either -+ # the left or the right shift key: -+ ENGINE.do_process_key_event( -+ IBus.KEY_Shift_L, 0, -+ IBus.ModifierType.SHIFT_MASK) -+ ENGINE.do_process_key_event( -+ IBus.KEY_Shift_L, 0, -+ IBus.ModifierType.SHIFT_MASK | IBus.ModifierType.RELEASE_MASK) -+ self.assertEqual(ENGINE.mock_preedit_text, '工') -+ ENGINE.do_process_key_event(IBus.KEY_b, 0, 0) -+ ENGINE.do_process_key_event( -+ IBus.KEY_Shift_R, 0, -+ IBus.ModifierType.SHIFT_MASK) -+ ENGINE.do_process_key_event( -+ IBus.KEY_Shift_R, 0, -+ IBus.ModifierType.SHIFT_MASK | IBus.ModifierType.RELEASE_MASK) -+ self.assertEqual(ENGINE.mock_preedit_text, '工了') -+ ENGINE.do_process_key_event(IBus.KEY_c, 0, 0) -+ ENGINE.do_process_key_event( -+ IBus.KEY_Shift_R, 0, -+ IBus.ModifierType.SHIFT_MASK) -+ ENGINE.do_process_key_event( -+ IBus.KEY_Shift_R, 0, -+ IBus.ModifierType.SHIFT_MASK | IBus.ModifierType.RELEASE_MASK) -+ self.assertEqual(ENGINE.mock_preedit_text, '工了以') -+ ENGINE.do_process_key_event(IBus.KEY_d, 0, 0) -+ ENGINE.do_process_key_event( -+ IBus.KEY_Shift_L, 0, -+ IBus.ModifierType.SHIFT_MASK) -+ ENGINE.do_process_key_event( -+ IBus.KEY_Shift_L, 0, -+ IBus.ModifierType.SHIFT_MASK | IBus.ModifierType.RELEASE_MASK) -+ self.assertEqual(ENGINE.mock_preedit_text, '工了以在') -+ # Move left two characters in the preëdit: -+ ENGINE.do_process_key_event(IBus.KEY_Left, 0, 0) -+ ENGINE.do_process_key_event(IBus.KEY_Left, 0, 0) -+ # Switch to pinyin mode by pressing and releasing the right -+ # shift key: -+ ENGINE.do_process_key_event( -+ IBus.KEY_Shift_R, 0, -+ IBus.ModifierType.SHIFT_MASK) -+ ENGINE.do_process_key_event( -+ IBus.KEY_Shift_R, 0, -+ IBus.ModifierType.SHIFT_MASK | IBus.ModifierType.RELEASE_MASK) -+ ENGINE.do_process_key_event(IBus.KEY_n, 0, 0) -+ ENGINE.do_process_key_event(IBus.KEY_i, 0, 0) -+ ENGINE.do_process_key_event(IBus.KEY_numbersign, 0, 0) -+ ENGINE.do_process_key_event( -+ IBus.KEY_Shift_L, 0, -+ IBus.ModifierType.SHIFT_MASK) -+ ENGINE.do_process_key_event( -+ IBus.KEY_Shift_L, 0, -+ IBus.ModifierType.SHIFT_MASK | IBus.ModifierType.RELEASE_MASK) -+ self.assertEqual(ENGINE.mock_preedit_text, '工了你以在') -+ ENGINE.do_process_key_event(IBus.KEY_h, 0, 0) -+ ENGINE.do_process_key_event(IBus.KEY_a, 0, 0) -+ ENGINE.do_process_key_event(IBus.KEY_o, 0, 0) -+ ENGINE.do_process_key_event(IBus.KEY_numbersign, 0, 0) -+ ENGINE.do_process_key_event( -+ IBus.KEY_Shift_L, 0, -+ IBus.ModifierType.SHIFT_MASK) -+ ENGINE.do_process_key_event( -+ IBus.KEY_Shift_L, 0, -+ IBus.ModifierType.SHIFT_MASK | IBus.ModifierType.RELEASE_MASK) -+ self.assertEqual(ENGINE.mock_preedit_text, '工了你好以在') -+ # Move right two characters in the preëdit (triggers a commit to preëdit): -+ ENGINE.do_process_key_event(IBus.KEY_Right, 0, 0) -+ ENGINE.do_process_key_event(IBus.KEY_Right, 0, 0) -+ self.assertEqual(ENGINE.mock_auxiliary_text, 'd dhf dhfd\t#: abwd') -+ # commit the preëdit: -+ ENGINE.do_process_key_event(IBus.KEY_space, 0, 0) -+ self.assertEqual(ENGINE.mock_committed_text, '工了你好以在') -+ # Switch out of pinyin mode: -+ ENGINE.do_process_key_event( -+ IBus.KEY_Shift_R, 0, -+ IBus.ModifierType.SHIFT_MASK) -+ ENGINE.do_process_key_event( -+ IBus.KEY_Shift_R, 0, -+ IBus.ModifierType.SHIFT_MASK | IBus.ModifierType.RELEASE_MASK) -+ # “abwd” shown on the right of the auxiliary text above shows the -+ # newly defined shortcut for this phrase. Let’s try to type -+ # the same phrase again using the new shortcut: -+ ENGINE.do_process_key_event(IBus.KEY_a, 0, 0) -+ self.assertEqual(ENGINE.mock_preedit_text, '工') -+ ENGINE.do_process_key_event(IBus.KEY_b, 0, 0) -+ self.assertEqual(ENGINE.mock_preedit_text, '节') -+ ENGINE.do_process_key_event(IBus.KEY_w, 0, 0) -+ self.assertEqual(ENGINE.mock_preedit_text, '工了你好以在') -+ ENGINE.do_process_key_event(IBus.KEY_d, 0, 0) -+ self.assertEqual(ENGINE.mock_preedit_text, '工了你好以在') -+ # commit the preëdit: -+ ENGINE.do_process_key_event(IBus.KEY_space, 0, 0) -+ self.assertEqual(ENGINE.mock_committed_text, '工了你好以在工了你好以在') -+ -+class Stroke5TestCase(unittest.TestCase): -+ def setUp(self): -+ set_up('stroke5') -+ -+ def tearDown(self): -+ tear_down() -+ -+ def test_dummy(self): -+ self.assertEqual(True, True) -+ -+ def test_single_char_commit_with_space(self): -+ ENGINE.do_process_key_event(IBus.KEY_comma, 0, 0) -+ ENGINE.do_process_key_event(IBus.KEY_slash, 0, 0) -+ ENGINE.do_process_key_event(IBus.KEY_n, 0, 0) -+ ENGINE.do_process_key_event(IBus.KEY_m, 0, 0) -+ ENGINE.do_process_key_event(IBus.KEY_m, 0, 0) -+ ENGINE.do_process_key_event(IBus.KEY_space, 0, 0) -+ self.assertEqual(ENGINE.mock_committed_text, '的') -+ -+class TranslitTestCase(unittest.TestCase): -+ def setUp(self): -+ set_up('translit') -+ -+ def tearDown(self): -+ tear_down() -+ -+ def test_dummy(self): -+ self.assertEqual(True, True) -+ -+ def test_sh_multiple_match(self): -+ ENGINE.do_process_key_event(IBus.KEY_s, 0, 0) -+ self.assertEqual(ENGINE.mock_preedit_text, 'с') -+ ENGINE.do_process_key_event(IBus.KEY_h, 0, 0) -+ self.assertEqual(ENGINE.mock_preedit_text, 'ш') -+ ENGINE.do_process_key_event(IBus.KEY_s, 0, 0) -+ self.assertEqual(ENGINE.mock_committed_text, 'ш') -+ self.assertEqual(ENGINE.mock_preedit_text, 'с') -+ ENGINE.do_process_key_event(IBus.KEY_h, 0, 0) -+ self.assertEqual(ENGINE.mock_committed_text, 'ш') -+ self.assertEqual(ENGINE.mock_preedit_text, 'ш') -+ ENGINE.do_process_key_event(IBus.KEY_h, 0, 0) -+ self.assertEqual(ENGINE.mock_committed_text, 'шщ') -+ self.assertEqual(ENGINE.mock_preedit_text, '') -+ ENGINE.do_process_key_event(IBus.KEY_s, 0, 0) -+ self.assertEqual(ENGINE.mock_committed_text, 'шщ') -+ self.assertEqual(ENGINE.mock_preedit_text, 'с') -+ ENGINE.do_process_key_event(IBus.KEY_space, 0, 0) -+ self.assertEqual(ENGINE.mock_committed_text, 'шщс ') diff --git a/SOURCES/fix-test-cases-for-old-ibus-table.patch b/SOURCES/fix-test-cases-for-old-ibus-table.patch deleted file mode 100644 index b025655..0000000 --- a/SOURCES/fix-test-cases-for-old-ibus-table.patch +++ /dev/null @@ -1,166 +0,0 @@ -diff -Nru ibus-table-1.9.18/engine/table.py ibus-table-1.9.18.new/engine/table.py ---- ibus-table-1.9.18/engine/table.py 2020-07-23 17:21:31.934904346 +0200 -+++ ibus-table-1.9.18.new/engine/table.py 2020-07-23 17:28:19.342746241 +0200 -@@ -1752,6 +1752,9 @@ - self._input_mode) - self._update_ui() - -+ def get_pinyin_mode(self): -+ return self._editor._py_mode -+ - def set_onechar_mode(self, mode=False, update_dconf=True): - if mode == self._editor._onechar: - return -diff -Nru ibus-table-1.9.18/tests/test_0_gtk.py ibus-table-1.9.18.new/tests/test_0_gtk.py ---- ibus-table-1.9.18/tests/test_0_gtk.py 2020-07-23 17:21:31.940904285 +0200 -+++ ibus-table-1.9.18.new/tests/test_0_gtk.py 2020-07-23 17:37:23.234210681 +0200 -@@ -205,14 +205,14 @@ - object_path = '%s/%d' % (self.ENGINE_PATH, self.__id) - db_dir = '/usr/share/ibus-table/tables' - db_file = os.path.join(db_dir, engine_name + '.db') -- database = tabsqlitedb.TabSqliteDb(filename=db_file, user_db=':memory:') -- self.__engine = table.TabEngine( -+ database = tabsqlitedb.tabsqlitedb(filename=db_file, user_db=':memory:') -+ self.__engine = table.tabengine( - self.__bus, - object_path, - database) - self.__engine.connect('focus-in', self.__engine_focus_in) - self.__engine.connect('focus-out', self.__engine_focus_out) -- # FIXME: Need to connect 'reset' after TabEngine.clear_all_input_and_preedit() -+ # FIXME: Need to connect 'reset' after tabengine.clear_all_input_and_preedit() - # is called. - self.__engine.connect_after('reset', self.__engine_reset) - self.__bus.get_connection().signal_subscribe( -diff -Nru ibus-table-1.9.18/tests/test_it.py ibus-table-1.9.18.new/tests/test_it.py ---- ibus-table-1.9.18/tests/test_it.py 2020-07-23 17:21:31.940904285 +0200 -+++ ibus-table-1.9.18.new/tests/test_it.py 2020-07-23 17:29:52.032800325 +0200 -@@ -78,7 +78,6 @@ - ORIG_SINGLE_WILDCARD_CHAR = None - ORIG_MULTI_WILDCARD_CHAR = None - ORIG_PINYIN_MODE = None --ORIG_SUGGESTION_MODE = None - - def backup_original_settings(): - global ENGINE -@@ -97,7 +96,6 @@ - global ORIG_SINGLE_WILDCARD_CHAR - global ORIG_MULTI_WILDCARD_CHAR - global ORIG_PINYIN_MODE -- global ORIG_SUGGESTION_MODE - ORIG_INPUT_MODE = ENGINE.get_input_mode() - ORIG_CHINESE_MODE = ENGINE.get_chinese_mode() - ORIG_LETTER_WIDTH = ENGINE.get_letter_width() -@@ -113,7 +111,6 @@ - ORIG_SINGLE_WILDCARD_CHAR = ENGINE.get_single_wildcard_char() - ORIG_MULTI_WILDCARD_CHAR = ENGINE.get_multi_wildcard_char() - ORIG_PINYIN_MODE = ENGINE.get_pinyin_mode() -- ORIG_SUGGESTION_MODE = ENGINE.get_suggestion_mode() - - def restore_original_settings(): - global ENGINE -@@ -132,7 +129,6 @@ - global ORIG_SINGLE_WILDCARD_CHAR - global ORIG_MULTI_WILDCARD_CHAR - global ORIG_PINYIN_MODE -- global ORIG_SUGGESTION_MODE - ENGINE.set_input_mode(ORIG_INPUT_MODE) - ENGINE.set_chinese_mode(ORIG_CHINESE_MODE) - ENGINE.set_letter_width(ORIG_LETTER_WIDTH[0], input_mode=0) -@@ -150,7 +146,6 @@ - ENGINE.set_single_wildcard_char(ORIG_SINGLE_WILDCARD_CHAR) - ENGINE.set_multi_wildcard_char(ORIG_MULTI_WILDCARD_CHAR) - ENGINE.set_pinyin_mode(ORIG_PINYIN_MODE) -- ENGINE.set_suggestion_mode(ORIG_SUGGESTION_MODE) - - def set_default_settings(): - global ENGINE -@@ -256,7 +251,6 @@ - ENGINE.set_multi_wildcard_char(multi_wildcard_char) - - ENGINE.set_pinyin_mode(False) -- ENGINE.set_suggestion_mode(False) - - def set_up(engine_name): - ''' -@@ -290,7 +284,7 @@ - assert IBus.PropList is not IBUS_PROP_LIST - assert IBus.PropList is MockPropList - # Reload the table module so that the patches -- # are applied to TabEngine: -+ # are applied to tabengine: - sys.path.insert(0, '../engine') - importlib.reload(table) - sys.path.pop(0) -@@ -302,9 +296,9 @@ - ENGINE = None - tear_down() - return False -- TABSQLITEDB = tabsqlitedb.TabSqliteDb( -+ TABSQLITEDB = tabsqlitedb.tabsqlitedb( - filename=db_file, user_db=':memory:') -- ENGINE = table.TabEngine( -+ ENGINE = table.tabengine( - bus, - '/com/redhat/IBus/engines/table/%s/engine/0' %engine_name, - TABSQLITEDB, -@@ -380,59 +374,6 @@ - self.assertEqual(ENGINE.mock_preedit_text, '') - self.assertEqual(ENGINE.mock_committed_text, '工爱工') - -- def test_suggestion_mode(self): -- if not ENGINE._ime_sg: -- self.skipTest("This engine does not have a suggestion mode.") -- # Suggestion mode is False by default: -- self.assertEqual(ENGINE.get_suggestion_mode(), False) -- self.assertEqual(ENGINE.get_pinyin_mode(), False) -- ENGINE.do_process_key_event(IBus.KEY_a, 0, 0) -- self.assertEqual(ENGINE.mock_preedit_text, '工') -- ENGINE.do_process_key_event(IBus.KEY_space, 0, 0) -- self.assertEqual(ENGINE.mock_preedit_text, '') -- self.assertEqual(ENGINE.mock_committed_text, '工') -- self.assertEqual(ENGINE._editor._lookup_table.mock_candidates, []) -- ENGINE.set_suggestion_mode(True) -- ENGINE.do_process_key_event(IBus.KEY_a, 0, 0) -- self.assertEqual(ENGINE.mock_preedit_text, '工') -- ENGINE.do_process_key_event(IBus.KEY_space, 0, 0) -- self.assertEqual(ENGINE.mock_preedit_text, '') -- self.assertEqual(ENGINE.mock_committed_text, '工工') -- self.assertEqual(ENGINE._editor._lookup_table.mock_candidates, -- ['工作人员 673 0', -- '工作会议 310 0', -- '工作报告 267 0', -- '工人阶级 146 0', -- '工作重点 78 0', -- '工作小组 73 0', -- '工业企业 71 0', -- '工业大学 69 0', -- '工作单位 61 0', -- '工业生产 58 0']) -- ENGINE.do_process_key_event(IBus.KEY_space, 0, 0) -- self.assertEqual(ENGINE.mock_preedit_text, '') -- self.assertEqual(ENGINE.mock_committed_text, '工工作人员') -- ENGINE.set_pinyin_mode(True) -- ENGINE.do_process_key_event(IBus.KEY_a, 0, 0) -- self.assertEqual(ENGINE.mock_preedit_text, '爱') -- ENGINE.do_process_key_event(IBus.KEY_space, 0, 0) -- self.assertEqual(ENGINE.mock_preedit_text, '') -- self.assertEqual(ENGINE.mock_committed_text, '工工作人员爱') -- self.assertEqual(ENGINE._editor._lookup_table.mock_candidates, -- ['爱因斯坦 1109 0', -- '爱情故事 519 0', -- '爱国主义 191 0', -- '爱尔兰语 91 0', -- '爱好和平 62 0', -- '爱情小说 58 0', -- '爱不释手 39 0', -- '爱国热情 35 0', -- '爱莫能助 34 0', -- '爱理不理 32 0']) -- ENGINE.do_process_key_event(IBus.KEY_space, 0, 0) -- self.assertEqual(ENGINE.mock_preedit_text, '') -- self.assertEqual(ENGINE.mock_committed_text, '工工作人员爱因斯坦') -- - def test_commit_to_preedit_switching_to_pinyin_defining_a_phrase(self): - ENGINE.do_process_key_event(IBus.KEY_a, 0, 0) - # commit to preëdit needs a press and release of either diff --git a/SOURCES/use-config-not-gsettings-for-gui-test.patch b/SOURCES/use-config-not-gsettings-for-gui-test.patch deleted file mode 100644 index 5643bf3..0000000 --- a/SOURCES/use-config-not-gsettings-for-gui-test.patch +++ /dev/null @@ -1,62 +0,0 @@ -diff -ru ibus-table-1.9.18.orig/tests/test_0_gtk.py ibus-table-1.9.18/tests/test_0_gtk.py ---- ibus-table-1.9.18.orig/tests/test_0_gtk.py 2020-07-27 09:41:25.799087549 +0200 -+++ ibus-table-1.9.18/tests/test_0_gtk.py 2020-07-28 12:23:43.127130148 +0200 -@@ -68,6 +68,7 @@ - - DONE_EXIT = True - ENGINE_NAME = 'wubi-jidian86' -+CONFIG_SECTION = 'engine/table/%s' % ENGINE_NAME - - from gtkcases import TestCases - -@@ -96,16 +97,18 @@ - class SimpleGtkTestCase(unittest.TestCase): - global DONE_EXIT - global ENGINE_NAME -+ global CONFIG_SECTION - ENGINE_PATH = '/com/redhat/IBus/engines/table/Test/Engine' - - @classmethod - def setUpClass(cls): - cls._flag = False - IBus.init() -- cls._gsettings = Gio.Settings( -- schema='org.freedesktop.ibus.engine.table', -- path='/org/freedesktop/ibus/engine/table/%s/' % ENGINE_NAME) -- cls._orig_chinesemode = cls._gsettings.get_int('chinesemode') -+ cls.__bus = IBus.Bus() -+ cls._config = cls.__bus.get_config() -+ cls._orig_chinesemode = table.variant_to_value( -+ cls._config.get_value( -+ CONFIG_SECTION, 'chinesemode')) - signums = [getattr(signal, s, None) for s in - 'SIGINT SIGTERM SIGHUP'.split()] - for signum in filter(None, signums): -@@ -116,7 +119,11 @@ - (signum, original_handler)) - @classmethod - def tearDownClass(cls): -- cls._gsettings.set_int('chinesemode', cls._orig_chinesemode) -+ if cls._orig_chinesemode: -+ cls._config.set_value( -+ CONFIG_SECTION, -+ 'chinesemode', -+ GLib.Variant.new_int32(cls._orig_chinesemode)) - - @classmethod - def signal_handler(cls, user_data): -@@ -136,10 +142,12 @@ - self.__inserted_text = '' - self.__commit_done = False - self.__reset_coming = False -- self._gsettings.set_int('chinesemode', 4) -+ self._config.set_value( -+ CONFIG_SECTION, -+ 'chinesemode', -+ GLib.Variant.new_int32(4)) - - def register_ibus_engine(self): -- self.__bus = IBus.Bus() - if not self.__bus.is_connected(): - self.fail('ibus-daemon is not running') - return False diff --git a/SPECS/ibus-table.spec b/SPECS/ibus-table.spec index a0948c6..bfeb27e 100644 --- a/SPECS/ibus-table.spec +++ b/SPECS/ibus-table.spec @@ -1,34 +1,17 @@ Name: ibus-table Version: 1.9.18 -Release: 4%{?dist} +Release: 3%{?dist} Summary: The Table engine for IBus platform License: LGPLv2+ Group: System Environment/Libraries URL: http://code.google.com/p/ibus/ Source0: http://mfabian.fedorapeople.org/ibus-table/%{name}-%{version}.tar.gz -Patch0: add-a-test-suite.patch -Patch1: add-a-gui-test.patch -Patch2: use-config-not-gsettings-for-gui-test.patch -Patch3: fix-test-cases-for-old-ibus-table.patch Requires: ibus > 1.3.0 Requires: python(abi) >= 3.3 Requires: %{__python3} BuildRequires: ibus-devel > 1.3.0 BuildRequires: python3-devel -# for the unit tests -BuildRequires: automake -BuildRequires: autoconf -BuildRequires: gettext-devel BuildRequires: libappstream-glib -BuildRequires: desktop-file-utils -BuildRequires: python3-mock -BuildRequires: python3-gobject -BuildRequires: python3-gobject-base -BuildRequires: dbus-x11 -BuildRequires: xorg-x11-server-Xvfb -BuildRequires: ibus-table-chinese-wubi-jidian -BuildRequires: ibus-table-chinese-cangjie -BuildRequires: ibus-table-chinese-stroke5 Obsoletes: ibus-table-additional < 1.2.0.20100111-5 @@ -46,25 +29,12 @@ Requires: %{name} = %{version}-%{release}, pkgconfig %description -n %{name}-devel Development files for %{name}. -%package tests -Summary: Tests for the %{name} package -Requires: %{name} = %{version}-%{release} - -%description tests -The %{name}-tests package contains tests that can be used to verify -the functionality of the installed %{name} package. - %prep %setup -q -%patch0 -p1 -b .add-a-test-suite -%patch1 -p1 -b .add-a-gui-test -%patch2 -p1 -b .use-config-not-gsettings-for-gui-test -%patch3 -p1 -b .fix-test-cases-for-old-ibus-table %build export PYTHON=%{__python3} -./autogen.sh --prefix=/usr -%configure --enable-installed-tests +%configure --disable-static --disable-additional %__make %{?_smp_mflags} %install @@ -76,38 +46,6 @@ export PYTHON=%{__python3} %check appstream-util validate-relax --nonet %{buildroot}/%{_datadir}/metainfo/*.appdata.xml -pushd engine -# run doctests - python3 table.py - python3 it_util.py -popd -export DISPLAY=:1 -Xvfb $DISPLAY -screen 0 1024x768x16 & -ibus-daemon -drx # not needed on Fedora 32 -# A window manager and and ibus-daemon are needed to run the GUI -# test tests/test_gtk.py, for example i3 can be used. -# -# To debug what is going on if there is a problem with the GUI test -# add BuildRequires: x11vnc and start a vnc server: -# -# x11vnc -display $DISPLAY -unixsock /tmp/mysock -bg -nopw -listen localhost -xkb -# -# Then one can view what is going on outside of the chroot with vncviewer: -# -# vncviewer /var/lib/mock/fedora-32-x86_64/root/tmp/mysock -# -# The GUI test will be skipped if XDG_SESSION_TYPE is not x11 or wayland. -# -#ibus-daemon -drx -#touch /tmp/i3config -#i3 -c /tmp/i3config & -#export XDG_SESSION_TYPE=x11 - -make check && rc=0 || rc=1 -cat tests/*.log -if [ $rc != 0 ] ; then - exit $rc -fi %clean %__rm -rf $RPM_BUILD_ROOT @@ -129,17 +67,7 @@ fi %defattr(-, root, root, -) %{_datadir}/pkgconfig/%{name}.pc -%files tests -%dir %{_libexecdir}/installed-tests -%{_libexecdir}/installed-tests/%{name} -%dir %{_datadir}/installed-tests -%{_datadir}/installed-tests/%{name} - %changelog -* Sat Jul 25 2020 Mike FABIAN - 1.9.18-4 -- Add gating tests to ibus-table -- Resolves: rhbz#1682163 - * Thu Sep 20 2018 Tomas Orsava - 1.9.18-3 - Require the Python interpreter directly instead of using the package name - Related: rhbz#1619153