aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/config.pri5
-rw-r--r--src/virtualkeyboard/3rdparty/t9write/t9write-build.pri86
-rw-r--r--src/virtualkeyboard/3rdparty/t9write/t9write.pro11
-rw-r--r--src/virtualkeyboard/3rdparty/t9write/unpack.py231
-rw-r--r--src/virtualkeyboard/content/components/InputModeKey.qml1
-rw-r--r--src/virtualkeyboard/content/components/Keyboard.qml2
-rw-r--r--src/virtualkeyboard/content/layouts/zh_CN/handwriting.qml116
-rw-r--r--src/virtualkeyboard/content/layouts/zh_CN/main.qml3
-rw-r--r--src/virtualkeyboard/content/styles/default/style.qml39
-rw-r--r--src/virtualkeyboard/content/styles/retro/style.qml35
-rw-r--r--src/virtualkeyboard/doc/src/build.qdoc130
-rw-r--r--src/virtualkeyboard/inputengine.cpp1
-rw-r--r--src/virtualkeyboard/inputengine.h3
-rw-r--r--src/virtualkeyboard/shifthandler.cpp2
-rw-r--r--src/virtualkeyboard/t9write.h49
-rw-r--r--src/virtualkeyboard/t9writedictionary.cpp117
-rw-r--r--src/virtualkeyboard/t9writedictionary.h67
-rw-r--r--src/virtualkeyboard/t9writeinputmethod.cpp994
-rw-r--r--src/virtualkeyboard/t9writeinputmethod.h5
-rw-r--r--src/virtualkeyboard/t9writeworker.cpp66
-rw-r--r--src/virtualkeyboard/t9writeworker.h24
-rw-r--r--src/virtualkeyboard/virtualkeyboard.pro62
-rw-r--r--tests/auto/inputpanel/data/inputpanel/handwriting.js12
-rw-r--r--tests/auto/inputpanel/data/inputpanel/inputpanel.qml4
-rw-r--r--tests/auto/inputpanel/data/inputpanel/unipen_data.js366
-rw-r--r--tests/auto/inputpanel/data/inputpanel/unipen_data_simp_chinese.js1851
-rw-r--r--tests/auto/inputpanel/data/tst_inputpanel.qml36
-rw-r--r--tests/auto/inputpanel/hwr_test_data/alphanumeric/113_100_0.txt118
-rwxr-xr-xtests/auto/inputpanel/hwr_test_data/build_unipen_data.py66
-rw-r--r--tests/auto/inputpanel/hwr_test_data/simp_chinese/20013_100_0.txt216
-rw-r--r--tests/auto/inputpanel/hwr_test_data/simp_chinese/25991_100_0.txt172
31 files changed, 4347 insertions, 543 deletions
diff --git a/src/config.pri b/src/config.pri
index 11ae1c0b..c1784b14 100644
--- a/src/config.pri
+++ b/src/config.pri
@@ -4,6 +4,11 @@ handwriting:!lipi-toolkit:!t9write {
equals(T9WRITE_FOUND, 1): CONFIG += t9write
else: CONFIG += lipi-toolkit
}
+t9write {
+ !handwriting: include(virtualkeyboard/3rdparty/t9write/t9write-build.pri)
+ equals(T9WRITE_CJK_FOUND, 1): CONFIG += t9write-cjk
+ equals(T9WRITE_ALPHABETIC_FOUND, 1): CONFIG += t9write-alphabetic
+}
# Disable built-in layouts
disable-layouts {
diff --git a/src/virtualkeyboard/3rdparty/t9write/t9write-build.pri b/src/virtualkeyboard/3rdparty/t9write/t9write-build.pri
index ab519b90..f810badf 100644
--- a/src/virtualkeyboard/3rdparty/t9write/t9write-build.pri
+++ b/src/virtualkeyboard/3rdparty/t9write/t9write-build.pri
@@ -1,25 +1,83 @@
#
# Automatically detects the T9Write build directory and sets the following variables:
#
-# T9WRITE_BUILD_DIR: A base directory for the architecture specific build directory
-# T9WRITE_ALPHABETIC_OBJ: Absolute path to the target object file
+# T9WRITE_FOUND: 0/1 T9Write SDK found
+# T9WRITE_BUILD_STATIC: 0/1 Static libraries found (0 == shared libraries)
+# T9WRITE_ALPHABETIC_FOUND: 0/1 T9 Write Alphabetic API header found
+# T9WRITE_CJK_FOUND: 0/1 T9 Write CJK API header found
+# T9WRITE_INCLUDE_DIRS: T9 Write include directories
+# T9WRITE_ALPHABETIC_LIBS: Absolute path to the target library file
+# T9WRITE_ALPHABETIC_BINS: Absolute path to the target binary file (shared library)
+# T9WRITE_CJK_LIBS: Absolute path to the target library file
+# T9WRITE_CJK_BINS: Absolute path to the target binary file (shared library)
#
+T9WRITE_FOUND = 0
+T9WRITE_ALPHABETIC_FOUND = 0
+T9WRITE_CJK_FOUND = 0
+T9WRITE_INCLUDE_DIRS = $$PWD/api
contains(QT_ARCH, arm) {
- T9WRITE_BUILD_DIR = $$files(build_Android_ARM*)
+ T9WRITE_BUILD_SHARED_DIR = lib/arm/shared
+ T9WRITE_BUILD_STATIC_DIR = lib/arm/static
} else:linux {
- T9WRITE_BUILD_DIR = $$files(build_Android_x86*)
+ T9WRITE_BUILD_SHARED_DIR = lib/linux-x86/shared
+ T9WRITE_BUILD_STATIC_DIR = lib/linux-x86/static
} else:win32 {
- T9WRITE_BUILD_DIR = $$files(build_VC*)
+ T9WRITE_BUILD_SHARED_DIR = lib/win32/shared
+ T9WRITE_BUILD_STATIC_DIR = lib/win32/static
}
-count(T9WRITE_BUILD_DIR, 1) {
- T9WRITE_FOUND = 1
- T9WRITE_INCLUDE_DIRS = \
- $$PWD/$$T9WRITE_BUILD_DIR/api \
- $$PWD/$$T9WRITE_BUILD_DIR/public
- T9WRITE_ALPHABETIC_LIBS = \
- $$PWD/$$files($$T9WRITE_BUILD_DIR/objects/t9write_alphabetic*.o*)
-} else {
- T9WRITE_FOUND = 0
+defineReplace(findStaticLibrary) {
+ win32 {
+ result = $$files($$1/*.obj)
+ isEmpty(result): result = $$files($$1/*.lib)
+ } else {
+ result = $$files($$1/*.o)
+ isEmpty(result): result = $$files($$1/*.a)
+ }
+ return($$result)
+}
+
+defineReplace(findSharedLibrary) {
+ win32 {
+ result = $$files($$1/*.lib)
+ } else {
+ result = $$files($$1/*.so)
+ }
+ return($$result)
+}
+
+defineReplace(findSharedBinary) {
+ win32 {
+ result = $$files($$1/*.dll)
+ } else {
+ result = $$files($$1/*.so)
+ }
+ return($$result)
+}
+
+for(include_dir, T9WRITE_INCLUDE_DIRS) {
+ exists($${include_dir}/decuma_hwr.h): T9WRITE_ALPHABETIC_FOUND = 1
+ exists($${include_dir}/decuma_hwr_cjk.h): T9WRITE_CJK_FOUND = 1
+}
+
+equals(T9WRITE_ALPHABETIC_FOUND, 1)|equals(T9WRITE_CJK_FOUND, 1) {
+ equals(T9WRITE_ALPHABETIC_FOUND, 1) {
+ T9WRITE_ALPHABETIC_LIBS = $$findSharedLibrary($$PWD/$$T9WRITE_BUILD_SHARED_DIR/alphabetic)
+ !isEmpty(T9WRITE_ALPHABETIC_LIBS) {
+ T9WRITE_ALPHABETIC_BINS = $$findSharedBinary($$PWD/$$T9WRITE_BUILD_SHARED_DIR/alphabetic)
+ } else {
+ T9WRITE_ALPHABETIC_LIBS = $$findStaticLibrary($$PWD/$$T9WRITE_BUILD_STATIC_DIR/alphabetic)
+ }
+ }
+ equals(T9WRITE_CJK_FOUND, 1) {
+ T9WRITE_CJK_LIBS = $$findSharedLibrary($$PWD/$$T9WRITE_BUILD_SHARED_DIR/cjk)
+ !isEmpty(T9WRITE_CJK_LIBS) {
+ T9WRITE_CJK_BINS = $$findSharedBinary($$PWD/$$T9WRITE_BUILD_SHARED_DIR/cjk)
+ } else {
+ T9WRITE_CJK_LIBS = $$findStaticLibrary($$PWD/$$T9WRITE_BUILD_STATIC_DIR/cjk)
+ }
+ }
+ equals(T9WRITE_ALPHABETIC_FOUND, 1):!isEmpty(T9WRITE_ALPHABETIC_LIBS): T9WRITE_FOUND = 1
+ equals(T9WRITE_CJK_FOUND, 1):!isEmpty(T9WRITE_CJK_LIBS): T9WRITE_FOUND = 1
}
diff --git a/src/virtualkeyboard/3rdparty/t9write/t9write.pro b/src/virtualkeyboard/3rdparty/t9write/t9write.pro
index 05f723a7..f6dddf1c 100644
--- a/src/virtualkeyboard/3rdparty/t9write/t9write.pro
+++ b/src/virtualkeyboard/3rdparty/t9write/t9write.pro
@@ -2,18 +2,19 @@ TARGET = qtt9write_db
CONFIG += static
-T9WRITE_LDBS = $$files(databases/XT9_LDBs/*.ldb)
-
T9WRITE_RESOURCE_FILES = \
- databases/HWR_LatinCG/_databas_le.bin \
- $$T9WRITE_LDBS
+ $$files(data/*.bin) \
+ $$files(data/*.ldb) \
+ $$files(data/*.hdb) \
+ $$files(data/*.phd)
# Note: Compression is disabled, because the resource is accessed directly from the memory
QMAKE_RESOURCE_FLAGS += -no-compress
+CONFIG += resources_big
include(../../generateresource.pri)
-RESOURCES += $$generate_resource(t9write_db.qrc, $$T9WRITE_RESOURCE_FILES)
+RESOURCES += $$generate_resource(t9write_db.qrc, $$T9WRITE_RESOURCE_FILES, /QtQuick/VirtualKeyboard/T9Write)
load(qt_helper_lib)
diff --git a/src/virtualkeyboard/3rdparty/t9write/unpack.py b/src/virtualkeyboard/3rdparty/t9write/unpack.py
new file mode 100644
index 00000000..1e213656
--- /dev/null
+++ b/src/virtualkeyboard/3rdparty/t9write/unpack.py
@@ -0,0 +1,231 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+#############################################################################
+##
+## Copyright (C) 2017 The Qt Company Ltd.
+## Contact: https://www.qt.io/licensing/
+##
+## This file is part of the Qt Virtual Keyboard module of the Qt Toolkit.
+##
+## $QT_BEGIN_LICENSE:GPL$
+## Commercial License Usage
+## Licensees holding valid commercial Qt licenses may use this file in
+## accordance with the commercial license agreement provided with the
+## Software or, alternatively, in accordance with the terms contained in
+## a written agreement between you and The Qt Company. For licensing terms
+## and conditions see https://www.qt.io/terms-conditions. For further
+## information use the contact form at https://www.qt.io/contact-us.
+##
+## GNU General Public License Usage
+## Alternatively, this file may be used under the terms of the GNU
+## General Public License version 3 or (at your option) any later version
+## approved by the KDE Free Qt Foundation. The licenses are as published by
+## the Free Software Foundation and appearing in the file LICENSE.GPL3
+## included in the packaging of this file. Please review the following
+## information to ensure the GNU General Public License requirements will
+## be met: https://www.gnu.org/licenses/gpl-3.0.html.
+##
+## $QT_END_LICENSE$
+##
+#############################################################################
+
+import os
+import sys
+import zipfile
+import tempfile
+import shutil
+import fnmatch
+
+#
+# This utility script unpacks the T9 Write SDK to appropriate directory
+# structure for Qt Virtual Keyboard.
+#
+# Usage: unpack.py <filename.zip> <target dir>
+#
+# The script will happily overwrite existing files, so be careful.
+#
+
+#
+# Unpack rule map
+#
+# Format:
+# 1. <target dir>: [ 'pattern1', 'pattern2', ... ]
+# - Each pattern is matched against the zip file contents. The file is
+# copied to target dir if the pattern matches. Each pattern is handled
+# independent of each other.
+#
+# 2. <target dir>: [ [ 'file group pattern', 'sub pattern1', ... ] ]
+# - First the file group pattern is matched against the zip file contents.
+# Then all the sub patterns are matched in the sub directory specified by
+# the first match. If all the sub patterns match, then first match from
+# file group pattern and all the matching files from sub pattterns are copied.
+# The purpose of this option is to copy coupled files, e.g. DLL and LIB
+# files found in the same directory.
+#
+
+UNPACK_RULES = {
+'api': [
+ '*/decuma_hwr.h',
+ '*/decuma_hwr_cjk.h',
+ '*/decuma_hwr_types.h',
+ '*/decuma_point.h',
+ '*/decumaBasicTypes.h',
+ '*/decumaBasicTypesMinMax.h',
+ '*/decumaCharacterSetType.h',
+ '*/decumaCurve.h',
+ '*/decumaFunctionalSupport.h',
+ '*/decumaFunctionalSupportCheck.h',
+ '*/decumaLanguages.h',
+ '*/decumaLiteFunctionalSupport.h',
+ '*/decumaRuntimeMallocData.h',
+ '*/decumaStatus.h',
+ '*/decumaStorageSpecifiers.h',
+ '*/decumaSymbolCategories.h',
+ '*/decumaUnicodeTypes.h',
+ '*/t9write_alpha_version.h',
+ '*/t9write_api_version.h',
+ '*/t9write_cjk_version.h',
+ '*/xxt9wApiOem.h',
+ '*/xxt9wOem.h',
+],
+'data': [
+ '*/_databas_le.bin',
+ '*/*.hdb',
+ '*/*.phd',
+ '*/*.ldb',
+],
+'lib/arm/static/alphabetic': [
+ '*T9Write_Alpha*/*Android_ARM*/*.a',
+ '*T9Write_Alpha*/*Android_ARM*/*.o',
+],
+'lib/arm/shared/alphabetic': [
+ '*T9Write_Alpha*/*Android_ARM*/*.so',
+],
+'lib/arm/static/cjk': [
+ '*T9Write_CJK*/*Android_ARM*/*.a',
+ '*T9Write_CJK*/*Android_ARM*/*.o',
+],
+'lib/arm/shared/cjk': [
+ '*T9Write_CJK*/*Android_ARM*/*.so',
+],
+'lib/linux/static/alphabetic': [
+ '*T9Write_Alpha*/*Android_x86*/*.a',
+ '*T9Write_Alpha*/*Android_x86*/*.o',
+],
+'lib/linux/shared/alphabetic': [
+ '*T9Write_Alpha*/*Android_x86*/*.so',
+],
+'lib/linux/static/cjk': [
+ '*T9Write_CJK*/*Android_x86*/*.a',
+ '*T9Write_CJK*/*Android_x86*/*.o',
+],
+'lib/linux/shared/cjk': [
+ '*T9Write_CJK*/*Android_x86*/*.so',
+],
+'lib/win32/static/alphabetic': [
+ '*T9Write_Alpha*/*.obj',
+],
+'lib/win32/shared/alphabetic': [
+ [ '*T9Write_Alpha*/*.dll', '*.lib' ],
+],
+'lib/win32/static/cjk': [
+ '*T9Write_CJK*/*.obj',
+],
+'lib/win32/shared/cjk': [
+ [ '*T9Write_CJK*/*.dll', '*.lib' ],
+],
+'lib/win32/shared/alphabetic': [
+ [ '*T9Write_Alpha*/*.dll', '*.lib' ],
+],
+}
+
+#
+# Blacklist
+#
+# File matching rules for blacklisted items. Matched before UNPACK_RULES.
+#
+
+BLACKLIST_RULES = [
+'*__MACOSX*',
+'*/.DS_Store',
+]
+
+def blacklist(file_list):
+ result = []
+ for file_name in file_list:
+ match = False
+ for blacklist_rule in BLACKLIST_RULES:
+ match = fnmatch.fnmatch(file_name, blacklist_rule)
+ if match:
+ break
+ if not match:
+ result.append(file_name)
+ return result
+
+def unzip(zip_file, target_dir):
+ zip_list = []
+ with zipfile.ZipFile(zip_file, 'r') as z:
+ zip_list = sorted(blacklist(z.namelist()))
+ zip_basename = os.path.splitext(os.path.basename(zip_file))[0]
+ if zip_list and zip_basename in zip_list[0]:
+ zip_basename = ''
+ zip_list = [os.path.join(zip_basename, zip_name).replace('\\', '/') for zip_name in zip_list]
+ z.extractall(os.path.join(target_dir, zip_basename))
+ return zip_list
+
+def match_file_list(file_list, base_dir, fnpattern):
+ return [file_name for file_name in file_list \
+ if fnmatch.fnmatch(file_name, fnpattern) and \
+ os.path.isfile(os.path.join(base_dir, file_name))]
+
+def unpack(zip_list, zip_dir, out_dir):
+ if not zip_list:
+ return
+
+ for (target_dir, match_rules) in UNPACK_RULES.items():
+ for match_rule in match_rules:
+ # Match
+ match_rule_group = match_rule if isinstance(match_rule, list) else [match_rule]
+ match_group_candidates = [match_file_list(zip_list, zip_dir, match_rule_group[0])]
+ if len(match_rule_group) > 1:
+ while len(match_group_candidates[0]) > 0:
+ match_group0_candidate = match_group_candidates[0][0]
+ all_sub_groups_match = True
+ for sub_group_rule in match_rule_group[1:]:
+ fnpattern = os.path.join(os.path.dirname(match_group0_candidate), sub_group_rule).replace('\\', '/')
+ sub_group_candidates = match_file_list(zip_list, zip_dir, fnpattern)
+ if not sub_group_candidates:
+ all_sub_groups_match = False
+ break
+ match_group_candidates.append(sub_group_candidates)
+ if all_sub_groups_match:
+ match_group_candidates[0] = [match_group0_candidate]
+ break
+ else:
+ match_group_candidates = [match_group_candidates[0][1:]]
+
+ # Copy
+ if match_group_candidates:
+ for match_group_candidate in match_group_candidates:
+ for zip_name in match_group_candidate:
+ dst_dir = os.path.join(out_dir, target_dir)
+ if not os.path.exists(dst_dir):
+ os.makedirs(dst_dir)
+ src = os.path.join(zip_dir, zip_name).replace('\\', '/')
+ dst = os.path.join(dst_dir, os.path.basename(zip_name)).replace('\\', '/')
+ print(zip_name + ' -> ' + dst)
+ shutil.copy2(src, dst)
+
+if __name__ == '__main__':
+ if len(sys.argv) != 3:
+ print("Usage: %s <filename.zip> <target dir>" % os.path.basename(__file__))
+ exit()
+
+ out_dir = sys.argv[2]
+ zip_dir = tempfile.mkdtemp()
+
+ try:
+ unpack(unzip(sys.argv[1], zip_dir), zip_dir, out_dir)
+ finally:
+ shutil.rmtree(zip_dir)
diff --git a/src/virtualkeyboard/content/components/InputModeKey.qml b/src/virtualkeyboard/content/components/InputModeKey.qml
index dcc10d54..d880867f 100644
--- a/src/virtualkeyboard/content/components/InputModeKey.qml
+++ b/src/virtualkeyboard/content/components/InputModeKey.qml
@@ -68,6 +68,7 @@ Key {
"あ", // InputEngine.Hiragana
"カ", // InputEngine.Katakana
"全角", // InputEngine.FullwidthLatin
+ "中文", // InputEngine.ChineseHandwriting
]
function __nextInputMode(inputMode) {
diff --git a/src/virtualkeyboard/content/components/Keyboard.qml b/src/virtualkeyboard/content/components/Keyboard.qml
index 2bd2ff2e..8dfd7f7d 100644
--- a/src/virtualkeyboard/content/components/Keyboard.qml
+++ b/src/virtualkeyboard/content/components/Keyboard.qml
@@ -1152,7 +1152,7 @@ Item {
inputMode = InputEngine.Dialable
else if ((InputContext.inputMethodHints & (Qt.ImhFormattedNumbersOnly | Qt.ImhDigitsOnly)) && inputModes.indexOf(InputEngine.Numeric) !== -1)
inputMode = InputEngine.Numeric
- else
+ else if (keyboardLayoutLoader.item.inputMode === -1)
inputMode = inputModes[0]
}
diff --git a/src/virtualkeyboard/content/layouts/zh_CN/handwriting.qml b/src/virtualkeyboard/content/layouts/zh_CN/handwriting.qml
new file mode 100644
index 00000000..598980d1
--- /dev/null
+++ b/src/virtualkeyboard/content/layouts/zh_CN/handwriting.qml
@@ -0,0 +1,116 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Virtual Keyboard module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+import QtQuick.Layouts 1.0
+import QtQuick.VirtualKeyboard 2.3
+
+KeyboardLayout {
+ function createInputMethod() {
+ return Qt.createQmlObject('import QtQuick 2.0; import QtQuick.VirtualKeyboard 2.3; HandwritingInputMethod {}', parent)
+ }
+ sharedLayouts: ['symbols']
+ inputMode: preferredInputMode()
+
+ Connections {
+ target: InputContext
+ onInputMethodHintsChanged: {
+ var newInputMode = preferredInputMode()
+ if (InputContext.inputEngine.inputModes.indexOf(newInputMode) !== -1)
+ InputContext.inputEngine.inputMode = newInputMode
+ }
+ }
+
+ function preferredInputMode() {
+ return InputContext.inputMethodHints &
+ (Qt.ImhPreferLatin | Qt.ImhEmailCharactersOnly | Qt.ImhUrlCharactersOnly |
+ Qt.ImhLatinOnly) ? InputEngine.Latin : InputEngine.ChineseHandwriting
+ }
+
+ KeyboardRow {
+ Layout.preferredHeight: 3
+ KeyboardColumn {
+ Layout.preferredWidth: bottomRow.width - hideKeyboardKey.width
+ KeyboardRow {
+ TraceInputKey {
+ objectName: "hwrInputArea"
+ patternRecognitionMode: InputEngine.HandwritingRecoginition
+ horizontalRulers:
+ InputContext.inputEngine.inputMode !== InputEngine.ChineseHandwriting ? [] :
+ [Math.round(boundingBox.height / 4), Math.round(boundingBox.height / 4) * 2, Math.round(boundingBox.height / 4) * 3]
+
+ }
+ }
+ }
+ KeyboardColumn {
+ Layout.preferredWidth: hideKeyboardKey.width
+ KeyboardRow {
+ BackspaceKey {}
+ }
+ KeyboardRow {
+ EnterKey {}
+ }
+ KeyboardRow {
+ ShiftKey { }
+ }
+ }
+ }
+ KeyboardRow {
+ id: bottomRow
+ Layout.preferredHeight: 1
+ keyWeight: 154
+ InputModeKey {
+ weight: 217
+ }
+ ChangeLanguageKey {
+ weight: 154
+ customLayoutsOnly: true
+ }
+ HandwritingModeKey {
+ weight: 154
+ }
+ SpaceKey {
+ weight: 864
+ }
+ Key {
+ key: Qt.Key_Apostrophe
+ text: "‘"
+ alternativeKeys: "《》〈〉•…々〆‘’“”「」¥"
+ }
+ Key {
+ key: Qt.Key_Period
+ text: "."
+ alternativeKeys: ":;,.、。?!"
+ }
+ HideKeyboardKey {
+ id: hideKeyboardKey
+ weight: 204
+ }
+ }
+}
diff --git a/src/virtualkeyboard/content/layouts/zh_CN/main.qml b/src/virtualkeyboard/content/layouts/zh_CN/main.qml
index c8e21c84..44b0ceb7 100644
--- a/src/virtualkeyboard/content/layouts/zh_CN/main.qml
+++ b/src/virtualkeyboard/content/layouts/zh_CN/main.qml
@@ -181,6 +181,9 @@ KeyboardLayout {
ChangeLanguageKey {
weight: 154
}
+ HandwritingModeKey {
+ weight: 154
+ }
SpaceKey {
weight: 864
}
diff --git a/src/virtualkeyboard/content/styles/default/style.qml b/src/virtualkeyboard/content/styles/default/style.qml
index 03f0344f..9d00bb11 100644
--- a/src/virtualkeyboard/content/styles/default/style.qml
+++ b/src/virtualkeyboard/content/styles/default/style.qml
@@ -757,7 +757,17 @@ KeyboardStyle {
Text {
id: hwrInputModeIndicator
visible: control.patternRecognitionMode === InputEngine.HandwritingRecoginition
- text: InputContext.inputEngine.inputMode === InputEngine.Latin ? "Abc" : "123"
+ text: {
+ switch (InputContext.inputEngine.inputMode) {
+ case InputEngine.Numeric:
+ case InputEngine.Dialable:
+ return "123"
+ case InputEngine.ChineseHandwriting:
+ return "中文"
+ default:
+ return "Abc"
+ }
+ }
color: "white"
anchors.left: parent.left
anchors.top: parent.top
@@ -786,23 +796,42 @@ KeyboardStyle {
ctx.strokeStyle = Qt.rgba(0xFF, 0xFF, 0xFF)
ctx.clearRect(0, 0, width, height)
var i
+ var margin = Math.round(30 * scaleHint)
if (control.horizontalRulers) {
for (i = 0; i < control.horizontalRulers.length; i++) {
ctx.beginPath()
- ctx.moveTo(0, control.horizontalRulers[i])
- ctx.lineTo(width, control.horizontalRulers[i])
+ var y = Math.round(control.horizontalRulers[i])
+ var rightMargin = Math.round(width - margin)
+ if (i + 1 === control.horizontalRulers.length) {
+ ctx.moveTo(margin, y)
+ ctx.lineTo(rightMargin, y)
+ } else {
+ var dashLen = Math.round(20 * scaleHint)
+ for (var dash = margin, dashCount = 0;
+ dash < rightMargin; dash += dashLen, dashCount++) {
+ if ((dashCount & 1) === 0) {
+ ctx.moveTo(dash, y)
+ ctx.lineTo(Math.min(dash + dashLen, rightMargin), y)
+ }
+ }
+ }
ctx.stroke()
}
}
if (control.verticalRulers) {
for (i = 0; i < control.verticalRulers.length; i++) {
ctx.beginPath()
- ctx.moveTo(control.verticalRulers[i], 0)
- ctx.lineTo(control.verticalRulers[i], height)
+ ctx.moveTo(control.verticalRulers[i], margin)
+ ctx.lineTo(control.verticalRulers[i], Math.round(height - margin))
ctx.stroke()
}
}
}
+ Connections {
+ target: control
+ onHorizontalRulersChanged: traceInputKeyGuideLines.requestPaint()
+ onVerticalRulersChanged: traceInputKeyGuideLines.requestPaint()
+ }
}
}
diff --git a/src/virtualkeyboard/content/styles/retro/style.qml b/src/virtualkeyboard/content/styles/retro/style.qml
index a1cb3ffc..3751608b 100644
--- a/src/virtualkeyboard/content/styles/retro/style.qml
+++ b/src/virtualkeyboard/content/styles/retro/style.qml
@@ -871,7 +871,17 @@ KeyboardStyle {
Text {
id: hwrInputModeIndicator
visible: control.patternRecognitionMode === InputEngine.HandwritingRecoginition
- text: InputContext.inputEngine.inputMode === InputEngine.Latin ? "Abc" : "123"
+ text: {
+ switch (InputContext.inputEngine.inputMode) {
+ case InputEngine.Numeric:
+ case InputEngine.Dialable:
+ return "123"
+ case InputEngine.ChineseHandwriting:
+ return "中文"
+ default:
+ return "Abc"
+ }
+ }
color: "black"
anchors.left: parent.left
anchors.top: parent.top
@@ -899,11 +909,25 @@ KeyboardStyle {
ctx.strokeStyle = Qt.rgba(0xFF, 0xFF, 0xFF)
ctx.clearRect(0, 0, width, height)
var i
+ var margin = Math.round(30 * scaleHint)
if (control.horizontalRulers) {
for (i = 0; i < control.horizontalRulers.length; i++) {
ctx.beginPath()
- ctx.moveTo(0, control.horizontalRulers[i])
- ctx.lineTo(width, control.horizontalRulers[i])
+ var y = Math.round(control.horizontalRulers[i])
+ var rightMargin = Math.round(width - margin)
+ if (i + 1 === control.horizontalRulers.length) {
+ ctx.moveTo(margin, y)
+ ctx.lineTo(rightMargin, y)
+ } else {
+ var dashLen = Math.round(20 * scaleHint)
+ for (var dash = margin, dashCount = 0;
+ dash < rightMargin; dash += dashLen, dashCount++) {
+ if ((dashCount & 1) === 0) {
+ ctx.moveTo(dash, y)
+ ctx.lineTo(Math.min(dash + dashLen, rightMargin), y)
+ }
+ }
+ }
ctx.stroke()
}
}
@@ -916,6 +940,11 @@ KeyboardStyle {
}
}
}
+ Connections {
+ target: control
+ onHorizontalRulersChanged: traceInputKeyGuideLines.requestPaint()
+ onVerticalRulersChanged: traceInputKeyGuideLines.requestPaint()
+ }
}
}
diff --git a/src/virtualkeyboard/doc/src/build.qdoc b/src/virtualkeyboard/doc/src/build.qdoc
index 3d9d9eac..9b18a54d 100644
--- a/src/virtualkeyboard/doc/src/build.qdoc
+++ b/src/virtualkeyboard/doc/src/build.qdoc
@@ -281,6 +281,136 @@ Hunspell sources and dictionary files is listed below:
(etc.)
\endcode
+\section2 T9 Write Integration
+
+T9 Write integration supports the T9 Write Alphabetic and T9 Write CJK engines. Both
+engines are integrated via T9WriteInputMethod. The input method can be initialized
+with either of the engines at runtime. The engine selection happens automatically
+based on input locale and input mode from the keyboard.
+
+\section3 T9 Write Compatibility
+
+Qt Virtual Keyboard is compatible with T9 Write v7.5.0 onward.
+
+The latest tested version is v7.8.1.
+
+\section3 T9 Write Build Preparations
+
+The contents of the SDK must be either manually copied to the directory structure
+described below, or by using the \e unpack.py script found in the t9write directory.
+
+To unpack the SDK using the script:
+
+\badcode
+$ cd src/virtualkeyboard/3rdparty/t9write/
+$ python unpack.py T9Write_Alpha_v7-8-0_SDK.zip .
+\endcode
+
+\badcode
+3rdparty
+└── t9write
+ ├─── api
+ │ ├─── decuma*.h
+ │ ├─── t9write*.h
+ │ └─── xxt9w*.h
+ ├─── data
+ │ ├─── *.bin [T9 Write Alphabetic]
+ │ ├─── *.hdb
+ │ ├─── *.phd
+ │ └─── *.ldb [T9 Write v7.5]
+ └─── lib
+ ├─── arm
+ │ ├─── shared
+ │ │ ├─── alphabetic
+ │ │ │ └─── *.so
+ │ │ └─── cjk
+ │ │ └─── *.so
+ │ └─── static
+ │ ├─── alphabetic
+ │ │ └─── *.a / *.o
+ │ └─── cjk
+ │ └─── *.a / *.o
+ ├─── linux-x86
+ │ ├─── shared
+ │ │ ├─── alphabetic
+ │ │ │ └─── *.so
+ │ │ └─── cjk
+ │ │ └─── *.so
+ │ └─── static
+ │ ├─── alphabetic
+ │ │ └─── *.a / *.o
+ │ └─── cjk
+ │ └─── *.a / *.o
+ └─── win32
+ ├─── shared
+ │ ├─── alphabetic
+ │ │ ├─── *.dll
+ │ │ └─── *.lib
+ │ └─── cjk
+ │ ├─── *.dll
+ │ └─── *.lib
+ └─── static
+ ├─── alphabetic
+ │ └─── *.lib / *.obj
+ └─── cjk
+ └─── *.lib / *.obj
+\endcode
+
+\note The above files are from the T9 Write demo SDK for Windows; the contents may vary for other
+ platforms.
+
+Where the contents of each directory are:
+
+\table
+\header
+ \li Directory
+ \li Description
+ \li Remarks
+\row
+ \li \e api
+ \li This directory should contain all of the API files
+ \li The API files usually located in the "api" and "public" directories
+ of the SDK, but sometimes in the "demo" directory.
+
+ When using both Alphabetic and CJK engines at the same time, any
+ overlapping files can be copied from either SDK.
+\row
+ \li \e data
+ \li This directory should contain all HWR databases and optionally
+ XT9 databases.
+ \li HWR database for the T9 Write Alphabetic:
+ \list
+ \li \e _databas_le.bin
+ \endlist
+
+ HWR database for the T9 Write CJK:
+ \list
+ \li \e cjk_HK_std_le.hdb HongKong Chinese
+ \li \e cjk_J_std_le.hdb Japanese
+ \li \e cjk_K_mkt_le.hdb Korean
+ \li \e cjk_S_gb18030_le.hdb Simplified Chinese
+ \li \e cjk_T_std_le.hdb Traditional Chinese
+ \endlist
+
+ Language database:
+ \list
+ \li File extension is either \e .ldb or \e .phd
+ \endlist
+\row
+ \li \e lib/<target>/<linkage>/<engine-variant>
+ \li Directory structure holding supported target builds.
+ \li These directories should hold the desired target libraries.
+ If both shared and static libraries are found, shared libraries
+ are preferred.
+
+ For example, to enable a static win32 build, copy
+ \e t9write_alphabetic_rel.obj to \e lib/win32/static/alphabetic
+ directory.
+\endtable
+
+Finally, the SDK is included in the build by adding CONFIG+=t9write to the
+qmake command line.
+
\section2 Static builds
The virtual keyboard can be built and linked statically against the application.
diff --git a/src/virtualkeyboard/inputengine.cpp b/src/virtualkeyboard/inputengine.cpp
index 10f7dc7b..dd04f645 100644
--- a/src/virtualkeyboard/inputengine.cpp
+++ b/src/virtualkeyboard/inputengine.cpp
@@ -764,6 +764,7 @@ void InputEngine::timerEvent(QTimerEvent *timerEvent)
\li \c InputEngine.Hiragana Hiragana input mode for Japanese.
\li \c InputEngine.Katakana Katakana input mode for Japanese.
\li \c InputEngine.FullwidthLatin Fullwidth latin input mode for East Asian languages.
+ \li \c InputEngine.ChineseHandwriting Chinese handwriting.
\endlist
*/
diff --git a/src/virtualkeyboard/inputengine.h b/src/virtualkeyboard/inputengine.h
index ce0122ac..d1b99ae4 100644
--- a/src/virtualkeyboard/inputengine.h
+++ b/src/virtualkeyboard/inputengine.h
@@ -73,7 +73,8 @@ public:
Hangul,
Hiragana,
Katakana,
- FullwidthLatin
+ FullwidthLatin,
+ ChineseHandwriting
};
enum PatternRecognitionMode {
PatternRecognitionDisabled,
diff --git a/src/virtualkeyboard/shifthandler.cpp b/src/virtualkeyboard/shifthandler.cpp
index 376410e3..a5f80d05 100644
--- a/src/virtualkeyboard/shifthandler.cpp
+++ b/src/virtualkeyboard/shifthandler.cpp
@@ -51,7 +51,7 @@ public:
resetWhenVisible(false),
manualShiftLanguageFilter(QSet<QLocale::Language>() << QLocale::Arabic << QLocale::Persian << QLocale::Hindi << QLocale::Korean),
manualCapsInputModeFilter(QSet<InputEngine::InputMode>() << InputEngine::Cangjie << InputEngine::Zhuyin),
- noAutoUppercaseInputModeFilter(QSet<InputEngine::InputMode>() << InputEngine::FullwidthLatin << InputEngine::Pinyin << InputEngine::Cangjie << InputEngine::Zhuyin),
+ noAutoUppercaseInputModeFilter(QSet<InputEngine::InputMode>() << InputEngine::FullwidthLatin << InputEngine::Pinyin << InputEngine::Cangjie << InputEngine::Zhuyin << InputEngine::ChineseHandwriting),
allCapsInputModeFilter(QSet<InputEngine::InputMode>() << InputEngine::Hiragana << InputEngine::Katakana)
{
timer.start();
diff --git a/src/virtualkeyboard/t9write.h b/src/virtualkeyboard/t9write.h
new file mode 100644
index 00000000..621d2312
--- /dev/null
+++ b/src/virtualkeyboard/t9write.h
@@ -0,0 +1,49 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Virtual Keyboard module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef T9WRITE_H
+#define T9WRITE_H
+
+#include "t9write_api_version.h"
+#ifdef HAVE_T9WRITE_ALPHABETIC
+#include "decuma_hwr.h"
+#endif
+#ifdef HAVE_T9WRITE_CJK
+#include "decuma_hwr_cjk.h"
+#endif
+
+#if defined(HAVE_T9WRITE_CJK) && defined(HAVE_T9WRITE_ALPHABETIC)
+#define DECUMA_API(FUNC_NAME) (cjk ? decumaCJK ## FUNC_NAME : decuma ## FUNC_NAME)
+#elif defined(HAVE_T9WRITE_CJK)
+#define DECUMA_API(FUNC_NAME) (decumaCJK ## FUNC_NAME)
+#else // defined(HAVE_T9WRITE_ALPHABETIC)
+#define DECUMA_API(FUNC_NAME) (decuma ## FUNC_NAME)
+#endif
+
+#endif // T9WRITE_H
diff --git a/src/virtualkeyboard/t9writedictionary.cpp b/src/virtualkeyboard/t9writedictionary.cpp
new file mode 100644
index 00000000..d15b16e3
--- /dev/null
+++ b/src/virtualkeyboard/t9writedictionary.cpp
@@ -0,0 +1,117 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Virtual Keyboard module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "t9writedictionary.h"
+#include "virtualkeyboarddebug.h"
+
+namespace QtVirtualKeyboard {
+
+T9WriteDictionary::T9WriteDictionary(DECUMA_SESSION *decumaSession,
+ const DECUMA_MEM_FUNCTIONS &memFuncs,
+ bool cjk) :
+ decumaSession(decumaSession),
+ memFuncs(memFuncs),
+ cjk(cjk),
+ sourceData(0),
+ sourceSize(0),
+ convertedData(0),
+ convertedSize(0)
+{
+}
+
+T9WriteDictionary::~T9WriteDictionary()
+{
+ if (convertedData) {
+ DECUMA_STATUS status = DECUMA_API(DestroyConvertedDictionary)(&convertedData, &memFuncs);
+ Q_ASSERT(status == decumaNoError);
+ Q_ASSERT(convertedData == 0);
+ }
+}
+
+bool T9WriteDictionary::load(const QString &fileName)
+{
+ if (sourceData || convertedData)
+ return false;
+
+ file.setFileName(fileName);
+ if (file.open(QIODevice::ReadOnly)) {
+ sourceSize = file.size();
+ sourceData = file.map(0, sourceSize, QFile::NoOptions);
+ if (!sourceData) {
+ sourceSize = 0;
+ qWarning() << "Could not read dictionary file" << fileName;
+ }
+ file.close();
+ } else {
+ qWarning() << "Could not open dictionary file" << fileName;
+ }
+
+ return sourceData != 0;
+}
+
+bool T9WriteDictionary::convert(const DECUMA_SRC_DICTIONARY_INFO &dictionaryInfo)
+{
+ if (!sourceData || convertedData)
+ return false;
+
+ DECUMA_STATUS status;
+ status = DECUMA_API(ConvertDictionary)(&convertedData, sourceData, (DECUMA_UINT32)sourceSize,
+ &dictionaryInfo, &convertedSize, &memFuncs);
+
+ if (status != decumaNoError) {
+ qWarning() << "Could not convert dictionary" << file.fileName();
+ file.unmap((uchar *)sourceData);
+ sourceSize = 0;
+ sourceData = 0;
+ }
+
+ return status == decumaNoError;
+}
+
+QString T9WriteDictionary::fileName() const
+{
+ return file.fileName();
+}
+
+const void *T9WriteDictionary::data() const
+{
+ return convertedData ? convertedData : sourceData;
+}
+
+qint64 T9WriteDictionary::size() const
+{
+ return convertedData ? convertedSize : sourceSize;
+}
+
+bool T9WriteDictionary::isConverted() const
+{
+ return convertedData != 0;
+}
+
+} // namespace QtVirtualKeyboard
diff --git a/src/virtualkeyboard/t9writedictionary.h b/src/virtualkeyboard/t9writedictionary.h
new file mode 100644
index 00000000..dc2d9475
--- /dev/null
+++ b/src/virtualkeyboard/t9writedictionary.h
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Virtual Keyboard module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef T9WRITEDICTIONARY_H
+#define T9WRITEDICTIONARY_H
+
+#include <QtGlobal>
+#include <QFile>
+#include "t9write.h"
+
+namespace QtVirtualKeyboard {
+
+class T9WriteDictionary
+{
+ Q_DISABLE_COPY(T9WriteDictionary)
+public:
+ explicit T9WriteDictionary(DECUMA_SESSION *decumaSession, const DECUMA_MEM_FUNCTIONS &memFuncs, bool cjk);
+ ~T9WriteDictionary();
+
+ bool load(const QString &fileName);
+ bool convert(const DECUMA_SRC_DICTIONARY_INFO &dictionaryInfo);
+
+ QString fileName() const;
+ const void *data() const;
+ qint64 size() const;
+ bool isConverted() const;
+
+private:
+ QFile file;
+ DECUMA_SESSION *decumaSession;
+ const DECUMA_MEM_FUNCTIONS &memFuncs;
+ bool cjk;
+ void *sourceData;
+ qint64 sourceSize;
+ void *convertedData;
+ DECUMA_UINT32 convertedSize;
+};
+
+}
+
+#endif // T9WRITEDICTIONARY_H
diff --git a/src/virtualkeyboard/t9writeinputmethod.cpp b/src/virtualkeyboard/t9writeinputmethod.cpp
index eb4cf9fb..3028d7d0 100644
--- a/src/virtualkeyboard/t9writeinputmethod.cpp
+++ b/src/virtualkeyboard/t9writeinputmethod.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Virtual Keyboard module of the Qt Toolkit.
@@ -34,15 +34,27 @@
#include "t9writeworker.h"
#include "virtualkeyboarddebug.h"
#include <QDirIterator>
+#include <QCryptographicHash>
#ifdef QT_VIRTUALKEYBOARD_DEBUG
#include <QTime>
#endif
+#include "handwritinggesturerecognizer.h"
+#ifdef QT_VIRTUALKEYBOARD_RECORD_TRACE_INPUT
+#include "unipentrace.h"
+#include <QStandardPaths>
+#endif
-#include "decuma_hwr.h"
#include "decumaStatus.h"
#include "decumaSymbolCategories.h"
#include "decumaLanguages.h"
-#include "xxt9wOem.h"
+
+/* Set to 1 to enable T9 Write log.
+
+ The log is routed to qDebug() and it can be enabled for troubleshooting
+ and when reporting issues. The log must not to be enabled in production
+ build.
+*/
+#define QT_VIRTUALKEYBOARD_T9WRITE_LOG 0
namespace QtVirtualKeyboard {
@@ -94,12 +106,26 @@ class T9WriteInputMethodPrivate : public AbstractInputMethodPrivate
Q_DECLARE_PUBLIC(T9WriteInputMethod)
public:
+ enum EngineMode {
+ EngineUninitialized,
+ Alphabetic,
+ SimplifiedChinese,
+ TraditionalChinese,
+ HongKongChinese,
+ Japanese,
+ Korean
+ };
+
T9WriteInputMethodPrivate(T9WriteInputMethod *q_ptr) :
AbstractInputMethodPrivate(),
q_ptr(q_ptr),
+ cjk(false),
+ engineMode(EngineUninitialized),
+ defaultHwrDbPath(QLatin1String(":/QtQuick/VirtualKeyboard/T9Write/data/")),
+ defaultDictionaryDbPath(defaultHwrDbPath),
dictionaryLock(QMutex::Recursive),
- convertedDictionary(0),
attachedDictionary(0),
+ traceListHardLimit(32),
resultId(0),
resultTimer(0),
decumaSession(0),
@@ -107,6 +133,9 @@ public:
arcAdditionStarted(false),
ignoreUpdate(false),
textCase(InputEngine::Lower)
+#ifdef QT_VIRTUALKEYBOARD_RECORD_TRACE_INPUT
+ , unipenTrace()
+#endif
{
}
@@ -128,34 +157,74 @@ public:
free(ptr);
}
- void initEngine()
+#if QT_VIRTUALKEYBOARD_T9WRITE_LOG
+ static void decumaLogString(void *pUserData, const char *pLogString, DECUMA_UINT32 nLogStringLength)
+ {
+ static QMutex s_logMutex;
+ static QByteArray s_logString;
+ Q_UNUSED(pUserData)
+ Q_UNUSED(nLogStringLength)
+ QMutexLocker guard(&s_logMutex);
+ s_logString.append(pLogString, nLogStringLength);
+ if (s_logString.endsWith('\n')) {
+ while (s_logString.endsWith('\n'))
+ s_logString.chop(1);
+ qDebug() << (const char *)s_logString.constData();
+ s_logString.clear();
+ }
+ }
+#endif
+
+ bool initEngine(EngineMode newEngineMode)
{
- VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethodPrivate::initEngine()";
+ if (engineMode == newEngineMode)
+ return engineMode != EngineUninitialized;
+
+ VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethodPrivate::initEngine()" << newEngineMode;
if (decumaSession)
- return;
+ exitEngine();
- symbolCategories.clear();
- symbolCategories.append(DECUMA_CATEGORY_ANSI);
- languageCategories.clear();
- languageCategories.append(DECUMA_LANG_EN);
+ if (newEngineMode == EngineUninitialized)
+ return false;
+
+ switch (newEngineMode) {
+ case Alphabetic:
+ cjk = false;
+ break;
+ case SimplifiedChinese:
+ case TraditionalChinese:
+ case HongKongChinese:
+ case Japanese:
+ case Korean:
+ cjk = true;
+ break;
+ default:
+ Q_ASSERT(0 && "Invalid EngineMode!");
+ return false;
+ }
+ engineMode = newEngineMode;
memset(&sessionSettings, 0, sizeof(sessionSettings));
- QString latinDb = findLatinDb(":/databases/HWR_LatinCG/");
- hwrDbFile.setFileName(latinDb);
+ QString hwrDb = findHwrDb(engineMode, defaultHwrDbPath);
+ hwrDbFile.setFileName(hwrDb);
if (!hwrDbFile.open(QIODevice::ReadOnly)) {
- qWarning() << "Could not open hwr database file" << latinDb;
- return;
+ qCritical() << "Could not open HWR database" << hwrDb;
+ exitEngine();
+ return false;
}
sessionSettings.pStaticDB = (DECUMA_STATIC_DB_PTR)hwrDbFile.map(0, hwrDbFile.size(), QFile::NoOptions);
if (!sessionSettings.pStaticDB) {
- hwrDbFile.close();
- qWarning() << "Could not map hwr database" << latinDb;
- return;
+ qCritical() << "Could not read HWR database" << hwrDb;
+ exitEngine();
+ return false;
}
+ symbolCategories.append(DECUMA_CATEGORY_ANSI);
+ languageCategories.append(DECUMA_LANG_EN);
+
sessionSettings.recognitionMode = mcrMode;
sessionSettings.bMinimizeAddArcPreProcessing = 1;
sessionSettings.writingDirection = unknownWriting;
@@ -164,17 +233,25 @@ public:
sessionSettings.charSet.pLanguages = languageCategories.data();
sessionSettings.charSet.nLanguages = languageCategories.size();
- session = QByteArray(decumaGetSessionSize(), 0);
+ session = QByteArray(DECUMA_API(GetSessionSize)(), 0);
decumaSession = (DECUMA_SESSION *)(!session.isEmpty() ? session.data() : 0);
- DECUMA_STATUS status = decumaBeginSession(decumaSession, &sessionSettings, &memFuncs);
+ DECUMA_STATUS status = DECUMA_API(BeginSession)(decumaSession, &sessionSettings, &memFuncs);
Q_ASSERT(status == decumaNoError);
if (status != decumaNoError) {
- qWarning() << "Could not initialize T9Write engine" << status;
+ qCritical() << "Could not initialize T9Write engine" << status;
+ exitEngine();
+ return false;
}
- worker.reset(new T9WriteWorker(decumaSession));
+#if QT_VIRTUALKEYBOARD_T9WRITE_LOG
+ DECUMA_API(StartLogging)(decumaSession, 0, decumaLogString);
+#endif
+
+ worker.reset(new T9WriteWorker(decumaSession, cjk));
worker->start();
+
+ return true;
}
void exitEngine()
@@ -188,36 +265,62 @@ public:
hwrDbFile.close();
}
- detachDictionary(&attachedDictionary);
- destroyConvertedDictionary(&convertedDictionary);
+ if (attachedDictionary) {
+ detachDictionary(attachedDictionary);
+ attachedDictionary.reset();
+ }
+ loadedDictionary.reset();
if (decumaSession) {
- decumaEndSession(decumaSession);
+#if QT_VIRTUALKEYBOARD_T9WRITE_LOG
+ DECUMA_API(StopLogging)(decumaSession);
+#endif
+ DECUMA_API(EndSession)(decumaSession);
decumaSession = 0;
session.clear();
}
memset(&sessionSettings, 0, sizeof(sessionSettings));
- }
- QString findLatinDb(const QString &dir)
- {
- QString latinDb;
- QDirIterator it(dir, QDirIterator::NoIteratorFlags);
- while (it.hasNext()) {
- QString fileEntry = it.next();
+ symbolCategories.clear();
+ languageCategories.clear();
- if (!fileEntry.endsWith(QLatin1String(".bin")))
- continue;
+ engineMode = EngineUninitialized;
+ cjk = false;
+ }
- latinDb = fileEntry;
+ QString findHwrDb(EngineMode mode, const QString &dir) const
+ {
+ QString hwrDbPath(dir);
+ switch (mode) {
+ case Alphabetic:
+ hwrDbPath.append(QLatin1String("_databas_le.bin"));
break;
+ case SimplifiedChinese:
+ hwrDbPath.append(QLatin1String("cjk_S_gb18030_le.hdb"));
+ break;
+ case TraditionalChinese:
+ hwrDbPath.append(QLatin1String("cjk_T_std_le.hdb"));
+ break;
+ case HongKongChinese:
+ hwrDbPath.append(QLatin1String("cjk_HK_std_le.hdb"));
+ break;
+ case Japanese:
+ hwrDbPath.append(QLatin1String("cjk_J_std_le.hdb"));
+ break;
+ case Korean:
+ hwrDbPath.append(QLatin1String("cjk_K_mkt_le.hdb"));
+ break;
+ default:
+ return QString();
}
- return latinDb;
+ return QFileInfo::exists(hwrDbPath) ? hwrDbPath : QString();
}
- QString findDictionary(const QString &dir, const QLocale &locale)
+ QString findDictionary(const QString &dir, const QLocale &locale, DECUMA_SRC_DICTIONARY_TYPE &srcType)
{
+ srcType = numberOfSrcDictionaryTypes;
+
QStringList languageCountry = locale.name().split("_");
if (languageCountry.length() != 2)
return QString();
@@ -227,11 +330,27 @@ public:
while (it.hasNext()) {
QString fileEntry = it.next();
- if (!fileEntry.endsWith(QLatin1String(".ldb")))
+ if (!fileEntry.contains("_" + languageCountry[0].toUpper()))
continue;
- if (!fileEntry.contains("_" + languageCountry[0].toUpper()))
+ if (fileEntry.endsWith(QLatin1String(".ldb"))) {
+#if T9WRITEAPIMAJORVERNUM >= 20
+ qCritical() << "Incompatible T9 Write dictionary" << fileEntry;
+ continue;
+#else
+ srcType = decumaXT9LDB;
+#endif
+ } else if (fileEntry.endsWith(QLatin1String(".phd"))) {
+#if T9WRITEAPIMAJORVERNUM >= 20
+ srcType = decumaPortableHWRDictionary;
+#else
+ qCritical() << "Incompatible T9 Write dictionary" << fileEntry;
+ continue;
+#endif
+ } else {
+ qWarning() << "Incompatible T9 Write dictionary" << fileEntry;
continue;
+ }
dictionary = fileEntry;
break;
@@ -240,201 +359,117 @@ public:
return dictionary;
}
- bool attachDictionary(void *dictionary)
+ bool attachDictionary(const QSharedPointer<T9WriteDictionary> &dictionary)
{
- VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethodPrivate::attachDictionary():" << dictionary;
-
QMutexLocker dictionaryGuard(&dictionaryLock);
Q_ASSERT(decumaSession != 0);
Q_ASSERT(dictionary != 0);
-
- DECUMA_STATUS status = decumaAttachConvertedDictionary(decumaSession, dictionary);
- Q_ASSERT(status == decumaNoError);
+ VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethodPrivate::attachDictionary():" << dictionary->fileName();
+#if T9WRITEAPIMAJORVERNUM >= 20
+ DECUMA_STATUS status = DECUMA_API(AttachDictionary)(decumaSession, dictionary->data(), dictionary->size());
+#else
+ DECUMA_STATUS status = DECUMA_API(AttachConvertedDictionary)(decumaSession, dictionary->data());
+#endif
return status == decumaNoError;
}
- void detachDictionary(void **dictionary)
+ void detachDictionary(const QSharedPointer<T9WriteDictionary> &dictionary)
{
QMutexLocker dictionaryGuard(&dictionaryLock);
- Q_ASSERT(decumaSession != 0);
- if (!dictionary || !*dictionary)
+ if (!dictionary)
return;
- VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethodPrivate::detachDictionary():" << *dictionary;
+ VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethodPrivate::detachDictionary():" << dictionary->fileName();
- DECUMA_STATUS status = decumaDetachDictionary(decumaSession, *dictionary);
- Q_UNUSED(status)
- Q_ASSERT(status == decumaNoError);
- *dictionary = 0;
- }
-
- void destroyConvertedDictionary(void **dictionary)
- {
- QMutexLocker dictionaryGuard(&dictionaryLock);
Q_ASSERT(decumaSession != 0);
- if (!dictionary || !*dictionary)
- return;
-
- VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethodPrivate::destroyConvertedDictionary():" << *dictionary;
-
- DECUMA_STATUS status = decumaDestroyConvertedDictionary(dictionary, &memFuncs);
+ DECUMA_STATUS status = DECUMA_API(DetachDictionary)(decumaSession, dictionary->data());
Q_UNUSED(status)
Q_ASSERT(status == decumaNoError);
- Q_ASSERT(*dictionary == 0);
}
bool setInputMode(const QLocale &locale, InputEngine::InputMode inputMode)
{
+ Q_Q(T9WriteInputMethod);
VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethodPrivate::setInputMode():" << locale << inputMode;
- Q_Q(T9WriteInputMethod);
- DECUMA_UINT32 language = mapToDecumaLanguage(locale);
+ finishRecognition();
+
+ if (!initEngine(mapLocaleToEngineMode(locale)))
+ return false;
+
+ DECUMA_UINT32 language = mapToDecumaLanguage(locale, inputMode);
if (language == DECUMA_LANG_GSMDEFAULT) {
- qWarning() << "Handwriting input does not support the language" << locale.name();
+ qWarning() << "Handwriting is not supported in" << locale.name();
return false;
}
int isLanguageSupported = 0;
- decumaDatabaseIsLanguageSupported(sessionSettings.pStaticDB, language, &isLanguageSupported);
+ DECUMA_API(DatabaseIsLanguageSupported)(sessionSettings.pStaticDB, language, &isLanguageSupported);
if (!isLanguageSupported) {
- qWarning() << "Handwriting input does not support the language" << locale.name();
+ qWarning() << "Handwriting is not supported in" << locale.name();
return false;
}
- finishRecognition();
-
- bool languageChanged = languageCategories.isEmpty() || !languageCategories.contains(language);
+ bool languageChanged = languageCategories.isEmpty() || languageCategories.first() != language;
if (languageChanged) {
languageCategories.clear();
languageCategories.append(language);
- }
- // Choose the symbol categories by input mode, script and input method hints
- bool leftToRightGestures = true;
- Qt::InputMethodHints inputMethodHints = q->inputContext()->inputMethodHints();
- symbolCategories.clear();
- switch (inputMode) {
- case InputEngine::Latin:
- if (inputMethodHints.testFlag(Qt::ImhEmailCharactersOnly)) {
- symbolCategories.append(DECUMA_CATEGORY_EMAIL);
- } else if (inputMethodHints.testFlag(Qt::ImhUrlCharactersOnly)) {
- symbolCategories.append(DECUMA_CATEGORY_URL);
- } else {
- bool includeDigits = true;
- bool includeBasicPunctuation = true;
- switch (locale.script()) {
- case QLocale::LatinScript:
- if (language == DECUMA_LANG_EN)
- symbolCategories.append(DECUMA_CATEGORY_ANSI);
- else
- symbolCategories.append(DECUMA_CATEGORY_ISO8859_1);
- break;
-
- case QLocale::CyrillicScript:
- symbolCategories.append(DECUMA_CATEGORY_CYRILLIC);
- break;
-
- case QLocale::GreekScript:
- symbolCategories.append(DECUMA_CATEGORY_GREEK);
- break;
-
- default:
- qWarning() << "Handwriting input does not support the language" << locale.name();
- return false;
- }
-
- if (includeDigits)
- symbolCategories.append(DECUMA_CATEGORY_DIGIT);
-
- if (includeBasicPunctuation) {
- symbolCategories.append(DECUMA_CATEGORY_BASIC_PUNCTUATIONS);
- symbolCategories.append(DECUMA_CATEGORY_CONTRACTION_MARK);
- }
-
- if (language == DECUMA_LANG_ES)
- symbolCategories.append(DECUMA_CATEGORY_SPANISH_PUNCTUATIONS);
- }
- break;
-
- case InputEngine::Numeric:
- symbolCategories.append(DECUMA_CATEGORY_DIGIT);
- if (!inputMethodHints.testFlag(Qt::ImhDigitsOnly))
- symbolCategories.append(DECUMA_CATEGORY_NUM_SUP);
- break;
-
- case InputEngine::Dialable:
- symbolCategories.append(DECUMA_CATEGORY_PHONE_NUMBER);
- break;
-
- default:
- return false;
+ // Add English as secondary language for punctuation
+ if (language == DECUMA_LANG_PRC)
+ languageCategories.append(DECUMA_LANG_EN);
}
- if (leftToRightGestures) {
- symbolCategories.append(DECUMA_CATEGORY_BACKSPACE_STROKE);
- symbolCategories.append(DECUMA_CATEGORY_RETURN_STROKE);
- symbolCategories.append(DECUMA_CATEGORY_WHITESPACE_STROKE);
- }
-
- /* The dictionary is loaded in the background thread. Once the loading is
- complete the dictionary will be attached to the current session. The
- attachment happens in the worker thread context, thus the direct
- connection for the signal handler and the mutex protecting the
- converted dictionary for concurrent access.
-
- The loading operation is blocking for the main thread only if the
- user starts handwriting input before the operation is complete.
- */
- {
- QMutexLocker dictionaryGuard(&dictionaryLock);
-
- // Select recognition mode
- // Note: MCR mode is preferred in all cases, since it eliminates the need
- // for recognition timer, thus provides better user experience.
- sessionSettings.recognitionMode = inputMethodHints.testFlag(Qt::ImhHiddenText) ? scrMode : mcrMode;
-
- // Detach previous dictionary if the language is being changed
- // or the recognizer mode is single-character mode
- if ((languageChanged || inputMethodHints.testFlag(Qt::ImhNoPredictiveText) || sessionSettings.recognitionMode == scrMode) && attachedDictionary) {
- detachDictionary(&attachedDictionary);
- }
-
- // Check if a dictionary needs to be loaded
- if (languageChanged || !convertedDictionary) {
- destroyConvertedDictionary(&convertedDictionary);
- dictionaryFileName = findDictionary(":/databases/XT9_LDBs/", locale);
- if (!dictionaryFileName.isEmpty()) {
- if (dictionaryTask.isNull() || dictionaryTask->fileUri != dictionaryFileName) {
- dictionaryTask.reset(new T9WriteDictionaryTask(dictionaryFileName, memFuncs));
- q->connect(dictionaryTask.data(), SIGNAL(completed(QString,void*)),
- SLOT(dictionaryLoadCompleted(QString,void*)), Qt::DirectConnection);
- worker->addTask(dictionaryTask);
- }
- }
- }
+ if (!updateSymbolCategories(language, locale, inputMode))
+ return false;
+ updateRecognitionMode(language, locale, inputMode);
+ updateDictionary(language, locale, languageChanged);
+ static const QList<DECUMA_UINT32> rtlLanguages = QList<DECUMA_UINT32>()
+ << DECUMA_LANG_AR << DECUMA_LANG_IW << DECUMA_LANG_FA << DECUMA_LANG_UR;
+ sessionSettings.writingDirection = rtlLanguages.contains(language) ? rightToLeft : unknownWriting;
- // Attach existing dictionary, if necessary
- if (sessionSettings.recognitionMode == mcrMode && !inputMethodHints.testFlag(Qt::ImhNoPredictiveText) &&
- convertedDictionary && !attachedDictionary) {
- attachDictionary(convertedDictionary);
- attachedDictionary = convertedDictionary;
- }
- }
+ VIRTUALKEYBOARD_DEBUG() << " -> language categories:" << languageCategories;
+ VIRTUALKEYBOARD_DEBUG() << " -> symbol categories:" << symbolCategories;
+ VIRTUALKEYBOARD_DEBUG() << " -> recognition mode:" << sessionSettings.recognitionMode;
// Change session settings
sessionSettings.charSet.pSymbolCategories = symbolCategories.data();
sessionSettings.charSet.nSymbolCategories = symbolCategories.size();
sessionSettings.charSet.pLanguages = languageCategories.data();
sessionSettings.charSet.nLanguages = languageCategories.size();
- DECUMA_STATUS status = decumaChangeSessionSettings(decumaSession, &sessionSettings);
+ DECUMA_STATUS status = DECUMA_API(ChangeSessionSettings)(decumaSession, &sessionSettings);
Q_ASSERT(status == decumaNoError);
- caseFormatter.preferLowercase = inputMethodHints.testFlag(Qt::ImhPreferLowercase);
+ caseFormatter.preferLowercase = q->inputContext()->inputMethodHints().testFlag(Qt::ImhPreferLowercase);
return status == decumaNoError;
}
- DECUMA_UINT32 mapToDecumaLanguage(const QLocale &locale)
+ EngineMode mapLocaleToEngineMode(const QLocale &locale)
+ {
+#ifdef HAVE_T9WRITE_CJK
+ switch (locale.language()) {
+ case QLocale::Chinese: {
+ if (locale.script() == QLocale::TraditionalChineseScript)
+ return locale.country() == QLocale::HongKong ? HongKongChinese : TraditionalChinese;
+ return SimplifiedChinese;
+ break;
+ }
+ default:
+ break;
+ }
+#else
+ Q_UNUSED(locale)
+#endif
+
+#ifdef HAVE_T9WRITE_ALPHABETIC
+ return T9WriteInputMethodPrivate::Alphabetic;
+#else
+ return T9WriteInputMethodPrivate::EngineUninitialized;
+#endif
+ }
+
+ DECUMA_UINT32 mapToDecumaLanguage(const QLocale &locale, InputEngine::InputMode inputMode)
{
static const QLocale::Language maxLanguage = QLocale::Vietnamese;
static const DECUMA_UINT32 languageMap[maxLanguage + 1] = {
@@ -573,22 +608,319 @@ public:
DECUMA_LANG_VI // Vietnamese = 132
};
+ int localeLanguage = locale.language();
if (locale.language() > maxLanguage)
return DECUMA_LANG_GSMDEFAULT;
- DECUMA_UINT32 language = languageMap[locale.language()];
+ DECUMA_UINT32 language = languageMap[localeLanguage];
+ if (language == DECUMA_LANG_PRC) {
+ if (inputMode != InputEngine::ChineseHandwriting)
+ language = DECUMA_LANG_EN;
+ }
return language;
}
+ void updateRecognitionMode(DECUMA_UINT32 language, const QLocale &locale,
+ InputEngine::InputMode inputMode)
+ {
+ Q_Q(T9WriteInputMethod);
+ Q_UNUSED(language)
+ Q_UNUSED(locale)
+ Q_UNUSED(inputMode)
+
+ // Select recognition mode
+ // Note: MCR mode is preferred, as it does not require recognition
+ // timer and provides better user experience.
+ sessionSettings.recognitionMode = mcrMode;
+
+ // Use scrMode with hidden text or with no predictive mode
+ if (inputMode != InputEngine::ChineseHandwriting) {
+ const Qt::InputMethodHints inputMethodHints = q->inputContext()->inputMethodHints();
+ if (inputMethodHints.testFlag(Qt::ImhHiddenText) || inputMethodHints.testFlag(Qt::ImhNoPredictiveText))
+ sessionSettings.recognitionMode = scrMode;
+ }
+ }
+
+ bool updateSymbolCategories(DECUMA_UINT32 language, const QLocale &locale,
+ InputEngine::InputMode inputMode)
+ {
+ // Handle CJK in separate method
+ if (cjk)
+ return updateSymbolCategoriesCjk(language, locale, inputMode);
+
+ symbolCategories.clear();
+
+ // Choose the symbol categories by input mode, script and input method hints
+ bool leftToRightGestures = true;
+ Q_Q(T9WriteInputMethod);
+ const Qt::InputMethodHints inputMethodHints = q->inputContext()->inputMethodHints();
+ switch (inputMode) {
+ case InputEngine::Latin:
+ if (inputMethodHints.testFlag(Qt::ImhEmailCharactersOnly)) {
+ symbolCategories.append(DECUMA_CATEGORY_EMAIL);
+ } else if (inputMethodHints.testFlag(Qt::ImhUrlCharactersOnly)) {
+ symbolCategories.append(DECUMA_CATEGORY_URL);
+ } else {
+ bool includeDigits = true;
+ bool includeBasicPunctuation = true;
+ switch (locale.script()) {
+ case QLocale::LatinScript:
+ if (language == DECUMA_LANG_EN)
+ symbolCategories.append(DECUMA_CATEGORY_ANSI);
+ else
+ symbolCategories.append(DECUMA_CATEGORY_ISO8859_1);
+ break;
+
+ case QLocale::CyrillicScript:
+ symbolCategories.append(DECUMA_CATEGORY_CYRILLIC);
+ break;
+
+ case QLocale::GreekScript:
+ symbolCategories.append(DECUMA_CATEGORY_GREEK);
+ break;
+
+ default:
+ qWarning() << "Handwriting is not supported in" << locale.name();
+ return false;
+ }
+
+ if (includeDigits)
+ symbolCategories.append(DECUMA_CATEGORY_DIGIT);
+
+ if (includeBasicPunctuation) {
+ symbolCategories.append(DECUMA_CATEGORY_BASIC_PUNCTUATIONS);
+ symbolCategories.append(DECUMA_CATEGORY_CONTRACTION_MARK);
+ }
+
+ if (language == DECUMA_LANG_ES)
+ symbolCategories.append(DECUMA_CATEGORY_SPANISH_PUNCTUATIONS);
+ }
+ break;
+
+ case InputEngine::Numeric:
+ symbolCategories.append(DECUMA_CATEGORY_DIGIT);
+ if (!inputMethodHints.testFlag(Qt::ImhDigitsOnly))
+ symbolCategories.append(DECUMA_CATEGORY_NUM_SUP);
+ break;
+
+ case InputEngine::Dialable:
+ symbolCategories.append(DECUMA_CATEGORY_PHONE_NUMBER);
+ break;
+
+ default:
+ qWarning() << "Handwriting is not supported in" << locale.name();
+ return false;
+ }
+
+ if (leftToRightGestures) {
+ symbolCategories.append(DECUMA_CATEGORY_BACKSPACE_STROKE);
+ symbolCategories.append(DECUMA_CATEGORY_RETURN_STROKE);
+ symbolCategories.append(DECUMA_CATEGORY_WHITESPACE_STROKE);
+ }
+
+ return true;
+ }
+
+ bool updateSymbolCategoriesCjk(DECUMA_UINT32 language, const QLocale &locale,
+ InputEngine::InputMode inputMode)
+ {
+ Q_UNUSED(language)
+ Q_ASSERT(cjk);
+
+ symbolCategories.clear();
+
+ switch (inputMode) {
+ case InputEngine::Latin:
+ symbolCategories.append(DECUMA_CATEGORY_ANSI);
+ symbolCategories.append(DECUMA_CATEGORY_CJK_SYMBOL);
+ symbolCategories.append(DECUMA_CATEGORY_PUNCTUATIONS);
+ break;
+
+ case InputEngine::Numeric:
+ symbolCategories.append(DECUMA_CATEGORY_DIGIT);
+ symbolCategories.append(DECUMA_CATEGORY_CJK_SYMBOL);
+ symbolCategories.append(DECUMA_CATEGORY_PUNCTUATIONS);
+ break;
+
+ case InputEngine::Dialable:
+ symbolCategories.append(DECUMA_CATEGORY_DIGIT);
+ symbolCategories.append(DECUMA_CATEGORY_CJK_SYMBOL);
+ break;
+
+ case InputEngine::ChineseHandwriting:
+ switch (locale.script()) {
+ case QLocale::SimplifiedChineseScript:
+ symbolCategories.append(DECUMA_CATEGORY_GB2312_A);
+ symbolCategories.append(DECUMA_CATEGORY_GB2312_B_CHARS_ONLY);
+ symbolCategories.append(DECUMA_CATEGORY_GBK_3);
+ symbolCategories.append(DECUMA_CATEGORY_GBK_4);
+ symbolCategories.append(DECUMA_CATEGORY_CJK_SYMBOL);
+ symbolCategories.append(DECUMA_CATEGORY_CJK_GENERAL_PUNCTUATIONS);
+ symbolCategories.append(DECUMA_CATEGORY_PUNCTUATIONS);
+ break;
+
+ case QLocale::TraditionalChineseScript:
+ symbolCategories.append(DECUMA_CATEGORY_BIGFIVE);
+ if (language == DECUMA_LANG_HK)
+ symbolCategories.append(DECUMA_CATEGORY_HKSCS_CHARS_ONLY);
+ symbolCategories.append(DECUMA_CATEGORY_CJK_SYMBOL);
+ symbolCategories.append(DECUMA_CATEGORY_CJK_GENERAL_PUNCTUATIONS);
+ symbolCategories.append(DECUMA_CATEGORY_PUNCTUATIONS);
+ break;
+
+ default:
+ qWarning() << "Chinese handwriting is not supported in" << locale.name();
+ return false;
+ }
+ break;
+
+ default:
+ return false;
+ }
+
+ return true;
+ }
+
+ void updateDictionary(DECUMA_UINT32 language, const QLocale &locale, bool languageChanged)
+ {
+ Q_Q(T9WriteInputMethod);
+
+ /* The dictionary is loaded in the background thread. Once the loading is
+ complete the dictionary will be attached to the current session. The
+ attachment happens in the worker thread context, thus the direct
+ connection for the signal handler and the mutex protecting the
+ converted dictionary for concurrent access.
+ The loading operation is blocking for the main thread only if the
+ user starts handwriting input before the operation is complete.
+ */
+ QMutexLocker dictionaryGuard(&dictionaryLock);
+
+ // Detach previous dictionary if the language is being changed
+ // or the recognizer mode is single-character mode
+ const Qt::InputMethodHints inputMethodHints = q->inputContext()->inputMethodHints();
+ if ((languageChanged || inputMethodHints.testFlag(Qt::ImhNoPredictiveText) || sessionSettings.recognitionMode == scrMode) && attachedDictionary) {
+ detachDictionary(attachedDictionary);
+ attachedDictionary.reset();
+ }
+
+ // Check if a dictionary needs to be loaded
+ if (languageChanged || !loadedDictionary) {
+ loadedDictionary.reset();
+
+ DECUMA_SRC_DICTIONARY_INFO dictionaryInfo;
+ memset(&dictionaryInfo, 0, sizeof(dictionaryInfo));
+
+ QList<QLocale> decumaLocales;
+ decumaLocales.append(locale);
+
+ // CJK: No dictionary for latin input
+ if (cjk && language == DECUMA_LANG_EN)
+ decumaLocales.clear();
+
+ dictionaryFileName.clear();
+ QLocale decumaLocale;
+ for (QLocale tryLocale : decumaLocales) {
+ dictionaryFileName = findDictionary(defaultDictionaryDbPath, tryLocale, dictionaryInfo.srcType);
+ if (!dictionaryFileName.isEmpty()) {
+ decumaLocale = tryLocale;
+ break;
+ }
+ }
+ if (!dictionaryFileName.isEmpty()) {
+ if (dictionaryTask.isNull() || dictionaryTask->dictionaryFileName != dictionaryFileName) {
+ VIRTUALKEYBOARD_DEBUG() << " -> load dictionary:" << dictionaryFileName;
+
+ bool convertDictionary = true;
+#if defined(HAVE_T9WRITE_CJK) && T9WRITEAPIMAJORVERNUM >= 20
+ // Chinese dictionary cannot be converted (PHD)
+ if (dictionaryInfo.srcType == decumaPortableHWRDictionary && decumaLocale.language() == QLocale::Chinese)
+ convertDictionary = false;
+#endif
+
+ QSharedPointer<T9WriteDictionary> newDictionary(new T9WriteDictionary(decumaSession, memFuncs, cjk));
+ dictionaryTask.reset(new T9WriteDictionaryTask(newDictionary, dictionaryFileName, convertDictionary, dictionaryInfo));
+
+ QObject::connect(dictionaryTask.data(), &T9WriteDictionaryTask::completed,
+ q, &T9WriteInputMethod::dictionaryLoadCompleted, Qt::DirectConnection);
+ worker->addTask(dictionaryTask);
+ }
+ }
+ }
+
+ // Attach existing dictionary, if available
+ if (sessionSettings.recognitionMode == mcrMode && !inputMethodHints.testFlag(Qt::ImhNoPredictiveText) &&
+ loadedDictionary && !attachedDictionary) {
+ if (attachDictionary(loadedDictionary))
+ attachedDictionary = loadedDictionary;
+ }
+ }
+
+ QByteArray getContext(InputEngine::PatternRecognitionMode patternRecognitionMode,
+ const QVariantMap &traceCaptureDeviceInfo,
+ const QVariantMap &traceScreenInfo) const
+ {
+ QCryptographicHash hash(QCryptographicHash::Md5);
+
+ hash.addData((const char *)&patternRecognitionMode, sizeof(patternRecognitionMode));
+
+ QByteArray mapData;
+ QDataStream ds(&mapData, QIODevice::WriteOnly);
+ ds << traceCaptureDeviceInfo;
+ ds << traceScreenInfo;
+ hash.addData(mapData);
+
+ return hash.result();
+ }
+
+ void setContext(InputEngine::PatternRecognitionMode patternRecognitionMode,
+ const QVariantMap &traceCaptureDeviceInfo,
+ const QVariantMap &traceScreenInfo)
+ {
+ QByteArray context = getContext(patternRecognitionMode, traceCaptureDeviceInfo, traceScreenInfo);
+ if (context == currentContext)
+ return;
+ currentContext = context;
+
+ VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethodPrivate::setContext():" << QString(context.toHex());
+
+ finishRecognition();
+
+ const int dpi = traceCaptureDeviceInfo.value("dpi", 96).toInt();
+ static const int INSTANT_GESTURE_WIDTH_THRESHOLD_MM = 25;
+ static const int INSTANT_GESTURE_HEIGHT_THRESHOLD_MM = 25;
+ instantGestureSettings.widthThreshold = INSTANT_GESTURE_WIDTH_THRESHOLD_MM / 25.4 * dpi;
+ instantGestureSettings.heightThreshold = INSTANT_GESTURE_HEIGHT_THRESHOLD_MM / 25.4 * dpi;
+
+ gestureRecognizer.setDpi(dpi);
+
+ QVariantList horizontalRulers(traceScreenInfo.value("horizontalRulers", QVariantList()).toList());
+ if (horizontalRulers.count() >= 2) {
+ sessionSettings.baseline = horizontalRulers.last().toInt();
+ sessionSettings.helpline = 0;
+ sessionSettings.topline = horizontalRulers.first().toInt();
+ sessionSettings.supportLineSet = baselineAndTopline;
+ } else {
+ sessionSettings.baseline = 0;
+ sessionSettings.helpline = 0;
+ sessionSettings.topline = 0;
+ sessionSettings.supportLineSet = baselineAndTopline;
+ }
+
+ DECUMA_STATUS status = DECUMA_API(ChangeSessionSettings)(decumaSession, &sessionSettings);
+ Q_ASSERT(status == decumaNoError);
+ }
+
Trace *traceBegin(int traceId, InputEngine::PatternRecognitionMode patternRecognitionMode,
const QVariantMap &traceCaptureDeviceInfo, const QVariantMap &traceScreenInfo)
{
Q_UNUSED(traceId)
Q_UNUSED(patternRecognitionMode)
- Q_UNUSED(traceCaptureDeviceInfo)
Q_UNUSED(traceScreenInfo)
+ if (!worker)
+ return 0;
+
stopResultTimer();
// Dictionary must be completed before the arc addition can begin
@@ -606,30 +938,34 @@ public:
recognitionTask.reset();
}
- const int dpi = traceCaptureDeviceInfo.value("dpi", 96).toInt();
- static const int INSTANT_GESTURE_WIDTH_THRESHOLD_MM = 25;
- static const int INSTANT_GESTURE_HEIGHT_THRESHOLD_MM = 25;
- instantGestureSettings.widthThreshold = INSTANT_GESTURE_WIDTH_THRESHOLD_MM / 25.4 * dpi;
- instantGestureSettings.heightThreshold = INSTANT_GESTURE_HEIGHT_THRESHOLD_MM / 25.4 * dpi;
+#ifdef QT_VIRTUALKEYBOARD_RECORD_TRACE_INPUT
+ if (!unipenTrace)
+ unipenTrace.reset(new UnipenTrace(traceCaptureDeviceInfo, traceScreenInfo));
+#endif
+
+ setContext(patternRecognitionMode, traceCaptureDeviceInfo, traceScreenInfo);
DECUMA_STATUS status;
if (!arcAdditionStarted) {
- status = decumaBeginArcAddition(decumaSession);
+ status = DECUMA_API(BeginArcAddition)(decumaSession);
Q_ASSERT(status == decumaNoError);
arcAdditionStarted = true;
}
DECUMA_UINT32 arcID = (DECUMA_UINT32)traceId;
- status = decumaStartNewArc(decumaSession, arcID);
+ status = DECUMA_API(StartNewArc)(decumaSession, arcID);
Q_ASSERT(status == decumaNoError);
if (status != decumaNoError) {
- decumaEndArcAddition(decumaSession);
+ DECUMA_API(EndArcAddition)(decumaSession);
arcAdditionStarted = false;
return NULL;
}
Trace *trace = new Trace();
+#ifdef QT_VIRTUALKEYBOARD_RECORD_TRACE_INPUT
+ trace->setChannels(QStringList("t"));
+#endif
traceList.append(trace);
return trace;
@@ -638,7 +974,7 @@ public:
void traceEnd(Trace *trace)
{
if (trace->isCanceled()) {
- decumaCancelArc(decumaSession, trace->traceId());
+ DECUMA_API(CancelArc)(decumaSession, trace->traceId());
traceList.removeOne(trace);
delete trace;
} else {
@@ -646,10 +982,16 @@ public:
}
if (!traceList.isEmpty()) {
Q_ASSERT(arcAdditionStarted);
- if (countActiveTraces() == 0)
+ if (countActiveTraces() == 0) {
restartRecognition();
+ if (cjk) {
+ // For some reason gestures don't seem to work in CJK mode
+ // Using our own gesture recognizer as fallback
+ handleGesture();
+ }
+ }
} else if (arcAdditionStarted) {
- decumaEndArcAddition(decumaSession);
+ DECUMA_API(EndArcAddition)(decumaSession);
arcAdditionStarted = false;
}
}
@@ -675,17 +1017,26 @@ public:
Q_ASSERT(decumaSession != 0);
const QVariantList points = trace->points();
+ Q_ASSERT(!points.isEmpty());
DECUMA_UINT32 arcID = (DECUMA_UINT32)trace->traceId();
DECUMA_STATUS status;
for (const QVariant &p : points) {
const QPoint pt(p.toPointF().toPoint());
- status = decumaAddPoint(decumaSession, (DECUMA_COORD)pt.x(),(DECUMA_COORD)pt.y(), arcID);
- Q_ASSERT(status == decumaNoError);
+ status = DECUMA_API(AddPoint)(decumaSession, (DECUMA_COORD)pt.x(),(DECUMA_COORD)pt.y(), arcID);
+ if (status != decumaNoError) {
+ VIRTUALKEYBOARD_DEBUG() << "decumaAddPoint failed" << status;
+ finishRecognition();
+ return;
+ }
}
- status = decumaCommitArc(decumaSession, arcID);
- Q_ASSERT(status == decumaNoError);
+ status = DECUMA_API(CommitArc)(decumaSession, arcID);
+ if (status != decumaNoError) {
+ VIRTUALKEYBOARD_DEBUG() << "decumaCommitArc failed" << status;
+ finishRecognition();
+ return;
+ }
}
void noteSelected(int index)
@@ -696,7 +1047,7 @@ public:
VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethodPrivate::noteSelected():" << index;
Q_ASSERT(index >= 0 && index < wordCandidatesHwrResultIndex.length());
int resultIndex = wordCandidatesHwrResultIndex[index];
- DECUMA_STATUS status = decumaNoteSelectedCandidate(decumaSession, resultIndex);
+ DECUMA_STATUS status = DECUMA_API(NoteSelectedCandidate)(decumaSession, resultIndex);
Q_UNUSED(status)
Q_ASSERT(status == decumaNoError);
}
@@ -723,6 +1074,8 @@ public:
bool finishRecognition(bool emitSelectionListChanged = true)
{
VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethodPrivate::finishRecognition()";
+ if (!worker)
+ return false;
bool result = !traceList.isEmpty();
@@ -742,7 +1095,7 @@ public:
}
if (arcAdditionStarted) {
- decumaEndArcAddition(decumaSession);
+ DECUMA_API(EndArcAddition)(decumaSession);
arcAdditionStarted = false;
}
@@ -762,11 +1115,18 @@ public:
scrResult.clear();
caseFormatter.clear();
+#ifdef QT_VIRTUALKEYBOARD_RECORD_TRACE_INPUT
+ unipenTrace.reset();
+#endif
+
return result;
}
bool select(int index = -1)
{
+ if (!worker)
+ return false;
+
if (sessionSettings.recognitionMode == mcrMode && wordCandidates.isEmpty()) {
finishRecognition();
return false;
@@ -783,6 +1143,25 @@ public:
index = index >= 0 ? index : activeWordIndex;
noteSelected(index);
QString finalWord = wordCandidates.at(index);
+
+#ifdef QT_VIRTUALKEYBOARD_RECORD_TRACE_INPUT
+ // Record trace
+ if (unipenTrace) {
+ if (finalWord.length() == 1) {
+ // In recording mode, the text case must match with the current text case
+ QChar ch(finalWord.at(0));
+ if (!ch.isLetter() || (ch.isUpper() == (textCase == InputEngine::Upper))) {
+ QStringList homeLocations = QStandardPaths::standardLocations(QStandardPaths::HomeLocation);
+ if (!homeLocations.isEmpty()) {
+ unipenTrace->setDirectory(QStringLiteral("%1/%2").arg(homeLocations.at(0)).arg("VIRTUAL_KEYBOARD_TRACES"));
+ unipenTrace->record(traceList);
+ unipenTrace->save(ch.unicode(), 100);
+ }
+ }
+ }
+ }
+#endif
+
finishRecognition();
q->inputContext()->commit(finalWord);
} else if (sessionSettings.recognitionMode == scrMode) {
@@ -865,8 +1244,11 @@ public:
bool wordCandidatesChanged = wordCandidates != newWordCandidates;
+#ifndef QT_VIRTUALKEYBOARD_RECORD_TRACE_INPUT
// Delete trace history
- if (sessionSettings.recognitionMode == mcrMode && !symbolStrokes.isEmpty()) {
+ const InputEngine::InputMode inputMode = q->inputEngine()->inputMode();
+ if (sessionSettings.recognitionMode == mcrMode && !symbolStrokes.isEmpty() &&
+ inputMode != InputEngine::ChineseHandwriting) {
int activeTraces = symbolStrokes.at(symbolStrokes.count() - 1).toInt();
if (symbolStrokes.count() > 1)
activeTraces += symbolStrokes.at(symbolStrokes.count() - 2).toInt();
@@ -874,7 +1256,14 @@ public:
delete traceList.takeFirst();
}
- // Look for a gesture at the end of first result
+ // Enforce hard limit for number of traces
+ if (traceList.count() >= traceListHardLimit) {
+ VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethodPrivate::processResult(): Clearing traces (hard limit):" << traceList.count();
+ clearTraces();
+ }
+#endif
+
+ // Find a gesture at the end of the first result
if (!gesture.isEmpty()) {
DECUMA_UNICODE gestureSymbol = gesture.at(0).unicode();
@@ -892,8 +1281,8 @@ public:
break;
default:
- finishRecognition();
ic->commit(ic->preeditText());
+ finishRecognition();
break;
}
@@ -920,6 +1309,85 @@ public:
}
}
+ bool handleGesture()
+ {
+ if (countActiveTraces() > 0)
+ return false;
+
+ QVariantMap gesture(gestureRecognizer.recognize(traceList.mid(traceList.length() - 1, 1)));
+ if (gesture.isEmpty())
+ return false;
+
+ VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethodPrivate::handleGesture():" << gesture;
+
+ if (gesture[QLatin1String("type")].toString() == QLatin1String("swipe")) {
+
+ static const int SWIPE_ANGLE_THRESHOLD = 15; // degrees +-
+
+ qreal swipeLength = gesture[QLatin1String("length")].toReal();
+ if (swipeLength >= instantGestureSettings.widthThreshold) {
+
+ Q_Q(T9WriteInputMethod);
+ InputContext *ic = q->inputContext();
+ if (!ic)
+ return false;
+
+ qreal swipeAngle = gesture[QLatin1String("angle_degrees")].toReal();
+ int swipeTouchCount = gesture[QLatin1String("touch_count")].toInt();
+
+ // Swipe left
+ if (swipeAngle <= 180 + SWIPE_ANGLE_THRESHOLD && swipeAngle >= 180 - SWIPE_ANGLE_THRESHOLD) {
+ if (swipeTouchCount == 1) {
+ // Single swipe: backspace
+ ic->inputEngine()->virtualKeyClick(Qt::Key_Backspace, QString(), Qt::NoModifier);
+ return true;
+ }
+ return false;
+ }
+
+ // Swipe right
+ const InputEngine::InputMode inputMode = q->inputEngine()->inputMode();
+ if (inputMode != InputEngine::ChineseHandwriting) {
+ if (swipeAngle <= SWIPE_ANGLE_THRESHOLD || swipeAngle >= 360 - SWIPE_ANGLE_THRESHOLD) {
+ if (swipeTouchCount == 1) {
+ // Single swipe: space
+ ic->inputEngine()->virtualKeyClick(Qt::Key_Space, QString(" "), Qt::NoModifier);
+ return true;
+ }
+ return false;
+ }
+ }
+
+ // Swipe up
+ if (swipeAngle <= 270 + SWIPE_ANGLE_THRESHOLD && swipeAngle >= 270 - SWIPE_ANGLE_THRESHOLD) {
+ if (swipeTouchCount == 1) {
+ // Single swipe: toggle input mode
+ select();
+ if (!(ic->inputMethodHints() & (Qt::ImhDialableCharactersOnly | Qt::ImhFormattedNumbersOnly | Qt::ImhDigitsOnly))) {
+ QList<int> inputModes = ic->inputEngine()->inputModes();
+ // Filter out duplicate numeric mode (in favor of Numeric)
+ int indexOfNumericInputMode = inputModes.indexOf(InputEngine::Numeric);
+ int indexOfDialableInputMode = inputModes.indexOf(InputEngine::Dialable);
+ if (indexOfNumericInputMode != -1 && indexOfDialableInputMode != -1)
+ inputModes.removeAt(inputMode != InputEngine::Dialable ?
+ indexOfDialableInputMode :
+ indexOfNumericInputMode);
+ if (inputModes.count() > 1) {
+ int inputModeIndex = inputModes.indexOf((int)inputMode) + 1;
+ if (inputModeIndex >= inputModes.count())
+ inputModeIndex = 0;
+ ic->inputEngine()->setInputMode((InputEngine::InputMode)inputModes.at(inputModeIndex));
+ }
+ }
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
bool isValidInputChar(const QChar &c) const
{
if (c.isLetterOrNumber())
@@ -948,17 +1416,23 @@ public:
T9WriteInputMethod *q_ptr;
static const DECUMA_MEM_FUNCTIONS memFuncs;
+ bool cjk;
+ EngineMode engineMode;
+ QByteArray currentContext;
DECUMA_SESSION_SETTINGS sessionSettings;
DECUMA_INSTANT_GESTURE_SETTINGS instantGestureSettings;
+ QString defaultHwrDbPath;
+ QString defaultDictionaryDbPath;
QFile hwrDbFile;
QVector<DECUMA_UINT32> languageCategories;
QVector<DECUMA_UINT32> symbolCategories;
QScopedPointer<T9WriteWorker> worker;
QList<Trace *> traceList;
+ int traceListHardLimit;
QMutex dictionaryLock;
QString dictionaryFileName;
- void *convertedDictionary;
- void *attachedDictionary;
+ QSharedPointer<T9WriteDictionary> loadedDictionary;
+ QSharedPointer<T9WriteDictionary> attachedDictionary;
QSharedPointer<T9WriteDictionaryTask> dictionaryTask;
QSharedPointer<T9WriteRecognitionTask> recognitionTask;
int resultId;
@@ -974,6 +1448,10 @@ public:
bool ignoreUpdate;
InputEngine::TextCase textCase;
T9WriteCaseFormatter caseFormatter;
+ HandwritingGestureRecognizer gestureRecognizer;
+#ifdef QT_VIRTUALKEYBOARD_RECORD_TRACE_INPUT
+ QScopedPointer<UnipenTrace> unipenTrace;
+#endif
};
const DECUMA_MEM_FUNCTIONS T9WriteInputMethodPrivate::memFuncs = {
@@ -991,8 +1469,6 @@ const DECUMA_MEM_FUNCTIONS T9WriteInputMethodPrivate::memFuncs = {
T9WriteInputMethod::T9WriteInputMethod(QObject *parent) :
AbstractInputMethod(*new T9WriteInputMethodPrivate(this), parent)
{
- Q_D(T9WriteInputMethod);
- d->initEngine();
}
T9WriteInputMethod::~T9WriteInputMethod()
@@ -1003,18 +1479,58 @@ T9WriteInputMethod::~T9WriteInputMethod()
QList<InputEngine::InputMode> T9WriteInputMethod::inputModes(const QString &locale)
{
- Q_UNUSED(locale)
- return QList<InputEngine::InputMode>()
- << InputEngine::Latin
- << InputEngine::Numeric
- << InputEngine::Dialable;
+ Q_D(T9WriteInputMethod);
+ QList<InputEngine::InputMode> availableInputModes;
+ const Qt::InputMethodHints inputMethodHints(inputContext()->inputMethodHints());
+ T9WriteInputMethodPrivate::EngineMode mode = d->mapLocaleToEngineMode(QLocale(locale));
+
+ // Add primary input mode
+ switch (mode) {
+#ifdef HAVE_T9WRITE_ALPHABETIC
+ case T9WriteInputMethodPrivate::Alphabetic:
+ if (d->findHwrDb(T9WriteInputMethodPrivate::Alphabetic, d->defaultHwrDbPath).isEmpty())
+ return availableInputModes;
+ if (!(inputMethodHints & (Qt::ImhDialableCharactersOnly | Qt::ImhFormattedNumbersOnly | Qt::ImhDigitsOnly | Qt::ImhLatinOnly)))
+ availableInputModes.append(InputEngine::Latin);
+ break;
+#endif
+#ifdef HAVE_T9WRITE_CJK
+ case T9WriteInputMethodPrivate::SimplifiedChinese:
+ case T9WriteInputMethodPrivate::TraditionalChinese:
+ case T9WriteInputMethodPrivate::HongKongChinese:
+ if (d->findHwrDb(mode, d->defaultHwrDbPath).isEmpty())
+ return availableInputModes;
+ if (!(inputMethodHints & (Qt::ImhDialableCharactersOnly | Qt::ImhFormattedNumbersOnly | Qt::ImhDigitsOnly | Qt::ImhLatinOnly)))
+ availableInputModes.append(InputEngine::ChineseHandwriting);
+ break;
+#endif
+ default:
+ return availableInputModes;
+ }
+
+ // Add exclusive input modes
+ if (inputMethodHints.testFlag(Qt::ImhDialableCharactersOnly) || inputMethodHints.testFlag(Qt::ImhDigitsOnly)) {
+ availableInputModes.append(InputEngine::Dialable);
+ } else if (inputMethodHints.testFlag(Qt::ImhFormattedNumbersOnly)) {
+ availableInputModes.append(InputEngine::Numeric);
+ } else if (inputMethodHints.testFlag(Qt::ImhLatinOnly)) {
+ availableInputModes.append(InputEngine::Latin);
+ } else {
+ // Add other input modes
+ Q_ASSERT(!availableInputModes.isEmpty());
+ if (!availableInputModes.contains(InputEngine::Latin))
+ availableInputModes.append(InputEngine::Latin);
+ availableInputModes.append(InputEngine::Numeric);
+ }
+
+ return availableInputModes;
}
bool T9WriteInputMethod::setInputMode(const QString &locale, InputEngine::InputMode inputMode)
{
Q_D(T9WriteInputMethod);
d->select();
- return d->setInputMode(locale, inputMode);
+ return d->setInputMode(QLocale(locale), inputMode);
}
bool T9WriteInputMethod::setTextCase(InputEngine::TextCase textCase)
@@ -1045,6 +1561,13 @@ bool T9WriteInputMethod::keyEvent(Qt::Key key, const QString &text, Qt::Keyboard
if (preeditText.length() > 1) {
preeditText.chop(1);
ic->setPreeditText(preeditText);
+ // WA: T9Write CJK may crash in some cases with long stringStart.
+ // Therefore we commit the current input and finish the recognition.
+ if (d->cjk) {
+ ic->commit();
+ d->finishRecognition();
+ return true;
+ }
d->caseFormatter.ensureLength(preeditText.length(), d->textCase);
T9WriteCaseFormatter caseFormatter(d->caseFormatter);
d->finishRecognition(false);
@@ -1182,11 +1705,13 @@ bool T9WriteInputMethod::reselect(int cursorPosition, const InputEngine::Reselec
if (!ic)
return false;
+ const InputEngine::InputMode inputMode = inputEngine()->inputMode();
+ const int maxLength = inputMode == InputEngine::ChineseHandwriting ? 0 : 32;
const QString surroundingText = ic->surroundingText();
int replaceFrom = 0;
if (reselectFlags.testFlag(InputEngine::WordBeforeCursor)) {
- for (int i = cursorPosition - 1; i >= 0; --i) {
+ for (int i = cursorPosition - 1; i >= 0 && d->stringStart.length() < maxLength; --i) {
QChar c = surroundingText.at(i);
if (!d->isValidInputChar(c))
break;
@@ -1206,7 +1731,7 @@ bool T9WriteInputMethod::reselect(int cursorPosition, const InputEngine::Reselec
}
if (reselectFlags.testFlag(InputEngine::WordAfterCursor)) {
- for (int i = cursorPosition; i < surroundingText.length(); ++i) {
+ for (int i = cursorPosition; i < surroundingText.length() && d->stringStart.length() < maxLength; ++i) {
QChar c = surroundingText.at(i);
if (!d->isValidInputChar(c))
break;
@@ -1224,7 +1749,7 @@ bool T9WriteInputMethod::reselect(int cursorPosition, const InputEngine::Reselec
if (d->stringStart.isEmpty())
return false;
- if (reselectFlags.testFlag(InputEngine::WordAtCursor) && replaceFrom == -d->stringStart.length()) {
+ if (reselectFlags.testFlag(InputEngine::WordAtCursor) && replaceFrom == -d->stringStart.length() && d->stringStart.length() < maxLength) {
d->stringStart.clear();
return false;
}
@@ -1258,35 +1783,38 @@ void T9WriteInputMethod::timerEvent(QTimerEvent *timerEvent)
if (timerId == d->resultTimer) {
if (d->sessionSettings.recognitionMode == mcrMode) {
d->stopResultTimer();
- d->clearTraces();
+#ifndef QT_VIRTUALKEYBOARD_RECORD_TRACE_INPUT
+ const InputEngine::InputMode inputMode = inputEngine()->inputMode();
+ if (inputMode != InputEngine::ChineseHandwriting)
+ d->clearTraces();
+#endif
} else if (d->sessionSettings.recognitionMode == scrMode) {
d->select();
}
}
}
-void T9WriteInputMethod::dictionaryLoadCompleted(const QString &fileUri, void *dictionary)
+void T9WriteInputMethod::dictionaryLoadCompleted(QSharedPointer<T9WriteDictionary> dictionary)
{
Q_D(T9WriteInputMethod);
// Note: This method is called in worker thread context
QMutexLocker dictionaryGuard(&d->dictionaryLock);
- VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethod::dictionaryLoadCompleted():" << fileUri << dictionary;
-
if (!dictionary)
return;
+ VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethod::dictionaryLoadCompleted():"
+ << dictionary->fileName() << dictionary->data() << dictionary->size();
+
InputContext *ic = inputContext();
- if (ic && fileUri == d->dictionaryFileName) {
- d->convertedDictionary = dictionary;
+ if (ic && dictionary->fileName() == d->dictionaryFileName) {
+ d->loadedDictionary = dictionary;
if (d->sessionSettings.recognitionMode == mcrMode &&
!ic->inputMethodHints().testFlag(Qt::ImhNoPredictiveText) &&
!d->attachedDictionary) {
- d->attachDictionary(d->convertedDictionary);
- d->attachedDictionary = d->convertedDictionary;
+ if (d->attachDictionary(d->loadedDictionary))
+ d->attachedDictionary = d->loadedDictionary;
}
- } else {
- d->destroyConvertedDictionary(&dictionary);
}
}
@@ -1315,4 +1843,10 @@ void T9WriteInputMethod::resultsAvailable(const QVariantList &resultList)
d->resultsAvailable(resultList);
}
+void T9WriteInputMethod::recognitionError(int status)
+{
+ VIRTUALKEYBOARD_DEBUG() << "T9WriteInputMethod::recognitionError():" << status;
+ reset();
+}
+
} // namespace QtVirtualKeyboard
diff --git a/src/virtualkeyboard/t9writeinputmethod.h b/src/virtualkeyboard/t9writeinputmethod.h
index 9b87ebdf..173abd99 100644
--- a/src/virtualkeyboard/t9writeinputmethod.h
+++ b/src/virtualkeyboard/t9writeinputmethod.h
@@ -31,10 +31,12 @@
#define T9WRITEINPUTMETHOD_H
#include "abstractinputmethod.h"
+#include <QSharedPointer>
namespace QtVirtualKeyboard {
class T9WriteInputMethodPrivate;
+class T9WriteDictionary;
class T9WriteInputMethod : public AbstractInputMethod
{
@@ -70,8 +72,9 @@ protected:
void timerEvent(QTimerEvent *timerEvent);
protected slots:
- void dictionaryLoadCompleted(const QString &fileUri, void *dictionary);
+ void dictionaryLoadCompleted(QSharedPointer<T9WriteDictionary> dictionary);
void resultsAvailable(const QVariantList &resultList);
+ void recognitionError(int status);
};
}
diff --git a/src/virtualkeyboard/t9writeworker.cpp b/src/virtualkeyboard/t9writeworker.cpp
index 733033ce..c2f33124 100644
--- a/src/virtualkeyboard/t9writeworker.cpp
+++ b/src/virtualkeyboard/t9writeworker.cpp
@@ -58,10 +58,14 @@ void T9WriteTask::wait()
\internal
*/
-T9WriteDictionaryTask::T9WriteDictionaryTask(const QString &fileUri,
- const DECUMA_MEM_FUNCTIONS &memFuncs) :
- fileUri(fileUri),
- memFuncs(memFuncs)
+T9WriteDictionaryTask::T9WriteDictionaryTask(QSharedPointer<T9WriteDictionary> dictionary,
+ const QString &dictionaryFileName,
+ bool convertDictionary,
+ const DECUMA_SRC_DICTIONARY_INFO &dictionaryInfo) :
+ dictionary(dictionary),
+ dictionaryFileName(dictionaryFileName),
+ convertDictionary(convertDictionary),
+ dictionaryInfo(dictionaryInfo)
{
}
@@ -74,33 +78,19 @@ void T9WriteDictionaryTask::run()
perf.start();
#endif
- void *dictionary = 0;
-
- QFile dictionaryFile(fileUri);
- if (dictionaryFile.open(QIODevice::ReadOnly)) {
- uchar *dictionaryData = dictionaryFile.map(0, dictionaryFile.size(), QFile::NoOptions);
- if (dictionaryData) {
-
- DECUMA_SRC_DICTIONARY_INFO dictionaryInfo;
- memset(&dictionaryInfo, 0, sizeof(dictionaryInfo));
- dictionaryInfo.srcType = decumaXT9LDB;
- DECUMA_UINT32 dictionarySize = 0;
- DECUMA_STATUS status = decumaConvertDictionary(&dictionary, dictionaryData, dictionaryFile.size(), &dictionaryInfo, &dictionarySize, &memFuncs);
- Q_UNUSED(status)
- Q_ASSERT(status == decumaNoError);
- dictionaryFile.unmap(dictionaryData);
- } else {
- qWarning() << "Could not map dictionary file" << fileUri;
- }
- } else {
- qWarning() << "Could not open dictionary file" << fileUri;
+ bool result = false;
+ if (dictionary) {
+ result = dictionary->load(dictionaryFileName);
+ if (result && convertDictionary)
+ result = dictionary->convert(dictionaryInfo);
}
#ifdef QT_VIRTUALKEYBOARD_DEBUG
VIRTUALKEYBOARD_DEBUG() << "T9WriteDictionaryTask::run(): time:" << perf.elapsed() << "ms";
#endif
- emit completed(fileUri, dictionary);
+ if (result)
+ emit completed(dictionary);
}
/*!
@@ -109,6 +99,7 @@ void T9WriteDictionaryTask::run()
*/
T9WriteRecognitionResult::T9WriteRecognitionResult(int id, int maxResults, int maxCharsPerWord) :
+ status(decumaNoError),
numResults(0),
instantGesture(0),
id(id),
@@ -176,16 +167,18 @@ void T9WriteRecognitionTask::run()
perf.start();
#endif
- DECUMA_STATUS status = decumaIndicateInstantGesture(decumaSession, &result->instantGesture, &instantGestureSettings);
- Q_ASSERT(status == decumaNoError);
+ DECUMA_STATUS status;
+ if (!cjk) {
+ status = DECUMA_API(IndicateInstantGesture)(decumaSession, &result->instantGesture, &instantGestureSettings);
+ Q_ASSERT(status == decumaNoError);
+ }
DECUMA_INTERRUPT_FUNCTIONS interruptFunctions;
interruptFunctions.pShouldAbortRecognize = shouldAbortRecognize;
interruptFunctions.pUserData = (void *)this;
- status = decumaRecognize(decumaSession, result->results.data(), result->results.size(), &result->numResults, result->maxCharsPerWord, &recSettings, &interruptFunctions);
- if (status == decumaAbortRecognitionUnsupported)
- status = decumaRecognize(decumaSession, result->results.data(), result->results.size(), &result->numResults, result->maxCharsPerWord, &recSettings, NULL);
- Q_ASSERT(status == decumaNoError);
+ result->status = DECUMA_API(Recognize)(decumaSession, result->results.data(), result->results.size(), &result->numResults, result->maxCharsPerWord, &recSettings, &interruptFunctions);
+ if (result->status == decumaAbortRecognitionUnsupported)
+ result->status = DECUMA_API(Recognize)(decumaSession, result->results.data(), result->results.size(), &result->numResults, result->maxCharsPerWord, &recSettings, NULL);
QStringList resultList;
QString gesture;
@@ -261,6 +254,11 @@ void T9WriteRecognitionResultsTask::run()
if (!result)
return;
+ if (result->status != decumaNoError) {
+ emit recognitionError(result->status);
+ return;
+ }
+
QVariantList resultList;
for (int i = 0; i < result->numResults; i++)
{
@@ -306,12 +304,13 @@ void T9WriteRecognitionResultsTask::run()
\internal
*/
-T9WriteWorker::T9WriteWorker(DECUMA_SESSION *decumaSession, QObject *parent) :
+T9WriteWorker::T9WriteWorker(DECUMA_SESSION *decumaSession, const bool cjk, QObject *parent) :
QThread(parent),
taskSema(),
taskLock(),
decumaSession(decumaSession),
- abort(false)
+ abort(false),
+ cjk(cjk)
{
}
@@ -369,6 +368,7 @@ void T9WriteWorker::run()
}
if (currentTask) {
currentTask->decumaSession = decumaSession;
+ currentTask->cjk = cjk;
currentTask->run();
currentTask->runSema.release();
}
diff --git a/src/virtualkeyboard/t9writeworker.h b/src/virtualkeyboard/t9writeworker.h
index 23a4cf02..5dc8c0d9 100644
--- a/src/virtualkeyboard/t9writeworker.h
+++ b/src/virtualkeyboard/t9writeworker.h
@@ -41,7 +41,8 @@
#include <QMap>
#include <QVector>
-#include "decuma_hwr.h"
+#include "t9write.h"
+#include "t9writedictionary.h"
namespace QtVirtualKeyboard {
@@ -59,6 +60,7 @@ public:
protected:
DECUMA_SESSION *decumaSession;
+ bool cjk;
private:
QSemaphore runSema;
@@ -68,16 +70,20 @@ class T9WriteDictionaryTask : public T9WriteTask
{
Q_OBJECT
public:
- explicit T9WriteDictionaryTask(const QString &fileUri,
- const DECUMA_MEM_FUNCTIONS &memFuncs);
+ explicit T9WriteDictionaryTask(QSharedPointer<T9WriteDictionary> dictionary,
+ const QString &dictionaryFileName,
+ bool convertDictionary,
+ const DECUMA_SRC_DICTIONARY_INFO &dictionaryInfo);
void run();
- const QString fileUri;
- const DECUMA_MEM_FUNCTIONS &memFuncs;
+ QSharedPointer<T9WriteDictionary> dictionary;
+ const QString dictionaryFileName;
+ bool convertDictionary;
+ const DECUMA_SRC_DICTIONARY_INFO dictionaryInfo;
signals:
- void completed(const QString &fileUri, void *dictionary);
+ void completed(QSharedPointer<T9WriteDictionary> dictionary);
};
class T9WriteRecognitionResult
@@ -87,12 +93,14 @@ class T9WriteRecognitionResult
public:
explicit T9WriteRecognitionResult(int id, int maxResults, int maxCharsPerWord);
+ DECUMA_STATUS status;
QVector<DECUMA_HWR_RESULT> results;
DECUMA_UINT16 numResults;
int instantGesture;
const int id;
const int maxResults;
const int maxCharsPerWord;
+
private:
QVector<DECUMA_UNICODE> _chars;
QVector<DECUMA_INT16> _symbolChars;
@@ -135,6 +143,7 @@ public:
signals:
void resultsAvailable(const QVariantList &resultList);
+ void recognitionError(int status);
private:
QSharedPointer<T9WriteRecognitionResult> result;
@@ -144,7 +153,7 @@ class T9WriteWorker : public QThread
{
Q_OBJECT
public:
- explicit T9WriteWorker(DECUMA_SESSION *decumaSession, QObject *parent = 0);
+ explicit T9WriteWorker(DECUMA_SESSION *decumaSession, const bool cjk, QObject *parent = 0);
~T9WriteWorker();
void addTask(QSharedPointer<T9WriteTask> task);
@@ -176,6 +185,7 @@ private:
QMutex taskLock;
DECUMA_SESSION *decumaSession;
QAtomicInteger<bool> abort;
+ const bool cjk;
};
} // namespace QtVirtualKeyboard
diff --git a/src/virtualkeyboard/virtualkeyboard.pro b/src/virtualkeyboard/virtualkeyboard.pro
index 25b1586f..e16eaba9 100644
--- a/src/virtualkeyboard/virtualkeyboard.pro
+++ b/src/virtualkeyboard/virtualkeyboard.pro
@@ -74,8 +74,9 @@ LAYOUT_FILES += \
contains(CONFIG, lang-en.*) {
LAYOUT_FILES += \
content/layouts/en_GB/main.qml \
- content/layouts/en_GB/handwriting.qml \
content/layouts/en_GB/symbols.qml
+t9write-alphabetic|lipi-toolkit: LAYOUT_FILES += \
+ content/layouts/en_GB/handwriting.qml
}
contains(CONFIG, lang-ar.*) {
LAYOUT_FILES += \
@@ -88,21 +89,21 @@ contains(CONFIG, lang-da.*) {
LAYOUT_FILES += \
content/layouts/da_DK/main.qml \
content/layouts/da_DK/symbols.qml
-t9write: LAYOUT_FILES += \
+t9write-alphabetic: LAYOUT_FILES += \
content/layouts/da_DK/handwriting.qml
}
contains(CONFIG, lang-de.*) {
LAYOUT_FILES += \
content/layouts/de_DE/main.qml \
content/layouts/de_DE/symbols.qml
-t9write: LAYOUT_FILES += \
+t9write-alphabetic: LAYOUT_FILES += \
content/layouts/de_DE/handwriting.qml
}
contains(CONFIG, lang-es.*) {
LAYOUT_FILES += \
content/layouts/es_ES/main.qml \
content/layouts/es_ES/symbols.qml
-t9write: LAYOUT_FILES += \
+t9write-alphabetic: LAYOUT_FILES += \
content/layouts/es_ES/handwriting.qml
}
contains(CONFIG, lang-fa.*) {
@@ -116,14 +117,14 @@ contains(CONFIG, lang-fi.*) {
LAYOUT_FILES += \
content/layouts/fi_FI/main.qml \
content/layouts/fi_FI/symbols.qml
-t9write: LAYOUT_FILES += \
+t9write-alphabetic: LAYOUT_FILES += \
content/layouts/fi_FI/handwriting.qml
}
contains(CONFIG, lang-fr.*) {
LAYOUT_FILES += \
content/layouts/fr_FR/main.qml \
content/layouts/fr_FR/symbols.qml
-t9write: LAYOUT_FILES += \
+t9write-alphabetic: LAYOUT_FILES += \
content/layouts/fr_FR/handwriting.qml
}
contains(CONFIG, lang-hi.*) {
@@ -135,7 +136,7 @@ contains(CONFIG, lang-it.*) {
LAYOUT_FILES += \
content/layouts/it_IT/main.qml \
content/layouts/it_IT/symbols.qml
-t9write: LAYOUT_FILES += \
+t9write-alphabetic: LAYOUT_FILES += \
content/layouts/it_IT/handwriting.qml
}
contains(CONFIG, lang-ja.*) {
@@ -152,48 +153,50 @@ contains(CONFIG, lang-nb.*) {
LAYOUT_FILES += \
content/layouts/nb_NO/main.qml \
content/layouts/nb_NO/symbols.qml
-t9write: LAYOUT_FILES += \
+t9write-alphabetic: LAYOUT_FILES += \
content/layouts/nb_NO/handwriting.qml
}
contains(CONFIG, lang-pl.*) {
LAYOUT_FILES += \
content/layouts/pl_PL/main.qml \
content/layouts/pl_PL/symbols.qml
-t9write: LAYOUT_FILES += \
+t9write-alphabetic: LAYOUT_FILES += \
content/layouts/pl_PL/handwriting.qml
}
contains(CONFIG, lang-pt.*) {
LAYOUT_FILES += \
content/layouts/pt_PT/main.qml \
content/layouts/pt_PT/symbols.qml
-t9write: LAYOUT_FILES += \
+t9write-alphabetic: LAYOUT_FILES += \
content/layouts/pt_PT/handwriting.qml
}
contains(CONFIG, lang-ro.*) {
LAYOUT_FILES += \
content/layouts/ro_RO/main.qml \
content/layouts/ro_RO/symbols.qml
-t9write: LAYOUT_FILES += \
+t9write-alphabetic: LAYOUT_FILES += \
content/layouts/ro_RO/handwriting.qml
}
contains(CONFIG, lang-ru.*) {
LAYOUT_FILES += \
content/layouts/ru_RU/main.qml \
content/layouts/ru_RU/symbols.qml
-t9write: LAYOUT_FILES += \
+t9write-alphabetic: LAYOUT_FILES += \
content/layouts/ru_RU/handwriting.qml
}
contains(CONFIG, lang-sv.*) {
LAYOUT_FILES += \
content/layouts/sv_SE/main.qml \
content/layouts/sv_SE/symbols.qml
-t9write: LAYOUT_FILES += \
+t9write-alphabetic: LAYOUT_FILES += \
content/layouts/sv_SE/handwriting.qml
}
contains(CONFIG, lang-zh(_CN)?) {
LAYOUT_FILES += \
content/layouts/zh_CN/main.qml \
content/layouts/zh_CN/symbols.qml
+t9write-cjk: LAYOUT_FILES += \
+ content/layouts/zh_CN/handwriting.qml
}
contains(CONFIG, lang-zh(_TW)?) {
LAYOUT_FILES += \
@@ -340,18 +343,41 @@ lipi-toolkit {
t9write {
include(3rdparty/t9write/t9write-build.pri)
equals(T9WRITE_FOUND, 0): \
- error("T9Write SDK could not be found. Please make sure you have extracted" \
- "the contents of the T9Write SDK to $$PWD/3rdparty/t9write")
+ error("T9Write SDK could not be found. For more information, see" \
+ "the documentation in Building Qt Virtual Keyboard")
SOURCES += \
t9writeinputmethod.cpp \
- t9writeworker.cpp
+ t9writeworker.cpp \
+ t9writedictionary.cpp
HEADERS += \
t9writeinputmethod.h \
- t9writeworker.h
+ t9writeworker.h \
+ t9writedictionary.h \
+ t9write.h
DEFINES += HAVE_T9WRITE
QMAKE_USE += t9write_db
INCLUDEPATH += $$T9WRITE_INCLUDE_DIRS
- LIBS += $$T9WRITE_ALPHABETIC_LIBS
+ t9write-alphabetic {
+ LIBS += $$T9WRITE_ALPHABETIC_LIBS
+ DEFINES += HAVE_T9WRITE_ALPHABETIC
+ !isEmpty(T9WRITE_ALPHABETIC_BINS) {
+ t9write_alphabetic_bins.files = $$T9WRITE_ALPHABETIC_BINS
+ t9write_alphabetic_bins.path = $$[QT_INSTALL_BINS]
+ INSTALLS += t9write_alphabetic_bins
+ !prefix_build: COPIES += t9write_alphabetic_bins
+ }
+ }
+ t9write-cjk {
+ LIBS += $$T9WRITE_CJK_LIBS
+ DEFINES += HAVE_T9WRITE_CJK
+ !isEmpty(T9WRITE_CJK_BINS) {
+ t9write_cjk_bins.files = $$T9WRITE_CJK_BINS
+ t9write_cjk_bins.path = $$[QT_INSTALL_BINS]
+ INSTALLS += t9write_cjk_bins
+ !prefix_build: COPIES += t9write_cjk_bins
+ }
+ }
+ DEFINES += QT_VIRTUALKEYBOARD_DEBUG
}
record-trace-input {
diff --git a/tests/auto/inputpanel/data/inputpanel/handwriting.js b/tests/auto/inputpanel/data/inputpanel/handwriting.js
index 5f5fd4bc..7ec5e179 100644
--- a/tests/auto/inputpanel/data/inputpanel/handwriting.js
+++ b/tests/auto/inputpanel/data/inputpanel/handwriting.js
@@ -30,17 +30,23 @@
.pragma library
.import "unipen_data.js" as UnipenData
+.import "unipen_data_simp_chinese.js" as UnipenDataSimpChinese
function emulate(testcase, hwrInputArea, ch, instant) {
var chKey = (((typeof ch == "number") ? ch : ch.charCodeAt(0)) + 0x100000000).toString(16).substr(1)
while (chKey.length > 4 && chKey[0] === '0')
chKey = chKey.substring(1)
chKey = "0x" + chKey
- if (!UnipenData.unipenData.hasOwnProperty(chKey))
+ var unipenData
+ if (UnipenData.unipenData.hasOwnProperty(chKey))
+ unipenData = UnipenData
+ else if (UnipenDataSimpChinese.unipenData.hasOwnProperty(chKey))
+ unipenData = UnipenDataSimpChinese
+ else
return false
- var chData = UnipenData.unipenData[chKey]
+ var chData = unipenData.unipenData[chKey]
var scale = Math.min(hwrInputArea.width / chData[".X_DIM"], hwrInputArea.height / chData[".Y_DIM"])
- var strokes = UnipenData.unipenData[chKey][".PEN"]
+ var strokes = unipenData.unipenData[chKey][".PEN"]
var t = 0
for (var strokeIndex = 0; strokeIndex < strokes.length; strokeIndex++) {
var stroke = strokes[strokeIndex]
diff --git a/tests/auto/inputpanel/data/inputpanel/inputpanel.qml b/tests/auto/inputpanel/data/inputpanel/inputpanel.qml
index f082115a..05667524 100644
--- a/tests/auto/inputpanel/data/inputpanel/inputpanel.qml
+++ b/tests/auto/inputpanel/data/inputpanel/inputpanel.qml
@@ -29,7 +29,7 @@
import QtTest 1.0
import QtQuick 2.0
-import QtQuick.VirtualKeyboard 2.2
+import QtQuick.VirtualKeyboard 2.3
import QtQuick.VirtualKeyboard.Settings 2.2
import "handwriting.js" as Handwriting
import "utils.js" as Utils
@@ -263,6 +263,8 @@ InputPanel {
return InputEngine.Katakana
else if (inputModeName === "FullwidthLatin")
return InputEngine.FullwidthLatin
+ else if (inputModeName === "ChineseHandwriting")
+ return InputEngine.ChineseHandwriting
else
return -1
}
diff --git a/tests/auto/inputpanel/data/inputpanel/unipen_data.js b/tests/auto/inputpanel/data/inputpanel/unipen_data.js
index 4c69b43d..83ad1a2c 100644
--- a/tests/auto/inputpanel/data/inputpanel/unipen_data.js
+++ b/tests/auto/inputpanel/data/inputpanel/unipen_data.js
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Virtual Keyboard module of the Qt Toolkit.
@@ -10170,239 +10170,339 @@ var unipenData = {
".PEN": [
[
[
- 435,
- 78,
+ 407,
+ 75,
0
],
[
- 435,
- 77,
- 13
- ],
- [
- 435,
- 76,
- 33
- ],
- [
- 435,
- 75,
- 70
+ 406,
+ 74,
+ 76
],
[
- 434,
+ 403,
74,
- 84
+ 96
],
[
- 434,
+ 401,
73,
- 109
+ 100
],
[
- 431,
- 70,
- 130
+ 395,
+ 73,
+ 128
],
[
- 417,
- 65,
- 156
+ 392,
+ 73,
+ 129
],
[
- 407,
- 65,
- 174
+ 388,
+ 73,
+ 131
],
[
385,
- 70,
- 194
+ 73,
+ 162
],
[
- 377,
+ 375,
73,
- 214
+ 162
],
[
- 361,
- 83,
- 238
+ 370,
+ 75,
+ 163
],
[
- 355,
- 88,
- 247
+ 360,
+ 78,
+ 195
],
[
349,
- 100,
- 275
+ 85,
+ 196
],
[
- 354,
- 113,
- 310
+ 342,
+ 91,
+ 228
],
[
- 360,
- 117,
- 338
+ 338,
+ 98,
+ 229
],
[
- 368,
- 120,
- 357
+ 337,
+ 107,
+ 261
],
[
- 379,
- 120,
- 377
+ 337,
+ 115,
+ 261
],
[
- 408,
- 110,
- 412
+ 342,
+ 125,
+ 294
],
[
- 414,
- 105,
- 437
+ 347,
+ 133,
+ 295
],
[
- 423,
- 95,
- 462
+ 351,
+ 135,
+ 299
],
[
- 424,
- 91,
- 484
+ 353,
+ 139,
+ 328
],
[
- 426,
- 87,
- 511
+ 359,
+ 143,
+ 329
],
[
- 426,
- 83,
- 541
+ 363,
+ 144,
+ 331
],
[
- 427,
- 81,
+ 365,
+ 145,
+ 361
+ ],
+ [
+ 369,
+ 148,
+ 362
+ ],
+ [
+ 371,
+ 148,
+ 363
+ ],
+ [
+ 372,
+ 148,
+ 395
+ ],
+ [
+ 376,
+ 146,
+ 396
+ ],
+ [
+ 382,
+ 143,
+ 427
+ ],
+ [
+ 387,
+ 137,
+ 428
+ ],
+ [
+ 391,
+ 129,
+ 461
+ ],
+ [
+ 395,
+ 123,
+ 461
+ ],
+ [
+ 400,
+ 118,
+ 494
+ ],
+ [
+ 401,
+ 113,
+ 494
+ ],
+ [
+ 402,
+ 108,
+ 527
+ ],
+ [
+ 404,
+ 103,
+ 528
+ ],
+ [
+ 406,
+ 99,
+ 531
+ ],
+ [
+ 406,
+ 96,
+ 561
+ ],
+ [
+ 408,
+ 90,
+ 561
+ ],
+ [
+ 408,
+ 88,
564
],
[
- 426,
- 80,
- 591
+ 408,
+ 86,
+ 593
],
[
- 427,
- 79,
- 648
+ 409,
+ 84,
+ 594
],
[
- 427,
+ 409,
+ 83,
+ 627
+ ],
+ [
+ 409,
+ 82,
+ 661
+ ],
+ [
+ 409,
81,
- 698
+ 694
],
[
- 427,
+ 409,
83,
- 722
+ 844
],
[
- 428,
- 90,
- 741
+ 409,
+ 89,
+ 878
],
[
- 430,
+ 409,
+ 93,
+ 879
+ ],
+ [
+ 409,
98,
- 760
+ 884
],
[
- 432,
- 118,
- 788
+ 408,
+ 103,
+ 910
],
[
- 434,
- 129,
- 797
+ 407,
+ 118,
+ 911
],
[
- 436,
- 151,
- 829
+ 407,
+ 135,
+ 943
],
[
- 436,
- 160,
- 844
+ 407,
+ 157,
+ 944
],
[
- 437,
+ 407,
166,
- 863
+ 948
],
[
- 437,
- 171,
- 899
+ 409,
+ 175,
+ 976
],
[
- 437,
- 173,
- 921
+ 410,
+ 186,
+ 977
],
[
- 437,
- 176,
- 938
+ 410,
+ 191,
+ 979
],
[
- 437,
- 177,
- 964
+ 411,
+ 196,
+ 1009
],
[
- 437,
- 178,
- 985
+ 411,
+ 203,
+ 1010
],
[
- 437,
- 179,
- 1013
+ 413,
+ 207,
+ 1011
],
[
- 437,
- 180,
- 1032
+ 413,
+ 211,
+ 1042
],
[
- 437,
- 181,
- 1101
+ 413,
+ 223,
+ 1043
],
[
- 438,
- 181,
- 1136
+ 413,
+ 230,
+ 1076
+ ],
+ [
+ 413,
+ 234,
+ 1077
+ ],
+ [
+ 413,
+ 235,
+ 1109
+ ],
+ [
+ 413,
+ 236,
+ 1110
]
]
],
".POINTS_PER_SECOND": 60,
".SEGMENT": "CHARACTER",
".VERSION": "1.0",
- ".X_DIM": 821,
- ".X_POINTS_PER_INCH": 100,
- ".Y_DIM": 211,
- ".Y_POINTS_PER_INCH": 100
+ ".X_DIM": 1032,
+ ".X_POINTS_PER_INCH": 95,
+ ".Y_DIM": 263,
+ ".Y_POINTS_PER_INCH": 95
},
"0x0072": {
".COORD": [
diff --git a/tests/auto/inputpanel/data/inputpanel/unipen_data_simp_chinese.js b/tests/auto/inputpanel/data/inputpanel/unipen_data_simp_chinese.js
new file mode 100644
index 00000000..b5942e6b
--- /dev/null
+++ b/tests/auto/inputpanel/data/inputpanel/unipen_data_simp_chinese.js
@@ -0,0 +1,1851 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Virtual Keyboard module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+var unipenData = {
+ "0x4e2d": {
+ ".COORD": [
+ "X",
+ "Y",
+ "T"
+ ],
+ ".HIERARCHY": "CHARACTER",
+ ".PEN": [
+ [
+ [
+ 185,
+ 59,
+ 0
+ ],
+ [
+ 185,
+ 61,
+ 181
+ ],
+ [
+ 185,
+ 63,
+ 202
+ ],
+ [
+ 185,
+ 65,
+ 205
+ ],
+ [
+ 185,
+ 68,
+ 235
+ ],
+ [
+ 185,
+ 70,
+ 236
+ ],
+ [
+ 185,
+ 73,
+ 236
+ ],
+ [
+ 185,
+ 75,
+ 268
+ ],
+ [
+ 185,
+ 80,
+ 269
+ ],
+ [
+ 185,
+ 84,
+ 301
+ ],
+ [
+ 185,
+ 87,
+ 302
+ ],
+ [
+ 185,
+ 89,
+ 334
+ ],
+ [
+ 185,
+ 92,
+ 335
+ ],
+ [
+ 185,
+ 94,
+ 368
+ ],
+ [
+ 184,
+ 97,
+ 369
+ ],
+ [
+ 184,
+ 98,
+ 372
+ ],
+ [
+ 184,
+ 99,
+ 401
+ ],
+ [
+ 184,
+ 101,
+ 402
+ ],
+ [
+ 184,
+ 102,
+ 405
+ ],
+ [
+ 184,
+ 104,
+ 434
+ ],
+ [
+ 184,
+ 105,
+ 467
+ ],
+ [
+ 184,
+ 106,
+ 501
+ ],
+ [
+ 184,
+ 107,
+ 534
+ ],
+ [
+ 184,
+ 108,
+ 535
+ ],
+ [
+ 184,
+ 109,
+ 567
+ ],
+ [
+ 184,
+ 111,
+ 567
+ ],
+ [
+ 184,
+ 112,
+ 604
+ ],
+ [
+ 184,
+ 113,
+ 634
+ ],
+ [
+ 184,
+ 114,
+ 667
+ ],
+ [
+ 184,
+ 115,
+ 700
+ ],
+ [
+ 184,
+ 116,
+ 734
+ ],
+ [
+ 184,
+ 117,
+ 767
+ ],
+ [
+ 184,
+ 118,
+ 801
+ ],
+ [
+ 184,
+ 119,
+ 805
+ ],
+ [
+ 184,
+ 120,
+ 833
+ ],
+ [
+ 184,
+ 121,
+ 837
+ ],
+ [
+ 184,
+ 123,
+ 866
+ ],
+ [
+ 184,
+ 124,
+ 902
+ ],
+ [
+ 184,
+ 125,
+ 937
+ ],
+ [
+ 184,
+ 126,
+ 997
+ ],
+ [
+ 184,
+ 127,
+ 1040
+ ],
+ [
+ 184,
+ 128,
+ 1149
+ ]
+ ],
+ [
+ [
+ 184,
+ 63,
+ 1871
+ ],
+ [
+ 185,
+ 63,
+ 1917
+ ],
+ [
+ 186,
+ 63,
+ 1918
+ ],
+ [
+ 188,
+ 63,
+ 1947
+ ],
+ [
+ 190,
+ 62,
+ 1948
+ ],
+ [
+ 191,
+ 62,
+ 1948
+ ],
+ [
+ 196,
+ 62,
+ 1980
+ ],
+ [
+ 202,
+ 62,
+ 1981
+ ],
+ [
+ 209,
+ 61,
+ 2014
+ ],
+ [
+ 217,
+ 61,
+ 2015
+ ],
+ [
+ 228,
+ 61,
+ 2047
+ ],
+ [
+ 241,
+ 61,
+ 2048
+ ],
+ [
+ 248,
+ 61,
+ 2052
+ ],
+ [
+ 253,
+ 61,
+ 2080
+ ],
+ [
+ 264,
+ 61,
+ 2081
+ ],
+ [
+ 267,
+ 61,
+ 2084
+ ],
+ [
+ 271,
+ 61,
+ 2114
+ ],
+ [
+ 277,
+ 61,
+ 2114
+ ],
+ [
+ 279,
+ 61,
+ 2117
+ ],
+ [
+ 283,
+ 61,
+ 2147
+ ],
+ [
+ 288,
+ 61,
+ 2148
+ ],
+ [
+ 290,
+ 61,
+ 2149
+ ],
+ [
+ 292,
+ 61,
+ 2180
+ ],
+ [
+ 296,
+ 61,
+ 2181
+ ],
+ [
+ 298,
+ 61,
+ 2213
+ ],
+ [
+ 299,
+ 61,
+ 2214
+ ],
+ [
+ 301,
+ 60,
+ 2246
+ ],
+ [
+ 302,
+ 60,
+ 2437
+ ],
+ [
+ 303,
+ 60,
+ 2485
+ ],
+ [
+ 304,
+ 60,
+ 2532
+ ],
+ [
+ 305,
+ 60,
+ 2564
+ ],
+ [
+ 305,
+ 61,
+ 2596
+ ],
+ [
+ 305,
+ 62,
+ 2629
+ ],
+ [
+ 305,
+ 64,
+ 2630
+ ],
+ [
+ 305,
+ 66,
+ 2662
+ ],
+ [
+ 305,
+ 67,
+ 2663
+ ],
+ [
+ 306,
+ 69,
+ 2695
+ ],
+ [
+ 306,
+ 71,
+ 2696
+ ],
+ [
+ 306,
+ 75,
+ 2729
+ ],
+ [
+ 306,
+ 78,
+ 2730
+ ],
+ [
+ 306,
+ 79,
+ 2732
+ ],
+ [
+ 306,
+ 81,
+ 2762
+ ],
+ [
+ 306,
+ 85,
+ 2763
+ ],
+ [
+ 306,
+ 87,
+ 2764
+ ],
+ [
+ 306,
+ 89,
+ 2796
+ ],
+ [
+ 306,
+ 93,
+ 2796
+ ],
+ [
+ 306,
+ 95,
+ 2796
+ ],
+ [
+ 306,
+ 101,
+ 2828
+ ],
+ [
+ 305,
+ 105,
+ 2829
+ ],
+ [
+ 305,
+ 109,
+ 2862
+ ],
+ [
+ 305,
+ 113,
+ 2863
+ ],
+ [
+ 304,
+ 119,
+ 2896
+ ],
+ [
+ 304,
+ 122,
+ 2896
+ ],
+ [
+ 304,
+ 123,
+ 2901
+ ],
+ [
+ 303,
+ 126,
+ 2929
+ ],
+ [
+ 303,
+ 128,
+ 2929
+ ],
+ [
+ 303,
+ 130,
+ 2962
+ ],
+ [
+ 303,
+ 131,
+ 2962
+ ],
+ [
+ 303,
+ 132,
+ 2995
+ ],
+ [
+ 303,
+ 133,
+ 3029
+ ]
+ ],
+ [
+ [
+ 184,
+ 129,
+ 4367
+ ],
+ [
+ 188,
+ 129,
+ 4409
+ ],
+ [
+ 190,
+ 129,
+ 4410
+ ],
+ [
+ 191,
+ 129,
+ 4413
+ ],
+ [
+ 193,
+ 129,
+ 4441
+ ],
+ [
+ 197,
+ 128,
+ 4442
+ ],
+ [
+ 198,
+ 128,
+ 4444
+ ],
+ [
+ 200,
+ 128,
+ 4474
+ ],
+ [
+ 203,
+ 128,
+ 4475
+ ],
+ [
+ 205,
+ 127,
+ 4476
+ ],
+ [
+ 209,
+ 127,
+ 4508
+ ],
+ [
+ 219,
+ 127,
+ 4509
+ ],
+ [
+ 227,
+ 127,
+ 4541
+ ],
+ [
+ 237,
+ 127,
+ 4542
+ ],
+ [
+ 244,
+ 127,
+ 4574
+ ],
+ [
+ 246,
+ 127,
+ 4575
+ ],
+ [
+ 248,
+ 127,
+ 4607
+ ],
+ [
+ 250,
+ 127,
+ 4608
+ ],
+ [
+ 252,
+ 126,
+ 4640
+ ],
+ [
+ 255,
+ 126,
+ 4641
+ ],
+ [
+ 257,
+ 126,
+ 4644
+ ],
+ [
+ 259,
+ 125,
+ 4674
+ ],
+ [
+ 261,
+ 125,
+ 4675
+ ],
+ [
+ 263,
+ 125,
+ 4677
+ ],
+ [
+ 265,
+ 125,
+ 4707
+ ],
+ [
+ 266,
+ 125,
+ 4740
+ ],
+ [
+ 269,
+ 125,
+ 4741
+ ],
+ [
+ 270,
+ 125,
+ 4774
+ ],
+ [
+ 275,
+ 125,
+ 4775
+ ],
+ [
+ 279,
+ 125,
+ 4807
+ ],
+ [
+ 283,
+ 125,
+ 4808
+ ],
+ [
+ 287,
+ 125,
+ 4840
+ ],
+ [
+ 289,
+ 125,
+ 4841
+ ],
+ [
+ 291,
+ 125,
+ 4873
+ ],
+ [
+ 292,
+ 125,
+ 4874
+ ],
+ [
+ 293,
+ 125,
+ 4876
+ ],
+ [
+ 294,
+ 125,
+ 4907
+ ],
+ [
+ 296,
+ 125,
+ 4908
+ ],
+ [
+ 298,
+ 125,
+ 4909
+ ],
+ [
+ 299,
+ 125,
+ 4940
+ ],
+ [
+ 302,
+ 125,
+ 4941
+ ],
+ [
+ 303,
+ 126,
+ 4974
+ ],
+ [
+ 304,
+ 126,
+ 5006
+ ],
+ [
+ 305,
+ 126,
+ 5007
+ ],
+ [
+ 306,
+ 126,
+ 5040
+ ]
+ ],
+ [
+ [
+ 244,
+ 32,
+ 6303
+ ],
+ [
+ 244,
+ 34,
+ 6340
+ ],
+ [
+ 244,
+ 35,
+ 6355
+ ],
+ [
+ 244,
+ 37,
+ 6356
+ ],
+ [
+ 244,
+ 40,
+ 6386
+ ],
+ [
+ 244,
+ 46,
+ 6387
+ ],
+ [
+ 244,
+ 50,
+ 6388
+ ],
+ [
+ 244,
+ 54,
+ 6419
+ ],
+ [
+ 245,
+ 67,
+ 6420
+ ],
+ [
+ 247,
+ 75,
+ 6453
+ ],
+ [
+ 248,
+ 84,
+ 6453
+ ],
+ [
+ 249,
+ 89,
+ 6486
+ ],
+ [
+ 249,
+ 96,
+ 6487
+ ],
+ [
+ 250,
+ 102,
+ 6519
+ ],
+ [
+ 250,
+ 107,
+ 6520
+ ],
+ [
+ 251,
+ 111,
+ 6537
+ ],
+ [
+ 251,
+ 113,
+ 6553
+ ],
+ [
+ 251,
+ 118,
+ 6553
+ ],
+ [
+ 251,
+ 120,
+ 6557
+ ],
+ [
+ 251,
+ 122,
+ 6586
+ ],
+ [
+ 251,
+ 126,
+ 6587
+ ],
+ [
+ 251,
+ 128,
+ 6588
+ ],
+ [
+ 251,
+ 130,
+ 6619
+ ],
+ [
+ 252,
+ 133,
+ 6620
+ ],
+ [
+ 252,
+ 135,
+ 6620
+ ],
+ [
+ 252,
+ 138,
+ 6652
+ ],
+ [
+ 252,
+ 141,
+ 6653
+ ],
+ [
+ 252,
+ 144,
+ 6686
+ ],
+ [
+ 252,
+ 147,
+ 6686
+ ],
+ [
+ 252,
+ 151,
+ 6719
+ ],
+ [
+ 252,
+ 154,
+ 6719
+ ],
+ [
+ 252,
+ 156,
+ 6724
+ ],
+ [
+ 252,
+ 157,
+ 6752
+ ],
+ [
+ 252,
+ 161,
+ 6753
+ ],
+ [
+ 252,
+ 162,
+ 6756
+ ],
+ [
+ 252,
+ 165,
+ 6786
+ ],
+ [
+ 252,
+ 167,
+ 6788
+ ],
+ [
+ 252,
+ 170,
+ 6819
+ ],
+ [
+ 252,
+ 172,
+ 6820
+ ],
+ [
+ 252,
+ 174,
+ 6852
+ ],
+ [
+ 252,
+ 176,
+ 6852
+ ],
+ [
+ 252,
+ 178,
+ 6885
+ ],
+ [
+ 252,
+ 180,
+ 6886
+ ],
+ [
+ 252,
+ 182,
+ 6918
+ ],
+ [
+ 252,
+ 183,
+ 6919
+ ],
+ [
+ 252,
+ 184,
+ 6935
+ ],
+ [
+ 251,
+ 185,
+ 6952
+ ],
+ [
+ 251,
+ 186,
+ 6956
+ ],
+ [
+ 251,
+ 187,
+ 6985
+ ],
+ [
+ 251,
+ 188,
+ 7018
+ ],
+ [
+ 251,
+ 189,
+ 7205
+ ],
+ [
+ 251,
+ 190,
+ 7293
+ ]
+ ]
+ ],
+ ".POINTS_PER_SECOND": 60,
+ ".SEGMENT": "CHARACTER",
+ ".VERSION": "1.0",
+ ".X_DIM": 1032,
+ ".X_POINTS_PER_INCH": 95,
+ ".Y_DIM": 263,
+ ".Y_POINTS_PER_INCH": 95
+ },
+ "0x6587": {
+ ".COORD": [
+ "X",
+ "Y",
+ "T"
+ ],
+ ".HIERARCHY": "CHARACTER",
+ ".PEN": [
+ [
+ [
+ 415,
+ 26,
+ 0
+ ],
+ [
+ 416,
+ 28,
+ 46
+ ],
+ [
+ 417,
+ 28,
+ 63
+ ],
+ [
+ 418,
+ 30,
+ 96
+ ],
+ [
+ 419,
+ 31,
+ 97
+ ],
+ [
+ 420,
+ 31,
+ 100
+ ],
+ [
+ 422,
+ 34,
+ 130
+ ],
+ [
+ 424,
+ 36,
+ 163
+ ],
+ [
+ 425,
+ 38,
+ 163
+ ],
+ [
+ 426,
+ 39,
+ 164
+ ],
+ [
+ 427,
+ 42,
+ 196
+ ],
+ [
+ 428,
+ 45,
+ 196
+ ],
+ [
+ 429,
+ 47,
+ 229
+ ],
+ [
+ 430,
+ 50,
+ 230
+ ],
+ [
+ 431,
+ 52,
+ 263
+ ],
+ [
+ 431,
+ 54,
+ 263
+ ],
+ [
+ 432,
+ 55,
+ 296
+ ],
+ [
+ 433,
+ 56,
+ 296
+ ],
+ [
+ 433,
+ 57,
+ 300
+ ],
+ [
+ 433,
+ 58,
+ 330
+ ],
+ [
+ 433,
+ 59,
+ 330
+ ],
+ [
+ 433,
+ 60,
+ 364
+ ],
+ [
+ 433,
+ 61,
+ 365
+ ],
+ [
+ 433,
+ 62,
+ 404
+ ]
+ ],
+ [
+ [
+ 356,
+ 80,
+ 1935
+ ],
+ [
+ 357,
+ 80,
+ 1980
+ ],
+ [
+ 358,
+ 81,
+ 1981
+ ],
+ [
+ 359,
+ 81,
+ 2008
+ ],
+ [
+ 361,
+ 81,
+ 2012
+ ],
+ [
+ 363,
+ 81,
+ 2041
+ ],
+ [
+ 365,
+ 81,
+ 2045
+ ],
+ [
+ 367,
+ 81,
+ 2075
+ ],
+ [
+ 370,
+ 81,
+ 2075
+ ],
+ [
+ 372,
+ 81,
+ 2076
+ ],
+ [
+ 375,
+ 81,
+ 2108
+ ],
+ [
+ 379,
+ 81,
+ 2109
+ ],
+ [
+ 385,
+ 80,
+ 2141
+ ],
+ [
+ 390,
+ 80,
+ 2142
+ ],
+ [
+ 396,
+ 79,
+ 2174
+ ],
+ [
+ 403,
+ 79,
+ 2175
+ ],
+ [
+ 406,
+ 79,
+ 2180
+ ],
+ [
+ 410,
+ 77,
+ 2208
+ ],
+ [
+ 416,
+ 77,
+ 2209
+ ],
+ [
+ 420,
+ 76,
+ 2213
+ ],
+ [
+ 422,
+ 76,
+ 2241
+ ],
+ [
+ 428,
+ 75,
+ 2242
+ ],
+ [
+ 431,
+ 75,
+ 2245
+ ],
+ [
+ 434,
+ 74,
+ 2274
+ ],
+ [
+ 443,
+ 73,
+ 2275
+ ],
+ [
+ 446,
+ 73,
+ 2276
+ ],
+ [
+ 451,
+ 71,
+ 2307
+ ],
+ [
+ 460,
+ 70,
+ 2308
+ ],
+ [
+ 464,
+ 68,
+ 2341
+ ],
+ [
+ 468,
+ 67,
+ 2341
+ ],
+ [
+ 470,
+ 66,
+ 2374
+ ],
+ [
+ 475,
+ 64,
+ 2374
+ ],
+ [
+ 477,
+ 64,
+ 2392
+ ],
+ [
+ 479,
+ 63,
+ 2407
+ ],
+ [
+ 484,
+ 62,
+ 2408
+ ],
+ [
+ 485,
+ 61,
+ 2440
+ ],
+ [
+ 487,
+ 60,
+ 2441
+ ],
+ [
+ 489,
+ 59,
+ 2444
+ ],
+ [
+ 491,
+ 57,
+ 2474
+ ],
+ [
+ 493,
+ 56,
+ 2508
+ ],
+ [
+ 495,
+ 55,
+ 2508
+ ],
+ [
+ 496,
+ 54,
+ 2508
+ ],
+ [
+ 498,
+ 53,
+ 2540
+ ],
+ [
+ 499,
+ 52,
+ 2541
+ ],
+ [
+ 500,
+ 50,
+ 2573
+ ],
+ [
+ 501,
+ 49,
+ 2607
+ ],
+ [
+ 502,
+ 48,
+ 2640
+ ],
+ [
+ 503,
+ 47,
+ 2641
+ ],
+ [
+ 504,
+ 46,
+ 2673
+ ],
+ [
+ 504,
+ 45,
+ 2724
+ ],
+ [
+ 504,
+ 44,
+ 3053
+ ]
+ ],
+ [
+ [
+ 434,
+ 82,
+ 4671
+ ],
+ [
+ 434,
+ 84,
+ 4718
+ ],
+ [
+ 434,
+ 86,
+ 4735
+ ],
+ [
+ 434,
+ 88,
+ 4740
+ ],
+ [
+ 434,
+ 89,
+ 4768
+ ],
+ [
+ 434,
+ 92,
+ 4769
+ ],
+ [
+ 433,
+ 94,
+ 4772
+ ],
+ [
+ 432,
+ 96,
+ 4802
+ ],
+ [
+ 430,
+ 101,
+ 4802
+ ],
+ [
+ 429,
+ 103,
+ 4804
+ ],
+ [
+ 429,
+ 106,
+ 4835
+ ],
+ [
+ 428,
+ 112,
+ 4836
+ ],
+ [
+ 428,
+ 114,
+ 4836
+ ],
+ [
+ 425,
+ 121,
+ 4868
+ ],
+ [
+ 423,
+ 127,
+ 4869
+ ],
+ [
+ 421,
+ 132,
+ 4901
+ ],
+ [
+ 417,
+ 137,
+ 4902
+ ],
+ [
+ 414,
+ 143,
+ 4934
+ ],
+ [
+ 412,
+ 148,
+ 4935
+ ],
+ [
+ 406,
+ 156,
+ 4968
+ ],
+ [
+ 401,
+ 163,
+ 4969
+ ],
+ [
+ 398,
+ 166,
+ 4972
+ ],
+ [
+ 397,
+ 169,
+ 5001
+ ],
+ [
+ 393,
+ 173,
+ 5002
+ ],
+ [
+ 392,
+ 175,
+ 5005
+ ],
+ [
+ 391,
+ 176,
+ 5034
+ ],
+ [
+ 390,
+ 178,
+ 5035
+ ],
+ [
+ 389,
+ 179,
+ 5036
+ ],
+ [
+ 388,
+ 180,
+ 5067
+ ],
+ [
+ 387,
+ 181,
+ 5068
+ ],
+ [
+ 386,
+ 182,
+ 5101
+ ],
+ [
+ 385,
+ 182,
+ 5589
+ ],
+ [
+ 384,
+ 182,
+ 5616
+ ]
+ ],
+ [
+ [
+ 385,
+ 84,
+ 6399
+ ],
+ [
+ 386,
+ 84,
+ 6468
+ ],
+ [
+ 387,
+ 87,
+ 6489
+ ],
+ [
+ 388,
+ 90,
+ 6531
+ ],
+ [
+ 391,
+ 95,
+ 6531
+ ],
+ [
+ 392,
+ 97,
+ 6532
+ ],
+ [
+ 394,
+ 99,
+ 6564
+ ],
+ [
+ 396,
+ 106,
+ 6564
+ ],
+ [
+ 401,
+ 113,
+ 6597
+ ],
+ [
+ 406,
+ 120,
+ 6598
+ ],
+ [
+ 411,
+ 126,
+ 6630
+ ],
+ [
+ 416,
+ 132,
+ 6631
+ ],
+ [
+ 422,
+ 138,
+ 6663
+ ],
+ [
+ 426,
+ 143,
+ 6664
+ ],
+ [
+ 427,
+ 143,
+ 6668
+ ],
+ [
+ 429,
+ 145,
+ 6697
+ ],
+ [
+ 432,
+ 150,
+ 6697
+ ],
+ [
+ 433,
+ 153,
+ 6700
+ ],
+ [
+ 436,
+ 156,
+ 6730
+ ],
+ [
+ 440,
+ 160,
+ 6730
+ ],
+ [
+ 444,
+ 163,
+ 6732
+ ],
+ [
+ 447,
+ 165,
+ 6763
+ ],
+ [
+ 454,
+ 171,
+ 6764
+ ],
+ [
+ 457,
+ 173,
+ 6797
+ ],
+ [
+ 458,
+ 173,
+ 6798
+ ],
+ [
+ 459,
+ 174,
+ 6830
+ ],
+ [
+ 460,
+ 175,
+ 6831
+ ],
+ [
+ 462,
+ 176,
+ 6863
+ ],
+ [
+ 466,
+ 178,
+ 6864
+ ],
+ [
+ 471,
+ 179,
+ 6897
+ ],
+ [
+ 473,
+ 179,
+ 6897
+ ],
+ [
+ 474,
+ 180,
+ 6900
+ ],
+ [
+ 475,
+ 180,
+ 6930
+ ],
+ [
+ 477,
+ 180,
+ 6964
+ ],
+ [
+ 478,
+ 180,
+ 6996
+ ],
+ [
+ 479,
+ 180,
+ 7029
+ ],
+ [
+ 480,
+ 180,
+ 7064
+ ],
+ [
+ 481,
+ 181,
+ 7097
+ ],
+ [
+ 483,
+ 181,
+ 7100
+ ],
+ [
+ 485,
+ 181,
+ 7149
+ ],
+ [
+ 486,
+ 181,
+ 7180
+ ],
+ [
+ 487,
+ 181,
+ 7213
+ ],
+ [
+ 488,
+ 181,
+ 7304
+ ],
+ [
+ 489,
+ 181,
+ 7346
+ ],
+ [
+ 490,
+ 181,
+ 7396
+ ],
+ [
+ 491,
+ 181,
+ 7428
+ ],
+ [
+ 491,
+ 182,
+ 7629
+ ]
+ ]
+ ],
+ ".POINTS_PER_SECOND": 60,
+ ".SEGMENT": "CHARACTER",
+ ".VERSION": "1.0",
+ ".X_DIM": 1032,
+ ".X_POINTS_PER_INCH": 95,
+ ".Y_DIM": 263,
+ ".Y_POINTS_PER_INCH": 95
+ }
+}
diff --git a/tests/auto/inputpanel/data/tst_inputpanel.qml b/tests/auto/inputpanel/data/tst_inputpanel.qml
index 7d83d31d..95cbe5d1 100644
--- a/tests/auto/inputpanel/data/tst_inputpanel.qml
+++ b/tests/auto/inputpanel/data/tst_inputpanel.qml
@@ -105,6 +105,11 @@ Rectangle {
verify(inputPanel.setLocale(locale))
if (localeChanged && !(textInput.inputMethodHints & Qt.ImhNoPredictiveText))
wait(300)
+ if (data !== undefined && data.hasOwnProperty("initHwrMode") && data.initHwrMode) {
+ if (!inputPanel.setHandwritingMode(true))
+ expectFail("", "Handwriting not enabled")
+ verify(inputPanel.handwritingMode === true)
+ }
if (data !== undefined && data.hasOwnProperty("initInputMode")) {
var inputMode = inputPanel.mapInputMode(data.initInputMode)
if (!inputPanel.isInputModeSupported(inputMode))
@@ -1093,19 +1098,16 @@ Rectangle {
function test_hwrInputSequence_data() {
return [
- { initInputMethodHints: Qt.ImhNoPredictiveText, toggleShiftCount: 0, inputSequence: "abcdefghij", outputText: "Abcdefghij" },
- { initInputMethodHints: Qt.ImhNoPredictiveText, toggleShiftCount: 1, inputSequence: "klmnopqrst", outputText: "klmnopqrst" },
- { initInputMethodHints: Qt.ImhNoPredictiveText, toggleShiftCount: 3, inputSequence: "uvwxyz", outputText: "UVWXYZ" },
+ { initHwrMode: true, initInputMethodHints: Qt.ImhNoPredictiveText, toggleShiftCount: 0, inputSequence: "abcdefghij", outputText: "Abcdefghij" },
+ { initHwrMode: true, initInputMethodHints: Qt.ImhNoPredictiveText, toggleShiftCount: 1, inputSequence: "klmnopqrst", outputText: "klmnopqrst" },
+ { initHwrMode: true, initInputMethodHints: Qt.ImhNoPredictiveText, toggleShiftCount: 3, inputSequence: "uvwxyz", outputText: "UVWXYZ" },
+ { initHwrMode: true, initInputMethodHints: Qt.ImhNone, initLocale: "zh_CN", initInputMode: "ChineseHandwriting", inputSequence: "\u4e2d\u6587", outputText: "\u4e2d\u6587" },
]
}
function test_hwrInputSequence(data) {
prepareTest(data)
- if (!inputPanel.setHandwritingMode(true))
- expectFail("", "Handwriting not enabled")
- verify(inputPanel.handwritingMode === true)
-
for (var i = 0; i < data.toggleShiftCount; i++) {
inputPanel.toggleShift()
}
@@ -1126,20 +1128,20 @@ Rectangle {
function test_hwrNumericInputSequence_data() {
return [
- { initInputMethodHints: Qt.ImhNoPredictiveText | Qt.ImhPreferNumbers, modeSwitchAllowed: true, inputSequence: "0123456789", outputText: "0123456789" },
- { initInputMethodHints: Qt.ImhNoPredictiveText | Qt.ImhDigitsOnly, modeSwitchAllowed: false, inputSequence: "1234567890", outputText: "1234567890" },
- { initInputMethodHints: Qt.ImhNoPredictiveText | Qt.ImhFormattedNumbersOnly, modeSwitchAllowed: false, inputSequence: "1234567890+", outputText: "1234567890+" },
- { initInputMethodHints: Qt.ImhNoPredictiveText | Qt.ImhDialableCharactersOnly, modeSwitchAllowed: false, inputSequence: "1234567890+", outputText: "1234567890+" },
+ { initHwrMode: true, initInputMethodHints: Qt.ImhNoPredictiveText | Qt.ImhPreferNumbers, modeSwitchAllowed: true, inputSequence: "0123456789", outputText: "0123456789" },
+ { initHwrMode: true, initInputMethodHints: Qt.ImhNoPredictiveText | Qt.ImhDigitsOnly, modeSwitchAllowed: false, inputSequence: "1234567890", outputText: "1234567890" },
+ { initHwrMode: true, initInputMethodHints: Qt.ImhNoPredictiveText | Qt.ImhFormattedNumbersOnly, modeSwitchAllowed: false, inputSequence: "1234567890+", outputText: "1234567890+" },
+ { initHwrMode: true, initInputMethodHints: Qt.ImhNoPredictiveText | Qt.ImhDialableCharactersOnly, modeSwitchAllowed: false, inputSequence: "1234567890+", outputText: "1234567890+" },
+ { initHwrMode: true, initLocale: "zh_CN", initInputMethodHints: Qt.ImhNoPredictiveText | Qt.ImhPreferNumbers, modeSwitchAllowed: true, inputSequence: "0123456789", outputText: "0123456789" },
+ { initHwrMode: true, initLocale: "zh_CN", initInputMethodHints: Qt.ImhNoPredictiveText | Qt.ImhDigitsOnly, modeSwitchAllowed: false, inputSequence: "1234567890", outputText: "1234567890" },
+ { initHwrMode: true, initLocale: "zh_CN", initInputMethodHints: Qt.ImhNoPredictiveText | Qt.ImhFormattedNumbersOnly, modeSwitchAllowed: false, inputSequence: "1234567890+", outputText: "1234567890+" },
+ { initHwrMode: true, initLocale: "zh_CN", initInputMethodHints: Qt.ImhNoPredictiveText | Qt.ImhDialableCharactersOnly, modeSwitchAllowed: false, inputSequence: "1234567890+", outputText: "1234567890+" },
]
}
function test_hwrNumericInputSequence(data) {
prepareTest(data)
- if (!inputPanel.setHandwritingMode(true))
- expectFail("", "Handwriting not enabled")
- verify(inputPanel.handwritingMode === true)
-
for (var inputIndex in data.inputSequence) {
verify(inputPanel.emulateHandwriting(data.inputSequence.charAt(inputIndex), true))
}
@@ -1265,6 +1267,7 @@ Rectangle {
function test_hwrFullScreenGestures_data() {
return [
{ initInputMethodHints: Qt.ImhNoPredictiveText, inputSequence: ["a","b","c",Qt.Key_Backspace,Qt.Key_Space,"c"], outputText: "Ab c" },
+ { initHwrMode: true, initInputMethodHints: Qt.ImhNone, initLocale: "zh_CN", initInputMode: "ChineseHandwriting", inputSequence: ["\u4e2d", "\u6587", Qt.Key_Backspace], outputText: "\u4e2d" },
]
}
@@ -1298,7 +1301,8 @@ Rectangle {
prepareTest(data)
if (!handwritingInputPanel.enabled)
- skip("Handwriting not enabled")
+ expectFail("", "Handwriting not enabled")
+ verify(handwritingInputPanel.enabled)
handwritingInputPanel.available = true
if (!inputPanel.wordCandidateListVisibleHint)
skip("Word candidates not available (spell correction/hwr suggestions)")
diff --git a/tests/auto/inputpanel/hwr_test_data/alphanumeric/113_100_0.txt b/tests/auto/inputpanel/hwr_test_data/alphanumeric/113_100_0.txt
index 714ad4c8..14d57b77 100644
--- a/tests/auto/inputpanel/hwr_test_data/alphanumeric/113_100_0.txt
+++ b/tests/auto/inputpanel/hwr_test_data/alphanumeric/113_100_0.txt
@@ -2,55 +2,75 @@
.HIERARCHY CHARACTER
.COORD X Y T
.SEGMENT CHARACTER
-.X_DIM 821
-.Y_DIM 211
-.X_POINTS_PER_INCH 100
-.Y_POINTS_PER_INCH 100
+.X_DIM 1032
+.Y_DIM 263
+.X_POINTS_PER_INCH 95
+.Y_POINTS_PER_INCH 95
.POINTS_PER_SECOND 60
.PEN_DOWN
-435 78 0
-435 77 13
-435 76 33
-435 75 70
-434 74 84
-434 73 109
-431 70 130
-417 65 156
-407 65 174
-385 70 194
-377 73 214
-361 83 238
-355 88 247
-349 100 275
-354 113 310
-360 117 338
-368 120 357
-379 120 377
-408 110 412
-414 105 437
-423 95 462
-424 91 484
-426 87 511
-426 83 541
-427 81 564
-426 80 591
-427 79 648
-427 81 698
-427 83 722
-428 90 741
-430 98 760
-432 118 788
-434 129 797
-436 151 829
-436 160 844
-437 166 863
-437 171 899
-437 173 921
-437 176 938
-437 177 964
-437 178 985
-437 179 1013
-437 180 1032
-437 181 1101
-438 181 1136
+407 75 0
+406 74 76
+403 74 96
+401 73 100
+395 73 128
+392 73 129
+388 73 131
+385 73 162
+375 73 162
+370 75 163
+360 78 195
+349 85 196
+342 91 228
+338 98 229
+337 107 261
+337 115 261
+342 125 294
+347 133 295
+351 135 299
+353 139 328
+359 143 329
+363 144 331
+365 145 361
+369 148 362
+371 148 363
+372 148 395
+376 146 396
+382 143 427
+387 137 428
+391 129 461
+395 123 461
+400 118 494
+401 113 494
+402 108 527
+404 103 528
+406 99 531
+406 96 561
+408 90 561
+408 88 564
+408 86 593
+409 84 594
+409 83 627
+409 82 661
+409 81 694
+409 83 844
+409 89 878
+409 93 879
+409 98 884
+408 103 910
+407 118 911
+407 135 943
+407 157 944
+407 166 948
+409 175 976
+410 186 977
+410 191 979
+411 196 1009
+411 203 1010
+413 207 1011
+413 211 1042
+413 223 1043
+413 230 1076
+413 234 1077
+413 235 1109
+413 236 1110
.PEN_UP
diff --git a/tests/auto/inputpanel/hwr_test_data/build_unipen_data.py b/tests/auto/inputpanel/hwr_test_data/build_unipen_data.py
index 639912b9..eb4231c3 100755
--- a/tests/auto/inputpanel/hwr_test_data/build_unipen_data.py
+++ b/tests/auto/inputpanel/hwr_test_data/build_unipen_data.py
@@ -2,19 +2,30 @@
#############################################################################
##
-## Copyright (C) 2015 The Qt Company Ltd
-## All rights reserved.
-## For any questions to The Qt Company, please use contact form at http://qt.io
+## Copyright (C) 2017 The Qt Company Ltd.
+## Contact: https://www.qt.io/licensing/
##
-## This file is part of the Qt Virtual Keyboard add-on.
+## This file is part of the Qt Virtual Keyboard module of the Qt Toolkit.
##
-## Licensees holding valid commercial license for Qt may use this file in
-## accordance with the Qt License Agreement provided with the Software
-## or, alternatively, in accordance with the terms contained in a written
-## agreement between you and The Qt Company.
+## $QT_BEGIN_LICENSE:GPL$
+## Commercial License Usage
+## Licensees holding valid commercial Qt licenses may use this file in
+## accordance with the commercial license agreement provided with the
+## Software or, alternatively, in accordance with the terms contained in
+## a written agreement between you and The Qt Company. For licensing terms
+## and conditions see https://www.qt.io/terms-conditions. For further
+## information use the contact form at https://www.qt.io/contact-us.
##
-## If you have questions regarding the use of this file, please use
-## contact form at http://qt.io
+## GNU General Public License Usage
+## Alternatively, this file may be used under the terms of the GNU
+## General Public License version 3 or (at your option) any later version
+## approved by the KDE Free Qt Foundation. The licenses are as published by
+## the Free Software Foundation and appearing in the file LICENSE.GPL3
+## included in the packaging of this file. Please review the following
+## information to ensure the GNU General Public License requirements will
+## be met: https://www.gnu.org/licenses/gpl-3.0.html.
+##
+## $QT_END_LICENSE$
##
#############################################################################
@@ -28,23 +39,34 @@ import re
unipen_file_pattern = re.compile(r'(^[0-9]{2,9}).*\.txt')
def print_header():
- print """/******************************************************************************
+ print """/****************************************************************************
+**
+** Copyright (C) %s The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
**
-** Copyright (C) %s The Qt Company Ltd
-** All rights reserved.
-** For any questions to The Qt Company, please use contact form at http://qt.io
+** This file is part of the Qt Virtual Keyboard module of the Qt Toolkit.
**
-** This file is part of the Qt Virtual Keyboard add-on.
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
**
-** Licensees holding valid commercial license for Qt may use this file in
-** accordance with the Qt License Agreement provided with the Software
-** or, alternatively, in accordance with the terms contained in a written
-** agreement between you and The Qt Company.
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
-** If you have questions regarding the use of this file, please use
-** contact form at http://qt.io
+** $QT_END_LICENSE$
**
-******************************************************************************/""" % datetime.datetime.now().year
+****************************************************************************/""" % datetime.datetime.now().year
def scan_unipen_files(path):
file_list = []
diff --git a/tests/auto/inputpanel/hwr_test_data/simp_chinese/20013_100_0.txt b/tests/auto/inputpanel/hwr_test_data/simp_chinese/20013_100_0.txt
new file mode 100644
index 00000000..27414acd
--- /dev/null
+++ b/tests/auto/inputpanel/hwr_test_data/simp_chinese/20013_100_0.txt
@@ -0,0 +1,216 @@
+.VERSION 1.0
+.HIERARCHY CHARACTER
+.COORD X Y T
+.SEGMENT CHARACTER
+.X_DIM 1032
+.Y_DIM 263
+.X_POINTS_PER_INCH 95
+.Y_POINTS_PER_INCH 95
+.POINTS_PER_SECOND 60
+.PEN_DOWN
+185 59 0
+185 61 181
+185 63 202
+185 65 205
+185 68 235
+185 70 236
+185 73 236
+185 75 268
+185 80 269
+185 84 301
+185 87 302
+185 89 334
+185 92 335
+185 94 368
+184 97 369
+184 98 372
+184 99 401
+184 101 402
+184 102 405
+184 104 434
+184 105 467
+184 106 501
+184 107 534
+184 108 535
+184 109 567
+184 111 567
+184 112 604
+184 113 634
+184 114 667
+184 115 700
+184 116 734
+184 117 767
+184 118 801
+184 119 805
+184 120 833
+184 121 837
+184 123 866
+184 124 902
+184 125 937
+184 126 997
+184 127 1040
+184 128 1149
+.PEN_UP
+.PEN_DOWN
+184 63 1871
+185 63 1917
+186 63 1918
+188 63 1947
+190 62 1948
+191 62 1948
+196 62 1980
+202 62 1981
+209 61 2014
+217 61 2015
+228 61 2047
+241 61 2048
+248 61 2052
+253 61 2080
+264 61 2081
+267 61 2084
+271 61 2114
+277 61 2114
+279 61 2117
+283 61 2147
+288 61 2148
+290 61 2149
+292 61 2180
+296 61 2181
+298 61 2213
+299 61 2214
+301 60 2246
+302 60 2437
+303 60 2485
+304 60 2532
+305 60 2564
+305 61 2596
+305 62 2629
+305 64 2630
+305 66 2662
+305 67 2663
+306 69 2695
+306 71 2696
+306 75 2729
+306 78 2730
+306 79 2732
+306 81 2762
+306 85 2763
+306 87 2764
+306 89 2796
+306 93 2796
+306 95 2796
+306 101 2828
+305 105 2829
+305 109 2862
+305 113 2863
+304 119 2896
+304 122 2896
+304 123 2901
+303 126 2929
+303 128 2929
+303 130 2962
+303 131 2962
+303 132 2995
+303 133 3029
+.PEN_UP
+.PEN_DOWN
+184 129 4367
+188 129 4409
+190 129 4410
+191 129 4413
+193 129 4441
+197 128 4442
+198 128 4444
+200 128 4474
+203 128 4475
+205 127 4476
+209 127 4508
+219 127 4509
+227 127 4541
+237 127 4542
+244 127 4574
+246 127 4575
+248 127 4607
+250 127 4608
+252 126 4640
+255 126 4641
+257 126 4644
+259 125 4674
+261 125 4675
+263 125 4677
+265 125 4707
+266 125 4740
+269 125 4741
+270 125 4774
+275 125 4775
+279 125 4807
+283 125 4808
+287 125 4840
+289 125 4841
+291 125 4873
+292 125 4874
+293 125 4876
+294 125 4907
+296 125 4908
+298 125 4909
+299 125 4940
+302 125 4941
+303 126 4974
+304 126 5006
+305 126 5007
+306 126 5040
+.PEN_UP
+.PEN_DOWN
+244 32 6303
+244 34 6340
+244 35 6355
+244 37 6356
+244 40 6386
+244 46 6387
+244 50 6388
+244 54 6419
+245 67 6420
+247 75 6453
+248 84 6453
+249 89 6486
+249 96 6487
+250 102 6519
+250 107 6520
+251 111 6537
+251 113 6553
+251 118 6553
+251 120 6557
+251 122 6586
+251 126 6587
+251 128 6588
+251 130 6619
+252 133 6620
+252 135 6620
+252 138 6652
+252 141 6653
+252 144 6686
+252 147 6686
+252 151 6719
+252 154 6719
+252 156 6724
+252 157 6752
+252 161 6753
+252 162 6756
+252 165 6786
+252 167 6788
+252 170 6819
+252 172 6820
+252 174 6852
+252 176 6852
+252 178 6885
+252 180 6886
+252 182 6918
+252 183 6919
+252 184 6935
+251 185 6952
+251 186 6956
+251 187 6985
+251 188 7018
+251 189 7205
+251 190 7293
+.PEN_UP
diff --git a/tests/auto/inputpanel/hwr_test_data/simp_chinese/25991_100_0.txt b/tests/auto/inputpanel/hwr_test_data/simp_chinese/25991_100_0.txt
new file mode 100644
index 00000000..eec059f5
--- /dev/null
+++ b/tests/auto/inputpanel/hwr_test_data/simp_chinese/25991_100_0.txt
@@ -0,0 +1,172 @@
+.VERSION 1.0
+.HIERARCHY CHARACTER
+.COORD X Y T
+.SEGMENT CHARACTER
+.X_DIM 1032
+.Y_DIM 263
+.X_POINTS_PER_INCH 95
+.Y_POINTS_PER_INCH 95
+.POINTS_PER_SECOND 60
+.PEN_DOWN
+415 26 0
+416 28 46
+417 28 63
+418 30 96
+419 31 97
+420 31 100
+422 34 130
+424 36 163
+425 38 163
+426 39 164
+427 42 196
+428 45 196
+429 47 229
+430 50 230
+431 52 263
+431 54 263
+432 55 296
+433 56 296
+433 57 300
+433 58 330
+433 59 330
+433 60 364
+433 61 365
+433 62 404
+.PEN_UP
+.PEN_DOWN
+356 80 1935
+357 80 1980
+358 81 1981
+359 81 2008
+361 81 2012
+363 81 2041
+365 81 2045
+367 81 2075
+370 81 2075
+372 81 2076
+375 81 2108
+379 81 2109
+385 80 2141
+390 80 2142
+396 79 2174
+403 79 2175
+406 79 2180
+410 77 2208
+416 77 2209
+420 76 2213
+422 76 2241
+428 75 2242
+431 75 2245
+434 74 2274
+443 73 2275
+446 73 2276
+451 71 2307
+460 70 2308
+464 68 2341
+468 67 2341
+470 66 2374
+475 64 2374
+477 64 2392
+479 63 2407
+484 62 2408
+485 61 2440
+487 60 2441
+489 59 2444
+491 57 2474
+493 56 2508
+495 55 2508
+496 54 2508
+498 53 2540
+499 52 2541
+500 50 2573
+501 49 2607
+502 48 2640
+503 47 2641
+504 46 2673
+504 45 2724
+504 44 3053
+.PEN_UP
+.PEN_DOWN
+434 82 4671
+434 84 4718
+434 86 4735
+434 88 4740
+434 89 4768
+434 92 4769
+433 94 4772
+432 96 4802
+430 101 4802
+429 103 4804
+429 106 4835
+428 112 4836
+428 114 4836
+425 121 4868
+423 127 4869
+421 132 4901
+417 137 4902
+414 143 4934
+412 148 4935
+406 156 4968
+401 163 4969
+398 166 4972
+397 169 5001
+393 173 5002
+392 175 5005
+391 176 5034
+390 178 5035
+389 179 5036
+388 180 5067
+387 181 5068
+386 182 5101
+385 182 5589
+384 182 5616
+.PEN_UP
+.PEN_DOWN
+385 84 6399
+386 84 6468
+387 87 6489
+388 90 6531
+391 95 6531
+392 97 6532
+394 99 6564
+396 106 6564
+401 113 6597
+406 120 6598
+411 126 6630
+416 132 6631
+422 138 6663
+426 143 6664
+427 143 6668
+429 145 6697
+432 150 6697
+433 153 6700
+436 156 6730
+440 160 6730
+444 163 6732
+447 165 6763
+454 171 6764
+457 173 6797
+458 173 6798
+459 174 6830
+460 175 6831
+462 176 6863
+466 178 6864
+471 179 6897
+473 179 6897
+474 180 6900
+475 180 6930
+477 180 6964
+478 180 6996
+479 180 7029
+480 180 7064
+481 181 7097
+483 181 7100
+485 181 7149
+486 181 7180
+487 181 7213
+488 181 7304
+489 181 7346
+490 181 7396
+491 181 7428
+491 182 7629
+.PEN_UP