diff options
author | Jarkko Koivikko <jarkko.koivikko@code-q.fi> | 2015-10-15 10:18:22 +0300 |
---|---|---|
committer | Jarkko Koivikko <jarkko.koivikko@code-q.fi> | 2015-10-27 10:13:51 +0000 |
commit | 81c0d7d4df40c1130a8e7c98411c045b7eafc475 (patch) | |
tree | bee9a753c20b80d6630f39fee86e7758f2aae926 | |
parent | 251a86bd64856c2f48ac62f8f8747ddf21982876 (diff) |
Add Cangjie input method for Traditional Chinese
This change adds Cangjie input method for Traditional Chinese.
The input method is based on the TCIME input method for Android.
https://code.google.com/p/android-traditional-chinese-ime/
The Cangjie input method is enabled by adding CONFIG+=tcime into
qmake command line.
The version of the Cangjie input is v3.
Change-Id: Ibc49484d1277c332fc17e9b49b4227256ad88d5a
Reviewed-by: Liang Qi <liang.qi@theqtcompany.com>
Reviewed-by: Mitch Curtis <mitch.curtis@theqtcompany.com>
Reviewed-by: Rainer Keller <rainer.keller@theqtcompany.com>
36 files changed, 2298 insertions, 10 deletions
diff --git a/src/src.pro b/src/src.pro index 12dbd983..ca5b8481 100644 --- a/src/src.pro +++ b/src/src.pro @@ -19,6 +19,11 @@ pinyin { virtualkeyboard.depends += sub-virtualkeyboard-3rdparty-pinyin } +tcime { + SUBDIRS += virtualkeyboard/3rdparty/tcime + virtualkeyboard.depends += sub-virtualkeyboard-3rdparty-tcime +} + lipi-toolkit { SUBDIRS += virtualkeyboard/3rdparty/lipi-toolkit virtualkeyboard.depends += sub-virtualkeyboard-3rdparty-lipi-toolkit diff --git a/src/virtualkeyboard/3rdparty/tcime/COPYING b/src/virtualkeyboard/3rdparty/tcime/COPYING new file mode 100644 index 00000000..606b8475 --- /dev/null +++ b/src/virtualkeyboard/3rdparty/tcime/COPYING @@ -0,0 +1,92 @@ +The project in general is under the following licenses: + +================================================================================ +Copyright 2010 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +================================================================================ + +File dict_phrases.dat is built from libTabe; the licenses of libTabe is: + +================================================================================ +/* + * Copyrighy (c) 1999 TaBE Project. + * Copyright (c) 1999 Pai-Hsiang Hsiao. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * . Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * . Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * . Neither the name of the TaBE Project nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Copyright (c) 1999 Computer Systems and Communication Lab, + * Institute of Information Science, Academia Sinica. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * . Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * . Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * . Neither the name of the Computer Systems and Communication Lab + * nor the names of its contributors may be used to endorse or + * promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +Copyright 1996 Chih-Hao Tsai @ Beckman Institute, University of Illinois +c-tsai4@uiuc.edu http://casper.beckman.uiuc.edu/~c-tsai4 +================================================================================ + diff --git a/src/virtualkeyboard/3rdparty/tcime/cangjiedictionary.cpp b/src/virtualkeyboard/3rdparty/tcime/cangjiedictionary.cpp new file mode 100644 index 00000000..1a50d371 --- /dev/null +++ b/src/virtualkeyboard/3rdparty/tcime/cangjiedictionary.cpp @@ -0,0 +1,141 @@ +/* + * Qt implementation of TCIME library + * This file is part of the Qt Virtual Keyboard module. + * Contact: http://www.qt.io/licensing/ + * + * Copyright (C) 2015 The Qt Company + * Copyright 2010 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "cangjiedictionary.h" +#include "cangjietable.h" + +using namespace tcime; + +bool CangjieDictionary::_simplified = false; + +CangjieDictionary::CangjieDictionary() : + WordDictionary(), + _collator(QLocale("zh_TW")) +{ +} + +bool CangjieDictionary::simplified() const +{ + return _simplified; +} + +void CangjieDictionary::setSimplified(bool simplified) +{ + _simplified = simplified; +} + +QStringList CangjieDictionary::getWords(const QString &input) const +{ + // Look up the index in the dictionary for the specified input. + int primaryIndex = CangjieTable::getPrimaryIndex(input); + if (primaryIndex < 0 || primaryIndex >= dictionary().size()) + return QStringList(); + + // [25 * 26] char[] array; each primary entry points to a char[] + // containing words with the same primary index; then words can be looked up + // by their secondary index stored at the beginning of each char[]. + const DictionaryEntry &data = dictionary()[primaryIndex]; + if (data.isEmpty()) + return QStringList(); + + if (_simplified) + // Sort words of this primary index for simplified-cangjie. + return sortWords(data); + + int secondaryIndex = CangjieTable::getSecondaryIndex(input); + if (secondaryIndex < 0) + return QStringList(); + + // Find words match this secondary index for cangjie. + return searchWords(secondaryIndex, data); +} + +class DictionaryComparator +{ +public: + explicit DictionaryComparator(const std::vector<QCollatorSortKey> &sortKeys) : + sortKeys(sortKeys) + {} + + bool operator()(int a, int b) + { + return sortKeys[a] < sortKeys[b]; + } + +private: + const std::vector<QCollatorSortKey> &sortKeys; +}; + +QStringList CangjieDictionary::sortWords(const DictionaryEntry &data) const +{ + int length = data.size() / 2; + std::vector<QCollatorSortKey> sortKeys; + QVector<int> keys; + sortKeys.reserve(length); + keys.reserve(length); + for (int i = 0; i < length; ++i) { + sortKeys.push_back(_collator.sortKey(data[length + i])); + keys.append(i); + } + DictionaryComparator dictionaryComparator(sortKeys); + std::sort(keys.begin(), keys.end(), dictionaryComparator); + + QStringList words; + for (int i = 0; i < length; ++i) + words.append(data[length + keys[i]]); + + return words; +} + +QStringList CangjieDictionary::searchWords(int secondaryIndex, const DictionaryEntry &data) const +{ + int length = data.size() / 2; + + DictionaryEntry::ConstIterator start = data.constBegin(); + DictionaryEntry::ConstIterator end = start + length; + DictionaryEntry::ConstIterator rangeStart = qBinaryFind(start, end, (DictionaryWord)secondaryIndex); + if (rangeStart == end) + return QStringList(); + + // There may be more than one words with the same index; look up words with + // the same secondary index. + while (rangeStart != start) { + if (*(rangeStart - 1) != (DictionaryWord)secondaryIndex) + break; + rangeStart--; + } + + DictionaryEntry::ConstIterator rangeEnd = rangeStart + 1; + while (rangeEnd != end) { + if (*rangeEnd != (DictionaryWord)secondaryIndex) + break; + rangeEnd++; + } + + QStringList words; + words.reserve(rangeEnd - rangeStart); + for (DictionaryEntry::ConstIterator rangeIndex = rangeStart; rangeIndex < rangeEnd; ++rangeIndex) { + DictionaryEntry::ConstIterator item(rangeIndex + length); + words.append(*item); + } + + return words; +} diff --git a/src/virtualkeyboard/3rdparty/tcime/cangjiedictionary.h b/src/virtualkeyboard/3rdparty/tcime/cangjiedictionary.h new file mode 100644 index 00000000..b87013a9 --- /dev/null +++ b/src/virtualkeyboard/3rdparty/tcime/cangjiedictionary.h @@ -0,0 +1,54 @@ +/* + * Qt implementation of TCIME library + * This file is part of the Qt Virtual Keyboard module. + * Contact: http://www.qt.io/licensing/ + * + * Copyright (C) 2015 The Qt Company + * Copyright 2010 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CANGJIEDICTIONARY_H +#define CANGJIEDICTIONARY_H + +#include "worddictionary.h" +#include <QCollator> + +namespace tcime { + +/** + * Extends WordDictionary to provide cangjie word-suggestions. + */ +class CangjieDictionary : public WordDictionary +{ +public: + CangjieDictionary(); + + bool simplified() const; + void setSimplified(bool simplified); + + QStringList getWords(const QString &input) const; + +private: + QStringList sortWords(const DictionaryEntry &data) const; + QStringList searchWords(int secondaryIndex, const DictionaryEntry &data) const; + +private: + QCollator _collator; + static bool _simplified; +}; + +} + +#endif // CANGJIEDICTIONARY_H diff --git a/src/virtualkeyboard/3rdparty/tcime/cangjietable.cpp b/src/virtualkeyboard/3rdparty/tcime/cangjietable.cpp new file mode 100644 index 00000000..ee8ae4f8 --- /dev/null +++ b/src/virtualkeyboard/3rdparty/tcime/cangjietable.cpp @@ -0,0 +1,111 @@ +/* + * Qt implementation of TCIME library + * This file is part of the Qt Virtual Keyboard module. + * Contact: http://www.qt.io/licensing/ + * + * Copyright (C) 2015 The Qt Company + * Copyright 2010 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "cangjietable.h" + +using namespace tcime; + +const int CangjieTable::BASE_NUMBER = 26; +const int CangjieTable::MAX_CODE_LENGTH = 5; +const int CangjieTable::MAX_SIMPLIFIED_CODE_LENGTH = 2; + +const QMap<QChar, int> &CangjieTable::letters() +{ + static QMap<QChar, int> letters; + if (letters.isEmpty()) { + int i = 1; + letters.insert(0x65e5, i++); + letters.insert(0x6708, i++); + letters.insert(0x91d1, i++); + letters.insert(0x6728, i++); + letters.insert(0x6c34, i++); + letters.insert(0x706b, i++); + letters.insert(0x571f, i++); + letters.insert(0x7af9, i++); + letters.insert(0x6208, i++); + letters.insert(0x5341, i++); + letters.insert(0x5927, i++); + letters.insert(0x4e2d, i++); + letters.insert(0x4e00, i++); + letters.insert(0x5f13, i++); + letters.insert(0x4eba, i++); + letters.insert(0x5fc3, i++); + letters.insert(0x624b, i++); + letters.insert(0x53e3, i++); + letters.insert(0x5c38, i++); + letters.insert(0x5eff, i++); + letters.insert(0x5c71, i++); + letters.insert(0x5973, i++); + letters.insert(0x7530, i++); + letters.insert(0x96e3, i++); + letters.insert(0x535c, i++); + } + return letters; +} + +bool CangjieTable::isLetter(const QChar &c) +{ + static const QMap<QChar, int> &letters = CangjieTable::letters(); + return letters.contains(c); +} + +int CangjieTable::getPrimaryIndex(const QString &code) +{ + static const QMap<QChar, int> &letters = CangjieTable::letters(); + int length = code.length(); + if ((length < 1) || (length > MAX_CODE_LENGTH)) + return -1; + + QChar c = code.at(0); + if (!isLetter(c)) + return -1; + + // The first letter cannot be absent in the code; therefore, the numerical + // index of the first letter starts from 0 instead. + int index = (letters[c] - 1) * BASE_NUMBER; + if (length < 2) + return index; + + c = code.at(length - 1); + if (!isLetter(c)) + return -1; + + return index + letters[c]; +} + +int CangjieTable::getSecondaryIndex(const QString &code) +{ + static const QMap<QChar, int> &letters = CangjieTable::letters(); + int index = 0; + int last = code.length() - 1; + for (int i = 1; i < last; i++) { + QChar c = code.at(i); + if (!isLetter(c)) + return -1; + index = index * BASE_NUMBER + letters[c]; + } + + int maxEnd = MAX_CODE_LENGTH - 1; + for (int i = last; i < maxEnd; i++) + index = index * BASE_NUMBER; + + return index; +} diff --git a/src/virtualkeyboard/3rdparty/tcime/cangjietable.h b/src/virtualkeyboard/3rdparty/tcime/cangjietable.h new file mode 100644 index 00000000..54adced3 --- /dev/null +++ b/src/virtualkeyboard/3rdparty/tcime/cangjietable.h @@ -0,0 +1,79 @@ +/* + * Qt implementation of TCIME library + * This file is part of the Qt Virtual Keyboard module. + * Contact: http://www.qt.io/licensing/ + * + * Copyright (C) 2015 The Qt Company + * Copyright 2010 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CANGJIETABLE_H +#define CANGJIETABLE_H + +#include <QMap> +#include <QChar> +#include <QString> + +namespace tcime { + +/** + * Defines cangjie letters and calculates the index of the given cangjie code. + */ +class CangjieTable +{ + Q_DISABLE_COPY(CangjieTable) + CangjieTable() {} + + // Cangjie 25 letters with number-index starting from 1: + // 日月金木水火土竹戈十大中一弓人心手口尸廿山女田難卜 + static const QMap<QChar, int> &letters(); + static const int BASE_NUMBER; + +public: + + // Cangjie codes contain at most five letters. A cangjie code can be + // converted to a numerical code by the number-index of each letter. + // The absent letter will be indexed as 0 if the cangjie code contains less + // than five-letters. + static const int MAX_CODE_LENGTH; + static const int MAX_SIMPLIFIED_CODE_LENGTH; + + /** + * Returns {@code true} only if the given character is a valid cangjie letter. + */ + static bool isLetter(const QChar &c); + + /** + * Returns the primary index calculated by the first and last letter of + * the given cangjie code. + * + * @param code should not be null. + * @return -1 for invalid code. + */ + static int getPrimaryIndex(const QString &code); + + /** + * Returns the secondary index calculated by letters between the first and + * last letter of the given cangjie code. + * + * @param code should not be null. + * @return -1 for invalid code. + */ + static int getSecondaryIndex(const QString &code); +}; + +} + +#endif // CANGJIETABLE_H diff --git a/src/virtualkeyboard/3rdparty/tcime/data/java/dict_cangjie.dat b/src/virtualkeyboard/3rdparty/tcime/data/java/dict_cangjie.dat Binary files differnew file mode 100644 index 00000000..1c692c48 --- /dev/null +++ b/src/virtualkeyboard/3rdparty/tcime/data/java/dict_cangjie.dat diff --git a/src/virtualkeyboard/3rdparty/tcime/data/java/dict_phrases.dat b/src/virtualkeyboard/3rdparty/tcime/data/java/dict_phrases.dat Binary files differnew file mode 100644 index 00000000..0b34ee1f --- /dev/null +++ b/src/virtualkeyboard/3rdparty/tcime/data/java/dict_phrases.dat diff --git a/src/virtualkeyboard/3rdparty/tcime/data/qt/dict_cangjie.dat b/src/virtualkeyboard/3rdparty/tcime/data/qt/dict_cangjie.dat Binary files differnew file mode 100644 index 00000000..f99ed965 --- /dev/null +++ b/src/virtualkeyboard/3rdparty/tcime/data/qt/dict_cangjie.dat diff --git a/src/virtualkeyboard/3rdparty/tcime/data/qt/dict_phrases.dat b/src/virtualkeyboard/3rdparty/tcime/data/qt/dict_phrases.dat Binary files differnew file mode 100644 index 00000000..463301f9 --- /dev/null +++ b/src/virtualkeyboard/3rdparty/tcime/data/qt/dict_phrases.dat diff --git a/src/virtualkeyboard/3rdparty/tcime/phrasedictionary.cpp b/src/virtualkeyboard/3rdparty/tcime/phrasedictionary.cpp new file mode 100644 index 00000000..cdeaecdd --- /dev/null +++ b/src/virtualkeyboard/3rdparty/tcime/phrasedictionary.cpp @@ -0,0 +1,66 @@ +/* + * Qt implementation of TCIME library + * This file is part of the Qt Virtual Keyboard module. + * Contact: http://www.qt.io/licensing/ + * + * Copyright (C) 2015 The Qt Company + * Copyright 2010 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "phrasedictionary.h" + +using namespace tcime; + +PhraseDictionary::PhraseDictionary() : + WordDictionary() +{ +} + +QStringList PhraseDictionary::getWords(const QString &input) const +{ + if (input.length() != 1) + return QStringList(); + + // Phrases are stored in an array consisting of three character arrays. + // char[0][] contains a char[] of words to look for phrases. + // char[2][] contains a char[] of following words for char[0][]. + // char[1][] contains offsets of char[0][] words to map its following words. + // For example, there are 5 phrases: Aa, Aa', Bb, Bb', Cc. + // char[0][] { A, B, C } + // char[1][] { 0, 2, 4 } + // char[2][] { a, a', b, b', c} + const Dictionary &dict = dictionary(); + if (dict.length() != 3) + return QStringList(); + + const DictionaryEntry &words = dict[0]; + + DictionaryEntry::ConstIterator word = qBinaryFind(words, input.at(0)); + if (word == words.constEnd()) + return QStringList(); + + int index = word - words.constBegin(); + const DictionaryEntry &offsets = dict[1]; + const DictionaryEntry &phrases = dict[2]; + int offset = (int)offsets[index].unicode(); + int count = (index < offsets.length() - 1) ? + ((int)offsets[index + 1].unicode() - offset) : (phrases.length() - offset); + + QStringList result; + for (int i = 0; i < count; ++i) + result.append(phrases[offset + i]); + + return result; +} diff --git a/src/virtualkeyboard/3rdparty/tcime/phrasedictionary.h b/src/virtualkeyboard/3rdparty/tcime/phrasedictionary.h new file mode 100644 index 00000000..06fe9578 --- /dev/null +++ b/src/virtualkeyboard/3rdparty/tcime/phrasedictionary.h @@ -0,0 +1,43 @@ +/* + * Qt implementation of TCIME library + * This file is part of the Qt Virtual Keyboard module. + * Contact: http://www.qt.io/licensing/ + * + * Copyright (C) 2015 The Qt Company + * Copyright 2010 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PHRASEDICTIONARY_H +#define PHRASEDICTIONARY_H + +#include "worddictionary.h" + +namespace tcime { + +/** + * Reads a phrase dictionary and provides following-word suggestions as a list + * of characters for the given character. + */ +class PhraseDictionary : public WordDictionary +{ +public: + PhraseDictionary(); + + QStringList getWords(const QString &input) const; +}; + +} + +#endif // PHRASEDICTIONARY_H diff --git a/src/virtualkeyboard/3rdparty/tcime/tcime.pro b/src/virtualkeyboard/3rdparty/tcime/tcime.pro new file mode 100644 index 00000000..65261f46 --- /dev/null +++ b/src/virtualkeyboard/3rdparty/tcime/tcime.pro @@ -0,0 +1,29 @@ +TEMPLATE = lib +QT -= gui +TARGET = tcime +CONFIG += staticlib + +build_pass { + CONFIG(debug, debug|release): SUBPATH = debug + else: SUBPATH = release +} else { + debug_and_release: CONFIG += build_all +} + +DESTDIR = $$SUBPATH + +SOURCES += \ + cangjiedictionary.cpp \ + cangjietable.cpp \ + phrasedictionary.cpp \ + worddictionary.cpp + +HEADERS += \ + cangjiedictionary.h \ + cangjietable.h \ + phrasedictionary.h \ + worddictionary.h + +OTHER_FILES += \ + data/dict_cangjie.dat \ + data/dict_phrases.dat diff --git a/src/virtualkeyboard/3rdparty/tcime/tools/dict2qt.class b/src/virtualkeyboard/3rdparty/tcime/tools/dict2qt.class Binary files differnew file mode 100644 index 00000000..d1e70d8e --- /dev/null +++ b/src/virtualkeyboard/3rdparty/tcime/tools/dict2qt.class diff --git a/src/virtualkeyboard/3rdparty/tcime/tools/dict2qt.java b/src/virtualkeyboard/3rdparty/tcime/tools/dict2qt.java new file mode 100644 index 00000000..6dd81212 --- /dev/null +++ b/src/virtualkeyboard/3rdparty/tcime/tools/dict2qt.java @@ -0,0 +1,201 @@ +/****************************************************************************** +** +** 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 +** +** This file is part of the Qt Virtual Keyboard module. +** +** 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. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.io +** +******************************************************************************/ + +import java.io.BufferedInputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +public class dict2qt { + + public static void main(String[] args) { + boolean showHelp = false; + boolean showUsage = false; + boolean littleEndian = false; + String outputFileName = ""; + String inputFileName = ""; + File inputFile = null; + + if (args.length > 0) { + for (int i = 0; i < args.length; i++) { + if (args[i].startsWith("-")) { + if (args[i].compareTo("-h") == 0) { + showHelp = true; + showUsage = true; + break; + } else if (args[i].compareTo("-o") == 0) { + if (++i >= args.length) { + System.err.println("Error: missing argument <output file>"); + showUsage = true; + break; + } + outputFileName = args[i]; + } else if (args[i].compareTo("-le") == 0) { + littleEndian = true; + } else { + System.err.println("Error: unknown option '" + args[i] + "'"); + showUsage = true; + break; + } + } else if (inputFileName.isEmpty() && i + 1 == args.length) { + inputFileName = args[i]; + } else { + System.err.println("Error: unexpected argument '" + args[i] + "'"); + showUsage = true; + break; + } + } + + if (!showUsage && !showHelp) { + if (!inputFileName.isEmpty()) { + inputFile = new File(inputFileName); + if (!inputFile.exists()) { + System.err.println("Error: input file does not exist '" + inputFileName + "'"); + return; + } + if (outputFileName.isEmpty()) + outputFileName = inputFileName; + } + + if (inputFile == null) { + System.err.println("Error: missing argument file"); + showUsage = true; + } + } + } else { + showUsage = true; + } + + if (showUsage || showHelp) { + if (showHelp) { + System.err.println("TCIME dictionary converter for Qt Virtual Keyboard"); + System.err.println(""); + System.err.println("Copyright (C) 2015 The Qt Company Ltd - All rights reserved."); + System.err.println(""); + System.err.println(" This utility converts TCIME dictionaries to Qt compatible"); + System.err.println(" format. The dictionaries are basically Java char[][] arrays"); + System.err.println(" serialized to file with ObjectOutputStream."); + System.err.println(""); + System.err.println(" The corresponding data format in the Qt dictionary is"); + System.err.println(" QVector<QVector<ushort>>. The byte order is set to big endian"); + System.err.println(" by default, but can be changed with -le option."); + } + if (showUsage) { + System.err.println(""); + System.err.println("Usage: java dict2qt [options] file"); + System.err.println("Options:"); + System.err.println(" -o <output file> Place the output into <output file>"); + System.err.println(" -le Change byte order to little endian"); + System.err.println(" -h Display help"); + } + return; + } + + char[][] dictionary = null; + try { + dictionary = loadDictionary(new FileInputStream(inputFile)); + } catch (FileNotFoundException e) { + e.printStackTrace(); + return; + } + if (dictionary == null) + return; + + int dictionarySize = calculateDictionarySize(dictionary); + ByteBuffer buffer = ByteBuffer.allocate(dictionarySize); + if (littleEndian) + buffer.order(ByteOrder.LITTLE_ENDIAN); + else + buffer.order(ByteOrder.BIG_ENDIAN); + buffer.putInt(dictionary.length); + for (int i = 0; i < dictionary.length; i++) { + char[] data = dictionary[i]; + if (data != null) { + buffer.putInt(data.length); + for (int j = 0; j < data.length; j++) { + buffer.putChar(data[j]); + } + } else { + buffer.putInt(0); + } + } + + byte[] bytes = buffer.array(); + DataOutputStream dos = null; + try { + File outputFile = new File(outputFileName); + FileOutputStream os = new FileOutputStream(outputFile); + dos = new DataOutputStream(os); + dos.write(bytes, 0, buffer.position()); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } finally { + if (dos != null) { + try { + dos.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } + + static char[][] loadDictionary(InputStream ins) { + char[][] result = null; + ObjectInputStream oin = null; + try { + BufferedInputStream bis = new BufferedInputStream(ins); + oin = new ObjectInputStream(bis); + result = (char[][]) oin.readObject(); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } finally { + if (oin != null) { + try { + oin.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + return result; + } + + static int calculateDictionarySize(final char[][] dictionary) { + int result = 4; + for (int i = 0; i < dictionary.length; i++) { + char[] data = dictionary[i]; + result += 4; + if (data != null) + result += data.length * 2; + } + return result; + } + +} diff --git a/src/virtualkeyboard/3rdparty/tcime/worddictionary.cpp b/src/virtualkeyboard/3rdparty/tcime/worddictionary.cpp new file mode 100644 index 00000000..6bc0a9e2 --- /dev/null +++ b/src/virtualkeyboard/3rdparty/tcime/worddictionary.cpp @@ -0,0 +1,43 @@ +/* + * Qt implementation of TCIME library + * This file is part of the Qt Virtual Keyboard module. + * Contact: http://www.qt.io/licensing/ + * + * Copyright (C) 2015 The Qt Company + * Copyright 2010 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "worddictionary.h" +#include <QDataStream> +#include <QFile> + +using namespace tcime; + +bool WordDictionary::load(const QString &fileName, bool littleEndian) +{ + _dictionary.clear(); + + QFile dictionaryFile(fileName); + if (!dictionaryFile.open(QIODevice::ReadOnly)) + return false; + + QDataStream ds(&dictionaryFile); + if (littleEndian) + ds.setByteOrder(QDataStream::LittleEndian); + Q_ASSERT((ds.byteOrder() == QDataStream::LittleEndian) == littleEndian); + ds >> _dictionary; + + return !_dictionary.isEmpty(); +} diff --git a/src/virtualkeyboard/3rdparty/tcime/worddictionary.h b/src/virtualkeyboard/3rdparty/tcime/worddictionary.h new file mode 100644 index 00000000..3f1ea43e --- /dev/null +++ b/src/virtualkeyboard/3rdparty/tcime/worddictionary.h @@ -0,0 +1,61 @@ +/* + * Qt implementation of TCIME library + * This file is part of the Qt Virtual Keyboard module. + * Contact: http://www.qt.io/licensing/ + * + * Copyright (C) 2015 The Qt Company + * Copyright 2010 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WORDDICTIONARY_H +#define WORDDICTIONARY_H + +#include <QVector> +#include <QString> +#include <QStringList> + +namespace tcime { + +/** + * Reads a word-dictionary and provides word-suggestions as a list of characters + * for the specified input. + */ +class WordDictionary +{ + Q_DISABLE_COPY(WordDictionary) + +protected: + typedef QChar DictionaryWord; + typedef QVector<DictionaryWord> DictionaryEntry; + typedef QVector<DictionaryEntry> Dictionary; + + const Dictionary &dictionary() const { return _dictionary; } + +public: + WordDictionary() {} + virtual ~WordDictionary() {} + + bool isEmpty() const { return _dictionary.isEmpty(); } + + virtual bool load(const QString &fileName, bool littleEndian = false); + virtual QStringList getWords(const QString &input) const = 0; + +private: + Dictionary _dictionary; +}; + +} + +#endif // WORDDICTIONARY_H diff --git a/src/virtualkeyboard/content/components/ModeKey.qml b/src/virtualkeyboard/content/components/ModeKey.qml new file mode 100644 index 00000000..da975ecd --- /dev/null +++ b/src/virtualkeyboard/content/components/ModeKey.qml @@ -0,0 +1,52 @@ +/****************************************************************************** +** +** 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 +** +** This file is part of the Qt Virtual Keyboard module. +** +** 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. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.io +** +******************************************************************************/ + +import QtQuick 2.0 + +/*! + \qmltype ModeKey + \inqmlmodule QtQuick.Enterprise.VirtualKeyboard + \ingroup qtvirtualkeyboard-qml + \inherits Key + \since QtQuick.Enterprise.VirtualKeyboard 2.0 + + \brief Generic mode key for keyboard layouts. + + This key provides generic mode button functionality. + + A key press toggles the current mode without emitting key event + for input method processing. + + ModeKey can be used in situations where a particular mode is switched + "ON / OFF", and where the mode change does not require changing the + keyboard layout. When this component is used, the \l { BaseKey::displayText } { displayText } should + remain the same regardless of the mode, because the keyboard style + visualizes the status. +*/ + +Key { + /*! This property provides the current mode. + + The default is false. + */ + property bool mode + noKeyEvent: true + functionKey: true + onClicked: mode = !mode + keyPanelDelegate: keyboard.style ? keyboard.style.modeKeyPanel : undefined +} diff --git a/src/virtualkeyboard/content/content.qrc b/src/virtualkeyboard/content/content.qrc index 9814208e..f302501b 100644 --- a/src/virtualkeyboard/content/content.qrc +++ b/src/virtualkeyboard/content/content.qrc @@ -17,6 +17,7 @@ <file>components/KeyboardLayout.qml</file> <file>components/KeyboardLayoutLoader.qml</file> <file>components/KeyboardRow.qml</file> + <file>components/ModeKey.qml</file> <file>components/MultiSoundEffect.qml</file> <file>components/MultitapInputMethod.qml</file> <file>components/NumberKey.qml</file> diff --git a/src/virtualkeyboard/content/layouts/zh_TW/main.qml b/src/virtualkeyboard/content/layouts/zh_TW/main.qml new file mode 100644 index 00000000..784e2ec3 --- /dev/null +++ b/src/virtualkeyboard/content/layouts/zh_TW/main.qml @@ -0,0 +1,393 @@ +/****************************************************************************** +** +** 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 +** +** This file is part of the Qt Virtual Keyboard module. +** +** 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. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.io +** +******************************************************************************/ + +import QtQuick 2.0 +import QtQuick.Layouts 1.0 +import QtQuick.Enterprise.VirtualKeyboard 2.0 + +KeyboardLayoutLoader { + function createInputMethod() { + return Qt.createQmlObject('import QtQuick 2.0; import QtQuick.Enterprise.VirtualKeyboard 2.0; TCInputMethod {}', parent, "tcInputMethod") + } + sharedLayouts: ['symbols'] + sourceComponent: InputContext.shift ? pageLatin : pageCangjie + Component { + id: pageCangjie + KeyboardLayout { + keyWeight: 160 + smallTextVisible: true + KeyboardRow { + Key { + text: "\u624B" + smallText: "q" + } + Key { + text: "\u7530" + smallText: "w" + } + Key { + text: "\u6C34" + smallText: "e" + } + Key { + text: "\u53E3" + smallText: "r" + } + Key { + text: "\u5EFF" + smallText: "t" + } + Key { + text: "\u535C" + smallText: "y" + } + Key { + text: "\u5C71" + smallText: "u" + } + Key { + text: "\u6208" + smallText: "i" + } + Key { + text: "\u4EBA" + smallText: "o" + } + Key { + text: "\u5FC3" + smallText: "p" + } + BackspaceKey {} + } + KeyboardRow { + FillerKey { + weight: 56 + } + Key { + text: "\u65E5" + smallText: "a" + } + Key { + text: "\u5C38" + smallText: "s" + } + Key { + text: "\u6728" + smallText: "d" + } + Key { + text: "\u706B" + smallText: "f" + } + Key { + text: "\u571F" + smallText: "g" + } + Key { + text: "\u7AF9" + smallText: "h" + } + Key { + text: "\u5341" + smallText: "j" + } + Key { + text: "\u5927" + smallText: "k" + } + Key { + text: "\u4E2D" + smallText: "l" + } + EnterKey { + weight: 283 + } + } + KeyboardRow { + keyWeight: 156 + ModeKey { + id: simplifiedModeKey + key: Qt.Key_Mode_switch + displayText: "速成" + Component.onCompleted: updateBinding() + Connections { + target: InputContext.inputEngine + onInputMethodChanged: simplifiedModeKey.updateBinding() + } + function updateBinding() { + if (InputContext.inputEngine.inputMethod && InputContext.inputEngine.inputMethod.hasOwnProperty("simplified")) { + simplifiedModeKey.mode = InputContext.inputEngine.inputMethod.simplified + InputContext.inputEngine.inputMethod.simplified = Qt.binding(function() { return simplifiedModeKey.mode }) + } + } + } + Key { + text: "\u91CD" + smallText: "z" + } + Key { + text: "\u96E3" + smallText: "x" + } + Key { + text: "\u91D1" + smallText: "c" + } + Key { + text: "\u5973" + smallText: "v" + } + Key { + text: "\u6708" + smallText: "b" + } + Key { + text: "\u5F13" + smallText: "n" + } + Key { + text: "\u4E00" + smallText: "m" + } + Key { + key: Qt.Key_Comma + text: "\uFF0C" + alternativeKeys: "\uFF0C\uFF1B\u3001" + } + Key { + key: Qt.Key_Period + text: "\uFF0E" + alternativeKeys: "\uFF0E\uFF1A\u3002" + } + ShiftKey { + weight: 204 + } + } + KeyboardRow { + keyWeight: 154 + SymbolModeKey { + weight: 217 + } + ChangeLanguageKey { + weight: 154 + } + SpaceKey { + weight: 864 + } + Key { + key: Qt.Key_Question + text: "\uFF1F" + alternativeKeys: "\uFF1F\uFF01" + } + Key { + key: 0xE000 + text: ":-)" + smallTextVisible: false + alternativeKeys: [ ";-)", ":-)", ":-D", ":-(", "<3" ] + } + HideKeyboardKey { + weight: 204 + } + } + } + } + Component { + id: pageLatin + KeyboardLayout { + keyWeight: 160 + smallTextVisible: true + KeyboardRow { + Key { + key: Qt.Key_Q + text: "q" + } + Key { + key: Qt.Key_W + text: "w" + } + Key { + key: Qt.Key_E + text: "e" + } + Key { + key: Qt.Key_R + text: "r" + } + Key { + key: Qt.Key_T + text: "t" + } + Key { + key: Qt.Key_Y + text: "y" + } + Key { + key: Qt.Key_U + text: "u" + } + Key { + key: Qt.Key_I + text: "i" + } + Key { + key: Qt.Key_O + text: "o" + } + Key { + key: Qt.Key_P + text: "p" + } + BackspaceKey {} + } + KeyboardRow { + FillerKey { + weight: 56 + } + Key { + key: Qt.Key_A + text: "a" + } + Key { + key: Qt.Key_S + text: "s" + } + Key { + key: Qt.Key_D + text: "d" + } + Key { + key: Qt.Key_F + text: "f" + } + Key { + key: Qt.Key_G + text: "g" + } + Key { + key: Qt.Key_H + text: "h" + } + Key { + key: Qt.Key_J + text: "j" + } + Key { + key: Qt.Key_K + text: "k" + } + Key { + key: Qt.Key_L + text: "l" + } + EnterKey { + weight: 283 + } + } + KeyboardRow { + keyWeight: 156 + ModeKey { + id: simplifiedModeKey + key: Qt.Key_Mode_switch + enabled: InputContext.inputEngine.inputMode == InputEngine.Cangjie + displayText: "速成" + Component.onCompleted: updateBinding() + Connections { + target: InputContext.inputEngine + onInputMethodChanged: simplifiedModeKey.updateBinding() + } + function updateBinding() { + if (InputContext.inputEngine.inputMethod && InputContext.inputEngine.inputMethod.hasOwnProperty("simplified")) { + simplifiedModeKey.mode = InputContext.inputEngine.inputMethod.simplified + InputContext.inputEngine.inputMethod.simplified = Qt.binding(function() { return simplifiedModeKey.mode }) + } + } + } + Key { + key: Qt.Key_Z + text: "z" + } + Key { + key: Qt.Key_X + text: "x" + } + Key { + key: Qt.Key_C + text: "c" + } + Key { + key: Qt.Key_V + text: "v" + } + Key { + key: Qt.Key_B + text: "b" + } + Key { + key: Qt.Key_N + text: "n" + } + Key { + key: Qt.Key_M + text: "m" + } + Key { + key: Qt.Key_Comma + text: "\uFF0C" + alternativeKeys: "\uFF0C\uFF1B\u3001" + } + Key { + key: Qt.Key_Period + text: "\uFF0E" + alternativeKeys: "\uFF0E\uFF1A\u3002" + } + ShiftKey { + weight: 204 + } + } + KeyboardRow { + keyWeight: 154 + SymbolModeKey { + weight: 217 + } + ChangeLanguageKey { + weight: 154 + } + HandwritingModeKey { + weight: 154 + } + SpaceKey { + weight: 864 + } + Key { + key: Qt.Key_Question + text: "\uFF1F" + alternativeKeys: "\uFF1F\uFF01" + } + Key { + key: 0xE000 + text: ":-)" + smallTextVisible: false + alternativeKeys: [ ";-)", ":-)", ":-D", ":-(", "<3" ] + } + HideKeyboardKey { + weight: 204 + } + } + } + } +} diff --git a/src/virtualkeyboard/content/layouts/zh_TW/symbols.qml b/src/virtualkeyboard/content/layouts/zh_TW/symbols.qml new file mode 100644 index 00000000..72757e85 --- /dev/null +++ b/src/virtualkeyboard/content/layouts/zh_TW/symbols.qml @@ -0,0 +1,283 @@ +/****************************************************************************** +** +** 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 +** +** This file is part of the Qt Virtual Keyboard module. +** +** 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. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.io +** +******************************************************************************/ + +import QtQuick 2.0 +import QtQuick.Layouts 1.0 +import QtQuick.Enterprise.VirtualKeyboard 2.0 + +KeyboardLayoutLoader { + function createInputMethod() { + return Qt.createQmlObject('import QtQuick 2.0; import QtQuick.Enterprise.VirtualKeyboard 2.0; TCInputMethod {}', parent, "tcInputMethod") + } + sharedLayouts: ['main'] + property int page + readonly property int numPages: 3 + property var keysPage1: [ + "1234567890", + "@#$%^&*()", + "“”、=:;!?~" + ] + property var keysPage2: [ + "-+/\\|[]{}·", + "<>,.:;!?~", + "/\"'_§¥€£¢" + ] + property var keysPage3: [ + "()〔〕〈〉《》【】", + "→←↑↓↔■□●○", + "\『』「」★☆◆◇" + ] + sourceComponent: { + switch (page) { + case 2: return page3 + case 1: return page2 + default: return page1 + } + } + Component { + id: page1 + KeyboardLayout { + keyWeight: 160 + KeyboardRow { + Repeater { + model: keysPage1[0].length + Key { + key: keysPage1[0][index].charCodeAt(0) + text: keysPage1[0][index] + } + } + BackspaceKey {} + } + KeyboardRow { + FillerKey { + weight: 56 + } + Repeater { + model: keysPage1[1].length + Key { + key: keysPage1[1][index].charCodeAt(0) + text: keysPage1[1][index] + } + } + EnterKey { + weight: 283 + } + } + KeyboardRow { + keyWeight: 156 + Key { + displayText: (page + 1) + "/" + numPages + functionKey: true + onClicked: page = (page + 1) % numPages + } + Repeater { + model: keysPage1[2].length + Key { + key: keysPage1[2][index].charCodeAt(0) + text: keysPage1[2][index] + } + } + Key { + weight: 204 + displayText: (page + 1) + "/" + numPages + functionKey: true + onClicked: page = (page + 1) % numPages + } + } + KeyboardRow { + keyWeight: 154 + SymbolModeKey { + weight: 217 + displayText: "ABC" + } + ChangeLanguageKey { + weight: 154 + } + SpaceKey { + weight: 864 + } + Key { + key: 0x2014 + text: "—" + } + Key { + key: 0xE000 + text: ":-)" + alternativeKeys: [ ";-)", ":-)", ":-D", ":-(", "<3" ] + } + HideKeyboardKey { + weight: 204 + } + } + } + } + Component { + id: page2 + KeyboardLayout { + keyWeight: 160 + KeyboardRow { + Repeater { + model: keysPage2[0].length + Key { + key: keysPage2[0][index].charCodeAt(0) + text: keysPage2[0][index] + } + } + BackspaceKey {} + } + KeyboardRow { + FillerKey { + weight: 56 + } + Repeater { + model: keysPage2[1].length + Key { + key: keysPage2[1][index].charCodeAt(0) + text: keysPage2[1][index] + } + } + EnterKey { + weight: 283 + } + } + KeyboardRow { + keyWeight: 156 + Key { + displayText: (page + 1) + "/" + numPages + functionKey: true + onClicked: page = (page + 1) % numPages + } + Repeater { + model: keysPage2[2].length + Key { + key: keysPage2[2][index].charCodeAt(0) + text: keysPage2[2][index] + } + } + Key { + weight: 204 + displayText: (page + 1) + "/" + numPages + functionKey: true + onClicked: page = (page + 1) % numPages + } + } + KeyboardRow { + keyWeight: 154 + SymbolModeKey { + weight: 217 + displayText: "ABC" + } + ChangeLanguageKey { + weight: 154 + } + SpaceKey { + weight: 864 + } + Key { + key: 0x3002 + text: "。" + } + Key { + key: 0xE000 + text: ":-)" + alternativeKeys: [ ";-)", ":-)", ":-D", ":-(", "<3" ] + } + HideKeyboardKey { + weight: 204 + } + } + } + } + Component { + id: page3 + KeyboardLayout { + keyWeight: 160 + KeyboardRow { + Repeater { + model: keysPage3[0].length + Key { + key: keysPage3[0][index].charCodeAt(0) + text: keysPage3[0][index] + } + } + BackspaceKey {} + } + KeyboardRow { + FillerKey { + weight: 56 + } + Repeater { + model: keysPage3[1].length + Key { + key: keysPage3[1][index].charCodeAt(0) + text: keysPage3[1][index] + } + } + EnterKey { + weight: 283 + } + } + KeyboardRow { + keyWeight: 156 + Key { + displayText: (page + 1) + "/" + numPages + functionKey: true + onClicked: page = (page + 1) % numPages + } + Repeater { + model: keysPage3[2].length + Key { + key: keysPage3[2][index].charCodeAt(0) + text: keysPage3[2][index] + } + } + Key { + weight: 204 + displayText: (page + 1) + "/" + numPages + functionKey: true + onClicked: page = (page + 1) % numPages + } + } + KeyboardRow { + keyWeight: 154 + SymbolModeKey { + weight: 217 + displayText: "ABC" + } + ChangeLanguageKey { + weight: 154 + } + SpaceKey { + weight: 864 + } + Key { + key: 0x2026 + text: "…" + } + Key { + key: 0xE000 + text: ":-)" + alternativeKeys: [ ";-)", ":-)", ":-D", ":-(", "<3" ] + } + HideKeyboardKey { + weight: 204 + } + } + } + } +} diff --git a/src/virtualkeyboard/content/layouts_traditional_chinese.qrc b/src/virtualkeyboard/content/layouts_traditional_chinese.qrc new file mode 100644 index 00000000..6850cf84 --- /dev/null +++ b/src/virtualkeyboard/content/layouts_traditional_chinese.qrc @@ -0,0 +1,12 @@ +<RCC> + <qresource prefix="/content"> + <file>layouts/zh_TW/main.qml</file> + <file>layouts/zh_TW/symbols.qml</file> + <file>layouts/en_GB/dialpad.qml</file> + <file>layouts/en_GB/digits.qml</file> + <file>layouts/en_GB/handwriting.qml</file> + <file>layouts/en_GB/main.qml</file> + <file>layouts/en_GB/numbers.qml</file> + <file>layouts/en_GB/symbols.qml</file> + </qresource> +</RCC> diff --git a/src/virtualkeyboard/content/styles/default/style.qml b/src/virtualkeyboard/content/styles/default/style.qml index 4c2a653e..a3e47da9 100644 --- a/src/virtualkeyboard/content/styles/default/style.qml +++ b/src/virtualkeyboard/content/styles/default/style.qml @@ -25,7 +25,7 @@ import QtQuick.Enterprise.VirtualKeyboard.Styles 2.0 KeyboardStyle { id: currentStyle - readonly property bool pinyinMode: InputContext.inputEngine.inputMode === InputEngine.Pinyin + readonly property bool compactSelectionList: [InputEngine.Pinyin, InputEngine.Cangjie].indexOf(InputContext.inputEngine.inputMode) !== -1 readonly property string fontFamily: "Sans" readonly property real keyBackgroundMargin: Math.round(13 * scaleHint) readonly property real keyContentMargin: Math.round(45 * scaleHint) @@ -508,6 +508,70 @@ KeyboardStyle { ] } + modeKeyPanel: KeyPanel { + Rectangle { + id: modeKeyBackground + radius: 5 + color: "#1e1b18" + anchors.fill: parent + anchors.margins: keyBackgroundMargin + Text { + id: modeKeyText + text: control.displayText + color: "white" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + anchors.fill: parent + anchors.margins: keyContentMargin + font { + family: fontFamily + weight: Font.Normal + pixelSize: 44 * scaleHint + capitalization: Font.AllUppercase + } + } + Rectangle { + id: modeKeyIndicator + implicitHeight: parent.height * 0.1 + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + anchors.leftMargin: parent.width * 0.4 + anchors.rightMargin: parent.width * 0.4 + anchors.bottomMargin: parent.height * 0.12 + color: "#80c342" + radius: 3 + visible: control.mode + } + } + states: [ + State { + name: "pressed" + when: control.pressed + PropertyChanges { + target: modeKeyBackground + opacity: 0.80 + } + PropertyChanges { + target: modeKeyText + opacity: 0.6 + } + }, + State { + name: "disabled" + when: !control.enabled + PropertyChanges { + target: modeKeyBackground + opacity: 0.8 + } + PropertyChanges { + target: modeKeyText + opacity: 0.2 + } + } + ] + } + handwritingKeyPanel: KeyPanel { Rectangle { id: hwrKeyBackground @@ -622,7 +686,7 @@ KeyboardStyle { Text { id: selectionListLabel anchors.left: parent.left - anchors.leftMargin: Math.round((pinyinMode ? 50 : 140) * scaleHint) + anchors.leftMargin: Math.round((compactSelectionList ? 50 : 140) * scaleHint) anchors.verticalCenter: parent.verticalCenter text: decorateText(display, wordCompletionLength) color: "#80c342" diff --git a/src/virtualkeyboard/content/styles/retro/style.qml b/src/virtualkeyboard/content/styles/retro/style.qml index 5c454fae..08680bd2 100644 --- a/src/virtualkeyboard/content/styles/retro/style.qml +++ b/src/virtualkeyboard/content/styles/retro/style.qml @@ -25,7 +25,7 @@ import QtQuick.Enterprise.VirtualKeyboard.Styles 2.0 KeyboardStyle { id: currentStyle - readonly property bool pinyinMode: InputContext.inputEngine.inputMode === InputEngine.Pinyin + readonly property bool compactSelectionList: [InputEngine.Pinyin, InputEngine.Cangjie].indexOf(InputContext.inputEngine.inputMode) !== -1 readonly property string fontFamily: "Courier" readonly property real keyBackgroundMargin: Math.round(9 * scaleHint) readonly property real keyContentMargin: Math.round(50 * scaleHint) @@ -564,6 +564,63 @@ KeyboardStyle { ] } + modeKeyPanel: KeyPanel { + BorderImage { + id: modeKeyBackground + source: resourcePrefix + "images/key154px_black.png" + width: (parent.width - 2 * keyBackgroundMargin) / scale + height: sourceSize.height + anchors.centerIn: parent + border.left: 76 + border.top: 76 + border.right: 76 + border.bottom: 76 + horizontalTileMode: BorderImage.Stretch + scale: (parent.height - 2 * keyBackgroundMargin) / sourceSize.height + states: [ + State { + name: "mode" + when: control.mode + PropertyChanges { + target: modeKeyBackground + source: resourcePrefix + "images/key154px_capslock.png" + } + } + ] + } + Text { + id: modeKeyText + text: control.displayText + color: "#c5a96f" + anchors.centerIn: parent + font { + family: fontFamily + weight: Font.DemiBold + pixelSize: 74 * scaleHint + letterSpacing: -5 * scaleHint + capitalization: Font.AllUppercase + } + } + states: [ + State { + name: "pressed" + when: control.pressed + PropertyChanges { + target: modeKeyBackground + opacity: 0.70 + } + }, + State { + name: "disabled" + when: !control.enabled + PropertyChanges { + target: modeKeyBackground + opacity: 0.20 + } + } + ] + } + handwritingKeyPanel: KeyPanel { BorderImage { id: hwrKeyBackground @@ -728,7 +785,7 @@ KeyboardStyle { Text { id: selectionListLabel anchors.left: parent.left - anchors.leftMargin: Math.round((pinyinMode ? 50 : 140) * scaleHint) + anchors.leftMargin: Math.round((compactSelectionList ? 50 : 140) * scaleHint) anchors.verticalCenter: parent.verticalCenter text: decorateText(display, wordCompletionLength) color: "white" diff --git a/src/virtualkeyboard/declarativeinputengine.cpp b/src/virtualkeyboard/declarativeinputengine.cpp index 9fd54b25..aa153fb6 100644 --- a/src/virtualkeyboard/declarativeinputengine.cpp +++ b/src/virtualkeyboard/declarativeinputengine.cpp @@ -706,6 +706,7 @@ void DeclarativeInputEngine::timerEvent(QTimerEvent *timerEvent) \li \c InputEngine.Numeric Only numeric input is allowed. \li \c InputEngine.Dialable Only dialable input is allowed. \li \c InputEngine.Pinyin Pinyin input mode for Chinese. + \li \c InputEngine.Cangjie Cangjie input mode for Chinese. \li \c InputEngine.Hangul Hangul input mode for Korean. \li \c InputEngine.Hiragana Hiragana input mode for Japanese. \li \c InputEngine.Katakana Katakana input mode for Japanese. @@ -765,6 +766,8 @@ void DeclarativeInputEngine::timerEvent(QTimerEvent *timerEvent) Only dialable input is allowed. \value Pinyin Pinyin input mode for Chinese. + \value Cangjie + Cangjie input mode for Chinese. \value Hangul Hangul input mode for Korean. \value Hiragana diff --git a/src/virtualkeyboard/declarativeinputengine.h b/src/virtualkeyboard/declarativeinputengine.h index 99cbf646..fafc64f3 100644 --- a/src/virtualkeyboard/declarativeinputengine.h +++ b/src/virtualkeyboard/declarativeinputengine.h @@ -61,6 +61,7 @@ public: Numeric, Dialable, Pinyin, + Cangjie, Hangul, Hiragana, Katakana, diff --git a/src/virtualkeyboard/declarativeshifthandler.cpp b/src/virtualkeyboard/declarativeshifthandler.cpp index 904d7bf4..53c44d49 100644 --- a/src/virtualkeyboard/declarativeshifthandler.cpp +++ b/src/virtualkeyboard/declarativeshifthandler.cpp @@ -36,7 +36,8 @@ public: toggleShiftEnabled(false), shiftChanged(false), manualShiftLanguageFilter(QSet<QLocale::Language>() << QLocale::Arabic << QLocale::Persian << QLocale::Hindi << QLocale::Korean), - noAutoUppercaseInputModeFilter(QSet<DeclarativeInputEngine::InputMode>() << DeclarativeInputEngine::FullwidthLatin), + manualCapsInputModeFilter(QSet<DeclarativeInputEngine::InputMode>() << DeclarativeInputEngine::Cangjie), + noAutoUppercaseInputModeFilter(QSet<DeclarativeInputEngine::InputMode>() << DeclarativeInputEngine::FullwidthLatin << DeclarativeInputEngine::Pinyin << DeclarativeInputEngine::Cangjie), allCapsInputModeFilter(QSet<DeclarativeInputEngine::InputMode>() << DeclarativeInputEngine::Hiragana << DeclarativeInputEngine::Katakana) { } @@ -48,6 +49,7 @@ public: bool shiftChanged; QLocale locale; const QSet<QLocale::Language> manualShiftLanguageFilter; + const QSet<DeclarativeInputEngine::InputMode> manualCapsInputModeFilter; const QSet<DeclarativeInputEngine::InputMode> noAutoUppercaseInputModeFilter; const QSet<DeclarativeInputEngine::InputMode> allCapsInputModeFilter; }; @@ -149,7 +151,8 @@ void DeclarativeShiftHandler::toggleShift() if (d->manualShiftLanguageFilter.contains(d->locale.language())) { d->inputContext->setCapsLock(false); d->inputContext->setShift(!d->inputContext->shift()); - } else if (d->inputContext->inputMethodHints() & Qt::ImhNoAutoUppercase) { + } else if (d->inputContext->inputMethodHints() & Qt::ImhNoAutoUppercase || + d->manualCapsInputModeFilter.contains(d->inputContext->inputEngine()->inputMode())) { bool capsLock = d->inputContext->capsLock(); d->inputContext->setCapsLock(!capsLock); d->inputContext->setShift(!capsLock); @@ -174,7 +177,8 @@ void DeclarativeShiftHandler::reset() bool toggleShiftEnabled = !(inputMethodHints & (Qt::ImhUppercaseOnly | Qt::ImhLowercaseOnly)); // For filtered languages reset the initial shift status to lower case // and allow manual shift change - if (d->manualShiftLanguageFilter.contains(d->locale.language())) { + if (d->manualShiftLanguageFilter.contains(d->locale.language()) || + d->manualCapsInputModeFilter.contains(inputMode)) { preferUpperCase = false; autoCapitalizationEnabled = false; toggleShiftEnabled = true; diff --git a/src/virtualkeyboard/doc/src/build.qdoc b/src/virtualkeyboard/doc/src/build.qdoc index e314631c..a5c8c6a2 100644 --- a/src/virtualkeyboard/doc/src/build.qdoc +++ b/src/virtualkeyboard/doc/src/build.qdoc @@ -52,6 +52,9 @@ The project is split into the following subprojects: \row \li \e src/virtualkeyboard/3rdparty/pinyin/pinyin.pro \li A project file for compiling the PinyinIME library. +\row + \li \e src/virtualkeyboard/3rdparty/tcime/tcime.pro + \li A project file for compiling the TCIME library. \endtable The input methods are implemented either in C++ or QML. @@ -106,6 +109,11 @@ build targets. \li This option activates the Pinyin input method for the Simplified Chinese language and disables all other default languages, except English. \row + \li \e tcime + \li Enables the Cangjie input method for Traditional Chinese. + \li This option activates the Canjie input method for the Traditional Chinese + language and disables all other default languages, except English. +\row \li \e hangul \li Enables the Hangul input method for Korean. \li This option activates the Hangul input method for the Korean diff --git a/src/virtualkeyboard/doc/src/qtvirtualkeyboard-index.qdoc b/src/virtualkeyboard/doc/src/qtvirtualkeyboard-index.qdoc index 55a9db45..20d8b29c 100644 --- a/src/virtualkeyboard/doc/src/qtvirtualkeyboard-index.qdoc +++ b/src/virtualkeyboard/doc/src/qtvirtualkeyboard-index.qdoc @@ -40,7 +40,7 @@ \li Character preview and alternative character view. \li Automatic capitalization and space insertion. \li Scalability to different resolutions. - \li Support for different character sets (Latin, Simplified Chinese, + \li Support for different character sets (Latin, Simplified/Traditional Chinese, Hindi, Japanese, Arabic, Korean, and others). \li Support for most common input \l {Supported Languages}{languages}, with possibility to easily extend the language support. @@ -72,6 +72,7 @@ \li Portugese \li Russian \li Simplified Chinese + \li Traditional Chinese \li Spanish \li Swedish \endlist diff --git a/src/virtualkeyboard/plugin.cpp b/src/virtualkeyboard/plugin.cpp index a1599e85..bbd83131 100644 --- a/src/virtualkeyboard/plugin.cpp +++ b/src/virtualkeyboard/plugin.cpp @@ -30,6 +30,9 @@ #ifdef HAVE_PINYIN #include "pinyininputmethod.h" #endif +#ifdef HAVE_TCIME +#include "tcinputmethod.h" +#endif #ifdef HAVE_HANGUL #include "hangulinputmethod.h" #endif @@ -67,6 +70,9 @@ static QObject *createInputContextModule(QQmlEngine *engine, QJSEngine *scriptEn #ifdef HAVE_PINYIN << QLatin1String("PinyinInputMethod") #endif +#ifdef HAVE_TCIME + << QLatin1String("TCInputMethod") +#endif #ifdef HAVE_HANGUL << QLatin1String("HangulInputMethod") #endif @@ -121,6 +127,9 @@ QPlatformInputContext *PlatformInputContextPlugin::create(const QString &system, qmlRegisterType<PinyinInputMethod>(pluginUri, 1, 1, "PinyinInputMethod"); qmlRegisterType<PinyinInputMethod>(pluginUri, 2, 0, "PinyinInputMethod"); #endif +#ifdef HAVE_TCIME + qmlRegisterType<TCInputMethod>(pluginUri, 2, 0, "TCInputMethod"); +#endif #ifdef HAVE_HANGUL qmlRegisterType<HangulInputMethod>(pluginUri, 1, 3, "HangulInputMethod"); qmlRegisterType<HangulInputMethod>(pluginUri, 2, 0, "HangulInputMethod"); @@ -180,6 +189,7 @@ QPlatformInputContext *PlatformInputContextPlugin::create(const QString &system, qmlRegisterType(QUrl(componentsPath + QLatin1String("KeyboardRow.qml")), pluginUri, 2, 0, "KeyboardRow"); qmlRegisterType(QUrl(componentsPath + QLatin1String("Key.qml")), pluginUri, 1, 0, "Key"); qmlRegisterType(QUrl(componentsPath + QLatin1String("Key.qml")), pluginUri, 2, 0, "Key"); + qmlRegisterType(QUrl(componentsPath + QLatin1String("ModeKey.qml")), pluginUri, 2, 0, "ModeKey"); qmlRegisterType(QUrl(componentsPath + QLatin1String("MultiSoundEffect.qml")), pluginUri, 1, 1, "MultiSoundEffect"); qmlRegisterType(QUrl(componentsPath + QLatin1String("MultiSoundEffect.qml")), pluginUri, 2, 0, "MultiSoundEffect"); qmlRegisterType(QUrl(componentsPath + QLatin1String("MultitapInputMethod.qml")), pluginUri, 1, 0, "MultitapInputMethod"); diff --git a/src/virtualkeyboard/styles/KeyboardStyle.qml b/src/virtualkeyboard/styles/KeyboardStyle.qml index 541091fa..50add72b 100644 --- a/src/virtualkeyboard/styles/KeyboardStyle.qml +++ b/src/virtualkeyboard/styles/KeyboardStyle.qml @@ -155,6 +155,18 @@ QtObject { */ property Component symbolKeyPanel: null + /*! Template for the generic mode key. + + This template provides a visualization of the key in which the state + can be on / off. This template is used in situations where the key label + will remain the same regardless of status. + + The current state is available in the \c control.mode property. + + \note The delegate must be based on the KeyPanel type. + */ + property Component modeKeyPanel: null + /*! Template for the handwriting mode key. \note The delegate must be based on the KeyPanel type. diff --git a/src/virtualkeyboard/tcinputmethod.cpp b/src/virtualkeyboard/tcinputmethod.cpp new file mode 100644 index 00000000..a37161b1 --- /dev/null +++ b/src/virtualkeyboard/tcinputmethod.cpp @@ -0,0 +1,330 @@ +/****************************************************************************** +** +** 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 +** +** This file is part of the Qt Virtual Keyboard module. +** +** 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. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.io +** +******************************************************************************/ + +#include "tcinputmethod.h" +#include "declarativeinputengine.h" +#include "declarativeinputcontext.h" +#include "cangjiedictionary.h" +#include "cangjietable.h" +#include "phrasedictionary.h" +#include "virtualkeyboarddebug.h" + +#include <QLibraryInfo> + +using namespace tcime; + +class TCInputMethodPrivate : public AbstractInputMethodPrivate +{ + Q_DECLARE_PUBLIC(TCInputMethod) +public: + + TCInputMethodPrivate(TCInputMethod *q_ptr) : + AbstractInputMethodPrivate(), + q_ptr(q_ptr), + highlightIndex(-1) + {} + + bool setCandidates(const QStringList &values, bool highlightDefault) + { + bool candidatesChanged = candidates != values; + candidates = values; + highlightIndex = !candidates.isEmpty() && highlightDefault ? 0 : -1; + return candidatesChanged; + } + + bool clearCandidates() + { + if (candidates.isEmpty()) + return false; + + candidates.clear(); + highlightIndex = -1; + return true; + } + + QString pickHighlighted() const + { + return (highlightIndex >= 0 && highlightIndex < candidates.count()) ? candidates[highlightIndex] : QString(); + } + + void reset() + { + if (clearCandidates()) { + Q_Q(TCInputMethod); + emit q->selectionListChanged(DeclarativeSelectionListModel::WordCandidateList); + emit q->selectionListActiveItemChanged(DeclarativeSelectionListModel::WordCandidateList, highlightIndex); + } + input.clear(); + } + + bool checkSpecialCharInput() + { + if (input.length() == 1 && input.at(0).unicode() == 0x91CD) { + static const QStringList specialChars1 = QStringList() + << QChar(0xFF01) << QChar(0x2018) << QChar(0x3000) << QChar(0xFF0C) + << QChar(0x3001) << QChar(0x3002) << QChar(0xFF0E) << QChar(0xFF1B) + << QChar(0xFF1A) << QChar(0xFF1F) << QChar(0x300E) << QChar(0x300F) + << QChar(0x3010) << QChar(0x3011) << QChar(0xFE57) << QChar(0x2026) + << QChar(0x2025) << QChar(0xFE50) << QChar(0xFE51) << QChar(0xFE52) + << QChar(0x00B7) << QChar(0xFE54) << QChar(0x2574) << QChar(0x2027) + << QChar(0x2032) << QChar(0x2035) << QChar(0x301E) << QChar(0x301D) + << QChar(0x201D) << QChar(0x201C) << QChar(0x2019) << QChar(0xFE55) + << QChar(0xFE5D) << QChar(0xFE5E) << QChar(0xFE59) << QChar(0xFE5A) + << QChar(0xFE5B) << QChar(0xFE5C) << QChar(0xFE43) << QChar(0xFE44); + Q_Q(TCInputMethod); + if (setCandidates(specialChars1, true)) { + emit q->selectionListChanged(DeclarativeSelectionListModel::WordCandidateList); + emit q->selectionListActiveItemChanged(DeclarativeSelectionListModel::WordCandidateList, highlightIndex); + } + q->inputContext()->setPreeditText(candidates[highlightIndex]); + return true; + } else if (input.length() == 2 && input.at(0).unicode() == 0x91CD && input.at(1).unicode() == 0x96E3) { + static const QStringList specialChars2 = QStringList() + << QChar(0x3008) << QChar(0x3009) << QChar(0xFE31) << QChar(0x2013) + << QChar(0xFF5C) << QChar(0x300C) << QChar(0x300D) << QChar(0xFE40) + << QChar(0xFE3F) << QChar(0x2014) << QChar(0xFE3E) << QChar(0xFE3D) + << QChar(0x300A) << QChar(0x300B) << QChar(0xFE3B) << QChar(0xFE3C) + << QChar(0xFE56) << QChar(0xFE30) << QChar(0xFE39) << QChar(0xFE3A) + << QChar(0x3014) << QChar(0x3015) << QChar(0xFE37) << QChar(0xFE38) + << QChar(0xFE41) << QChar(0xFE42) << QChar(0xFF5B) << QChar(0xFF5D) + << QChar(0xFE35) << QChar(0xFE36) << QChar(0xFF08) << QChar(0xFF09) + << QChar(0xFE4F) << QChar(0xFE34) << QChar(0xFE33); + Q_Q(TCInputMethod); + if (setCandidates(specialChars2, true)) { + emit q->selectionListChanged(DeclarativeSelectionListModel::WordCandidateList); + emit q->selectionListActiveItemChanged(DeclarativeSelectionListModel::WordCandidateList, highlightIndex); + } + q->inputContext()->setPreeditText(candidates[highlightIndex]); + return true; + } + return false; + } + + TCInputMethod *q_ptr; + CangjieDictionary cangjieDictionary; + PhraseDictionary phraseDictionary; + QString input; + QStringList candidates; + int highlightIndex; +}; + +TCInputMethod::TCInputMethod(QObject *parent) : + AbstractInputMethod(*new TCInputMethodPrivate(this), parent) +{ +} + +TCInputMethod::~TCInputMethod() +{ +} + +bool TCInputMethod::simplified() const +{ + Q_D(const TCInputMethod); + return d->cangjieDictionary.simplified(); +} + +void TCInputMethod::setSimplified(bool simplified) +{ + Q_D(TCInputMethod); + VIRTUALKEYBOARD_DEBUG() << "TCInputMethod::setSimplified(): " << simplified; + if (d->cangjieDictionary.simplified() != simplified) { + d->reset(); + DeclarativeInputContext *ic = inputContext(); + if (ic) + ic->clear(); + d->cangjieDictionary.setSimplified(simplified); + emit simplifiedChanged(); + } +} + +QList<DeclarativeInputEngine::InputMode> TCInputMethod::inputModes(const QString &locale) +{ + Q_UNUSED(locale) + return QList<DeclarativeInputEngine::InputMode>() + << DeclarativeInputEngine::Cangjie; +} + +bool TCInputMethod::setInputMode(const QString &locale, DeclarativeInputEngine::InputMode inputMode) +{ + Q_UNUSED(locale) + Q_UNUSED(inputMode) + Q_D(TCInputMethod); + if (inputMode == DeclarativeInputEngine::Cangjie) { + if (d->cangjieDictionary.isEmpty()) { + QString cangjieDictionary(QString::fromLatin1(qgetenv("QT_VIRTUALKEYBOARD_CANGJIE_DICTIONARY").constData())); + if (cangjieDictionary.isEmpty()) + cangjieDictionary = QLibraryInfo::location(QLibraryInfo::DataPath) + "/qtvirtualkeyboard/tcime/dict_cangjie.dat"; + d->cangjieDictionary.load(cangjieDictionary); + } + if (d->phraseDictionary.isEmpty()) { + QString phraseDictionary(QString::fromLatin1(qgetenv("QT_VIRTUALKEYBOARD_PHRASE_DICTIONARY").constData())); + if (phraseDictionary.isEmpty()) + phraseDictionary = QLibraryInfo::location(QLibraryInfo::DataPath) + "/qtvirtualkeyboard/tcime/dict_phrases.dat"; + d->phraseDictionary.load(phraseDictionary); + } + return d->cangjieDictionary.isEmpty(); + } + return false; +} + +bool TCInputMethod::setTextCase(DeclarativeInputEngine::TextCase textCase) +{ + Q_UNUSED(textCase) + return true; +} + +bool TCInputMethod::keyEvent(Qt::Key key, const QString &text, Qt::KeyboardModifiers modifiers) +{ + Q_UNUSED(key) + Q_UNUSED(text) + Q_UNUSED(modifiers) + Q_D(TCInputMethod); + DeclarativeInputContext *ic = inputContext(); + bool accept = false; + switch (key) { + case Qt::Key_Enter: + case Qt::Key_Return: + update(); + break; + + case Qt::Key_Tab: + case Qt::Key_Space: + if (!d->input.isEmpty()) { + accept = true; + if (d->highlightIndex >= 0) + update(); + } else { + update(); + } + break; + + case Qt::Key_Backspace: + if (!d->input.isEmpty()) { + d->input.remove(d->input.length() - 1, 1); + ic->setPreeditText(d->input); + if (!d->checkSpecialCharInput()) { + if (d->setCandidates(d->cangjieDictionary.getWords(d->input), true)) { + emit selectionListChanged(DeclarativeSelectionListModel::WordCandidateList); + emit selectionListActiveItemChanged(DeclarativeSelectionListModel::WordCandidateList, d->highlightIndex); + } + } + accept = true; + } else if (d->clearCandidates()) { + emit selectionListChanged(DeclarativeSelectionListModel::WordCandidateList); + emit selectionListActiveItemChanged(DeclarativeSelectionListModel::WordCandidateList, d->highlightIndex); + } + break; + + default: + if (text.length() == 1) { + QChar c = text.at(0); + if (!d->input.contains(0x91CD) && CangjieTable::isLetter(c)) { + if (d->input.length() < (d->cangjieDictionary.simplified() ? CangjieTable::MAX_SIMPLIFIED_CODE_LENGTH : CangjieTable::MAX_CODE_LENGTH)) { + d->input.append(c); + ic->setPreeditText(d->input); + if (d->setCandidates(d->cangjieDictionary.getWords(d->input), true)) { + emit selectionListChanged(DeclarativeSelectionListModel::WordCandidateList); + emit selectionListActiveItemChanged(DeclarativeSelectionListModel::WordCandidateList, d->highlightIndex); + } + } + accept = true; + } else if (c.unicode() == 0x91CD) { + if (d->input.isEmpty()) { + d->input.append(c); + ic->setPreeditText(d->input); + d->checkSpecialCharInput(); + } + accept = true; + } else if (c.unicode() == 0x96E3) { + if (d->input.length() == 1) { + Q_ASSERT(d->input.at(0).unicode() == 0x91CD); + d->input.append(c); + ic->setPreeditText(d->input); + d->checkSpecialCharInput(); + } + accept = true; + } + } + if (!accept) + update(); + break; + } + return accept; +} + +QList<DeclarativeSelectionListModel::Type> TCInputMethod::selectionLists() +{ + return QList<DeclarativeSelectionListModel::Type>() << DeclarativeSelectionListModel::WordCandidateList; +} + +int TCInputMethod::selectionListItemCount(DeclarativeSelectionListModel::Type type) +{ + Q_UNUSED(type) + Q_D(TCInputMethod); + return d->candidates.count(); +} + +QVariant TCInputMethod::selectionListData(DeclarativeSelectionListModel::Type type, int index, int role) +{ + QVariant result; + Q_D(TCInputMethod); + switch (role) { + case DeclarativeSelectionListModel::DisplayRole: + result = QVariant(d->candidates.at(index)); + break; + case DeclarativeSelectionListModel::WordCompletionLengthRole: + result.setValue(0); + break; + default: + result = AbstractInputMethod::selectionListData(type, index, role); + break; + } + return result; +} + +void TCInputMethod::selectionListItemSelected(DeclarativeSelectionListModel::Type type, int index) +{ + Q_UNUSED(type) + Q_D(TCInputMethod); + QString finalWord = d->candidates.at(index); + reset(); + inputContext()->commit(finalWord); + if (d->setCandidates(d->phraseDictionary.getWords(finalWord.left(1)), false)) { + emit selectionListChanged(DeclarativeSelectionListModel::WordCandidateList); + emit selectionListActiveItemChanged(DeclarativeSelectionListModel::WordCandidateList, d->highlightIndex); + } +} + +void TCInputMethod::reset() +{ + Q_D(TCInputMethod); + d->reset(); +} + +void TCInputMethod::update() +{ + Q_D(TCInputMethod); + if (d->highlightIndex >= 0) { + QString finalWord = d->pickHighlighted(); + d->reset(); + inputContext()->commit(finalWord); + } else { + inputContext()->clear(); + d->reset(); + } +} diff --git a/src/virtualkeyboard/tcinputmethod.h b/src/virtualkeyboard/tcinputmethod.h new file mode 100644 index 00000000..480b535a --- /dev/null +++ b/src/virtualkeyboard/tcinputmethod.h @@ -0,0 +1,57 @@ +/****************************************************************************** +** +** 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 +** +** This file is part of the Qt Virtual Keyboard module. +** +** 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. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.io +** +******************************************************************************/ + +#ifndef TCINPUTMETHOD_H +#define TCINPUTMETHOD_H + +#include "abstractinputmethod.h" + +class TCInputMethodPrivate; + +class TCInputMethod : public AbstractInputMethod +{ + Q_OBJECT + Q_DECLARE_PRIVATE(TCInputMethod) + Q_PROPERTY(bool simplified READ simplified WRITE setSimplified NOTIFY simplifiedChanged) + +public: + explicit TCInputMethod(QObject *parent = 0); + ~TCInputMethod(); + + bool simplified() const; + void setSimplified(bool simplified); + + QList<DeclarativeInputEngine::InputMode> inputModes(const QString &locale); + bool setInputMode(const QString &locale, DeclarativeInputEngine::InputMode inputMode); + bool setTextCase(DeclarativeInputEngine::TextCase textCase); + + bool keyEvent(Qt::Key key, const QString &text, Qt::KeyboardModifiers modifiers); + + QList<DeclarativeSelectionListModel::Type> selectionLists(); + int selectionListItemCount(DeclarativeSelectionListModel::Type type); + QVariant selectionListData(DeclarativeSelectionListModel::Type type, int index, int role); + void selectionListItemSelected(DeclarativeSelectionListModel::Type type, int index); + + void reset(); + void update(); + +signals: + void simplifiedChanged(); +}; + +#endif diff --git a/src/virtualkeyboard/virtualkeyboard.pro b/src/virtualkeyboard/virtualkeyboard.pro index 5c03350b..17c67e45 100644 --- a/src/virtualkeyboard/virtualkeyboard.pro +++ b/src/virtualkeyboard/virtualkeyboard.pro @@ -58,9 +58,10 @@ RESOURCES += \ content/content.qrc pinyin: RESOURCES += content/layouts_pinyin.qrc +tcime: RESOURCES += content/layouts_traditional_chinese.qrc hangul: RESOURCES += content/layouts_hangul.qrc openwnn: RESOURCES += content/layouts_japanese.qrc -!pinyin:!hangul:!openwnn: RESOURCES += content/layouts.qrc +!tcime:!pinyin:!hangul:!openwnn: RESOURCES += content/layouts.qrc retro-style { DEFINES += QT_VIRTUALKEYBOARD_DEFAULT_STYLE=\\\"retro\\\" @@ -148,6 +149,22 @@ pinyin { INSTALLS += pinyin_data } +tcime { + SOURCES += \ + tcinputmethod.cpp + HEADERS += \ + tcinputmethod.h + DEFINES += HAVE_TCIME + INCLUDEPATH += 3rdparty/tcime + DEPENDPATH += 3rdparty/tcime + LIBS += -L$$OUT_PWD/3rdparty/tcime/$$SUBPATH -ltcime + tcime_data.files = \ + $$PWD/3rdparty/tcime/data/qt/dict_cangjie.dat \ + $$PWD/3rdparty/tcime/data/qt/dict_phrases.dat + tcime_data.path = $$DATAPATH/tcime + INSTALLS += tcime_data +} + hangul { SOURCES += \ hangulinputmethod.cpp \ diff --git a/tests/auto/inputpanel/data/inputpanel/inputpanel.qml b/tests/auto/inputpanel/data/inputpanel/inputpanel.qml index 6cf395b7..113431c2 100644 --- a/tests/auto/inputpanel/data/inputpanel/inputpanel.qml +++ b/tests/auto/inputpanel/data/inputpanel/inputpanel.qml @@ -46,6 +46,7 @@ InputPanel { readonly property var availableLocales: VirtualKeyboardSettings.availableLocales readonly property var activeLocales: VirtualKeyboardSettings.activeLocales readonly property int inputMode: InputContext.inputEngine.inputMode + readonly property var inputMethod: InputContext.inputEngine.inputMethod readonly property var keyboard: Utils.findChildByProperty(inputPanel, "objectName", "keyboard", null) readonly property bool handwritingMode: keyboard.handwritingMode readonly property var keyboardLayoutLoader: Utils.findChildByProperty(keyboard, "objectName", "keyboardLayoutLoader", null) @@ -175,7 +176,9 @@ InputPanel { function setLocale(inputLocale) { VirtualKeyboardSettings.locale = inputLocale - return Qt.inputMethod.locale.name.substring(0, 2) === inputLocale.substring(0, 2) + if (["ar", "fa"].indexOf(inputLocale.substring(0, 2)) !== -1) + return Qt.inputMethod.locale.name.substring(0, 2) === inputLocale.substring(0, 2) + return Qt.inputMethod.locale.name === inputLocale } function setActiveLocales(activeLocales) { @@ -191,6 +194,8 @@ InputPanel { return InputEngine.Dialable else if (inputModeName === "Pinyin") return InputEngine.Pinyin + else if (inputModeName === "Canjie") + return InputEngine.Canjie else if (inputModeName === "Hangul") return InputEngine.Hangul else if (inputModeName === "Hiragana") diff --git a/tests/auto/inputpanel/data/tst_inputpanel.qml b/tests/auto/inputpanel/data/tst_inputpanel.qml index a589acd9..d0cb4356 100644 --- a/tests/auto/inputpanel/data/tst_inputpanel.qml +++ b/tests/auto/inputpanel/data/tst_inputpanel.qml @@ -701,6 +701,59 @@ Rectangle { compare(textInput.text, data.outputText) } + function test_cangjieInputMethod_data() { + return [ + // "vehicle" + { initInputMethodHints: Qt.ImhNone, initLocale: "zh_TW", initSimplified: false, inputSequence: "\u5341\u7530\u5341", expectedCandidates: [ "\u8ECA" ], outputText: "\u8ECA" }, + // simplified mode: "vehicle" + { initInputMethodHints: Qt.ImhNone, initLocale: "zh_TW", initSimplified: true, inputSequence: "\u5341\u5341", expectedCandidates: [ "\u8ECA" ], outputText: "\u8ECA" }, + // "to thank" + { initInputMethodHints: Qt.ImhNone, initLocale: "zh_TW", initSimplified: false, inputSequence: "\u535C\u53E3\u7AF9\u7AF9\u6208", expectedCandidates: [ "\u8B1D" ], outputText: "\u8B1D" }, + // exceptions: "door" + { initInputMethodHints: Qt.ImhNone, initLocale: "zh_TW", initSimplified: false, inputSequence: "\u65E5\u5F13", expectedCandidates: [ "\u9580" ], outputText: "\u9580" }, + // exceptions: "small table" + { initInputMethodHints: Qt.ImhNone, initLocale: "zh_TW", initSimplified: false, inputSequence: "\u7AF9\u5F13", expectedCandidates: [ "\u51E0" ], outputText: "\u51E0" }, + // fixed decomposition + { initInputMethodHints: Qt.ImhNone, initLocale: "zh_TW", initSimplified: false, inputSequence: "\u7AF9\u96E3", expectedCandidates: [ "\u81FC" ], outputText: "\u81FC" }, + // input handling: valid input sequence + space + { initInputMethodHints: Qt.ImhNone, initLocale: "zh_TW", initSimplified: false, inputSequence: "\u5341\u7530\u5341 ", outputText: "\u8ECA" }, + // input handling: invalid input sequence + space + { initInputMethodHints: Qt.ImhNone, initLocale: "zh_TW", initSimplified: false, inputSequence: "\u5341\u7530 ", outputText: "" }, + // input handling: valid input sequence + enter + { initInputMethodHints: Qt.ImhNone, initLocale: "zh_TW", initSimplified: false, inputSequence: "\u5341\u7530\u5341\n", outputText: "\u8ECA\n" }, + // input handling: invalid input sequence + enter + { initInputMethodHints: Qt.ImhNone, initLocale: "zh_TW", initSimplified: false, inputSequence: "\u5341\u7530\n", outputText: "\n" }, + // input handling: valid input sequence + punctuation + { initInputMethodHints: Qt.ImhNone, initLocale: "zh_TW", initSimplified: false, inputSequence: "\u5341\u7530\u5341\uFF0E", outputText: "\u8ECA\uFF0E" }, + // input handling: invalid input sequence + punctuation + { initInputMethodHints: Qt.ImhNone, initLocale: "zh_TW", initSimplified: false, inputSequence: "\u5341\u7530\uFF0E", outputText: "\uFF0E" }, + ] + } + + function test_cangjieInputMethod(data) { + prepareTest(data) + + if (data.hasOwnProperty("initSimplified")) { + if (inputPanel.inputMethod.simplified !== data.initSimplified) + verify(inputPanel.virtualKeyClick(Qt.Key_Mode_switch)) + verify(inputPanel.inputMethod.simplified === data.initSimplified) + } + + for (var inputIndex in data.inputSequence) { + verify(inputPanel.virtualKeyClick(data.inputSequence[inputIndex])) + } + waitForRendering(inputPanel) + + if (data.expectedCandidates) { + for (var candidateIndex in data.expectedCandidates) { + verify(inputPanel.selectionListSearchSuggestion(data.expectedCandidates[candidateIndex])) + verify(inputPanel.selectionListSelectCurrentItem()) + } + } + + compare(textInput.text, data.outputText) + } + function test_hangulInputMethod_data() { return [ // Test boundaries of the Hangul Jamo BMP plane |