Revision control

Copy as Markdown

// 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 "hunspellinputmethod_p.h"
#include <QtVirtualKeyboard/qvirtualkeyboardinputcontext.h>
#include <QLoggingCategory>
QT_BEGIN_NAMESPACE
namespace QtVirtualKeyboard {
Q_LOGGING_CATEGORY(lcHunspell, "qt.virtualkeyboard.hunspell")
/*!
\class QtVirtualKeyboard::HunspellInputMethod
\internal
*/
HunspellInputMethod::HunspellInputMethod(HunspellInputMethodPrivate &dd, QObject *parent) :
QVirtualKeyboardAbstractInputMethod(dd, parent)
{
}
HunspellInputMethod::HunspellInputMethod(QObject *parent) :
QVirtualKeyboardAbstractInputMethod(*new HunspellInputMethodPrivate(this), parent)
{
}
HunspellInputMethod::~HunspellInputMethod()
{
}
QList<QVirtualKeyboardInputEngine::InputMode> HunspellInputMethod::inputModes(const QString &locale)
{
QList<QVirtualKeyboardInputEngine::InputMode> result;
switch (QLocale(locale).script()) {
case QLocale::GreekScript:
result.append(QVirtualKeyboardInputEngine::InputMode::Greek);
break;
case QLocale::CyrillicScript:
result.append(QVirtualKeyboardInputEngine::InputMode::Cyrillic);
break;
case QLocale::ArabicScript:
result.append(QVirtualKeyboardInputEngine::InputMode::Arabic);
break;
case QLocale::HebrewScript:
result.append(QVirtualKeyboardInputEngine::InputMode::Hebrew);
break;
default:
break;
}
result.append(QVirtualKeyboardInputEngine::InputMode::Latin);
result.append(QVirtualKeyboardInputEngine::InputMode::Numeric);
return result;
}
bool HunspellInputMethod::setInputMode(const QString &locale, QVirtualKeyboardInputEngine::InputMode inputMode)
{
Q_UNUSED(inputMode);
Q_D(HunspellInputMethod);
return d->createHunspell(locale);
}
bool HunspellInputMethod::setTextCase(QVirtualKeyboardInputEngine::TextCase textCase)
{
Q_UNUSED(textCase);
return true;
}
bool HunspellInputMethod::keyEvent(Qt::Key key, const QString &text, Qt::KeyboardModifiers modifiers)
{
Q_D(HunspellInputMethod);
QVirtualKeyboardInputContext *ic = inputContext();
Qt::InputMethodHints inputMethodHints = ic->inputMethodHints();
bool accept = false;
switch (key) {
case Qt::Key_Enter:
case Qt::Key_Return:
case Qt::Key_Tab:
case Qt::Key_Space:
update();
break;
case Qt::Key_Backspace:
{
QString word = d->wordCandidates.wordAt(0);
if (!word.isEmpty()) {
word.remove(word.size() - 1, 1);
ic->setPreeditText(word);
if (!word.isEmpty()) {
d->wordCandidates.updateWord(0, word);
if (d->updateSuggestions()) {
emit selectionListChanged(QVirtualKeyboardSelectionListModel::Type::WordCandidateList);
emit selectionListActiveItemChanged(QVirtualKeyboardSelectionListModel::Type::WordCandidateList, d->wordCandidates.index());
}
} else {
d->reset();
}
accept = true;
}
break;
}
default:
if (inputMethodHints.testFlag(Qt::ImhNoPredictiveText))
break;
if (d->dictionaryState == HunspellInputMethodPrivate::DictionaryNotLoaded) {
update();
break;
}
if (text.size() > 0) {
QChar c = text.at(0);
QString word = d->wordCandidates.wordAt(0);
bool addToWord = d->isValidInputChar(c) && (!word.isEmpty() || !d->isJoiner(c));
if (addToWord) {
QString newText = text;
/* Automatic space insertion. */
if (word.isEmpty()) {
QString surroundingText = ic->surroundingText();
int cursorPosition = ic->cursorPosition();
/* Rules for automatic space insertion:
- Surrounding text is not empty
- Cursor is at the end of the line
- No space before the cursor
- No spefic characters before the cursor; minus and apostrophe
*/
if (!surroundingText.isEmpty() && cursorPosition == surroundingText.size()) {
QChar lastChar = surroundingText.at(cursorPosition - 1);
if (!lastChar.isSpace() &&
lastChar != QLatin1Char(Qt::Key_Minus) &&
d->isAutoSpaceAllowed()) {
// auto-insertion of space might trigger auto-capitalization
bool wasShiftActive = ic->isShiftActive();
ic->commit(QLatin1String(" "));
if (ic->isShiftActive() && !wasShiftActive)
newText = newText.toUpper();
}
}
}
/* Ignore possible call to update() function when sending initial
pre-edit text. The update is triggered if the text editor has
a selection which the pre-edit text will replace.
*/
d->ignoreUpdate = word.isEmpty();
word.append(newText);
d->wordCandidates.updateWord(0, word);
ic->setPreeditText(word);
d->ignoreUpdate = false;
if (d->updateSuggestions()) {
emit selectionListChanged(QVirtualKeyboardSelectionListModel::Type::WordCandidateList);
emit selectionListActiveItemChanged(QVirtualKeyboardSelectionListModel::Type::WordCandidateList, d->wordCandidates.index());
}
accept = true;
} else if (text.size() > 1) {
bool addSpace = !word.isEmpty() || d->autoSpaceAllowed;
update();
d->autoSpaceAllowed = true;
if (addSpace && d->isAutoSpaceAllowed())
ic->commit(QLatin1String(" "));
ic->commit(text);
d->autoSpaceAllowed = addSpace;
accept = true;
} else {
update();
inputContext()->sendKeyClick(key, text, modifiers);
d->autoSpaceAllowed = true;
accept = true;
}
}
break;
}
return accept;
}
QList<QVirtualKeyboardSelectionListModel::Type> HunspellInputMethod::selectionLists()
{
Q_D(const HunspellInputMethod);
QVirtualKeyboardInputContext *ic = inputContext();
if (!ic)
return QList<QVirtualKeyboardSelectionListModel::Type>();
Qt::InputMethodHints inputMethodHints = ic->inputMethodHints();
if (d->dictionaryState == HunspellInputMethodPrivate::DictionaryNotLoaded || inputMethodHints.testFlag(Qt::ImhNoPredictiveText) || inputMethodHints.testFlag(Qt::ImhHiddenText))
return QList<QVirtualKeyboardSelectionListModel::Type>();
return QList<QVirtualKeyboardSelectionListModel::Type>() << QVirtualKeyboardSelectionListModel::Type::WordCandidateList;
}
int HunspellInputMethod::selectionListItemCount(QVirtualKeyboardSelectionListModel::Type type)
{
Q_UNUSED(type);
Q_D(HunspellInputMethod);
return d->wordCandidates.size();
}
QVariant HunspellInputMethod::selectionListData(QVirtualKeyboardSelectionListModel::Type type, int index, QVirtualKeyboardSelectionListModel::Role role)
{
QVariant result;
Q_D(HunspellInputMethod);
switch (role) {
case QVirtualKeyboardSelectionListModel::Role::Display:
result = QVariant(d->wordCandidates.wordAt(index));
break;
case QVirtualKeyboardSelectionListModel::Role::WordCompletionLength:
{
const QString wordCandidate(d->wordCandidates.wordAt(index));
const QString word(d->wordCandidates.wordAt(0));
int wordCompletionLength = wordCandidate.size() - word.size();
result.setValue((wordCompletionLength > 0 && wordCandidate.startsWith(word)) ? wordCompletionLength : 0);
break;
}
case QVirtualKeyboardSelectionListModel::Role::Dictionary:
{
const QString wordCandidate(d->wordCandidates.wordAt(index));
QVirtualKeyboardSelectionListModel::DictionaryType dictionaryType =
d->userDictionaryWords && d->userDictionaryWords->contains(wordCandidate) ?
QVirtualKeyboardSelectionListModel::DictionaryType::User : QVirtualKeyboardSelectionListModel::DictionaryType::Default;
result = QVariant(static_cast<int>(dictionaryType));
break;
}
case QVirtualKeyboardSelectionListModel::Role::CanRemoveSuggestion:
result.setValue(index > 0 && d->wordCandidates.wordFlagsAt(index).testFlag(HunspellWordList::SpellCheckOk));
break;
default:
result = QVirtualKeyboardAbstractInputMethod::selectionListData(type, index, role);
break;
}
return result;
}
void HunspellInputMethod::selectionListItemSelected(QVirtualKeyboardSelectionListModel::Type type, int index)
{
Q_UNUSED(type);
Q_D(HunspellInputMethod);
d->wordCandidates.setIndex(index);
d->addToDictionary();
QString finalWord = d->wordCandidates.wordAt(index);
reset();
inputContext()->commit(finalWord);
d->autoSpaceAllowed = true;
}
bool HunspellInputMethod::selectionListRemoveItem(QVirtualKeyboardSelectionListModel::Type type, int index)
{
Q_D(HunspellInputMethod);
Q_UNUSED(type);
if (index <= 0 || index >= d->wordCandidates.size())
return false;
QString word = d->wordCandidates.wordAt(index);
d->removeFromDictionary(word);
return true;
}
bool HunspellInputMethod::reselect(int cursorPosition, const QVirtualKeyboardInputEngine::ReselectFlags &reselectFlags)
{
Q_D(HunspellInputMethod);
QString word(d->wordCandidates.wordAt(0));
Q_ASSERT(word.isEmpty());
if (d->dictionaryState == HunspellInputMethodPrivate::DictionaryNotLoaded)
return false;
QVirtualKeyboardInputContext *ic = inputContext();
if (!ic)
return false;
const QString surroundingText = ic->surroundingText();
int replaceFrom = 0;
if (reselectFlags.testFlag(QVirtualKeyboardInputEngine::ReselectFlag::WordBeforeCursor)) {
for (int i = cursorPosition - 1; i >= 0; --i) {
QChar c = surroundingText.at(i);
if (!d->isValidInputChar(c))
break;
word.insert(0, c);
--replaceFrom;
}
while (replaceFrom < 0 && d->isJoiner(word.at(0))) {
word.remove(0, 1);
++replaceFrom;
}
}
if (reselectFlags.testFlag(QVirtualKeyboardInputEngine::ReselectFlag::WordAtCursor) && replaceFrom == 0)
return false;
if (reselectFlags.testFlag(QVirtualKeyboardInputEngine::ReselectFlag::WordAfterCursor)) {
for (int i = cursorPosition; i < surroundingText.size(); ++i) {
QChar c = surroundingText.at(i);
if (!d->isValidInputChar(c))
break;
word.append(c);
}
while (replaceFrom > -word.size()) {
int lastPos = word.size() - 1;
if (!d->isJoiner(word.at(lastPos)))
break;
word.remove(lastPos, 1);
}
}
if (word.isEmpty())
return false;
if (reselectFlags.testFlag(QVirtualKeyboardInputEngine::ReselectFlag::WordAtCursor) && replaceFrom == -word.size())
return false;
if (d->isJoiner(word.at(0)))
return false;
if (d->isJoiner(word.at(word.size() - 1)))
return false;
d->wordCandidates.updateWord(0, word);
ic->setPreeditText(word, QList<QInputMethodEvent::Attribute>(), replaceFrom, word.size());
d->autoSpaceAllowed = false;
if (d->updateSuggestions()) {
emit selectionListChanged(QVirtualKeyboardSelectionListModel::Type::WordCandidateList);
emit selectionListActiveItemChanged(QVirtualKeyboardSelectionListModel::Type::WordCandidateList, d->wordCandidates.index());
}
return true;
}
void HunspellInputMethod::reset()
{
Q_D(HunspellInputMethod);
d->reset();
}
void HunspellInputMethod::update()
{
Q_D(HunspellInputMethod);
if (d->ignoreUpdate)
return;
QString finalWord;
if (!d->wordCandidates.isEmpty()) {
d->addToDictionary();
finalWord = d->wordCandidates.wordAt(d->wordCandidates.index());
}
d->reset();
if (!finalWord.isEmpty())
inputContext()->commit(finalWord);
d->autoSpaceAllowed = false;
}
} // namespace QtVirtualKeyboard
QT_END_NAMESPACE