1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the Qt Virtual Keyboard module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 or (at your option) any later version
20** approved by the KDE Free Qt Foundation. The licenses are as published by
21** the Free Software Foundation and appearing in the file LICENSE.GPL3
22** included in the packaging of this file. Please review the following
23** information to ensure the GNU General Public License requirements will
24** be met: https://www.gnu.org/licenses/gpl-3.0.html.
25**
26** $QT_END_LICENSE$
27**
28****************************************************************************/
29
30#include "pinyindecoderservice_p.h"
31#include "pinyinime.h"
32#include "dictdef.h"
33#include <QStandardPaths>
34#include <QFileInfo>
35#include <QDir>
36#include <QtCore/QLibraryInfo>
37#include <QLoggingCategory>
38
39QT_BEGIN_NAMESPACE
40namespace QtVirtualKeyboard {
41
42Q_DECLARE_LOGGING_CATEGORY(lcPinyin)
43
44using namespace ime_pinyin;
45
46QScopedPointer<PinyinDecoderService> PinyinDecoderService::_instance;
47
48/*!
49 \class QtVirtualKeyboard::PinyinDecoderService
50 \internal
51*/
52
53PinyinDecoderService::PinyinDecoderService(QObject *parent) :
54 QObject(parent),
55 initDone(false)
56{
57}
58
59PinyinDecoderService::~PinyinDecoderService()
60{
61 if (initDone) {
62 im_close_decoder();
63 initDone = false;
64 }
65}
66
67PinyinDecoderService *PinyinDecoderService::getInstance()
68{
69 if (!_instance)
70 _instance.reset(other: new PinyinDecoderService());
71 if (!_instance->init())
72 return nullptr;
73 return _instance.data();
74}
75
76bool PinyinDecoderService::init()
77{
78 if (initDone)
79 return true;
80
81 QString sysDict(qEnvironmentVariable(varName: "QT_VIRTUALKEYBOARD_PINYIN_DICTIONARY"));
82 if (!QFileInfo::exists(file: sysDict)) {
83 sysDict = QLatin1String(":///QtQuick/VirtualKeyboard/3rdparty/pinyin/data/dict_pinyin.dat");
84 if (!QFileInfo::exists(file: sysDict))
85 sysDict = QLibraryInfo::location(QLibraryInfo::DataPath) + QLatin1String("/qtvirtualkeyboard/pinyin/dict_pinyin.dat");
86 }
87
88 QString usrDictPath = QStandardPaths::writableLocation(type: QStandardPaths::ConfigLocation);
89 QFileInfo usrDictInfo(usrDictPath + QLatin1String("/qtvirtualkeyboard/pinyin/usr_dict.dat"));
90 if (!usrDictInfo.exists()) {
91 qCWarning(lcPinyin) << "PinyinDecoderService::init(): creating directory for user dictionary" << usrDictInfo.absolutePath();
92 QDir().mkpath(dirPath: usrDictInfo.absolutePath());
93 }
94
95 initDone = im_open_decoder(fn_sys_dict: sysDict.toUtf8().constData(), fn_usr_dict: usrDictInfo.absoluteFilePath().toUtf8().constData());
96 if (!initDone)
97 qCWarning(lcPinyin) << "Could not initialize pinyin engine. sys_dict:" << sysDict << "usr_dict:" << usrDictInfo.absoluteFilePath();
98
99 return initDone;
100}
101
102void PinyinDecoderService::setUserDictionary(bool enabled)
103{
104 if (enabled == im_is_user_dictionary_enabled())
105 return;
106 if (enabled) {
107 QString usrDictPath = QStandardPaths::writableLocation(type: QStandardPaths::ConfigLocation);
108 QFileInfo usrDictInfo(usrDictPath + QLatin1String("/qtvirtualkeyboard/pinyin/usr_dict.dat"));
109 im_init_user_dictionary(fn_usr_dict: usrDictInfo.absoluteFilePath().toUtf8().constData());
110 } else {
111 im_init_user_dictionary(fn_usr_dict: nullptr);
112 }
113}
114
115bool PinyinDecoderService::isUserDictionaryEnabled() const
116{
117 return im_is_user_dictionary_enabled();
118}
119
120void PinyinDecoderService::setLimits(int maxSpsLen, int maxHzsLen)
121{
122 if (maxSpsLen <= 0)
123 maxSpsLen = kMaxSearchSteps - 1;
124 if (maxHzsLen <= 0)
125 maxHzsLen = kMaxSearchSteps;
126 im_set_max_lens(max_sps_len: size_t(maxSpsLen), max_hzs_len: size_t(maxHzsLen));
127}
128
129int PinyinDecoderService::search(const QString &spelling)
130{
131 QByteArray spellingBuf = spelling.toLatin1();
132 return int(im_search(sps_buf: spellingBuf.constData(), sps_len: spellingBuf.length()));
133}
134
135int PinyinDecoderService::deleteSearch(int pos, bool isPosInSpellingId, bool clearFixedInThisStep)
136{
137 if (pos <= 0)
138 pos = 0;
139 return int(im_delsearch(pos: size_t(pos), is_pos_in_splid: isPosInSpellingId, clear_fixed_this_step: clearFixedInThisStep));
140}
141
142void PinyinDecoderService::resetSearch()
143{
144 im_reset_search();
145}
146
147QString PinyinDecoderService::pinyinString(bool decoded)
148{
149 size_t py_len;
150 const char *py = im_get_sps_str(decoded_len: &py_len);
151 if (!decoded)
152 py_len = strlen(s: py);
153
154 return QString(QLatin1String(py, (int)py_len));
155}
156
157int PinyinDecoderService::pinyinStringLength(bool decoded)
158{
159 size_t py_len;
160 const char *py = im_get_sps_str(decoded_len: &py_len);
161 if (!decoded)
162 py_len = strlen(s: py);
163 return (int)py_len;
164}
165
166QVector<int> PinyinDecoderService::spellingStartPositions()
167{
168 const unsigned short *spl_start;
169 int len;
170 // There will be len + 1 elements in the buffer when len > 0.
171 len = (int)im_get_spl_start_pos(spl_start);
172
173 QVector<int> arr;
174 arr.resize(asize: len + 2);
175 arr[0] = len; // element 0 is used to store the length of buffer.
176 for (int i = 0; i <= len; i++)
177 arr[i + 1] = spl_start[i];
178 return arr;
179}
180
181QString PinyinDecoderService::candidateAt(int index)
182{
183 Q_ASSERT(index >= 0);
184 QVector<QChar> candidateBuf;
185 candidateBuf.resize(asize: kMaxSearchSteps + 1);
186 if (!im_get_candidate(cand_id: size_t(index), cand_str: (char16 *)candidateBuf.data(), max_len: candidateBuf.length() - 1))
187 return QString();
188 candidateBuf.last() = u'\0';
189 return QString(candidateBuf.data());
190}
191
192QList<QString> PinyinDecoderService::fetchCandidates(int index, int count, int sentFixedLen)
193{
194 QList<QString> candidatesList;
195 for (int i = index; i < index + count; i++) {
196 QString retStr = candidateAt(index: i);
197 if (0 == i)
198 retStr.remove(i: 0, len: sentFixedLen);
199 candidatesList.append(t: retStr);
200 }
201 return candidatesList;
202}
203
204int PinyinDecoderService::chooceCandidate(int index)
205{
206 return int(im_choose(cand_id: index));
207}
208
209int PinyinDecoderService::cancelLastChoice()
210{
211 return int(im_cancel_last_choice());
212}
213
214int PinyinDecoderService::fixedLength()
215{
216 return (int)im_get_fixed_len();
217}
218
219void PinyinDecoderService::flushCache()
220{
221 im_flush_cache();
222}
223
224QList<QString> PinyinDecoderService::predictionList(const QString &history)
225{
226 QList<QString> predictList;
227 char16 (*predictItems)[kMaxPredictSize + 1] = nullptr;
228 int predictNum = int(im_get_predicts(his_buf: history.utf16(), pre_buf&: predictItems));
229 predictList.reserve(alloc: predictNum);
230 for (int i = 0; i < predictNum; i++)
231 predictList.append(t: QString((QChar *)predictItems[i]));
232 return predictList;
233}
234
235} // namespace QtVirtualKeyboard
236QT_END_NAMESPACE
237

source code of qtvirtualkeyboard/src/plugins/pinyin/plugin/pinyindecoderservice.cpp