diff --git a/Makefile.am b/Makefile.am index d4253d6..e95006a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -14,7 +14,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -SUBDIRS = marisa-glib libkkc data tools tests po +SUBDIRS = marisa-glib libkkc tools data tests po DISTCHECK_CONFIGURE_FLAGS = --enable-docs ACLOCAL_AMFLAGS = -I m4 @@ -22,20 +22,22 @@ if HAVE_VALADOC SUBDIRS += docs endif -GITIGNOREFILES = \ - INSTALL \ - aclocal.m4 \ - compile \ - config.guess \ - config.h.in \ - config.sub \ - depcomp \ - install-sh \ - ltmain.sh \ - missing \ - mkinstalldirs \ - `find "m4" -type f -name "*.m4" ! -name "vala.m4" -print` \ - $(top_builddir)/lcov.html/* \ +GITIGNOREFILES = \ + INSTALL \ + aclocal.m4 \ + compile \ + config.guess \ + config.h.in \ + config.sub \ + depcomp \ + install-sh \ + ltmain.sh \ + missing \ + mkinstalldirs \ + `find "m4" -type f -name "*.m4" ! -name "vala.m4" -print` \ + $(top_builddir)/lcov.html/* \ + data/rules/*/*.pot \ + test-driver \ $(NULL) distclean-local: diff --git a/README b/README deleted file mode 100644 index bec5c53..0000000 --- a/README +++ /dev/null @@ -1,58 +0,0 @@ -libkkc - Japanese Kana Kanji conversion library -*- coding: utf-8 -*- - -* What's this? - -libkkc provides a converter from Kana-string to -Kana-Kanji-mixed-string. It was named after kkc.el in GNU Emacs, a -simple Kana Kanji converter, while libkkc tries to convert sentences -in a bit more complex way using N-gram language models. - -* Install - -1. compile and install marisa-trie 0.2.1 - - https://code.google.com/p/marisa-trie/ - -2. compile and install - - $ ./autogen.sh - $ make - $ make install - -3. run kkc program - - $ kkc - Type kana sentence in the following form: - SENTENCE [N-BEST [SEGMENT-BOUNDARY...]] - - >> わたしのなまえはなかのです - 0: <わたし/わたし><の/の><名前/なまえ><は/は><中野/なかの><で/で><す/す> - - # get 3 matches instead of 1 - >> わたしのなまえはなかのです 3 - 0: <わたし/わたし><の/の><名前/なまえ><は/は><中野/なかの><で/で><す/す> - 1: <私/わたし><の/の><名前/なまえ><は/は><中野/なかの><で/で><す/す> - 2: <わたし/わたし><の/の><名前/なまえ><は/は><中野/なかの><デス/です> - - # enlarge the second segment (の -> のな) - >> わたしのなまえはなかのです 1 3 5 - 0: <わたし/わたし><のな/のな><前/まえ><は/は><中野/なかの><で/で><す/す> - - # shrink the fourth segment (なかの -> なか) - >> わたしのなまえはなかのです 1 3 4 7 8 10 - 0: <わたし/わたし><の/の><名前/なまえ><は/は><中/なか><の/の><で/で><す/す> - -License: - -GPLv3+ - -Copyright (C) 2011-2014 Daiki Ueno -Copyright (C) 2011-2014 Red Hat, Inc. - -This file is free software; as a special exception the author gives -unlimited permission to copy and/or distribute it, with or without -modifications, as long as this notice is preserved. - -This file 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. diff --git a/README b/README new file mode 120000 index 0000000..42061c0 --- /dev/null +++ b/README @@ -0,0 +1 @@ +README.md \ No newline at end of file diff --git a/configure.ac b/configure.ac index e044965..a36d98c 100644 --- a/configure.ac +++ b/configure.ac @@ -176,6 +176,8 @@ if test "x$found_introspection" = "xyes" -a "x$enable_vapigen" != "xno"; then fi AM_CONDITIONAL(ENABLE_VAPIGEN, [test "x$enable_vapigen" = "xyes"]) +AC_CHECK_PROGS(JSON_VALIDATE, json-validate, true) + AC_CONFIG_HEADERS([config.h]) AC_CONFIG_FILES([Makefile marisa-glib/Makefile @@ -185,9 +187,11 @@ tools/Makefile tests/Makefile tests/lib/Makefile data/Makefile +data/dbus/Makefile data/rules/Makefile data/rules/default/Makefile data/rules/act/Makefile +data/rules/atok/Makefile data/rules/azik/Makefile data/rules/azik-jp106/Makefile data/rules/kzik/Makefile diff --git a/data/Makefile.am b/data/Makefile.am index 794738f..9c03b32 100644 --- a/data/Makefile.am +++ b/data/Makefile.am @@ -14,6 +14,6 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -SUBDIRS = rules templates +SUBDIRS = dbus rules templates -include $(top_srcdir)/git.mk diff --git a/data/dbus/Makefile.am b/data/dbus/Makefile.am new file mode 100644 index 0000000..a68fa73 --- /dev/null +++ b/data/dbus/Makefile.am @@ -0,0 +1,24 @@ +# Copyright (C) 2011-2015 Daiki Ueno +# Copyright (C) 2011-2015 Red Hat, Inc. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +EXTRA_DIST = \ + org.du_a.Kkc.CandidateList.xml \ + org.du_a.Kkc.Context.xml \ + org.du_a.Kkc.SegmentList.xml \ + org.du_a.Kkc.Server.xml \ + $(NULL) + +-include $(top_srcdir)/git.mk diff --git a/data/dbus/org.du_a.Kkc.CandidateList.xml b/data/dbus/org.du_a.Kkc.CandidateList.xml new file mode 100644 index 0000000..684d6fd --- /dev/null +++ b/data/dbus/org.du_a.Kkc.CandidateList.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/dbus/org.du_a.Kkc.Context.xml b/data/dbus/org.du_a.Kkc.Context.xml new file mode 100644 index 0000000..f989227 --- /dev/null +++ b/data/dbus/org.du_a.Kkc.Context.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/dbus/org.du_a.Kkc.SegmentList.xml b/data/dbus/org.du_a.Kkc.SegmentList.xml new file mode 100644 index 0000000..43d578b --- /dev/null +++ b/data/dbus/org.du_a.Kkc.SegmentList.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/dbus/org.du_a.Kkc.Server.xml b/data/dbus/org.du_a.Kkc.Server.xml new file mode 100644 index 0000000..749abb4 --- /dev/null +++ b/data/dbus/org.du_a.Kkc.Server.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + diff --git a/data/rules/Makefile.am b/data/rules/Makefile.am index 0e88d21..d1a1293 100644 --- a/data/rules/Makefile.am +++ b/data/rules/Makefile.am @@ -17,6 +17,7 @@ SUBDIRS = \ default \ act \ + atok \ azik \ azik-jp106 \ kzik \ @@ -31,6 +32,9 @@ SUBDIRS = \ EXTRA_DIST = \ README.rules \ rule.mk \ + metadata-schema.json \ + keymap-schema.json \ + rom-kana-schema.json \ $(NULL) -include $(top_srcdir)/git.mk diff --git a/data/rules/act/Makefile.am b/data/rules/act/Makefile.am index 2524bd6..16ad93c 100644 --- a/data/rules/act/Makefile.am +++ b/data/rules/act/Makefile.am @@ -8,24 +8,16 @@ files = \ keymap/latin.json \ keymap/wide-latin.json \ rom-kana/default.json \ - $(NULL) -metadata_in = \ - metadata.json.in \ + metadata.json \ $(NULL) nobase_rules_DATA = \ $(files) \ - $(metadata_in:.in=) \ $(NULL) EXTRA_DIST = \ $(files) \ - $(metadata_in) \ - $(NULL) - -CLEANFILES = \ - metadata.json \ $(NULL) --include $(top_srcdir)/data/rules/rule.mk +include $(top_srcdir)/data/rules/rule.mk -include $(top_srcdir)/git.mk diff --git a/data/rules/act/metadata.json b/data/rules/act/metadata.json new file mode 100644 index 0000000..0136cc4 --- /dev/null +++ b/data/rules/act/metadata.json @@ -0,0 +1,5 @@ +{ + "name": "ACT", + "description": "Extended romaji input method based on AZIK for Dvorak keyboard layout, developed by Kiyoshi Kimura ", + "priority": 10 +} diff --git a/data/rules/act/metadata.json.in b/data/rules/act/metadata.json.in deleted file mode 100644 index f4b7721..0000000 --- a/data/rules/act/metadata.json.in +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": _("ACT"), - "description": _("Extended romaji input method based on AZIK for Dvorak keyboard layout, developed by Kiyoshi Kimura "), - "priority": 10 -} diff --git a/data/rules/atok/Makefile.am b/data/rules/atok/Makefile.am new file mode 100644 index 0000000..d6fe7eb --- /dev/null +++ b/data/rules/atok/Makefile.am @@ -0,0 +1,24 @@ +rulesdir = $(pkgdatadir)/rules/atok + +files = \ + keymap/default.json \ + keymap/hankaku-katakana.json \ + keymap/hiragana.json \ + keymap/katakana.json \ + keymap/latin.json \ + keymap/wide-latin.json \ + keymap/direct.json \ + rom-kana/default.json \ + metadata.json \ + $(NULL) + +nobase_rules_DATA = \ + $(files) \ + $(NULL) + +EXTRA_DIST = \ + $(files) \ + $(NULL) + +include $(top_srcdir)/data/rules/rule.mk +-include $(top_srcdir)/git.mk diff --git a/data/rules/atok/keymap/default.json b/data/rules/atok/keymap/default.json new file mode 100644 index 0000000..a2e091b --- /dev/null +++ b/data/rules/atok/keymap/default.json @@ -0,0 +1,31 @@ +{ + "include": [ + "default/default" + ], + "define": { + "keymap": { + "C-[": "abort", + "C-g": "delete-forward", + "C-q": null, + "C-F7": "register", + "C-Down": "next-candidate", + "S-space": "next-candidate", + "S-Henkan_Mode": "next-candidate", + "C-Up": "previous-candidate", + "Muhenkan": null, + "C-l": "expand-segment", + "C-k": "shrink-segment", + "Right": "expand-segment", + "Left": "shrink-segment", + "S-Left": "previous-segment", + "S-Right": "next-segment", + "C-Left": "first-segment", + "C-Right": "last-segment", + "C-u": "convert-hiragana", + "C-i": "convert-katakana", + "C-o": "convert-hankaku-katakana", + "C-p": "convert-wide-latin", + "C-@": "convert-latin" + } + } +} diff --git a/data/rules/atok/keymap/direct.json b/data/rules/atok/keymap/direct.json new file mode 100644 index 0000000..b11a387 --- /dev/null +++ b/data/rules/atok/keymap/direct.json @@ -0,0 +1,9 @@ +{ + "define": { + "keymap": { + "Hiragana_Katakana": "set-input-mode-hiragana", + "Zenkaku_Hankaku": "set-input-mode-hiragana", + "Henkan_Mode": "set-input-mode-hiragana" + } + } +} diff --git a/data/rules/atok/keymap/hankaku-katakana.json b/data/rules/atok/keymap/hankaku-katakana.json new file mode 100644 index 0000000..84e46f5 --- /dev/null +++ b/data/rules/atok/keymap/hankaku-katakana.json @@ -0,0 +1,10 @@ +{ + "include": [ + "default" + ], + "define": { + "keymap": { + "Hiragana_Katakana": "set-input-mode-hiragana" + } + } +} diff --git a/data/rules/atok/keymap/hiragana.json b/data/rules/atok/keymap/hiragana.json new file mode 100644 index 0000000..be63908 --- /dev/null +++ b/data/rules/atok/keymap/hiragana.json @@ -0,0 +1,12 @@ +{ + "include": [ + "default" + ], + "define": { + "keymap": { + "Hiragana_Katakana": "set-input-mode-katakana", + "Henkan_Mode": "set-input-mode-direct", + "Muhenkan": "set-input-mode-latin" + } + } +} diff --git a/data/rules/atok/keymap/katakana.json b/data/rules/atok/keymap/katakana.json new file mode 100644 index 0000000..84e46f5 --- /dev/null +++ b/data/rules/atok/keymap/katakana.json @@ -0,0 +1,10 @@ +{ + "include": [ + "default" + ], + "define": { + "keymap": { + "Hiragana_Katakana": "set-input-mode-hiragana" + } + } +} diff --git a/data/rules/atok/keymap/latin.json b/data/rules/atok/keymap/latin.json new file mode 100644 index 0000000..c3f7a78 --- /dev/null +++ b/data/rules/atok/keymap/latin.json @@ -0,0 +1,11 @@ +{ + "include": [ + "default" + ], + "define": { + "keymap": { + "Hiragana_Katakana": "set-input-mode-hiragana", + "Muhenkan": "set-input-mode-hiragana" + } + } +} diff --git a/data/rules/atok/keymap/wide-latin.json b/data/rules/atok/keymap/wide-latin.json new file mode 100644 index 0000000..84e46f5 --- /dev/null +++ b/data/rules/atok/keymap/wide-latin.json @@ -0,0 +1,10 @@ +{ + "include": [ + "default" + ], + "define": { + "keymap": { + "Hiragana_Katakana": "set-input-mode-hiragana" + } + } +} diff --git a/data/rules/atok/metadata.json b/data/rules/atok/metadata.json new file mode 100644 index 0000000..8c0d82b --- /dev/null +++ b/data/rules/atok/metadata.json @@ -0,0 +1,5 @@ +{ + "name": "ATOK", + "description": "The commercial Input Method ATOK like style", + "priority": 90 +} diff --git a/data/rules/atok/rom-kana/default.json b/data/rules/atok/rom-kana/default.json new file mode 100644 index 0000000..c179331 --- /dev/null +++ b/data/rules/atok/rom-kana/default.json @@ -0,0 +1,28 @@ +{ + "include": [ + "default/default" + ], + "define": { + "rom-kana": { + "dwu": ["", "どぅ" ], + "kwa": ["", "くぁ" ], + "lka": ["", "ヵ" ], + "lke": ["", "ヶ" ], + "ltu": ["", "っ" ], + "ltsu": ["", "っ" ], + "lwa": ["", "ゎ" ], + "tha": ["", "てゃ" ], + "tsa": ["", "つぁ" ], + "tsi": ["", "つぃ" ], + "tse": ["", "つぇ" ], + "tso": ["", "つぉ" ], + "twu": ["", "とぅ" ], + "wye": ["", "ゑ" ], + "wyi": ["", "ゐ" ], + "xye": ["", "ぇ" ], + "yi": ["", "い" ], + "zya": null, + "/": ["", "・", "/"] + } + } +} diff --git a/data/rules/azik-jp106/Makefile.am b/data/rules/azik-jp106/Makefile.am index 7311917..8d5e739 100644 --- a/data/rules/azik-jp106/Makefile.am +++ b/data/rules/azik-jp106/Makefile.am @@ -8,24 +8,16 @@ files = \ keymap/latin.json \ keymap/wide-latin.json \ rom-kana/default.json \ - $(NULL) -metadata_in = \ - metadata.json.in \ + metadata.json \ $(NULL) nobase_rules_DATA = \ $(files) \ - $(metadata_in:.in=) \ $(NULL) EXTRA_DIST = \ $(files) \ - $(metadata_in) \ - $(NULL) - -CLEANFILES = \ - metadata.json \ $(NULL) --include $(top_srcdir)/data/rules/rule.mk +include $(top_srcdir)/data/rules/rule.mk -include $(top_srcdir)/git.mk diff --git a/data/rules/azik-jp106/keymap/hiragana.json b/data/rules/azik-jp106/keymap/hiragana.json index 60bffc8..e94115d 100644 --- a/data/rules/azik-jp106/keymap/hiragana.json +++ b/data/rules/azik-jp106/keymap/hiragana.json @@ -1,7 +1,7 @@ { "include": [ "default/default" - ] + ], "define": { "keymap": { "[": null, diff --git a/data/rules/azik-jp106/metadata.json b/data/rules/azik-jp106/metadata.json new file mode 100644 index 0000000..75efc94 --- /dev/null +++ b/data/rules/azik-jp106/metadata.json @@ -0,0 +1,5 @@ +{ + "name": "AZIK (Japanese 106 keyboard)", + "description": "Extended romaji input method developed by Kiyoshi Kimura ", + "priority": 10 +} diff --git a/data/rules/azik-jp106/metadata.json.in b/data/rules/azik-jp106/metadata.json.in deleted file mode 100644 index a7fe226..0000000 --- a/data/rules/azik-jp106/metadata.json.in +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": _("AZIK (Japanese 106 keyboard)"), - "description": _("Extended romaji input method developed by Kiyoshi Kimura "), - "priority": 10 -} diff --git a/data/rules/azik/Makefile.am b/data/rules/azik/Makefile.am index acc54bc..e4ada1c 100644 --- a/data/rules/azik/Makefile.am +++ b/data/rules/azik/Makefile.am @@ -8,24 +8,16 @@ files = \ keymap/latin.json \ keymap/wide-latin.json \ rom-kana/default.json \ - $(NULL) -metadata_in = \ - metadata.json.in \ + metadata.json \ $(NULL) nobase_rules_DATA = \ $(files) \ - $(metadata_in:.in=) \ $(NULL) EXTRA_DIST = \ $(files) \ - $(metadata_in) \ - $(NULL) - -CLEANFILES = \ - metadata.json \ $(NULL) --include $(top_srcdir)/data/rules/rule.mk +include $(top_srcdir)/data/rules/rule.mk -include $(top_srcdir)/git.mk diff --git a/data/rules/azik/keymap/default.json b/data/rules/azik/keymap/default.json index 307b53a..828a178 100644 --- a/data/rules/azik/keymap/default.json +++ b/data/rules/azik/keymap/default.json @@ -1,10 +1,5 @@ { "include": [ "default/default" - ], - "define": { - "keymap": { - ":": "upper-;" - } - } + ] } diff --git a/data/rules/azik/metadata.json b/data/rules/azik/metadata.json new file mode 100644 index 0000000..a7421dd --- /dev/null +++ b/data/rules/azik/metadata.json @@ -0,0 +1,5 @@ +{ + "name": "AZIK", + "description": "Extended romaji input method developed by Kiyoshi Kimura ", + "priority": 10 +} diff --git a/data/rules/azik/metadata.json.in b/data/rules/azik/metadata.json.in deleted file mode 100644 index 6c4fa9d..0000000 --- a/data/rules/azik/metadata.json.in +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": _("AZIK"), - "description": _("Extended romaji input method developed by Kiyoshi Kimura "), - "priority": 10 -} diff --git a/data/rules/default/Makefile.am b/data/rules/default/Makefile.am index e2e0a56..77d220a 100644 --- a/data/rules/default/Makefile.am +++ b/data/rules/default/Makefile.am @@ -9,24 +9,16 @@ files = \ keymap/wide-latin.json \ keymap/direct.json \ rom-kana/default.json \ - $(NULL) -metadata_in = \ - metadata.json.in \ + metadata.json \ $(NULL) nobase_rules_DATA = \ $(files) \ - $(metadata_in:.in=) \ $(NULL) EXTRA_DIST = \ $(files) \ - $(metadata_in) \ - $(NULL) - -CLEANFILES = \ - metadata.json \ $(NULL) --include $(top_srcdir)/data/rules/rule.mk +include $(top_srcdir)/data/rules/rule.mk -include $(top_srcdir)/git.mk diff --git a/data/rules/default/metadata.json b/data/rules/default/metadata.json new file mode 100644 index 0000000..78f492f --- /dev/null +++ b/data/rules/default/metadata.json @@ -0,0 +1,5 @@ +{ + "name": "Default", + "description": "Default typing rule", + "priority": 99 +} diff --git a/data/rules/default/metadata.json.in b/data/rules/default/metadata.json.in deleted file mode 100644 index f9aa8dc..0000000 --- a/data/rules/default/metadata.json.in +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": _("Default"), - "description": _("Default typing rule"), - "priority": 99 -} diff --git a/data/rules/kana/Makefile.am b/data/rules/kana/Makefile.am index 559ef16..b00df0b 100644 --- a/data/rules/kana/Makefile.am +++ b/data/rules/kana/Makefile.am @@ -9,24 +9,16 @@ files = \ keymap/wide-latin.json \ keymap/direct.json \ rom-kana/default.json \ - $(NULL) -metadata_in = \ - metadata.json.in \ + metadata.json \ $(NULL) nobase_rules_DATA = \ $(files) \ - $(metadata_in:.in=) \ $(NULL) EXTRA_DIST = \ $(files) \ - $(metadata_in) \ - $(NULL) - -CLEANFILES = \ - metadata.json \ $(NULL) --include $(top_srcdir)/data/rules/rule.mk +include $(top_srcdir)/data/rules/rule.mk -include $(top_srcdir)/git.mk diff --git a/data/rules/kana/metadata.json b/data/rules/kana/metadata.json new file mode 100644 index 0000000..e2dbe8f --- /dev/null +++ b/data/rules/kana/metadata.json @@ -0,0 +1,6 @@ +{ + "name": "Kana", + "description": "Direct Kana typing", + "filter": "kana", + "priority": 99 +} diff --git a/data/rules/kana/metadata.json.in b/data/rules/kana/metadata.json.in deleted file mode 100644 index 47efecc..0000000 --- a/data/rules/kana/metadata.json.in +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": _("Kana"), - "description": _("Direct Kana typing"), - "filter": "kana", - "priority": 99 -} diff --git a/data/rules/kana/rom-kana/default.json b/data/rules/kana/rom-kana/default.json index e5c573e..d68ca78 100644 --- a/data/rules/kana/rom-kana/default.json +++ b/data/rules/kana/rom-kana/default.json @@ -29,40 +29,70 @@ "^@": ["", "べ"], "^[": ["", "ぺ"], "q": ["", "た", "タ", "た", "タ"], + "Q": ["", "た", "タ", "た", "タ"], "q@": ["", "だ"], + "Q@": ["", "だ"], "w": ["", "て", "テ", "て", "テ"], + "W": ["", "て", "テ", "て", "テ"], "w@": ["", "で"], + "W@": ["", "で"], "e": ["", "い"], "E": ["", "ぃ"], "r": ["", "す", "ス", "す", "ス"], + "R": ["", "す", "ス", "す", "ス"], "r@": ["", "ず"], + "R@": ["", "ず"], "t": ["", "か", "カ", "か", "カ"], + "T": ["", "か", "カ", "か", "カ"], "t@": ["", "が"], + "T@": ["", "が"], "y": ["", "ん"], + "Y": ["", "ん"], "u": ["", "な"], + "U": ["", "な"], "i": ["", "に"], + "I": ["", "に"], "o": ["", "ら"], + "O": ["", "ら"], "p": ["", "せ", "セ", "せ", "セ"], + "P": ["", "せ", "セ", "せ", "セ"], "p@": ["", "ぜ"], + "P@": ["", "ぜ"], "@": ["", "゛"], "[": ["", "゜"], "{": ["", "「"], "a": ["", "ち", "チ", "ち", "チ"], + "A": ["", "ち", "チ", "ち", "チ"], "a@": ["", "ぢ"], + "A@": ["", "ぢ"], "s": ["", "と", "ト", "と", "ト"], + "S": ["", "と", "ト", "と", "ト"], "s@": ["", "ど"], + "S@": ["", "ど"], "d": ["", "し", "シ", "し", "シ"], + "D": ["", "し", "シ", "し", "シ"], "d@": ["", "じ"], + "D@": ["", "じ"], "f": ["", "は", "ハ", "は", "ハ"], + "F": ["", "は", "ハ", "は", "ハ"], "f@": ["", "ば"], + "F@": ["", "ば"], "f[": ["", "ぱ"], + "F[": ["", "ぱ"], "g": ["", "き", "キ", "き", "キ"], + "G": ["", "き", "キ", "き", "キ"], "g@": ["", "ぎ"], + "G@": ["", "ぎ"], "h": ["", "く", "ク", "く", "ク"], + "H": ["", "く", "ク", "く", "ク"], "h@": ["", "ぐ"], + "H@": ["", "ぐ"], "j": ["", "ま"], + "J": ["", "ま"], "k": ["", "の"], + "K": ["", "の"], "l": ["", "り"], + "L": ["", "り"], ";": ["", "れ"], ":": ["", "け", "ケ", "け", "ケ"], ":@": ["", "げ"], @@ -70,18 +100,30 @@ "}": ["", "」"], "z": ["", "つ", "ツ", "つ", "ツ"], "z@": ["", "づ"], + "Z@": ["", "づ"], "Z": ["", "っ"], "x": ["", "さ", "サ", "さ", "サ"], + "X": ["", "さ", "サ", "さ", "サ"], "x@": ["", "ざ"], + "X@": ["", "ざ"], "c": ["", "そ", "ソ", "そ", "ソ"], + "C": ["", "そ", "ソ", "そ", "ソ"], "c@": ["", "ぞ"], + "C@": ["", "ぞ"], "v": ["", "ひ", "ヒ", "ひ", "ヒ"], + "V": ["", "ひ", "ヒ", "ひ", "ヒ"], "v@": ["", "び"], + "V@": ["", "び"], "v[": ["", "ぴ"], + "V[": ["", "ぴ"], "b": ["", "こ", "コ", "こ", "コ"], + "B": ["", "こ", "コ", "こ", "コ"], "b@": ["", "ご"], + "B@": ["", "ご"], "n": ["", "み"], + "N": ["", "み"], "m": ["", "も"], + "M": ["", "も"], ",": ["", "ね"], "<": ["", "、"], ".": ["", "る"], diff --git a/data/rules/keymap-schema.json b/data/rules/keymap-schema.json new file mode 100644 index 0000000..34c945e --- /dev/null +++ b/data/rules/keymap-schema.json @@ -0,0 +1,54 @@ +{ + "type": "object", + "properties": { + "include": { + "type": "array", + "items": { + "type": "string" + } + }, + "define" : { + "type": "object", + "properties": { + "keymap": { + "type": "object", + "patternProperties": { + ".*": { + "enum": [ + null, + "abort", + "first-segment", + "last-segment", + "commit", + "complete", + "delete", + "delete-forward", + "quote", + "register", + "next-candidate" + "previous-candidate", + "purge-candidate", + "next-segment", + "previous-segment", + "expand-segment", + "shrink-segment", + "set-input-mode-hiragana", + "set-input-mode-katakana", + "set-input-mode-hankaku-katakana", + "set-input-mode-latin", + "set-input-mode-wide-latin", + "set-input-mode-direct", + "convert-hiragana", + "convert-katakana", + "convert-hankaku-katakana", + "convert-latin", + "convert-wide-latin", + "original-candidate" + ] + } + } + } + } + } + } +} diff --git a/data/rules/kzik/Makefile.am b/data/rules/kzik/Makefile.am index fb38c42..fa900b5 100644 --- a/data/rules/kzik/Makefile.am +++ b/data/rules/kzik/Makefile.am @@ -8,24 +8,16 @@ files = \ keymap/latin.json \ keymap/wide-latin.json \ rom-kana/default.json \ - $(NULL) -metadata_in = \ - metadata.json.in \ + metadata.json \ $(NULL) nobase_rules_DATA = \ $(files) \ - $(metadata_in:.in=) \ $(NULL) EXTRA_DIST = \ $(files) \ - $(metadata_in) \ - $(NULL) - -CLEANFILES = \ - metadata.json \ $(NULL) --include $(top_srcdir)/data/rules/rule.mk +include $(top_srcdir)/data/rules/rule.mk -include $(top_srcdir)/git.mk diff --git a/data/rules/kzik/keymap/default.json b/data/rules/kzik/keymap/default.json index 307b53a..828a178 100644 --- a/data/rules/kzik/keymap/default.json +++ b/data/rules/kzik/keymap/default.json @@ -1,10 +1,5 @@ { "include": [ "default/default" - ], - "define": { - "keymap": { - ":": "upper-;" - } - } + ] } diff --git a/data/rules/kzik/metadata.json b/data/rules/kzik/metadata.json new file mode 100644 index 0000000..5f79ac1 --- /dev/null +++ b/data/rules/kzik/metadata.json @@ -0,0 +1,5 @@ +{ + "name": "KZIK", + "description": "Extended romaji input method based on AZIK, developed by OHASHI Hideya ", + "priority": 10 +} diff --git a/data/rules/kzik/metadata.json.in b/data/rules/kzik/metadata.json.in deleted file mode 100644 index 6472671..0000000 --- a/data/rules/kzik/metadata.json.in +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": _("KZIK"), - "description": _("Extended romaji input method based on AZIK, developed by OHASHI Hideya "), - "priority": 10 -} diff --git a/data/rules/metadata-schema.json b/data/rules/metadata-schema.json new file mode 100644 index 0000000..584b3c2 --- /dev/null +++ b/data/rules/metadata-schema.json @@ -0,0 +1,16 @@ +{ + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "priority": { + "type": "integer", + "minimum": 0, + "maximum": 100 + } + } +} diff --git a/data/rules/nicola/Makefile.am b/data/rules/nicola/Makefile.am index 045ced1..ae7dbe9 100644 --- a/data/rules/nicola/Makefile.am +++ b/data/rules/nicola/Makefile.am @@ -9,24 +9,16 @@ files = \ keymap/wide-latin.json \ keymap/direct.json \ rom-kana/default.json \ - $(NULL) -metadata_in = \ - metadata.json.in \ + metadata.json \ $(NULL) nobase_rules_DATA = \ $(files) \ - $(metadata_in:.in=) \ $(NULL) EXTRA_DIST = \ $(files) \ - $(metadata_in) \ - $(NULL) - -CLEANFILES = \ - metadata.json \ $(NULL) --include $(top_srcdir)/data/rules/rule.mk +include $(top_srcdir)/data/rules/rule.mk -include $(top_srcdir)/git.mk diff --git a/data/rules/nicola/metadata.json b/data/rules/nicola/metadata.json new file mode 100644 index 0000000..85b1398 --- /dev/null +++ b/data/rules/nicola/metadata.json @@ -0,0 +1,6 @@ +{ + "name": "NICOLA", + "description": "Input method using thumb shift keyboard developed by the NICOLA (NIhongo-nyuuryoku COnsortium LAyout) project ", + "filter": "nicola", + "priority": 10 +} diff --git a/data/rules/nicola/metadata.json.in b/data/rules/nicola/metadata.json.in deleted file mode 100644 index 2458e40..0000000 --- a/data/rules/nicola/metadata.json.in +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": _("NICOLA"), - "description": _("Input method using thumb shift keyboard developed by the NICOLA (NIhongo-nyuuryoku COnsortium LAyout) project "), - "filter": "nicola", - "priority": 10 -} diff --git a/data/rules/rom-kana-schema.json b/data/rules/rom-kana-schema.json new file mode 100644 index 0000000..84b0fde --- /dev/null +++ b/data/rules/rom-kana-schema.json @@ -0,0 +1,36 @@ +{ + "type": "object", + "properties": { + "include": { + "type": "array", + "items": { + "type": "string" + } + }, + "define" : { + "type": "object", + "properties": { + "rom-kana": { + "type": "object", + "patternProperties": { + ".*": { + "anyOf": [ + { + "type": "array", + "minItems": 2, + "maxItems": 5, + "items": { + "type": "string" + } + }, + { + "type": "null" + } + ] + } + } + } + } + } + } +} diff --git a/data/rules/rule.mk b/data/rules/rule.mk index 1da19d1..f125d80 100644 --- a/data/rules/rule.mk +++ b/data/rules/rule.mk @@ -1,9 +1,26 @@ -.SUFFIXES: .json .json.in +SUFFIXES = .json .pot -edit = sed -e 's!\(^ *"[^"]*": *\)_(\("[^"]*"\))!\1\2!g' -.json.in.json: +.json.pot: $(AM_V_GEN) rm -f $@ $@.tmp; \ srcdir=''; \ test -f ./$< || srcdir=$(srcdir)/; \ - $(edit) $${srcdir}$< >$@.tmp; \ - mv $@.tmp $@ + $(top_builddir)/tools/gen-metadata-pot $${srcdir}$< \ + '$$.name' '$$.description' >$@.tmp && mv $@.tmp $@ + +# 'make check' in po/ requires metadata.pot +all-local: metadata.pot + +check-local: + $(AM_V_at)$(JSON_VALIDATE) \ + --schema $(top_srcdir)/data/rules/metadata-schema.json \ + metadata.json + $(AM_V_at)$(JSON_VALIDATE) \ + --schema $(top_srcdir)/data/rules/keymap-schema.json \ + keymap/*.json + $(AM_V_at)$(JSON_VALIDATE) \ + --schema $(top_srcdir)/data/rules/rom-kana-schema.json \ + rom-kana/*.json + +metadata.pot: metadata.json $(top_srcdir)/tools/gen-metadata-pot.c + +EXTRA_DIST += metadata.pot diff --git a/data/rules/tcode/Makefile.am b/data/rules/tcode/Makefile.am index 02e8fce..9e0f9ef 100644 --- a/data/rules/tcode/Makefile.am +++ b/data/rules/tcode/Makefile.am @@ -7,24 +7,16 @@ files = \ keymap/latin.json \ keymap/wide-latin.json \ rom-kana/default.json \ - $(NULL) -metadata_in = \ - metadata.json.in \ + metadata.json \ $(NULL) nobase_rules_DATA = \ $(files) \ - $(metadata_in:.in=) \ $(NULL) EXTRA_DIST = \ $(files) \ - $(metadata_in) \ - $(NULL) - -CLEANFILES = \ - metadata.json \ $(NULL) --include $(top_srcdir)/data/rules/rule.mk +include $(top_srcdir)/data/rules/rule.mk -include $(top_srcdir)/git.mk diff --git a/data/rules/tcode/metadata.json b/data/rules/tcode/metadata.json new file mode 100644 index 0000000..1ea7221 --- /dev/null +++ b/data/rules/tcode/metadata.json @@ -0,0 +1,5 @@ +{ + "name": "T-Code", + "description": "Japanese direct input method developed by the T-Code project ", + "priority": 10 +} diff --git a/data/rules/tcode/metadata.json.in b/data/rules/tcode/metadata.json.in deleted file mode 100644 index e127f3b..0000000 --- a/data/rules/tcode/metadata.json.in +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": _("T-Code"), - "description": _("Japanese direct input method developed by the T-Code project "), - "priority": 10 -} diff --git a/data/rules/trycode/Makefile.am b/data/rules/trycode/Makefile.am index f17c9e9..23eef01 100644 --- a/data/rules/trycode/Makefile.am +++ b/data/rules/trycode/Makefile.am @@ -7,24 +7,16 @@ files = \ keymap/latin.json \ keymap/wide-latin.json \ rom-kana/default.json \ - $(NULL) -metadata_in = \ - metadata.json.in \ + metadata.json \ $(NULL) nobase_rules_DATA = \ $(files) \ - $(metadata_in:.in=) \ $(NULL) EXTRA_DIST = \ $(files) \ - $(metadata_in) \ - $(NULL) - -CLEANFILES = \ - metadata.json \ $(NULL) --include $(top_srcdir)/data/rules/rule.mk +include $(top_srcdir)/data/rules/rule.mk -include $(top_srcdir)/git.mk diff --git a/data/rules/trycode/metadata.json b/data/rules/trycode/metadata.json new file mode 100644 index 0000000..7b9ad7a --- /dev/null +++ b/data/rules/trycode/metadata.json @@ -0,0 +1,5 @@ +{ + "name": "TRY-CODE", + "description": "Japanese direct input method based on T-Code, developed by Naoto Takahashi ", + "priority": 10 +} diff --git a/data/rules/trycode/metadata.json.in b/data/rules/trycode/metadata.json.in deleted file mode 100644 index c95340f..0000000 --- a/data/rules/trycode/metadata.json.in +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": _("TRY-CODE"), - "description": _("Japanese direct input method based on T-Code, developed by Naoto Takahashi "), - "priority": 10 -} diff --git a/data/rules/tutcode-touch16x/Makefile.am b/data/rules/tutcode-touch16x/Makefile.am index d432306..af14949 100644 --- a/data/rules/tutcode-touch16x/Makefile.am +++ b/data/rules/tutcode-touch16x/Makefile.am @@ -7,24 +7,16 @@ files = \ keymap/latin.json \ keymap/wide-latin.json \ rom-kana/default.json \ - $(NULL) -metadata_in = \ - metadata.json.in \ + metadata.json \ $(NULL) nobase_rules_DATA = \ $(files) \ - $(metadata_in:.in=) \ $(NULL) EXTRA_DIST = \ $(files) \ - $(metadata_in) \ - $(NULL) - -CLEANFILES = \ - metadata.json \ $(NULL) --include $(top_srcdir)/data/rules/rule.mk +include $(top_srcdir)/data/rules/rule.mk -include $(top_srcdir)/git.mk diff --git a/data/rules/tutcode-touch16x/metadata.json b/data/rules/tutcode-touch16x/metadata.json new file mode 100644 index 0000000..d8a41b1 --- /dev/null +++ b/data/rules/tutcode-touch16x/metadata.json @@ -0,0 +1,5 @@ +{ + "name": "TUT-Code (Touch16+)", + "description": "TUT-Code with Touch16+ extension", + "priority": 10 +} diff --git a/data/rules/tutcode-touch16x/metadata.json.in b/data/rules/tutcode-touch16x/metadata.json.in deleted file mode 100644 index 1112c7b..0000000 --- a/data/rules/tutcode-touch16x/metadata.json.in +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": _("TUT-Code (Touch16+)"), - "description": _("TUT-Code with Touch16+ extension"), - "priority": 10 -} diff --git a/data/rules/tutcode/Makefile.am b/data/rules/tutcode/Makefile.am index 486f003..ce4b99f 100644 --- a/data/rules/tutcode/Makefile.am +++ b/data/rules/tutcode/Makefile.am @@ -7,24 +7,16 @@ files = \ keymap/latin.json \ keymap/wide-latin.json \ rom-kana/default.json \ - $(NULL) -metadata_in = \ - metadata.json.in \ + metadata.json \ $(NULL) nobase_rules_DATA = \ $(files) \ - $(metadata_in:.in=) \ $(NULL) EXTRA_DIST = \ $(files) \ - $(metadata_in) \ - $(NULL) - -CLEANFILES = \ - metadata.json \ $(NULL) --include $(top_srcdir)/data/rules/rule.mk +include $(top_srcdir)/data/rules/rule.mk -include $(top_srcdir)/git.mk diff --git a/data/rules/tutcode/metadata.json b/data/rules/tutcode/metadata.json new file mode 100644 index 0000000..1d1685f --- /dev/null +++ b/data/rules/tutcode/metadata.json @@ -0,0 +1,5 @@ +{ + "name": "TUT-Code", + "description": "Japanese direct input method developed by Hajime Ohiwa and Takaaki Takashima ", + "priority": 10 +} diff --git a/data/rules/tutcode/metadata.json.in b/data/rules/tutcode/metadata.json.in deleted file mode 100644 index 8bc11ff..0000000 --- a/data/rules/tutcode/metadata.json.in +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": _("TUT-Code"), - "description": _("Japanese direct input method developed by Hajime Ohiwa and Takaaki Takashima "), - "priority": 10 -} diff --git a/data/templates/libkkc-data/tools/genfilter.py b/data/templates/libkkc-data/tools/genfilter.py index 5ffab32..0c5f75a 100644 --- a/data/templates/libkkc-data/tools/genfilter.py +++ b/data/templates/libkkc-data/tools/genfilter.py @@ -84,24 +84,24 @@ class FilterGenerator(object): def generate(self): size = os.fstat(self.infile.fileno()).st_size - n = size / self.record_size + n = size // self.record_size m = int(math.ceil(-n*math.log10(ERROR_RATE) / math.pow(math.log10(2), 2))) - m = (m/8 + 1)*8 + m = (m//8 + 1)*8 inmem = mmap.mmap(self.infile.fileno(), size, access=mmap.ACCESS_READ) - outmem = bytearray(m/8) - for i in xrange(0, n): + outmem = bytearray(m//8) + for i in range(0, n): offset = i*self.record_size b0, b1 = struct.unpack("=LL", inmem[offset:offset+8]) - for k in xrange(0, 4): + for k in range(0, 4): h = murmur_hash3_32(b0, b1, k) h = int(h * (m / float(0xFFFFFFFF))) - outmem[h/8] |= (1 << (h%8)) + outmem[h//8] |= (1 << (h%8)) inmem.close() - # Convert bytearray to str, for Python 2.6 compatibility. - self.outfile.write(str(outmem)) + # Convert bytearray to bytes, for Python 3 compatibility. + self.outfile.write(bytes(outmem)) if __name__ == '__main__': import sys @@ -110,7 +110,7 @@ if __name__ == '__main__': parser = argparse.ArgumentParser(description='filter') parser.add_argument('infile', type=argparse.FileType('r'), help='input file') - parser.add_argument('outfile', type=argparse.FileType('w'), + parser.add_argument('outfile', type=argparse.FileType('wb'), help='output file') parser.add_argument('record_size', type=int, help='record size') diff --git a/data/templates/libkkc-data/tools/sortlm.py b/data/templates/libkkc-data/tools/sortlm.py index a0dd8fe..40f0837 100644 --- a/data/templates/libkkc-data/tools/sortlm.py +++ b/data/templates/libkkc-data/tools/sortlm.py @@ -40,10 +40,10 @@ class SortedGenerator(object): self.__min_cost = 0.0 def read(self): - print "reading N-grams" + print("reading N-grams") self.__read_tries() self.__read_ngrams() - print "min cost = %lf" % self.__min_cost + print("min cost = %lf" % self.__min_cost) def __read_tries(self): while True: @@ -58,7 +58,7 @@ class SortedGenerator(object): line = self.__infile.readline() if line == "": break - line = line.strip() + line = line.strip('\n') if line == "": break match = self.__ngram_line_regex.match(line) @@ -89,7 +89,7 @@ class SortedGenerator(object): line = self.__infile.readline() if line == "": break - line = line.strip() + line = line.strip('\n') if line == "": break match = self.__ngram_line_regex.match(line) @@ -125,14 +125,11 @@ class SortedGenerator(object): def quantize(cost, min_cost): return max(0, min(65535, int(cost * 65535 / min_cost))) - def cmp_header(a, b): - return cmp(a[0], b[0]) - - print "writing 1-gram file" + print("writing 1-gram file") unigram_offsets = {} unigram_file = open("%s.1gram" % self.__output_prefix, "wb") offset = 0 - for ids, value in sorted(self.__ngram_entries[0].iteritems()): + for ids, value in sorted(self.__ngram_entries[0].items()): unigram_offsets[ids[0]] = offset s = struct.pack("=HHH", quantize(value[0], self.__min_cost), @@ -143,13 +140,13 @@ class SortedGenerator(object): offset += 1 unigram_file.close() - print "writing 2-gram file" + print("writing 2-gram file") bigram_offsets = {} bigram_file = open("%s.2gram" % self.__output_prefix, "wb") keys = self.__ngram_entries[1].keys() items = [(struct.pack("=LL", ids[1], unigram_offsets[ids[0]]), ids) for ids in keys] offset = 0 - for header, ids in sorted(items, cmp=cmp_header): + for header, ids in sorted(items, key=lambda x: x[0]): value = self.__ngram_entries[1][ids] bigram_offsets[ids] = offset s = struct.pack("=HH", @@ -160,11 +157,11 @@ class SortedGenerator(object): bigram_file.close() if len(self.__ngram_entries[2]) > 0: - print "writing 3-gram file" + print("writing 3-gram file") trigram_file = open("%s.3gram" % self.__output_prefix, "wb") keys = self.__ngram_entries[2].keys() items = [(struct.pack("=LL", ids[2], bigram_offsets[(ids[0], ids[1])]), ids) for ids in keys] - for header, ids in sorted(items, cmp=cmp_header): + for header, ids in sorted(items, key=lambda x: x[0]): value = self.__ngram_entries[2][ids] s = struct.pack("=H", quantize(value[0], self.__min_cost)) diff --git a/libkkc/Makefile.am b/libkkc/Makefile.am index 02ca2ab..28a9f68 100644 --- a/libkkc/Makefile.am +++ b/libkkc/Makefile.am @@ -108,6 +108,7 @@ libkkc_shell_sources = \ template.vala \ numeric-template.vala \ expression.vala \ + server.vala \ $(NULL) libkkc_la_SOURCES = \ diff --git a/libkkc/candidate-list.vala b/libkkc/candidate-list.vala index 92ddb79..e6e6bfe 100644 --- a/libkkc/candidate-list.vala +++ b/libkkc/candidate-list.vala @@ -122,7 +122,8 @@ namespace Kkc { } uint get_page_start_cursor_pos (uint pos) { - return (pos / page_size) * page_size; + var page_index = (pos - page_start) / page_size; + return page_index * page_size + page_start; } /** @@ -210,23 +211,33 @@ namespace Kkc { } } + bool update_cursor_pos (uint pos) { + if (0 <= pos && pos < _candidates.size && pos != _cursor_pos) { + _cursor_pos = (int) pos; + notify_property ("cursor-pos"); + return true; + } + return false; + } + bool cursor_move (int step) { if (_candidates.is_empty || step == 0) return false; + int start = _cursor_pos - (int) page_start; + int total = (int) _candidates.size - (int) page_start; + if (round) { - var pos = (_cursor_pos + step) % _candidates.size; + int pos = (start + step) % total; if (pos < 0) - pos += _candidates.size; - _cursor_pos = pos; - notify_property ("cursor-pos"); - return true; - } else { - var pos = _cursor_pos + step; - if (0 <= pos && pos < _candidates.size) { - _cursor_pos = pos; - notify_property ("cursor-pos"); + pos += total; + if (update_cursor_pos (pos + page_start)) return true; + } else { + var pos = start + step; + if (0 <= pos && pos < total) { + if (update_cursor_pos (pos + page_start)) + return true; } } @@ -239,7 +250,11 @@ namespace Kkc { * @return `true` if cursor position has changed, `false` otherwise. */ public bool cursor_up () { - return cursor_move (-1); + if (_cursor_pos >= page_start) + return cursor_move (-1); + else if (update_cursor_pos (_cursor_pos - 1)) + return true; + return false; } /** @@ -248,32 +263,35 @@ namespace Kkc { * @return `true` if cursor position has changed, `false` otherwise */ public bool cursor_down () { - return cursor_move (1); + if (_cursor_pos >= page_start) + return cursor_move (1); + else if (update_cursor_pos (_cursor_pos + 1)) + return true; + return false; } bool page_move (int step) { if (_candidates.is_empty || step == 0) return false; + int start = _cursor_pos - (int) page_start; + int total = (int) _candidates.size - (int) page_start; + if (round) { - var pos = (_cursor_pos + page_size * step) % _candidates.size; + int pos = (start + (int) page_size * step) % total; if (pos < 0) - pos += _candidates.size; - pos = get_page_start_cursor_pos (pos); - if (pos != _cursor_pos) { - _cursor_pos = (int) pos; - notify_property ("cursor-pos"); - return true; + pos += total; + if (pos + (int) page_start < _candidates.size) { + var new_pos = get_page_start_cursor_pos (pos + page_start); + if (update_cursor_pos (new_pos)) + return true; } } else { - var pos = _cursor_pos + page_size * step; - if (0 <= pos && pos < _candidates.size) { - pos = get_page_start_cursor_pos (pos); - if (pos != _cursor_pos) { - _cursor_pos = (int) pos; - notify_property ("cursor-pos"); + var pos = start + (int) page_size * step; + if (0 <= pos && pos < total) { + var new_pos = get_page_start_cursor_pos (pos + page_start); + if (update_cursor_pos (new_pos)) return true; - } } } return false; diff --git a/libkkc/context.vala b/libkkc/context.vala index e328c34..d94a248 100644 --- a/libkkc/context.vala +++ b/libkkc/context.vala @@ -326,6 +326,35 @@ namespace Kkc { } } + /** + * Process an explicit command in the context. + * + * This function is rarely used in programs but called from + * D-Bus service. + * + * @param command a command + * + * @return `true` if the command is handled, `false` otherwise + */ + internal bool process_command_event (string command) { + var key = new KeyEvent (Keysyms.VoidSymbol, 0, 0); + while (true) { + var handler_type = state.handler_type; + var handler = handlers.get (handler_type); + state.this_command_key = key; + if (handler.dispatch_command (command, state, key)) { + notify_property ("input"); + return true; + } + // state.handler_type may change if handler cannot + // handle the event. In that case retry with the new + // handler. Otherwise exit the loop. + if (handler_type == state.handler_type) { + return false; + } + } + } + /** * Reset the context. */ diff --git a/libkkc/convert-segment-state-handler.vala b/libkkc/convert-segment-state-handler.vala index 3a4e05d..10a48b2 100644 --- a/libkkc/convert-segment-state-handler.vala +++ b/libkkc/convert-segment-state-handler.vala @@ -36,6 +36,8 @@ namespace Kkc { do_select_unhandled); register_command_callback ("last-segment", do_select_unhandled); + register_command_callback ("commit", + do_select_unhandled); register_command_callback ("delete", do_clear_unhandled); register_command_callback ("original-candidate", @@ -49,21 +51,19 @@ namespace Kkc { "convert-" + enum_value.value_nick, do_clear_unhandled); } - - register_command_callback (null, do_select_unhandled); } - bool do_next_candidate (string? command, State state, KeyEvent key) { + bool do_next_candidate (string command, State state, KeyEvent key) { state.candidates.cursor_down (); return true; } - bool do_previous_candidate (string? command, State state, KeyEvent key) { + bool do_previous_candidate (string command, State state, KeyEvent key) { state.candidates.cursor_up (); return true; } - bool do_purge_candidate (string? command, State state, KeyEvent key) { + bool do_purge_candidate (string command, State state, KeyEvent key) { if (state.candidates.cursor_pos >= 0) { var candidate = state.candidates.get (); state.purge_candidate (candidate); @@ -72,21 +72,29 @@ namespace Kkc { return true; } - bool do_select_unhandled (string? command, State state, KeyEvent key) { + bool do_select_unhandled (string command, State state, KeyEvent key) { if (state.candidates.cursor_pos >= 0) state.candidates.select (); state.handler_type = typeof (ConvertSentenceStateHandler); return false; } - bool do_clear_unhandled (string? command, State state, KeyEvent key) { + bool do_clear_unhandled (string command, State state, KeyEvent key) { state.candidates.clear (); state.handler_type = typeof (ConvertSentenceStateHandler); return false; } - + + public override bool default_command_callback (string? command, + State state, + KeyEvent key) + { + return do_select_unhandled (command ?? "", state, key); + } + public override bool process_key_event (State state, KeyEvent key) { - return dispatch_command (state, key); + var command = state.lookup_key (key); + return dispatch_command (command, state, key); } } } diff --git a/libkkc/convert-sentence-state-handler.vala b/libkkc/convert-sentence-state-handler.vala index 476c8ae..ae97e68 100644 --- a/libkkc/convert-sentence-state-handler.vala +++ b/libkkc/convert-sentence-state-handler.vala @@ -25,7 +25,7 @@ namespace Kkc { this.mode = mode; } - public bool call (string? command, State state, KeyEvent key) { + public bool call (string command, State state, KeyEvent key) { state.convert_segment_by_kana_mode (mode); return true; } @@ -57,6 +57,7 @@ namespace Kkc { register_command_callback ("abort", do_clear_unhandled); register_command_callback ("delete", do_clear_unhandled); + register_command_callback ("commit", do_commit); var enum_class = (EnumClass) typeof (KanaMode).class_ref (); for (int i = enum_class.minimum; i <= enum_class.maximum; i++) { @@ -67,62 +68,70 @@ namespace Kkc { new ConvertCommandHandler ( (KanaMode) enum_value.value)); } - - register_command_callback (null, do_); } - bool do_original_candidate (string? command, State state, KeyEvent key) { + bool do_original_candidate (string command, State state, KeyEvent key) { var segment = state.segments[state.segments.cursor_pos]; segment.output = segment.input; return true; } - bool do_expand_segment (string? command, State state, KeyEvent key) { + bool do_expand_segment (string command, State state, KeyEvent key) { if (state.segments.cursor_pos < state.segments.size - 1) state.resize_segment (1); return true; } - bool do_shrink_segment (string? command, State state, KeyEvent key) { + bool do_shrink_segment (string command, State state, KeyEvent key) { if (state.segments[state.segments.cursor_pos].input.char_count () > 1) state.resize_segment (-1); return true; } - bool do_next_segment (string? command, State state, KeyEvent key) { + bool do_next_segment (string command, State state, KeyEvent key) { state.segments.next_segment (); return true; } - bool do_previous_segment (string? command, State state, KeyEvent key) { + bool do_previous_segment (string command, State state, KeyEvent key) { state.segments.previous_segment (); return true; } - bool do_first_segment (string? command, State state, KeyEvent key) { + bool do_first_segment (string command, State state, KeyEvent key) { state.segments.first_segment (); return true; } - bool do_last_segment (string? command, State state, KeyEvent key) { + bool do_last_segment (string command, State state, KeyEvent key) { state.segments.last_segment (); return true; } - bool do_start_segment_conversion (string? command, State state, KeyEvent key) { + bool do_start_segment_conversion (string command, State state, KeyEvent key) { state.lookup (state.segments[state.segments.cursor_pos]); state.candidates.first (); state.handler_type = typeof (ConvertSegmentStateHandler); return false; } - bool do_clear_unhandled (string? command, State state, KeyEvent key) { + bool do_clear_unhandled (string command, State state, KeyEvent key) { state.segments.clear (); state.handler_type = typeof (InitialStateHandler); return true; } - bool do_ (string? command, State state, KeyEvent key) { + bool do_commit (string command, State state, KeyEvent key) { + state.output.append (state.segments.get_output ()); + state.select_sentence (); + state.reset (); + return true; + } + + public override bool default_command_callback (string? command, + State state, + KeyEvent key) + { state.output.append (state.segments.get_output ()); state.select_sentence (); state.reset (); @@ -142,7 +151,8 @@ namespace Kkc { } public override bool process_key_event (State state, KeyEvent key) { - return dispatch_command (state, key); + var command = state.lookup_key (key); + return dispatch_command (command, state, key); } } } diff --git a/libkkc/encoding.vala b/libkkc/encoding.vala index fe9ced1..af64ef7 100644 --- a/libkkc/encoding.vala +++ b/libkkc/encoding.vala @@ -19,15 +19,15 @@ namespace Kkc { // XXX: we use Vala string to represent byte array, assuming that // it does not contain null element class EncodingConverter : Object, Initable { - static const int BUFSIZ = 4096; - static const string INTERNAL_ENCODING = "UTF-8"; + const int BUFSIZ = 4096; + const string INTERNAL_ENCODING = "UTF-8"; struct EncodingCodingSystemEntry { string key; string value; } - static const EncodingCodingSystemEntry ENCODING_TO_CODING_SYSTEM_RULE[] = { + const EncodingCodingSystemEntry ENCODING_TO_CODING_SYSTEM_RULE[] = { { "UTF-8", "utf-8" }, { "EUC-JP", "euc-jp" }, { "Shift_JIS", "shift_jis" }, diff --git a/libkkc/initial-state-handler.vala b/libkkc/initial-state-handler.vala index 3679b60..927560f 100644 --- a/libkkc/initial-state-handler.vala +++ b/libkkc/initial-state-handler.vala @@ -25,7 +25,7 @@ namespace Kkc { this.mode = mode; } - public bool call (string? command, State state, KeyEvent key) { + public bool call (string command, State state, KeyEvent key) { state.finish_input_editing (); if (state.input_characters.size > 0) { state.selection.erase (); @@ -61,21 +61,20 @@ namespace Kkc { register_command_callback ("last-segment", do_last_character); register_command_callback ("quote", do_quote); register_command_callback ("register", do_register); - - register_command_callback (null, do_); + register_command_callback ("commit", do_commit); } - bool do_quote (string? command, State state, KeyEvent key) { + bool do_quote (string command, State state, KeyEvent key) { state.quoted = true; return true; } - bool do_register (string? command, State state, KeyEvent key) { + bool do_register (string command, State state, KeyEvent key) { state.request_selection_text (); return true; } - bool do_abort (string? command, State state, KeyEvent key) { + bool do_abort (string command, State state, KeyEvent key) { if (state.overriding_input != null) { state.overriding_input = null; return true; @@ -90,7 +89,7 @@ namespace Kkc { return false; } - bool do_delete (string? command, State state, KeyEvent key) { + bool do_delete (string command, State state, KeyEvent key) { if (state.overriding_input != null) { state.overriding_input = null; return true; @@ -115,7 +114,7 @@ namespace Kkc { return false; } - bool do_delete_forward (string? command, State state, KeyEvent key) { + bool do_delete_forward (string command, State state, KeyEvent key) { if (state.input_characters_cursor_pos >= 0 && state.input_characters_cursor_pos < state.input_characters.size) { state.input_characters.remove_at ( @@ -128,7 +127,7 @@ namespace Kkc { return false; } - bool do_complete (string? command, State state, KeyEvent key) { + bool do_complete (string command, State state, KeyEvent key) { state.finish_input_editing (); if (state.input_characters.size > 0) { if (state.completion_iterator == null) @@ -144,7 +143,7 @@ namespace Kkc { return false; } - bool do_next_candidate (string? command, State state, KeyEvent key) { + bool do_next_candidate (string command, State state, KeyEvent key) { state.finish_input_editing (); if (state.input_characters.size == 0) return false; @@ -180,7 +179,7 @@ namespace Kkc { return true; } - bool do_next_character (string? command, State state, KeyEvent key) { + bool do_next_character (string command, State state, KeyEvent key) { state.finish_input_editing (); if (state.input_characters.size == 0) return false; @@ -194,7 +193,7 @@ namespace Kkc { return true; } - bool do_previous_character (string? command, State state, KeyEvent key) { + bool do_previous_character (string command, State state, KeyEvent key) { state.finish_input_editing (); if (state.input_characters.size == 0) return false; @@ -210,7 +209,7 @@ namespace Kkc { return true; } - bool do_first_character (string? command, State state, KeyEvent key) { + bool do_first_character (string command, State state, KeyEvent key) { state.finish_input_editing (); if (state.input_characters.size == 0) return false; @@ -219,7 +218,7 @@ namespace Kkc { return true; } - bool do_last_character (string? command, State state, KeyEvent key) { + bool do_last_character (string command, State state, KeyEvent key) { state.finish_input_editing (); if (state.input_characters.size == 0) return false; @@ -228,7 +227,28 @@ namespace Kkc { return true; } - bool do_ (string? command, State state, KeyEvent key) { + bool do_commit (string command, State state, KeyEvent key) { + bool retval = false; + + if (state.overriding_input != null) { + state.output.append (state.get_input ()); + state.overriding_input = null; + state.reset (); + retval = true; + } + + var last_input = state.get_input (); + state.finish_input_editing (); + var input = state.get_input (); + state.output.append (input); + state.reset (); + return retval || input.length > 0 || last_input != input; + } + + public override bool default_command_callback (string? command, + State state, + KeyEvent key) + { bool retval = false; if (state.overriding_input != null) { @@ -277,7 +297,6 @@ namespace Kkc { return true; } } - var last_input = state.get_input (); state.finish_input_editing (); var input = state.get_input (); @@ -347,7 +366,7 @@ namespace Kkc { return true; } - return dispatch_command (state, key); + return dispatch_command (command, state, key); } } } diff --git a/libkkc/key-event-filter.vala b/libkkc/key-event-filter.vala index 9d9a089..3ceb16e 100644 --- a/libkkc/key-event-filter.vala +++ b/libkkc/key-event-filter.vala @@ -53,7 +53,7 @@ namespace Kkc { * @see Rule */ class SimpleKeyEventFilter : KeyEventFilter { - static const uint[] modifier_keyvals = { + const uint[] modifier_keyvals = { Keysyms.Shift_L, Keysyms.Shift_R, Keysyms.Control_L, diff --git a/libkkc/key-event.vala b/libkkc/key-event.vala index 0baa85c..6e28aa6 100644 --- a/libkkc/key-event.vala +++ b/libkkc/key-event.vala @@ -148,7 +148,7 @@ namespace Kkc { throw new KeyEventFormatError.PARSE_FAILED ( "unknown keyval %s", _name); } - from_x_event (_keyval, 0, _modifiers); + this.from_x_event (_keyval, 0, _modifiers); } /** diff --git a/libkkc/keymap.vala b/libkkc/keymap.vala index f89c2a6..42af3b7 100644 --- a/libkkc/keymap.vala +++ b/libkkc/keymap.vala @@ -32,7 +32,7 @@ namespace Kkc { * Object representing a keymap. */ public class Keymap : Object { - static const KeymapCommandEntry Commands[] = { + const KeymapCommandEntry Commands[] = { { "abort", N_("Abort") }, { "first-segment", N_("First Segment") }, { "last-segment", N_("Last Segment") }, diff --git a/libkkc/rom-kana-utils.vala b/libkkc/rom-kana-utils.vala index 32cffcb..fe16960 100644 --- a/libkkc/rom-kana-utils.vala +++ b/libkkc/rom-kana-utils.vala @@ -38,7 +38,7 @@ namespace Kkc { string? hankaku_katakana; } - static const KanaTableEntry[] KanaTable = { + const KanaTableEntry[] KanaTable = { {'ア', "あ", "ア"}, {'イ', "い", "イ"}, {'ウ', "う", "ウ"}, {'エ', "え", "エ"}, {'オ', "お", "オ"}, {'カ', "か", "カ"}, {'キ', "き", "キ"}, {'ク', "く", "ク"}, {'ケ', "け", "ケ"}, @@ -73,13 +73,13 @@ namespace Kkc { {'、', "、", "、"}, {'・', "・", "・"}, {'ー', "ー", "ー"} }; - static const KanaTableEntry[] HankakuKatakanaSubstitute = { + const KanaTableEntry[] HankakuKatakanaSubstitute = { {'ヮ', null, "ワ"}, {'ヵ', null, "カ"}, {'ヶ', null, "ケ"} }; - static const string[] WideLatinTable = { + const string[] WideLatinTable = { " ", "!", "”", "#", "$", "%", "&", "’", "(", ")", "*", "+", ",", "−", ".", "/", "0", "1", "2", "3", "4", "5", "6", "7", @@ -94,7 +94,7 @@ namespace Kkc { "x", "y", "z", "{", "|", "}", "〜" }; - static const string[] KanaRomTable = { + const string[] KanaRomTable = { "x", "a", "x", "i", "x", "u", "x", "e", "x", "o", "k", "g", "k", "g", "k", "g", "k", "g", "k", "g", "s", "z", "s", "z", "s", "z", "s", "z", "s", "z", "t", "d", "t", @@ -126,20 +126,20 @@ namespace Kkc { return get_okurigana_prefix_for_char (head); } - static const string[] KanjiNumericTable = { + const string[] KanjiNumericTable = { "〇", "一", "二", "三", "四", "五", "六", "七", "八", "九" }; - static const string[] DaijiNumericTable = { + const string[] DaijiNumericTable = { "零", "壱", "弐", "参", "四", "伍", "六", "七", "八", "九" }; - static const string?[] KanjiNumericalPositionTable = { + const string?[] KanjiNumericalPositionTable = { null, "十", "百", "千", "万", null, null, null, "億", null, null, null, "兆", null, null, null, null, "京" }; - static const string?[] DaijiNumericalPositionTable = { + const string?[] DaijiNumericalPositionTable = { null, "拾", "百", "阡", "萬", null, null, null, "億", null, null, null, "兆", null, null, null, null, "京" }; diff --git a/libkkc/rom-kana.vala b/libkkc/rom-kana.vala index 529a1cc..96ecaa6 100644 --- a/libkkc/rom-kana.vala +++ b/libkkc/rom-kana.vala @@ -41,7 +41,7 @@ namespace Kkc { } } - static const string[] PUNCTUATION_RULE = {"。、", ".,", "。,", ".、"}; + const string[] PUNCTUATION_RULE = {"。、", ".,", "。,", ".、"}; class RomKanaNode : Object { internal RomKanaEntry? entry; @@ -410,7 +410,7 @@ namespace Kkc { * @return `true` if uc is in a valid range, `false` otherwise */ public bool is_valid (unichar uc) { - if (uc > 256) + if (uc >= 256) return false; uint8 mask = (uint8) (1 << (uc % 8)); return (current_node.valid[uc / 8] & mask) != 0 || diff --git a/libkkc/rule.vala b/libkkc/rule.vala index 61aa8ee..aa364a3 100644 --- a/libkkc/rule.vala +++ b/libkkc/rule.vala @@ -98,7 +98,7 @@ namespace Kkc { } else { throw new RuleParseError.FAILED ( - "\"rom-kana\" must have two to four elements"); + "\"rom-kana\" must have two to five elements"); } } else { throw new RuleParseError.FAILED ( @@ -122,6 +122,18 @@ namespace Kkc { else parent_map.set (key, value); } + + // Remove null entries added to parent_map, while + // traversing 'include'. We don't need to do that + // recursively, since those entries override the + // corresponding entries in ancestor maps. + var parent_iter = parent_map.map_iterator (); + while (parent_iter.next ()) { + var value = parent_iter.get_value (); + if (value.get_node_type () == Json.NodeType.NULL) + parent_iter.unset (); + } + load_rom_kana (root_node, parent_map); } } diff --git a/libkkc/server.vala b/libkkc/server.vala new file mode 100644 index 0000000..f4c25c0 --- /dev/null +++ b/libkkc/server.vala @@ -0,0 +1,492 @@ +/* + * Copyright (C) 2011-2015 Daiki Ueno + * Copyright (C) 2011-2015 Red Hat, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +using Gee; + +namespace Kkc +{ + namespace DBusUtils { + internal static void send_property_change (DBusConnection connection, + string object_path, + string interface_name, + string name, + Variant value) + { + var builder = new VariantBuilder (VariantType.ARRAY); + var invalid_builder = new VariantBuilder (new VariantType ("as")); + + builder.add ("{sv}", name, value); + + try { + connection.emit_signal (null, + object_path, + "org.freedesktop.DBus.Properties", + "PropertiesChanged", + new Variant ("(sa{sv}as)", + interface_name, + builder, + invalid_builder) + ); + } catch (Error e) { + stderr.printf ("%s\n", e.message); + } + } + } + + [DBus (name = "org.du_a.Kkc.CandidateList")] + public class DBusCandidateList : Object + { + DBusConnection connection; + string object_path; + Kkc.CandidateList candidates; + + public DBusCandidateList (DBusConnection connection, + string object_path, + Kkc.CandidateList candidates) + { + this.connection = connection; + this.object_path = object_path; + this.candidates = candidates; + this.candidates.populated.connect (() => { + this.populated (); + }); + this.candidates.selected.connect ((candidate) => { + this.selected (candidate.midasi, + candidate.okuri, + candidate.text, + candidate.annotation ?? ""); + }); + this.candidates.notify["cursor-pos"].connect ((p) => { + DBusUtils.send_property_change ( + connection, + object_path, + "org.du_a.Kkc.CandidateList", + "CursorPos", + new Variant.int32 (cursor_pos)); + }); + register (); + } + + ~DBusCandidateList () { + unregister (); + } + + public int cursor_pos { + get { + return this.candidates.cursor_pos; + } + } + + public int size { + get { + return this.candidates.size; + } + } + + public bool select_at (uint index_in_page) { + return this.candidates.select_at (index_in_page); + } + + public void select () { + this.candidates.select (); + } + + public bool first () { + return this.candidates.first (); + } + + public bool next () { + return this.candidates.next (); + } + + public bool previous () { + return this.candidates.previous (); + } + + public bool cursor_up () { + return this.candidates.cursor_up (); + } + + public bool cursor_down () { + return this.candidates.cursor_down (); + } + + public bool page_up () { + return this.candidates.page_up (); + } + + public bool page_down () { + return this.candidates.page_down (); + } + + public uint page_start { + get { + return this.candidates.page_start; + } + } + + public uint page_size { + get { + return this.candidates.page_size; + } + } + + public bool round { + get { + return this.candidates.round; + } + } + + public bool page_visible { + get { + return this.candidates.page_visible; + } + } + + public signal void populated (); + + public signal void selected (string midasi, bool okuri, + string text, string annotation); + + public new void @get (int index, out string midasi, out bool okuri, + out string text, out string annotation) + { + var candidate = this.candidates.get (index); + midasi = candidate.midasi; + okuri = candidate.okuri; + text = candidate.text; + annotation = candidate.annotation ?? ""; + } + + uint register_id = 0; + + void register () { + try { + register_id = connection.register_object (object_path, this); + } catch (IOError e) { + error ("Could not register D-Bus object at %s: %s", + object_path, e.message); + } + } + + internal void unregister () { + if (register_id > 0) { + connection.unregister_object (register_id); + register_id = 0; + } + } + } + + [DBus (name = "org.du_a.Kkc.SegmentList")] + public class DBusSegmentList : Object + { + DBusConnection connection; + string object_path; + Kkc.SegmentList segments; + + public DBusSegmentList (DBusConnection connection, + string object_path, + Kkc.SegmentList segments) + { + this.connection = connection; + this.object_path = object_path; + this.segments = segments; + this.segments.notify["cursor-pos"].connect ((p) => { + DBusUtils.send_property_change ( + connection, + object_path, + "org.du_a.Kkc.SegmentList", + "CursorPos", + new Variant.int32 (cursor_pos)); + }); + register (); + } + + ~DBusSegmentList () { + unregister (); + } + + public int cursor_pos { + get { + return this.segments.cursor_pos; + } + } + + public int size { + get { + return this.segments.size; + } + } + + public new void @get (int index, out string input, out string output) { + var segment = this.segments.get (index); + input = segment.input; + output = segment.output; + } + + public bool first_segment () { + return this.segments.first_segment (); + } + + public bool last_segment () { + return this.segments.last_segment (); + } + + public void next_segment () { + this.segments.next_segment (); + } + + public void previous_segment () { + this.segments.previous_segment (); + } + + public string get_output () { + return this.segments.get_output (); + } + + public string get_input () { + return this.segments.get_input (); + } + + uint register_id = 0; + + void register () { + try { + register_id = connection.register_object (object_path, this); + } catch (IOError e) { + error ("Could not register D-Bus object at %s: %s", + object_path, e.message); + } + } + + internal void unregister () { + if (register_id > 0) { + connection.unregister_object (register_id); + register_id = 0; + } + } + } + + [DBus (name = "org.du_a.Kkc.Context")] + public class DBusContext : Object + { + DBusConnection connection; + string object_path; + Kkc.Context context; + DBusCandidateList candidates; + DBusSegmentList segments; + + public DBusContext (DBusConnection connection, + string object_path, + Kkc.Context context) + { + this.connection = connection; + this.object_path = object_path; + this.context = context; + this.candidates = new DBusCandidateList ( + connection, + "%s/CandidateList".printf (object_path), + context.candidates); + this.segments = new DBusSegmentList ( + connection, + "%s/SegmentList".printf (object_path), + context.segments); + context.notify["input"].connect ((p) => { + DBusUtils.send_property_change ( + connection, + object_path, + "org.du_a.Kkc.Context", + "Input", + new Variant.string (input)); + }); + context.notify["input_cursor_pos"].connect ((p) => { + DBusUtils.send_property_change ( + connection, + object_path, + "org.du_a.Kkc.Context", + "InputCursorPos", + new Variant.int32 ((int32) input_cursor_pos)); + }); + register (); + } + + ~DBusContext () { + unregister (); + } + + public string input { + owned get { + return this.context.input; + } + } + + public int input_cursor_pos { + get { + return this.context.input_cursor_pos; + } + } + + public uint input_mode { + get { + return (uint) this.context.input_mode; + } + set { + this.context.input_mode = (InputMode) value; + } + } + + public uint punctuation_style { + get { + return (uint) this.context.punctuation_style; + } + set { + this.context.punctuation_style = (PunctuationStyle) value; + } + } + + public bool auto_correct { + get { + return this.context.auto_correct; + } + set { + this.context.auto_correct = value; + } + } + + public bool process_key_event (uint keyval, uint keycode, + uint modifiers) + { + var event = new Kkc.KeyEvent (keyval, keycode, + (ModifierType) modifiers); + return this.context.process_key_event (event); + } + + public bool process_command_event (string command) { + return this.context.process_command_event (command); + } + + public void reset () { + this.context.reset (); + } + + public bool has_output () { + return this.context.has_output (); + } + + public string peek_output () { + return this.context.peek_output (); + } + + public string poll_output () { + return this.context.poll_output (); + } + + public void clear_output () { + this.context.clear_output (); + } + + uint register_id = 0; + + void register () { + try { + register_id = connection.register_object (object_path, this); + } catch (IOError e) { + error ("Could not register D-Bus object at %s: %s", + object_path, e.message); + } + } + + internal void unregister () { + if (register_id > 0) { + connection.unregister_object (register_id); + candidates.unregister (); + segments.unregister (); + register_id = 0; + } + } + } + + [DBus (name = "org.du_a.Kkc.Server")] + public class DBusServer : Object { + DBusConnection connection; + Kkc.LanguageModel model; + Kkc.DictionaryList dictionaries; + Kkc.Rule? typing_rule; + uint own_name_id; + uint context_id = 0; + + public DBusServer (DBusConnection connection, + Kkc.LanguageModel model, + Kkc.DictionaryList dictionaries, + Kkc.Rule? typing_rule) { + this.connection = connection; + this.model = model; + this.dictionaries = dictionaries; + this.typing_rule = typing_rule; + own_name_id = Bus.own_name_on_connection ( + connection, + "org.du_a.Kkc.Server", + BusNameOwnerFlags.NONE, + on_name_acquired, on_name_lost); + } + + ~DBusServer () { + Bus.unown_name (own_name_id); + } + + void on_name_acquired (DBusConnection connection, string name) { + try { + connection.register_object ("/org/du_a/Kkc/Server", this); + } catch (IOError e) { + error ("Could not register D-Bus service %s: %s", + name, e.message); + } + } + + void on_name_lost (DBusConnection connection, string name) { + } + + public string create_context (BusName sender) { + var context = new Kkc.Context (this.model); + context.dictionaries = dictionaries; + if (typing_rule != null) + context.typing_rule = typing_rule; + var object_path = "/org/du_a/Kkc/Context_%u".printf (context_id++); + var dbus_context = new DBusContext (connection, + object_path, + context); + contexts.set (object_path, dbus_context); + Bus.watch_name_on_connection ( + connection, + sender, + BusNameWatcherFlags.NONE, + null, + (c, n) => { + destroy_context (object_path); + }); + return object_path; + } + + Map contexts = new HashMap (); + + public void destroy_context (string object_path) { + DBusContext context; + if (contexts.unset (object_path, out context)) + context.unregister (); + } + } +} diff --git a/libkkc/state.vala b/libkkc/state.vala index 4ba4c50..c4b3ba5 100644 --- a/libkkc/state.vala +++ b/libkkc/state.vala @@ -324,11 +324,14 @@ namespace Kkc { out _candidates)) { return template.expand (_candidates[0].text); } - template = new OkuriganaTemplate (input); - if (segment_dict.lookup_candidates (template.source, - template.okuri, - out _candidates)) { - return template.expand (_candidates[0].text); + var count = input.char_count (); + if (count > 1) { + template = new OkuriganaTemplate (input, count - 1); + if (segment_dict.lookup_candidates (template.source, + template.okuri, + out _candidates)) { + return template.expand (_candidates[0].text); + } } return null; } @@ -385,7 +388,10 @@ namespace Kkc { // 1. Look up candidates from user segment dictionaries. lookup_template (new NumericTemplate (normalized_input), true); lookup_template (new SimpleTemplate (normalized_input), true); - lookup_template (new OkuriganaTemplate (normalized_input), true); + for (var i = normalized_input.char_count (); i > 1; i--) { + lookup_template ( + new OkuriganaTemplate (normalized_input, i - 1), true); + } // 2. Look up the most frequently used unigram from language model. if (normalized_input.char_count () > 1) { @@ -405,7 +411,6 @@ namespace Kkc { // 3. Look up candidates from system segment dictionaries. lookup_template (new NumericTemplate (normalized_input), false); lookup_template (new SimpleTemplate (normalized_input), false); - lookup_template (new OkuriganaTemplate (normalized_input), false); // 4. Do sentence conversion with N-best search. @@ -445,9 +450,17 @@ namespace Kkc { builder.str); if (!kana_candidates.contains (sentence)) candidates.add (sentence); + + } + + // 4.3. Look up okuri-ari candidates from system segment + // dictionaries, for each possible okurigana combination. + for (var i = normalized_input.char_count (); i > 1; i--) { + lookup_template ( + new OkuriganaTemplate (normalized_input, i - 1), false); } - // 4.3. Add Kana candidates at the end. + // 4.4. Add Kana candidates at the end. candidates.add_all (kana_candidates); candidates.populated (); @@ -731,10 +744,10 @@ namespace Kkc { } interface CommandHandler : Object { - public abstract bool call (string? command, State state, KeyEvent key); + public abstract bool call (string command, State state, KeyEvent key); } - delegate bool CommandCallback (string? command, State state, KeyEvent key); + delegate bool CommandCallback (string command, State state, KeyEvent key); class CallbackCommandHandler : CommandHandler, Object { unowned CommandCallback cb; @@ -743,7 +756,7 @@ namespace Kkc { this.cb = cb; } - public bool call (string? command, + public bool call (string command, State state, KeyEvent key) { @@ -771,13 +784,19 @@ namespace Kkc { register_command_handler (command, new CallbackCommandHandler (cb)); } - public bool dispatch_command (State state, KeyEvent key) { - var command = state.lookup_key (key); + public abstract bool default_command_callback (string? command, + State state, + KeyEvent key); + + public bool dispatch_command (string? command, + State state, + KeyEvent key) + { if (command != null && command_handlers.has_key (command)) return command_handlers.get (command).call (command, state, key); - return default_command_handler.call (command, state, key); + return default_command_callback (command, state, key); } public abstract bool process_key_event (State state, KeyEvent key); diff --git a/libkkc/template.vala b/libkkc/template.vala index 7768f80..92c9995 100644 --- a/libkkc/template.vala +++ b/libkkc/template.vala @@ -42,19 +42,15 @@ namespace Kkc { string? okurigana = null; - public OkuriganaTemplate (string source) { - var count = source.char_count (); - if (count > 1) { - var last_char_index = source.index_of_nth_char (count - 1); - this.okurigana = source[last_char_index:source.length]; - string? prefix = RomKanaUtils.get_okurigana_prefix ( - this.okurigana); - this.source = source[0:last_char_index] + prefix; - this.okuri = true; - } else { - this.source = source; - this.okuri = false; - } + public OkuriganaTemplate (string source, int pos) { + assert (source.char_count () > 1); + assert (0 < pos && pos < source.char_count ()); + + var last_char_index = source.index_of_nth_char (pos); + this.okurigana = source[last_char_index:source.length]; + string? prefix = RomKanaUtils.get_okurigana_prefix (this.okurigana); + this.source = source[0:last_char_index] + prefix; + this.okuri = true; } public string expand (string text) { diff --git a/po/POTFILES.in b/po/POTFILES.in index cd2e1b8..aee91a8 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -1,13 +1,14 @@ libkkc/keymap.vala tools/kkc.vala -data/rules/tutcode/metadata.json.in -data/rules/azik/metadata.json.in -data/rules/trycode/metadata.json.in -data/rules/default/metadata.json.in -data/rules/azik-jp106/metadata.json.in -data/rules/tcode/metadata.json.in -data/rules/act/metadata.json.in -data/rules/kana/metadata.json.in -data/rules/nicola/metadata.json.in -data/rules/tutcode-touch16x/metadata.json.in -data/rules/kzik/metadata.json.in +data/rules/tutcode/metadata.pot +data/rules/azik/metadata.pot +data/rules/atok/metadata.pot +data/rules/trycode/metadata.pot +data/rules/default/metadata.pot +data/rules/azik-jp106/metadata.pot +data/rules/tcode/metadata.pot +data/rules/act/metadata.pot +data/rules/kana/metadata.pot +data/rules/nicola/metadata.pot +data/rules/tutcode-touch16x/metadata.pot +data/rules/kzik/metadata.pot diff --git a/tests/Makefile.am b/tests/Makefile.am index 2ac9145..ec6b547 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -248,4 +248,11 @@ clean-local: -rm -rf test-user-segment-dictionary -rm -rf test-user-sentence-dictionary +GITIGNOREFILES = \ + test-driver \ + data/rules \ + data/models \ + $(TESTS:=.c) \ + $(NULL) + -include $(top_srcdir)/git.mk diff --git a/tests/candidate-list.vala b/tests/candidate-list.vala index 54d9d38..f089cd0 100644 --- a/tests/candidate-list.vala +++ b/tests/candidate-list.vala @@ -35,9 +35,9 @@ class CandidateListTests : Kkc.TestCase { candidates.page_start = 2; candidates.page_size = 3; + candidates.add (new Kkc.Candidate ("a", false, "0")); candidates.add (new Kkc.Candidate ("a", false, "1")); candidates.add (new Kkc.Candidate ("a", false, "2")); - candidates.add (new Kkc.Candidate ("a", false, "3")); assert (!candidates.page_visible); candidates.cursor_down (); @@ -45,21 +45,25 @@ class CandidateListTests : Kkc.TestCase { candidates.cursor_down (); assert (candidates.page_visible); + candidates.add (new Kkc.Candidate ("a", false, "3")); candidates.add (new Kkc.Candidate ("a", false, "4")); - candidates.add (new Kkc.Candidate ("a", false, "5")); candidates.round = false; assert (!candidates.page_down ()); assert (!candidates.page_up ()); + candidates.add (new Kkc.Candidate ("a", false, "5")); + candidates.add (new Kkc.Candidate ("a", false, "6")); + candidates.add (new Kkc.Candidate ("a", false, "7")); + candidates.round = true; assert (candidates.page_down ()); - assert (candidates.cursor_pos == 0); + assert (candidates.cursor_pos == 5); assert (candidates.page_up ()); - assert (candidates.cursor_pos == 3); + assert (candidates.cursor_pos == 2); assert (candidates.select_at (1)); - assert (candidates.cursor_pos == 4); + assert (candidates.cursor_pos == 3); candidates.first (); assert (candidates.next ()); @@ -68,8 +72,9 @@ class CandidateListTests : Kkc.TestCase { assert (candidates.cursor_pos == 0); assert (candidates.next ()); assert (candidates.next ()); + assert (candidates.cursor_pos == 2); assert (candidates.next ()); - assert (candidates.cursor_pos == 0); + assert (candidates.cursor_pos == 5); } } diff --git a/tests/conversions-segment.json b/tests/conversions-segment.json index 63d0b9b..33baadf 100644 --- a/tests/conversions-segment.json +++ b/tests/conversions-segment.json @@ -122,11 +122,11 @@ { "keys": "w a t a s h i n o n a m a e h a n a k a n o d e s u SPC SPC", "input": "わたしのなまえはなかのです", - "segments": "わたしの名前は中野です", + "segments": "渡しの名前は中野です", "segments_size": 3, "segments_cursor_pos": 0, "output": "", - "candidates_size": 4, + "candidates_size": 5, "input_cursor_pos": -1 }, { @@ -136,7 +136,7 @@ "segments_size": 3, "segments_cursor_pos": 0, "output": "", - "candidates_size": 4, + "candidates_size": 5, "input_cursor_pos": -1 }, { @@ -152,17 +152,17 @@ { "keys": "w a t a s h i n o n a m a e h a n a k a n o d e s u SPC SPC Right", "input": "わたしのなまえはなかのです", - "segments": "わたしの名前は中野です", + "segments": "渡しの名前は中野です", "segments_size": 3, "segments_cursor_pos": 1, "output": "", - "candidates_size": 4, + "candidates_size": 5, "input_cursor_pos": -1 }, { "keys": "w a t a s h i n o n a m a e h a n a k a n o d e s u SPC SPC Right SPC", "input": "わたしのなまえはなかのです", - "segments": "わたしのなまえは中野です", + "segments": "渡しのなまえは中野です", "segments_size": 3, "segments_cursor_pos": 1, "output": "", @@ -172,7 +172,7 @@ { "keys": "w a t a s h i n o n a m a e h a n a k a n o d e s u SPC SPC Right SPC SPC", "input": "わたしのなまえはなかのです", - "segments": "わたしのナマエハ中野です", + "segments": "渡しのナマエハ中野です", "segments_size": 3, "segments_cursor_pos": 1, "output": "", diff --git a/tests/conversions-user-dictionary.json b/tests/conversions-user-dictionary.json index 6c52df5..c5ddace 100644 --- a/tests/conversions-user-dictionary.json +++ b/tests/conversions-user-dictionary.json @@ -29,12 +29,12 @@ "segments": "", "segments_size": 0, "segments_cursor_pos": -1, - "output": "わたしの名前はなかのです" + "output": "渡しの名前はなかのです" }, { "keys": "w a t a s h i n o n a m a e h a n a k a n o d e s u SPC", "input": "わたしのなまえはなかのです", - "segments": "わたしの名前はなかのです", + "segments": "渡しの名前はなかのです", "segments_size": 2, "segments_cursor_pos": 0, "output": "" @@ -42,7 +42,7 @@ { "keys": "w a t a s h i n o n a m a e h a n a k a n o d e s u SPC Right SPC Right Right SPC", "input": "わたしのなまえはなかのです", - "segments": "わたしのなまえはなかのです", + "segments": "渡しのなまえはなかのです", "segments_size": 2, "segments_cursor_pos": 1, "output": "" diff --git a/tests/template.vala b/tests/template.vala index 1f8fb5e..5900cd1 100644 --- a/tests/template.vala +++ b/tests/template.vala @@ -16,7 +16,7 @@ class TemplateTests : Kkc.TestCase { assert (source == "source"); assert (!okuri); - template = new Kkc.OkuriganaTemplate ("かう"); + template = new Kkc.OkuriganaTemplate ("かう", 1); template.get ("source", out source, "okuri", out okuri); diff --git a/tools/Makefile.am b/tools/Makefile.am index e65c513..7d05834 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -18,6 +18,7 @@ AM_CPPFLAGS = -include $(CONFIG_HEADER) bin_PROGRAMS = kkc bin_SCRIPTS = kkc-package-data +noinst_PROGRAMS = gen-metadata-pot kkc_VALAFLAGS = \ --vapidir=$(top_srcdir)/libkkc \ @@ -36,7 +37,17 @@ kkc_CFLAGS = \ kkc_LDADD = $(top_builddir)/libkkc/libkkc.la $(LIBKKC_LIBS) kkc_SOURCES = kkc.vala +gen_metadata_pot_VALAFLAGS = --pkg json-glib-1.0 --pkg posix $(VALAFLAGS) +gen_metadata_pot_SOURCES = gen-metadata-pot.vala +gen_metadata_pot_CFLAGS = $(JSON_GLIB_CFLAGS) +gen_metadata_pot_LDADD = $(JSON_GLIB_LIBS) + DISTCLEANFILES = kkc-package-data EXTRA_DIST = kkc-package-data.in +GITIGNOREFILES = \ + kkc.c \ + gen-metadata-pot.c \ + *_vala.stamp \ + $(NULL) -include $(top_srcdir)/git.mk diff --git a/tools/gen-metadata-pot.vala b/tools/gen-metadata-pot.vala new file mode 100644 index 0000000..4bcfdca --- /dev/null +++ b/tools/gen-metadata-pot.vala @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2015 Daiki Ueno + * Copyright (C) 2015 Red Hat, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +static int main (string[] args) { + if (args.length < 3) { + stderr.printf ("Usage: gen-metadata-pot FILE EXPR...\n"); + return Posix.EXIT_FAILURE; + } + + var parser = new Json.Parser (); + try { + parser.load_from_file (args[1]); + } catch (Error e) { + stderr.printf ("Can't load json file %s: %s\n", + args[1], e.message); + return Posix.EXIT_FAILURE; + } + + var root = parser.get_root (); + for (var i = 2; i < args.length; i++) { + Json.Node result; + try { + result = Json.Path.query (args[i], root); + } catch (Error e) { + stderr.printf ("can't parse json expression \"%s\": %s\n", + args[i], e.message); + return Posix.EXIT_FAILURE; + } + var array = result.get_array (); + array.foreach_element ((a, index_, node) => { + stdout.printf ("msgid \"%s\"\nmsgstr \"\"\n\n", + node.get_string ()); + }); + } + + return Posix.EXIT_SUCCESS; +} \ No newline at end of file diff --git a/tools/kkc.vala b/tools/kkc.vala index c383584..e9186d8 100644 --- a/tools/kkc.vala +++ b/tools/kkc.vala @@ -58,6 +58,7 @@ static void usage (string[] args, FileStream output) { help Shows this information decoder Run decoder context Run context + server Run server Use "%s COMMAND --help" to get help on each command. """).printf ( @@ -85,11 +86,13 @@ static int main (string[] args) { Environment.set_prgname ("%s %s".printf (args[0], new_args[0])); - Repl repl; + Tool tool; if (new_args[0] == "decoder") - repl = new DecoderRepl (); + tool = new DecoderTool (); else if (new_args[0] == "context") - repl = new ContextRepl (); + tool = new ContextTool (); + else if (new_args[0] == "server") + tool = new ServerTool (); else if (new_args[0] == "help") { usage (args, stdout); return 0; @@ -100,14 +103,14 @@ static int main (string[] args) { } try { - repl.parse_arguments (new_args); + tool.parse_arguments (new_args); } catch (Error e) { usage (args, stderr); return 1; } try { - repl.run (); + tool.run (); } catch (Error e) { return 1; } @@ -115,12 +118,12 @@ static int main (string[] args) { return 0; } -interface Repl : Object { +interface Tool : Object { public abstract bool parse_arguments (string[] args) throws Error; public abstract bool run () throws Error; } -class DecoderRepl : Object, Repl { +class DecoderTool : Object, Tool { public bool parse_arguments (string[] args) throws Error { var o = new OptionContext ( _("- run decoder on the command line")); @@ -175,7 +178,7 @@ class DecoderRepl : Object, Repl { } } -class ContextRepl : Object, Repl { +class ContextTool : Object, Tool { public bool parse_arguments (string[] args) throws Error { var o = new OptionContext ( _("- run context on the command line")); @@ -268,3 +271,81 @@ class ContextRepl : Object, Repl { return true; } } + +class ServerTool : Object, Tool { + public bool parse_arguments (string[] args) throws Error { + var o = new OptionContext ( + _("- run server on the command line")); + o.add_main_entries (context_entries, "libkkc"); + o.add_group ((owned) model_group); + + return o.parse (ref args); + } + + public bool run () throws Error { + if (opt_typing_rule == "?") { + var rules = Kkc.Rule.list (); + foreach (var rule in rules) { + stdout.printf ("%s - %s: %s\n", + rule.name, + rule.label, + rule.description); + } + return true; + } + + Kkc.LanguageModel model; + try { + var name = opt_model == null ? "sorted3" : opt_model; + model = Kkc.LanguageModel.load (name); + } catch (Kkc.LanguageModelError e) { + stderr.printf ("%s\n", e.message); + return false; + } + + var dictionaries = new Kkc.DictionaryList (); + if (opt_user_dictionary != null) { + try { + dictionaries.add ( + new Kkc.UserDictionary (opt_user_dictionary)); + } catch (GLib.Error e) { + stderr.printf ("can't open user dictionary %s: %s", + opt_user_dictionary, e.message); + return false; + } + } + + if (opt_system_dictionary == null) + opt_system_dictionary = Path.build_filename (Config.DATADIR, + "skk", "SKK-JISYO.L"); + + try { + dictionaries.add ( + new Kkc.SystemSegmentDictionary (opt_system_dictionary)); + } catch (GLib.Error e) { + stderr.printf ("can't open system dictionary %s: %s", + opt_system_dictionary, e.message); + return false; + } + + Kkc.Rule? typing_rule = null; + if (opt_typing_rule != null) { + try { + var metadata = Kkc.RuleMetadata.find (opt_typing_rule); + typing_rule = new Kkc.Rule (metadata); + } catch (Kkc.RuleParseError e) { + stderr.printf ("can't load rule \"%s\": %s\n", + opt_typing_rule, + e.message); + return false; + } + } + + var connection = Bus.get_sync (BusType.SESSION); + var server = new Kkc.DBusServer (connection, + model, dictionaries, typing_rule); + var loop = new MainLoop (null, true); + loop.run (); + return true; + } +}