Revision control
++ // Copyright (C) 2016 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
++ // Qt-Security score:significant reason:default
++
++ #include "pinyindecoderservice.h"
++ #include "pinyinime.h"
++ #include "dictdef.h"
++ #include <QStandardPaths>
++ #include <QFileInfo>
++ #include <QDir>
++ #include <QtCore/QLibraryInfo>
++ #include <QLoggingCategory>
++
++ QT_BEGIN_NAMESPACE
++ namespace QtVirtualKeyboard {
++
++ Q_DECLARE_LOGGING_CATEGORY(lcPinyin)
++
++ using namespace ime_pinyin;
++
++ QScopedPointer<PinyinDecoderService> PinyinDecoderService::_instance;
++
++ /*!
++ \class QtVirtualKeyboard::PinyinDecoderService
++ \internal
++ */
++
++ PinyinDecoderService::PinyinDecoderService(QObject *parent) :
++ QObject(parent),
++ initDone(false)
++ {
++ }
++
++ PinyinDecoderService::~PinyinDecoderService()
++ {
++ if (initDone) {
++ im_close_decoder();
++ initDone = false;
++ }
++ }
++
++ PinyinDecoderService *PinyinDecoderService::getInstance()
++ {
++ if (!_instance)
++ _instance.reset(new PinyinDecoderService());
++ if (!_instance->init())
++ return nullptr;
++ return _instance.data();
++ }
++
++ bool PinyinDecoderService::init()
++ {
++ if (initDone)
++ return true;
++
++ QString sysDict(qEnvironmentVariable("QT_VIRTUALKEYBOARD_PINYIN_DICTIONARY"));
++ if (!QFileInfo::exists(sysDict)) {
++ sysDict = QLibraryInfo::path(QLibraryInfo::DataPath) + QLatin1String("/qtvirtualkeyboard/pinyin/dict_pinyin.dat");
++ if (!QFileInfo::exists(sysDict))
++ sysDict = QLatin1String(":/qt-project.org/imports/QtQuick/VirtualKeyboard/3rdparty/pinyin/data/dict_pinyin.dat");
++ }
++
++ QString usrDictPath = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation);
++ QFileInfo usrDictInfo(usrDictPath + QLatin1String("/qtvirtualkeyboard/pinyin/usr_dict.dat"));
++ if (!usrDictInfo.exists()) {
++ qCWarning(lcPinyin) << "PinyinDecoderService::init(): creating directory for user dictionary" << usrDictInfo.absolutePath();
++ QDir().mkpath(usrDictInfo.absolutePath());
++ }
++
++ initDone = im_open_decoder(sysDict.toUtf8().constData(), usrDictInfo.absoluteFilePath().toUtf8().constData());
++ if (!initDone)
++ qCWarning(lcPinyin) << "Could not initialize pinyin engine. sys_dict:" << sysDict << "usr_dict:" << usrDictInfo.absoluteFilePath();
++
++ return initDone;
++ }
++
++ void PinyinDecoderService::setUserDictionary(bool enabled)
++ {
++ if (enabled == im_is_user_dictionary_enabled())
++ return;
++ if (enabled) {
++ QString usrDictPath = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation);
++ QFileInfo usrDictInfo(usrDictPath + QLatin1String("/qtvirtualkeyboard/pinyin/usr_dict.dat"));
++ im_init_user_dictionary(usrDictInfo.absoluteFilePath().toUtf8().constData());
++ } else {
++ im_init_user_dictionary(nullptr);
++ }
++ }
++
++ bool PinyinDecoderService::isUserDictionaryEnabled() const
++ {
++ return im_is_user_dictionary_enabled();
++ }
++
++ void PinyinDecoderService::setLimits(int maxSpsLen, int maxHzsLen)
++ {
++ if (maxSpsLen <= 0)
++ maxSpsLen = kMaxSearchSteps - 1;
++ if (maxHzsLen <= 0)
++ maxHzsLen = kMaxSearchSteps;
++ im_set_max_lens(size_t(maxSpsLen), size_t(maxHzsLen));
++ }
++
++ int PinyinDecoderService::search(const QString &spelling)
++ {
++ QByteArray spellingBuf = spelling.toLatin1();
++ return int(im_search(spellingBuf.constData(), spellingBuf.size()));
++ }
++
++ int PinyinDecoderService::deleteSearch(int pos, bool isPosInSpellingId, bool clearFixedInThisStep)
++ {
++ if (pos <= 0)
++ pos = 0;
++ return int(im_delsearch(size_t(pos), isPosInSpellingId, clearFixedInThisStep));
++ }
++
++ void PinyinDecoderService::resetSearch()
++ {
++ im_reset_search();
++ }
++
++ QString PinyinDecoderService::pinyinString(bool decoded)
++ {
++ size_t py_len;
++ const char *py = im_get_sps_str(&py_len);
++ if (!decoded)
++ py_len = strlen(py);
++
++ return QString(QLatin1String(py, (int)py_len));
++ }
++
++ int PinyinDecoderService::pinyinStringLength(bool decoded)
++ {
++ size_t py_len;
++ const char *py = im_get_sps_str(&py_len);
++ if (!decoded)
++ py_len = strlen(py);
++ return (int)py_len;
++ }
++
++ QList<int> PinyinDecoderService::spellingStartPositions()
++ {
++ const unsigned short *spl_start;
++ int len;
++ // There will be len + 1 elements in the buffer when len > 0.
++ len = (int)im_get_spl_start_pos(spl_start);
++
++ QList<int> arr;
++ arr.resize(len + 2);
++ arr[0] = len; // element 0 is used to store the length of buffer.
++ for (int i = 0; i <= len; i++)
++ arr[i + 1] = spl_start[i];
++ return arr;
++ }
++
++ QString PinyinDecoderService::candidateAt(int index)
++ {
++ Q_ASSERT(index >= 0);
++ QList<QChar> candidateBuf;
++ candidateBuf.resize(kMaxSearchSteps + 1);
++ if (!im_get_candidate(size_t(index), (char16 *)candidateBuf.data(), candidateBuf.size() - 1))
++ return QString();
++ candidateBuf.last() = u'\0';
++ return QString(candidateBuf.data());
++ }
++
++ QList<QString> PinyinDecoderService::fetchCandidates(int index, int count, int sentFixedLen)
++ {
++ QList<QString> candidatesList;
++ for (int i = index; i < index + count; i++) {
++ QString retStr = candidateAt(i);
++ if (0 == i)
++ retStr.remove(0, sentFixedLen);
++ candidatesList.append(retStr);
++ }
++ return candidatesList;
++ }
++
++ int PinyinDecoderService::chooseCandidate(int index)
++ {
++ return int(im_choose(index));
++ }
++
++ int PinyinDecoderService::cancelLastChoice()
++ {
++ return int(im_cancel_last_choice());
++ }
++
++ int PinyinDecoderService::fixedLength()
++ {
++ return (int)im_get_fixed_len();
++ }
++
++ void PinyinDecoderService::flushCache()
++ {
++ im_flush_cache();
++ }
++
++ QList<QString> PinyinDecoderService::predictionList(const QString &history)
++ {
++ QList<QString> predictList;
++ char16 (*predictItems)[kMaxPredictSize + 1] = nullptr;
++ int predictNum = int(im_get_predicts(history.utf16(), predictItems));
++ predictList.reserve(predictNum);
++ for (int i = 0; i < predictNum; i++)
++ predictList.append(QString((QChar *)predictItems[i]));
++ return predictList;
++ }
++
++ } // namespace QtVirtualKeyboard
++ QT_END_NAMESPACE