Revision control

++ // Copyright (C) 2016 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
++ // Qt-Security score:significant reason:default
++
++ #ifndef QQUICKTEXTINPUT_P_P_H
++ #define QQUICKTEXTINPUT_P_P_H
++
++ #include "qquicktextinput_p.h"
++ #include "qquicktext_p.h"
++ #include "qquickimplicitsizeitem_p_p.h"
++ #include "qquicktextutil_p.h"
++
++ #include <QtQml/qqml.h>
++ #include <QtCore/qelapsedtimer.h>
++ #include <QtCore/qpointer.h>
++ #include <QtCore/qbasictimer.h>
++ #if QT_CONFIG(accessibility)
++ #include <QtGui/qaccessible.h>
++ #endif
++ #include <QtGui/qclipboard.h>
++ #include <QtGui/qguiapplication.h>
++ #include <QtGui/qpalette.h>
++ #include <QtGui/qtextlayout.h>
++ #include <QtGui/qstylehints.h>
++ #include <private/qlazilyallocated_p.h>
++
++ #include "qplatformdefs.h"
++
++ #include <memory>
++
++ //
++ // W A R N I N G
++ // -------------
++ //
++ // This file is not part of the Qt API. It exists purely as an
++ // implementation detail. This header file may change from version to
++ // version without notice, or even be removed.
++ //
++ // We mean it.
++
++ QT_BEGIN_NAMESPACE
++
++ class QSGInternalTextNode;
++ class QInputControl;
++
++ class Q_QUICK_EXPORT QQuickTextInputPrivate : public QQuickImplicitSizeItemPrivate
++ #if QT_CONFIG(accessibility)
++ , public QAccessible::ActivationObserver
++ #endif
++ {
++ public:
++ Q_DECLARE_PUBLIC(QQuickTextInput)
++
++ typedef QQuickTextInput Public;
++
++ struct ExtraData {
++ ExtraData();
++
++ qreal padding;
++ qreal topPadding;
++ qreal leftPadding;
++ qreal rightPadding;
++ qreal bottomPadding;
++ bool explicitTopPadding : 1;
++ bool explicitLeftPadding : 1;
++ bool explicitRightPadding : 1;
++ bool explicitBottomPadding : 1;
++ bool implicitResize : 1;
++ };
++ QLazilyAllocated<ExtraData> extra;
++
++ QQuickTextInputPrivate()
++ : hscroll(0)
++ , vscroll(0)
++ , cursorItem(nullptr)
++ , textNode(nullptr)
++ , m_maskData(nullptr)
++ , color(QRgb(0xFF000000))
++ , selectionColor(QRgb(0xFF000080))
++ , selectedTextColor(QRgb(0xFFFFFFFF))
++ , m_cursor(0)
++ #if QT_CONFIG(im)
++ , m_preeditCursor(0)
++ , m_undoPreeditState(-1)
++ #endif
++ , m_blinkEnabled(false)
++ , m_blinkTimer(0)
++ , m_maxLength(32767)
++ , m_lastCursorPos(-1)
++ , m_undoState(0)
++ , m_selstart(0)
++ , m_selend(0)
++ #if QT_CONFIG(im)
++ , inputMethodHints(Qt::ImhNone)
++ #endif
++ , hAlign(QQuickTextInput::AlignLeft)
++ , vAlign(QQuickTextInput::AlignTop)
++ , wrapMode(QQuickTextInput::NoWrap)
++ , m_echoMode(QQuickTextInput::Normal)
++ , renderType(QQuickTextUtil::textRenderType<QQuickTextInput>())
++ , updateType(UpdatePaintNode)
++ , mouseSelectionMode(QQuickTextInput::SelectCharacters)
++ , m_layoutDirection(Qt::LayoutDirectionAuto)
++ , m_passwordCharacter(QGuiApplication::styleHints()->passwordMaskCharacter())
++ , m_passwordMaskDelay(QGuiApplication::styleHints()->passwordMaskDelay())
++ , focusOnPress(true)
++ , cursorVisible(false)
++ , cursorPending(false)
++ , autoScroll(true)
++ , selectByMouse(true)
++ , canPaste(false)
++ , canPasteValid(false)
++ , canUndo(false)
++ , canRedo(false)
++ , hAlignImplicit(true)
++ , selectPressed(false)
++ , hadSelectionOnMousePress(false)
++ , textLayoutDirty(true)
++ , persistentSelection(false)
++ , hasImState(false)
++ , m_separator(0)
++ , m_readOnly(0)
++ , m_textDirty(0)
++ #if QT_CONFIG(im)
++ , m_preeditDirty(0)
++ #endif
++ , m_selDirty(0)
++ , m_validInput(1)
++ , m_acceptableInput(1)
++ , m_blinkStatus(0)
++ , m_passwordEchoEditing(false)
++ , inLayout(false)
++ , requireImplicitWidth(false)
++ , overwriteMode(false)
++ , containsUnscalableGlyphs(false)
++ #if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
++ , selectByTouchDrag(false)
++ #endif
++ {
++ #if QT_CONFIG(accessibility)
++ QAccessible::installActivationObserver(this);
++ setAccessible();
++ #endif
++ }
++
++ ~QQuickTextInputPrivate()
++ {
++ // If this control is used for password input, we don't want the
++ // password data to stay in the process memory, therefore we need
++ // to zero it out
++ if (m_echoMode != QQuickTextInput::Normal)
++ m_text.fill(u'\0');
++
++ #if QT_CONFIG(accessibility)
++ QAccessible::removeActivationObserver(this);
++ #endif
++ }
++
++ void init();
++ void cancelInput();
++ void startCreatingCursor();
++ void ensureVisible(int position, int preeditCursor = 0, int preeditLength = 0);
++ void updateHorizontalScroll();
++ void updateVerticalScroll();
++ bool determineHorizontalAlignment();
++ bool setHAlign(QQuickTextInput::HAlignment, bool forceAlign = false);
++ void mirrorChange() override;
++ #if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
++ bool handleContextMenuEvent(QContextMenuEvent *event) override;
++ #endif
++ bool sendMouseEventToInputContext(QMouseEvent *event);
++ #if QT_CONFIG(im)
++ Qt::InputMethodHints effectiveInputMethodHints() const;
++ #endif
++ void handleFocusEvent(QFocusEvent *event);
++
++ virtual void readOnlyChanged(bool isReadOnly);
++ void echoModeChanged(QQuickTextInput::EchoMode echoMode);
++
++ #if QT_CONFIG(accessibility)
++ void accessibilityActiveChanged(bool active) override;
++ QAccessible::Role accessibleRole() const override;
++ #endif
++
++ struct MaskInputData {
++ enum Casemode { NoCaseMode, Upper, Lower };
++ QChar maskChar; // either the separator char or the inputmask
++ bool separator;
++ Casemode caseMode;
++ };
++
++ // undo/redo handling
++ enum CommandType { Separator, Insert, Remove, Delete, RemoveSelection, DeleteSelection, SetSelection };
++ struct Command {
++ inline Command() {}
++ inline Command(CommandType t, int p, QChar c, int ss, int se) : type(t),uc(c),pos(p),selStart(ss),selEnd(se) {}
++ uint type : 4;
++ QChar uc;
++ int pos, selStart, selEnd;
++ };
++
++ enum DrawFlags {
++ DrawText = 0x01,
++ DrawSelections = 0x02,
++ DrawCursor = 0x04,
++ DrawAll = DrawText | DrawSelections | DrawCursor
++ };
++
++ QElapsedTimer tripleClickTimer;
++ QSizeF contentSize;
++ QPointF pressPos;
++ QPointF tripleClickStartPoint;
++
++ QPointer<QQmlComponent> cursorComponent;
++ #if QT_CONFIG(validator)
++ QPointer<QValidator> m_validator;
++ #endif
++
++ qreal hscroll;
++ qreal vscroll;
++
++ QTextLayout m_textLayout;
++ QString m_text;
++ QString m_inputMask;
++ QString m_cancelText;
++ QFont font;
++ QFont sourceFont;
++
++ QQuickItem *cursorItem;
++ QSGInternalTextNode *textNode;
++ std::unique_ptr<MaskInputData[]> m_maskData;
++ QInputControl *m_inputControl;
++
++ QList<int> m_transactions;
++ QList<Command> m_history;
++
++ QColor color;
++ QColor selectionColor;
++ QColor selectedTextColor;
++
++ QBasicTimer m_passwordEchoTimer;
++ int lastSelectionStart;
++ int lastSelectionEnd;
++ int m_cursor;
++ #if QT_CONFIG(im)
++ int m_preeditCursor;
++ int m_undoPreeditState;
++ #endif
++ bool m_blinkEnabled;
++ int m_blinkTimer;
++ int m_maxLength;
++ int m_lastCursorPos;
++ int m_undoState;
++ int m_selstart;
++ int m_selend;
++
++ enum UpdateType {
++ UpdateNone,
++ UpdateOnlyPreprocess,
++ UpdatePaintNode
++ };
++
++ #if QT_CONFIG(im)
++ Qt::InputMethodHints inputMethodHints;
++ #endif
++ QQuickTextInput::HAlignment hAlign;
++ QQuickTextInput::VAlignment vAlign;
++ QQuickTextInput::WrapMode wrapMode;
++ QQuickTextInput::EchoMode m_echoMode;
++ QQuickTextInput::RenderType renderType;
++ UpdateType updateType;
++ QQuickTextInput::SelectionMode mouseSelectionMode;
++ Qt::LayoutDirection m_layoutDirection;
++
++ QChar m_blank;
++ QChar m_passwordCharacter;
++ int m_passwordMaskDelay;
++
++ bool focusOnPress:1;
++ bool cursorVisible:1;
++ bool cursorPending:1;
++ bool autoScroll:1;
++ bool selectByMouse:1;
++ bool canPaste:1;
++ bool canPasteValid:1;
++ bool canUndo:1;
++ bool canRedo:1;
++ bool hAlignImplicit:1;
++ bool selectPressed:1;
++ bool hadSelectionOnMousePress:1;
++ bool textLayoutDirty:1;
++ bool persistentSelection:1;
++ bool hasImState : 1;
++ bool m_separator : 1;
++ bool m_readOnly : 1;
++ bool m_textDirty : 1;
++ #if QT_CONFIG(im)
++ bool m_preeditDirty : 1;
++ #endif
++ bool m_selDirty : 1;
++ bool m_validInput : 1;
++ bool m_acceptableInput : 1;
++ bool m_blinkStatus : 1;
++ bool m_passwordEchoEditing : 1;
++ bool inLayout:1;
++ bool requireImplicitWidth:1;
++ bool overwriteMode:1;
++ bool containsUnscalableGlyphs:1;
++ #if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
++ bool selectByTouchDrag:1;
++ #endif
++
++ static inline QQuickTextInputPrivate *get(QQuickTextInput *t) {
++ return t->d_func();
++ }
++ bool hasPendingTripleClick() const {
++ return !tripleClickTimer.hasExpired(QGuiApplication::styleHints()->mouseDoubleClickInterval());
++ }
++
++ void setNativeCursorEnabled(bool) {
++ updateCursorBlinking();
++ }
++
++ int nextMaskBlank(int pos)
++ {
++ int c = findInMask(pos, true, false);
++ m_separator |= (c != pos);
++ return (c != -1 ? c : m_maxLength);
++ }
++
++ int prevMaskBlank(int pos)
++ {
++ int c = findInMask(pos, false, false);
++ m_separator |= (c != pos);
++ return (c != -1 ? c : 0);
++ }
++
++ bool isUndoAvailable() const { return !m_readOnly && m_undoState; }
++ bool isRedoAvailable() const { return !m_readOnly && m_undoState < (int)m_history.size(); }
++ void clearUndo() {
++ m_history.clear();
++ m_undoState = 0;
++ #if QT_CONFIG(im)
++ m_undoPreeditState = -1;
++ #endif
++ }
++
++ bool allSelected() const { return !m_text.isEmpty() && m_selstart == 0 && m_selend == (int)m_text.size(); }
++ bool hasSelectedText() const { return !m_text.isEmpty() && m_selend > m_selstart; }
++
++ void setSelection(int start, int length);
++
++ inline QString selectedText() const { return hasSelectedText() ? m_text.mid(m_selstart, m_selend - m_selstart) : QString(); }
++ QString textBeforeSelection() const { return hasSelectedText() ? m_text.left(m_selstart) : QString(); }
++ QString textAfterSelection() const { return hasSelectedText() ? m_text.mid(m_selend) : QString(); }
++
++ int selectionStart() const { return hasSelectedText() ? m_selstart : -1; }
++ int selectionEnd() const { return hasSelectedText() ? m_selend : -1; }
++
++ QRectF anchorRectangle() const;
++
++ int positionAt(qreal x, qreal y, QTextLine::CursorPosition position) const;
++ int positionAt(const QPointF &point, QTextLine::CursorPosition position = QTextLine::CursorBetweenCharacters) const {
++ return positionAt(point.x(), point.y(), position);
++ }
++
++ void removeSelection()
++ {
++ int priorState = m_undoState;
++ removeSelectedText();
++ finishChange(priorState);
++ }
++
++ int start() const { return 0; }
++ int end() const { return m_text.size(); }
++
++ QString realText() const;
++
++ #if QT_CONFIG(clipboard)
++ void copy(QClipboard::Mode mode = QClipboard::Clipboard) const;
++ void paste(QClipboard::Mode mode = QClipboard::Clipboard);
++ #endif
++
++ #if QT_CONFIG(im)
++ void commitPreedit();
++ void cancelPreedit();
++ #endif
++
++ Qt::CursorMoveStyle cursorMoveStyle() const { return m_textLayout.cursorMoveStyle(); }
++ void setCursorMoveStyle(Qt::CursorMoveStyle style) { m_textLayout.setCursorMoveStyle(style); }
++
++ void moveCursor(int pos, bool mark = false);
++ void cursorForward(bool mark, int steps)
++ {
++ int c = m_cursor;
++ if (steps > 0) {
++ while (steps--)
++ c = cursorMoveStyle() == Qt::VisualMoveStyle ? m_textLayout.rightCursorPosition(c)
++ : m_textLayout.nextCursorPosition(c);
++ } else if (steps < 0) {
++ while (steps++)
++ c = cursorMoveStyle() == Qt::VisualMoveStyle ? m_textLayout.leftCursorPosition(c)
++ : m_textLayout.previousCursorPosition(c);
++ }
++ moveCursor(c, mark);
++ }
++
++ void cursorWordForward(bool mark) { moveCursor(m_textLayout.nextCursorPosition(m_cursor, QTextLayout::SkipWords), mark); }
++ void cursorWordBackward(bool mark) { moveCursor(m_textLayout.previousCursorPosition(m_cursor, QTextLayout::SkipWords), mark); }
++
++ void home(bool mark) { moveCursor(0, mark); }
++ void end(bool mark) { moveCursor(q_func()->text().size(), mark); }
++
++ void backspace();
++ void del();
++ void deselect() { internalDeselect(); finishChange(); }
++ void selectAll() { m_selstart = m_selend = m_cursor = 0; moveCursor(m_text.size(), true); }
++
++ void insert(const QString &);
++ void clear();
++ void selectWordAtPos(int);
++
++ void setCursorPosition(int pos) { if (pos <= m_text.size()) moveCursor(qMax(0, pos)); }
++
++ bool fixup();
++
++ QString inputMask() const { return m_maskData ? m_inputMask + QLatin1Char(';') + m_blank : QString(); }
++ void setInputMask(const QString &mask)
++ {
++ parseInputMask(mask);
++ if (m_maskData)
++ moveCursor(nextMaskBlank(0));
++ }
++
++ // input methods
++ #if QT_CONFIG(im)
++ bool composeMode() const { return !m_textLayout.preeditAreaText().isEmpty(); }
++
++ QString preeditAreaText() const { return m_textLayout.preeditAreaText(); }
++ #endif
++
++ void updatePasswordEchoEditing(bool editing);
++
++ void cancelPasswordEchoTimer() {
++ m_passwordEchoTimer.stop();
++ }
++
++ Qt::LayoutDirection textDirection() const;
++ Qt::LayoutDirection layoutDirection() const;
++ void setLayoutDirection(Qt::LayoutDirection direction)
++ {
++ if (direction != m_layoutDirection) {
++ m_layoutDirection = direction;
++ updateDisplayText();
++ }
++ }
++
++ #if QT_CONFIG(im)
++ void processInputMethodEvent(QInputMethodEvent *event);
++ #endif
++ void processKeyEvent(QKeyEvent* ev);
++
++ void setBlinkingCursorEnabled(bool enable);
++ void updateCursorBlinking();
++
++ void updateLayout();
++ void updateBaselineOffset();
++
++ qreal calculateImplicitWidthForText(const QString &text) const;
++ qreal getImplicitWidth() const override;
++
++ inline qreal padding() const { return extra.isAllocated() ? extra->padding : 0.0; }
++ void setTopPadding(qreal value, bool reset = false);
++ void setLeftPadding(qreal value, bool reset = false);
++ void setRightPadding(qreal value, bool reset = false);
++ void setBottomPadding(qreal value, bool reset = false);
++
++ bool isImplicitResizeEnabled() const;
++ void setImplicitResizeEnabled(bool enabled);
++
++ private:
++ void removeSelectedText();
++ void internalSetText(const QString &txt, int pos = -1, bool edited = true);
++ void updateDisplayText(bool forceUpdate = false);
++
++ void internalInsert(const QString &s);
++ void internalDelete(bool wasBackspace = false);
++ void internalRemove(int pos);
++
++ inline void internalDeselect()
++ {
++ m_selDirty |= (m_selend > m_selstart);
++ m_selstart = m_selend = 0;
++ }
++
++ void internalUndo(int until = -1);
++ void internalRedo();
++ void emitUndoRedoChanged();
++
++ bool emitCursorPositionChanged();
++
++ bool finishChange(int validateFromState = -1, bool update = false, bool edited = true);
++
++ void addCommand(const Command& cmd);
++
++ inline void separate() { m_separator = true; }
++
++ bool separateSelection();
++ void deleteStartOfWord();
++ void deleteEndOfWord();
++ void deleteEndOfLine();
++
++ enum ValidatorState {
++ #if QT_CONFIG(validator)
++ InvalidInput = QValidator::Invalid,
++ IntermediateInput = QValidator::Intermediate,
++ AcceptableInput = QValidator::Acceptable
++ #else
++ InvalidInput,
++ IntermediateInput,
++ AcceptableInput
++ #endif
++ };
++
++ // masking
++ void parseInputMask(const QString &maskFields);
++ bool isValidInput(QChar key, QChar mask) const;
++ ValidatorState hasAcceptableInput(const QString &text) const;
++ void checkIsValid();
++ QString maskString(uint pos, const QString &str, bool clear = false) const;
++ QString clearString(uint pos, uint len) const;
++ QString stripString(const QString &str) const;
++ int findInMask(int pos, bool forward, bool findSeparator, QChar searchChar = QChar()) const;
++ };
++
++ QT_END_NAMESPACE
++
++ #endif // QQUICKTEXTINPUT_P_P_H