diff --git a/src/config.pri b/src/config.pri index 11ae1c0ba213f9c23e3a2f71a8f0a44abc4842f5..c1784b140c03f3dfaed24bd542542d4b28edbb93 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 ab519b90184b8f41459cd6d2f925d60e2f50248c..f810badfb7d84bda4f12785d4a3c5c891a746272 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 05f723a727b1fdef9b59053a9281a757106e4e7a..f6dddf1c45f453063b4623b4d7e47b62b2f598b4 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 0000000000000000000000000000000000000000..1e2136566fe56c914bd21263e7472da53f5dfb9b --- /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 dcc10d547339be1ceb007885ad416c723c0c197e..d880867f0d64e4ef3fb4fc4bf21baf74459fcfc0 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 2bd2ff2e8637f51f2bac1fe4290ee1bbaf3f365a..8dfd7f7d95dd0ea45a5bb9c931b3e318909e7adb 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 0000000000000000000000000000000000000000..598980d1b6a03a04da34834dc940cbe37d6fd5ff --- /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 c8e21c8459c5bb278c2fc8c32980cfb3cae40736..44b0ceb7ce9b454aae58f8cfa1add3381251f1c9 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 03f0344f1fccd2b225f4a5179a43e38ac0ee1b38..9d00bb11418c9cf796708eda46c4c50809346c3b 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 a1cb3ffcf18034f6e2e9f8b7d34b707ef3fa9627..3751608bde77c1799f2282a0b6f4d78c84191d81 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 3d9d9eace61745a41237ec46f6e8d49df121767f..9b18a54d11f0fc9e453a31488b5eb43d4e0b8747 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 10f7dc7b5cfdb9c8409d611fa9aa87b0d2565b58..dd04f645e566c4a835a26815a5927c14131f4a09 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 ce0122ac73fc854b43c740e58a46ae0caf0bff20..d1b99ae48c1a6b1414152b62fb9d5869a09120fd 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 376410e3fc12e11dfab8e904f52be1f3a8a44381..a5f80d05d66dc558274ef337987459996cd185fa 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 0000000000000000000000000000000000000000..621d231224e4ed82c4811370be70cfc020e804d7 --- /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 0000000000000000000000000000000000000000..d15b16e3bd4f686e481b786405d271f9accb6423 --- /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 0000000000000000000000000000000000000000..dc2d9475cb5b1a6e487b959089107d47e7f8851e --- /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 eb4cf9fbde37389d9a8c92ac524302abfb19f9f2..3028d7d01ebdfb5a83620b09e7e89fba92518ff2 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 9b87ebdf6f6017d57834040113db29f51fd59770..173abd990d1704cd6e9794b0bbaafa22c0d9b5a5 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 733033ce26fe716824a9de24d370401ab48b9913..c2f3312434202f0db75fba56bdc971e3c246966e 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 23a4cf026615891b4caf68d46a722d4974196474..5dc8c0d91b61015d7c08ecd9ef89c561f07cff11 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 25b1586f79299fbd3b16168f9dc6107c50d88014..e16eaba9fd81dc856770f7dddd65ed290e83734d 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 5f5fd4bc3190bd6d82a7bb8010a4f6a183b9684d..7ec5e1798fc603e5eead704cf086bdba7e8695f0 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 f082115a9e290339d130a27798bab5ee1fa9b315..05667524437c48dde0097e42d40b494374d7ef91 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 4c69b43dd2966e67de785040c6e87cd3a5a243a8..83ad1a2c50c741555665d64ab682a227ab32f861 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 0000000000000000000000000000000000000000..b5942e6b325f8da5c8b91ad391ea8b5abe1890b0 --- /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 7d83d31dfe510ceab5b12d2160f3107a47761daf..95cbe5d16a90544094f6bacf29a0ce212687cdb6 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 714ad4c8cfcc1cf0e27823a22105b1288821f3a0..14d57b7766feb0bd95b13586f826dbde7bc43ba3 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 639912b9151f87ffcf77829470b792e8343adbdf..eb4231c3d48f015712204e6a5842efda9cefe8fb 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 0000000000000000000000000000000000000000..27414acd4db6b1f2c4e9e9d2b1f5133f766c695b --- /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 0000000000000000000000000000000000000000..eec059f59d732b90b6a1194f6f40dc50e2798dbe --- /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