1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include "pinyindecoderservice_p.h"
5#include "pinyinime.h"
6#include "dictdef.h"
7#include <QStandardPaths>
8#include <QFileInfo>
9#include <QDir>
10#include <QtCore/QLibraryInfo>
11#include <QLoggingCategory>
12
13QT_BEGIN_NAMESPACE
14namespace QtVirtualKeyboard {
15
16Q_DECLARE_LOGGING_CATEGORY(lcPinyin)
17
18using namespace ime_pinyin;
19
20QScopedPointer<PinyinDecoderService> PinyinDecoderService::_instance;
21
22/*!
23 \class QtVirtualKeyboard::PinyinDecoderService
24 \internal
25*/
26
27PinyinDecoderService::PinyinDecoderService(QObject *parent) :
28 QObject(parent),
29 initDone(false)
30{
31}
32
33PinyinDecoderService::~PinyinDecoderService()
34{
35 if (initDone) {
36 im_close_decoder();
37 initDone = false;
38 }
39}
40
41PinyinDecoderService *PinyinDecoderService::getInstance()
42{
43 if (!_instance)
44 _instance.reset(other: new PinyinDecoderService());
45 if (!_instance->init())
46 return nullptr;
47 return _instance.data();
48}
49
50bool PinyinDecoderService::init()
51{
52 if (initDone)
53 return true;
54
55 QString sysDict(qEnvironmentVariable(varName: "QT_VIRTUALKEYBOARD_PINYIN_DICTIONARY"));
56 if (!QFileInfo::exists(file: sysDict)) {
57 sysDict = QLibraryInfo::path(p: QLibraryInfo::DataPath) + QLatin1String("/qtvirtualkeyboard/pinyin/dict_pinyin.dat");
58 if (!QFileInfo::exists(file: sysDict))
59 sysDict = QLatin1String(":/qt-project.org/imports/QtQuick/VirtualKeyboard/3rdparty/pinyin/data/dict_pinyin.dat");
60 }
61
62 QString usrDictPath = QStandardPaths::writableLocation(type: QStandardPaths::ConfigLocation);
63 QFileInfo usrDictInfo(usrDictPath + QLatin1String("/qtvirtualkeyboard/pinyin/usr_dict.dat"));
64 if (!usrDictInfo.exists()) {
65 qCWarning(lcPinyin) << "PinyinDecoderService::init(): creating directory for user dictionary" << usrDictInfo.absolutePath();
66 QDir().mkpath(dirPath: usrDictInfo.absolutePath());
67 }
68
69 initDone = im_open_decoder(fn_sys_dict: sysDict.toUtf8().constData(), fn_usr_dict: usrDictInfo.absoluteFilePath().toUtf8().constData());
70 if (!initDone)
71 qCWarning(lcPinyin) << "Could not initialize pinyin engine. sys_dict:" << sysDict << "usr_dict:" << usrDictInfo.absoluteFilePath();
72
73 return initDone;
74}
75
76void PinyinDecoderService::setUserDictionary(bool enabled)
77{
78 if (enabled == im_is_user_dictionary_enabled())
79 return;
80 if (enabled) {
81 QString usrDictPath = QStandardPaths::writableLocation(type: QStandardPaths::ConfigLocation);
82 QFileInfo usrDictInfo(usrDictPath + QLatin1String("/qtvirtualkeyboard/pinyin/usr_dict.dat"));
83 im_init_user_dictionary(fn_usr_dict: usrDictInfo.absoluteFilePath().toUtf8().constData());
84 } else {
85 im_init_user_dictionary(fn_usr_dict: nullptr);
86 }
87}
88
89bool PinyinDecoderService::isUserDictionaryEnabled() const
90{
91 return im_is_user_dictionary_enabled();
92}
93
94void PinyinDecoderService::setLimits(int maxSpsLen, int maxHzsLen)
95{
96 if (maxSpsLen <= 0)
97 maxSpsLen = kMaxSearchSteps - 1;
98 if (maxHzsLen <= 0)
99 maxHzsLen = kMaxSearchSteps;
100 im_set_max_lens(max_sps_len: size_t(maxSpsLen), max_hzs_len: size_t(maxHzsLen));
101}
102
103int PinyinDecoderService::search(const QString &spelling)
104{
105 QByteArray spellingBuf = spelling.toLatin1();
106 return int(im_search(sps_buf: spellingBuf.constData(), sps_len: spellingBuf.size()));
107}
108
109int PinyinDecoderService::deleteSearch(int pos, bool isPosInSpellingId, bool clearFixedInThisStep)
110{
111 if (pos <= 0)
112 pos = 0;
113 return int(im_delsearch(pos: size_t(pos), is_pos_in_splid: isPosInSpellingId, clear_fixed_this_step: clearFixedInThisStep));
114}
115
116void PinyinDecoderService::resetSearch()
117{
118 im_reset_search();
119}
120
121QString PinyinDecoderService::pinyinString(bool decoded)
122{
123 size_t py_len;
124 const char *py = im_get_sps_str(decoded_len: &py_len);
125 if (!decoded)
126 py_len = strlen(s: py);
127
128 return QString(QLatin1String(py, (int)py_len));
129}
130
131int PinyinDecoderService::pinyinStringLength(bool decoded)
132{
133 size_t py_len;
134 const char *py = im_get_sps_str(decoded_len: &py_len);
135 if (!decoded)
136 py_len = strlen(s: py);
137 return (int)py_len;
138}
139
140QList<int> PinyinDecoderService::spellingStartPositions()
141{
142 const unsigned short *spl_start;
143 int len;
144 // There will be len + 1 elements in the buffer when len > 0.
145 len = (int)im_get_spl_start_pos(spl_start);
146
147 QList<int> arr;
148 arr.resize(size: len + 2);
149 arr[0] = len; // element 0 is used to store the length of buffer.
150 for (int i = 0; i <= len; i++)
151 arr[i + 1] = spl_start[i];
152 return arr;
153}
154
155QString PinyinDecoderService::candidateAt(int index)
156{
157 Q_ASSERT(index >= 0);
158 QList<QChar> candidateBuf;
159 candidateBuf.resize(size: kMaxSearchSteps + 1);
160 if (!im_get_candidate(cand_id: size_t(index), cand_str: (char16 *)candidateBuf.data(), max_len: candidateBuf.size() - 1))
161 return QString();
162 candidateBuf.last() = u'\0';
163 return QString(candidateBuf.data());
164}
165
166QList<QString> PinyinDecoderService::fetchCandidates(int index, int count, int sentFixedLen)
167{
168 QList<QString> candidatesList;
169 for (int i = index; i < index + count; i++) {
170 QString retStr = candidateAt(index: i);
171 if (0 == i)
172 retStr.remove(i: 0, len: sentFixedLen);
173 candidatesList.append(t: retStr);
174 }
175 return candidatesList;
176}
177
178int PinyinDecoderService::chooceCandidate(int index)
179{
180 return int(im_choose(cand_id: index));
181}
182
183int PinyinDecoderService::cancelLastChoice()
184{
185 return int(im_cancel_last_choice());
186}
187
188int PinyinDecoderService::fixedLength()
189{
190 return (int)im_get_fixed_len();
191}
192
193void PinyinDecoderService::flushCache()
194{
195 im_flush_cache();
196}
197
198QList<QString> PinyinDecoderService::predictionList(const QString &history)
199{
200 QList<QString> predictList;
201 char16 (*predictItems)[kMaxPredictSize + 1] = nullptr;
202 int predictNum = int(im_get_predicts(his_buf: history.utf16(), pre_buf&: predictItems));
203 predictList.reserve(asize: predictNum);
204 for (int i = 0; i < predictNum; i++)
205 predictList.append(t: QString((QChar *)predictItems[i]));
206 return predictList;
207}
208
209} // namespace QtVirtualKeyboard
210QT_END_NAMESPACE
211

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