Revision control

++ // Copyright (C) 2017 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
++ // Qt-Security score:significant reason:default
++
++ #include <QtVirtualKeyboard/private/shadowinputcontext_p.h>
++ #include <QtVirtualKeyboard/qvirtualkeyboardinputcontext.h>
++ #include <QtVirtualKeyboard/private/virtualkeyboarddebug_p.h>
++
++ #include <QtCore/private/qobject_p.h>
++ #include <QGuiApplication>
++ #include <QQuickItem>
++
++ QT_BEGIN_NAMESPACE
++
++ namespace QtVirtualKeyboard {
++
++ class ShadowInputContextPrivate : public QObjectPrivate
++ {
++ public:
++ ShadowInputContextPrivate() :
++ QObjectPrivate(),
++ inputContext(nullptr),
++ anchorRectIntersectsClipRect(false),
++ cursorRectIntersectsClipRect(false),
++ selectionControlVisible(false)
++ {
++ }
++
++ QVirtualKeyboardInputContext *inputContext;
++ QPointer<QObject> inputItem;
++ QString preeditText;
++ QList<QInputMethodEvent::Attribute> preeditTextAttributes;
++ QRectF anchorRectangle;
++ QRectF cursorRectangle;
++ bool anchorRectIntersectsClipRect;
++ bool cursorRectIntersectsClipRect;
++ bool selectionControlVisible;
++ };
++
++ ShadowInputContext::ShadowInputContext(QObject *parent) :
++ QObject(*new ShadowInputContextPrivate(), parent)
++ {
++ }
++
++ void ShadowInputContext::setInputContext(QVirtualKeyboardInputContext *inputContext)
++ {
++ Q_D(ShadowInputContext);
++ d->inputContext = inputContext;
++ }
++
++ QObject *ShadowInputContext::inputItem() const
++ {
++ Q_D(const ShadowInputContext);
++ return d->inputItem.data();
++ }
++
++ void ShadowInputContext::setInputItem(QObject *inputItem)
++ {
++ Q_D(ShadowInputContext);
++ if (d->inputItem != inputItem) {
++ d->inputItem = inputItem;
++ emit inputItemChanged();
++ update(Qt::ImQueryAll);
++ }
++ }
++
++ QRectF ShadowInputContext::anchorRectangle() const
++ {
++ Q_D(const ShadowInputContext);
++ return d->anchorRectangle;
++ }
++
++ QRectF ShadowInputContext::cursorRectangle() const
++ {
++ Q_D(const ShadowInputContext);
++ return d->cursorRectangle;
++ }
++
++ bool ShadowInputContext::anchorRectIntersectsClipRect() const
++ {
++ Q_D(const ShadowInputContext);
++ return d->anchorRectIntersectsClipRect;
++ }
++
++ bool ShadowInputContext::cursorRectIntersectsClipRect() const
++ {
++ Q_D(const ShadowInputContext);
++ return d->cursorRectIntersectsClipRect;
++ }
++
++ bool ShadowInputContext::selectionControlVisible() const
++ {
++ Q_D(const ShadowInputContext);
++ return d->selectionControlVisible;
++ }
++
++ void ShadowInputContext::setSelectionOnFocusObject(const QPointF &anchorPos, const QPointF &cursorPos)
++ {
++ Q_D(ShadowInputContext);
++ QObject *focus = d->inputItem;
++ if (!focus)
++ return;
++
++ QQuickItem *quickItem = qobject_cast<QQuickItem *>(d->inputItem);
++ bool success;
++ int anchor = queryFocusObject(Qt::ImCursorPosition, quickItem ? quickItem->mapFromScene(anchorPos) : anchorPos).toInt(&success);
++ if (success) {
++ int cursor = queryFocusObject(Qt::ImCursorPosition, quickItem ? quickItem->mapFromScene(cursorPos) : cursorPos).toInt(&success);
++ if (success) {
++ if (anchor == cursor && anchorPos != cursorPos)
++ return;
++ QList<QInputMethodEvent::Attribute> imAttributes;
++ imAttributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Selection, anchor, cursor - anchor, QVariant()));
++ QInputMethodEvent event(QString(), imAttributes);
++ QGuiApplication::sendEvent(QGuiApplication::focusObject(), &event);
++ }
++ }
++ }
++
++ void ShadowInputContext::updateSelectionProperties()
++ {
++ Q_D(ShadowInputContext);
++ if (!d->inputItem)
++ return;
++
++ QInputMethodQueryEvent imQueryEvent(Qt::ImAnchorRectangle |
++ Qt::ImCursorRectangle |
++ Qt::ImInputItemClipRectangle);
++ QGuiApplication::sendEvent(d->inputItem, &imQueryEvent);
++ QQuickItem *quickItem = qobject_cast<QQuickItem *>(d->inputItem);
++ const QRectF anchorRect = imQueryEvent.value(Qt::ImAnchorRectangle).toRectF();
++ const QRectF cursorRect = imQueryEvent.value(Qt::ImCursorRectangle).toRectF();
++ const QRectF anchorRectangle = quickItem ? quickItem->mapRectToScene(anchorRect) : anchorRect;
++ const QRectF cursorRectangle = quickItem ? quickItem->mapRectToScene(cursorRect) : cursorRect;
++ const QRectF inputItemClipRect = imQueryEvent.value(Qt::ImInputItemClipRectangle).toRectF().adjusted(0, 0, cursorRectangle.width(), 0);
++ const bool anchorRectIntersectsClipRect = inputItemClipRect.intersects(anchorRect);
++ const bool cursorRectIntersectsClipRect = inputItemClipRect.intersects(cursorRect);
++ const bool selectionControlVisible = d->inputContext->isSelectionControlVisible();
++
++ const bool newAnchorRectangle = anchorRectangle != d->anchorRectangle;
++ const bool newCursorRectangle = cursorRectangle != d->cursorRectangle;
++ const bool newAnchorRectIntersectsClipRect = anchorRectIntersectsClipRect != d->anchorRectIntersectsClipRect;
++ const bool newCursorRectIntersectsClipRect = cursorRectIntersectsClipRect != d->cursorRectIntersectsClipRect;
++ const bool newSelectionControlVisible = selectionControlVisible != d->selectionControlVisible;
++
++ d->anchorRectangle = anchorRectangle;
++ d->cursorRectangle = cursorRectangle;
++ d->anchorRectIntersectsClipRect = anchorRectIntersectsClipRect;
++ d->cursorRectIntersectsClipRect = cursorRectIntersectsClipRect;
++ d->selectionControlVisible = selectionControlVisible;
++
++ if (newAnchorRectangle)
++ emit anchorRectangleChanged();
++ if (newCursorRectangle)
++ emit cursorRectangleChanged();
++ if (newAnchorRectIntersectsClipRect)
++ emit anchorRectIntersectsClipRectChanged();
++ if (newCursorRectIntersectsClipRect)
++ emit cursorRectIntersectsClipRectChanged();
++ if (newSelectionControlVisible)
++ emit selectionControlVisibleChanged();
++ }
++
++ void ShadowInputContext::update(Qt::InputMethodQueries queries)
++ {
++ Q_UNUSED(queries);
++ Q_D(ShadowInputContext);
++ if (!d->inputItem)
++ return;
++
++ QInputMethodQueryEvent imQueryEvent(Qt::ImQueryInput);
++ QGuiApplication::sendEvent(d->inputItem, &imQueryEvent);
++
++ const QString surroundingText = imQueryEvent.value(Qt::ImSurroundingText).toString();
++ const int cursorPosition = imQueryEvent.value(Qt::ImCursorPosition).toInt();
++ const int anchorPosition = imQueryEvent.value(Qt::ImAnchorPosition).toInt();
++
++ const QString newSurroundingText = d->inputContext->surroundingText();
++ const int newCursorPosition = d->inputContext->cursorPosition();
++ const int newAnchorPosition = d->inputContext->anchorPosition();
++
++ const QString newPreeditText = d->inputContext->preeditText();
++ const QList<QInputMethodEvent::Attribute> newPreeditAttributes = d->inputContext->preeditTextAttributes();
++
++ bool updateSurroundingText = newSurroundingText != surroundingText;
++ bool updateSelection = newCursorPosition != cursorPosition || newAnchorPosition != anchorPosition;
++ if (updateSurroundingText) {
++ QInputMethodEvent inputEvent;
++ inputEvent.setCommitString(newSurroundingText, -cursorPosition, surroundingText.size());
++ QGuiApplication::sendEvent(d->inputItem, &inputEvent);
++ }
++
++ if (updateSurroundingText || updateSelection) {
++ QList<QInputMethodEvent::Attribute> attributes;
++ attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Selection,
++ newAnchorPosition,
++ newCursorPosition - newAnchorPosition, QVariant()));
++ QInputMethodEvent inputEvent(QString(), attributes);
++ QGuiApplication::sendEvent(d->inputItem, &inputEvent);
++ }
++
++ const bool forcePreeditText = !newPreeditText.isEmpty() && (updateSurroundingText || updateSelection);
++ if (forcePreeditText || d->preeditText != newPreeditText || d->preeditTextAttributes != newPreeditAttributes) {
++ d->preeditText = newPreeditText;
++ d->preeditTextAttributes = newPreeditAttributes;
++ QInputMethodEvent inputEvent(d->preeditText, d->preeditTextAttributes);
++ QGuiApplication::sendEvent(d->inputItem, &inputEvent);
++ }
++
++ updateSelectionProperties();
++ }
++
++ QVariant ShadowInputContext::queryFocusObject(Qt::InputMethodQuery query, QVariant argument)
++ {
++ Q_D(ShadowInputContext);
++ QVariant retval;
++ QObject *focusObject = d->inputItem;
++ if (!focusObject)
++ return retval;
++
++ bool newMethodWorks = QMetaObject::invokeMethod(focusObject, "inputMethodQuery",
++ Qt::DirectConnection,
++ Q_RETURN_ARG(QVariant, retval),
++ Q_ARG(Qt::InputMethodQuery, query),
++ Q_ARG(QVariant, argument));
++ if (newMethodWorks)
++ return retval;
++
++ QInputMethodQueryEvent queryEvent(query);
++ QCoreApplication::sendEvent(focusObject, &queryEvent);
++ return queryEvent.value(query);
++ }
++
++ } // namespace QtVirtualKeyboard
++ QT_END_NAMESPACE