1/****************************************************************************
2**
3** Copyright (C) 2020 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtWidgets module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#ifndef QWIDGETLINECONTROL_P_H
41#define QWIDGETLINECONTROL_P_H
42
43//
44// W A R N I N G
45// -------------
46//
47// This file is not part of the Qt API. It exists purely as an
48// implementation detail. This header file may change from version to
49// version without notice, or even be removed.
50//
51// We mean it.
52//
53
54#include <QtWidgets/private/qtwidgetsglobal_p.h>
55
56#include "private/qwidget_p.h"
57#include "QtWidgets/qlineedit.h"
58#include "QtGui/qtextlayout.h"
59#include "QtWidgets/qstyleoption.h"
60#include "QtCore/qpointer.h"
61#include "QtGui/qclipboard.h"
62#include "QtGui/qinputmethod.h"
63#include "QtCore/qpoint.h"
64#if QT_CONFIG(completer)
65#include "QtWidgets/qcompleter.h"
66#endif
67#include "QtCore/qthread.h"
68#include "QtGui/private/qinputcontrol_p.h"
69
70#include "qplatformdefs.h"
71
72#include <vector>
73
74#ifdef DrawText
75# undef DrawText
76#endif
77
78QT_REQUIRE_CONFIG(lineedit);
79
80QT_BEGIN_NAMESPACE
81
82class Q_WIDGETS_EXPORT QWidgetLineControl : public QInputControl
83{
84 Q_OBJECT
85
86public:
87 QWidgetLineControl(const QString &txt = QString())
88 : QInputControl(LineEdit)
89 , m_cursor(0), m_preeditCursor(0), m_cursorWidth(0), m_layoutDirection(Qt::LayoutDirectionAuto),
90 m_hideCursor(false), m_separator(0), m_readOnly(0),
91 m_dragEnabled(0), m_echoMode(0), m_textDirty(0), m_selDirty(0),
92 m_validInput(1), m_blinkStatus(0), m_blinkEnabled(false), m_blinkTimer(0), m_deleteAllTimer(0),
93 m_ascent(0), m_maxLength(32767), m_lastCursorPos(-1),
94 m_tripleClickTimer(0), m_maskData(nullptr), m_modifiedState(0), m_undoState(0),
95 m_selstart(0), m_selend(0), m_passwordEchoEditing(false)
96 , m_passwordEchoTimer(0)
97 , m_passwordMaskDelay(-1)
98#if defined(QT_BUILD_INTERNAL)
99 , m_passwordMaskDelayOverride(-1)
100#endif
101 , m_keyboardScheme(0)
102 , m_accessibleObject(nullptr)
103 {
104 init(txt);
105 }
106
107 ~QWidgetLineControl()
108 {
109 // If this control is used for password input, we don't want the
110 // password data to stay in the process memory, therefore we need
111 // to zero it out
112 if (m_echoMode != QLineEdit::Normal)
113 m_text.fill(c: '\0');
114
115 delete [] m_maskData;
116 }
117
118 void setAccessibleObject(QObject *object)
119 {
120 Q_ASSERT(object);
121 m_accessibleObject = object;
122 }
123
124 QObject *accessibleObject()
125 {
126 if (m_accessibleObject)
127 return m_accessibleObject;
128 return parent();
129 }
130
131 int nextMaskBlank(int pos)
132 {
133 int c = findInMask(pos, forward: true, findSeparator: false);
134 m_separator |= (c != pos);
135 return (c != -1 ? c : m_maxLength);
136 }
137
138 int prevMaskBlank(int pos)
139 {
140 int c = findInMask(pos, forward: false, findSeparator: false);
141 m_separator |= (c != pos);
142 return (c != -1 ? c : 0);
143 }
144
145 bool isUndoAvailable() const;
146 bool isRedoAvailable() const;
147 void clearUndo() { m_history.clear(); m_modifiedState = m_undoState = 0; }
148
149 bool isModified() const { return m_modifiedState != m_undoState; }
150 void setModified(bool modified) { m_modifiedState = modified ? -1 : m_undoState; }
151
152 bool allSelected() const { return !m_text.isEmpty() && m_selstart == 0 && m_selend == (int)m_text.length(); }
153 bool hasSelectedText() const { return !m_text.isEmpty() && m_selend > m_selstart; }
154
155 int width() const { return qRound(d: m_textLayout.lineAt(i: 0).width()) + 1; }
156 int height() const { return qRound(d: m_textLayout.lineAt(i: 0).height()) + 1; }
157 int ascent() const { return m_ascent; }
158 qreal naturalTextWidth() const { return m_textLayout.lineAt(i: 0).naturalTextWidth(); }
159
160 void setSelection(int start, int length);
161
162 inline QString selectedText() const { return hasSelectedText() ? m_text.mid(position: m_selstart, n: m_selend - m_selstart) : QString(); }
163 QString textBeforeSelection() const { return hasSelectedText() ? m_text.left(n: m_selstart) : QString(); }
164 QString textAfterSelection() const { return hasSelectedText() ? m_text.mid(position: m_selend) : QString(); }
165
166 int selectionStart() const { return hasSelectedText() ? m_selstart : -1; }
167 int selectionEnd() const { return hasSelectedText() ? m_selend : -1; }
168 bool inSelection(int x) const
169 {
170 if (m_selstart >= m_selend)
171 return false;
172 int pos = xToPos(x, QTextLine::CursorOnCharacter);
173 return pos >= m_selstart && pos < m_selend;
174 }
175
176 void removeSelection()
177 {
178 int priorState = m_undoState;
179 removeSelectedText();
180 finishChange(validateFromState: priorState);
181 }
182
183 int start() const { return 0; }
184 int end() const { return m_text.length(); }
185
186#ifndef QT_NO_CLIPBOARD
187 void copy(QClipboard::Mode mode = QClipboard::Clipboard) const;
188 void paste(QClipboard::Mode mode = QClipboard::Clipboard);
189#endif
190
191 int cursor() const{ return m_cursor; }
192 int preeditCursor() const { return m_preeditCursor; }
193
194 int cursorWidth() const { return m_cursorWidth; }
195 void setCursorWidth(int value) { m_cursorWidth = value; }
196
197 Qt::CursorMoveStyle cursorMoveStyle() const { return m_textLayout.cursorMoveStyle(); }
198 void setCursorMoveStyle(Qt::CursorMoveStyle style) { m_textLayout.setCursorMoveStyle(style); }
199
200 void moveCursor(int pos, bool mark = false);
201 void cursorForward(bool mark, int steps)
202 {
203 int c = m_cursor;
204 if (steps > 0) {
205 while (steps--)
206 c = cursorMoveStyle() == Qt::VisualMoveStyle ? m_textLayout.rightCursorPosition(oldPos: c)
207 : m_textLayout.nextCursorPosition(oldPos: c);
208 } else if (steps < 0) {
209 while (steps++)
210 c = cursorMoveStyle() == Qt::VisualMoveStyle ? m_textLayout.leftCursorPosition(oldPos: c)
211 : m_textLayout.previousCursorPosition(oldPos: c);
212 }
213 moveCursor(pos: c, mark);
214 }
215
216 void cursorWordForward(bool mark) { moveCursor(pos: m_textLayout.nextCursorPosition(oldPos: m_cursor, mode: QTextLayout::SkipWords), mark); }
217 void cursorWordBackward(bool mark) { moveCursor(pos: m_textLayout.previousCursorPosition(oldPos: m_cursor, mode: QTextLayout::SkipWords), mark); }
218
219 void home(bool mark) { moveCursor(pos: 0, mark); }
220 void end(bool mark) { moveCursor(pos: m_text.length(), mark); }
221
222 int xToPos(int x, QTextLine::CursorPosition = QTextLine::CursorBetweenCharacters) const;
223 QRect rectForPos(int pos) const;
224 QRect cursorRect() const;
225 QRect anchorRect() const;
226
227 qreal cursorToX(int cursor) const { return m_textLayout.lineAt(i: 0).cursorToX(cursorPos: cursor); }
228 qreal cursorToX() const
229 {
230 int cursor = m_cursor;
231 if (m_preeditCursor != -1)
232 cursor += m_preeditCursor;
233 return cursorToX(cursor);
234 }
235
236 bool isReadOnly() const { return m_readOnly; }
237 void setReadOnly(bool enable);
238
239 QString text() const
240 {
241 QString content = m_text;
242 QString res = m_maskData ? stripString(str: content) : content;
243 return (res.isNull() ? QString::fromLatin1(str: "") : res);
244 }
245 void setText(const QString &txt)
246 {
247#ifndef QT_NO_IM
248 if (composeMode())
249 QGuiApplication::inputMethod()->reset();
250#endif
251 internalSetText(txt, pos: -1, edited: false);
252 }
253 void commitPreedit();
254
255 QString displayText() const { return m_textLayout.text(); }
256
257 QString surroundingText() const
258 {
259 return m_text.isNull() ? QString::fromLatin1(str: "") : m_text;
260 }
261
262 void backspace();
263 void del();
264 void deselect() { internalDeselect(); finishChange(); }
265 void selectAll() { m_selstart = m_selend = m_cursor = 0; moveCursor(pos: m_text.length(), mark: true); }
266
267 void insert(const QString &);
268 void clear();
269 void undo();
270 void redo() { internalRedo(); finishChange(); }
271 void selectWordAtPos(int);
272
273 uint echoMode() const { return m_echoMode; }
274 void setEchoMode(uint mode)
275 {
276 cancelPasswordEchoTimer();
277 m_echoMode = mode;
278 m_passwordEchoEditing = false;
279
280 // If this control is used for password input, we want to minimize
281 // the possibility of string reallocation not to leak (parts of)
282 // the password.
283 if (m_echoMode != QLineEdit::Normal)
284 m_text.reserve(asize: 30);
285
286 updateDisplayText();
287 }
288
289 int maxLength() const { return m_maxLength; }
290 void setMaxLength(int maxLength)
291 {
292 if (m_maskData)
293 return;
294 m_maxLength = maxLength;
295 setText(m_text);
296 }
297
298#ifndef QT_NO_VALIDATOR
299 const QValidator *validator() const { return m_validator; }
300 void setValidator(const QValidator *v) { m_validator = const_cast<QValidator*>(v); }
301#endif
302
303#if QT_CONFIG(completer)
304 QCompleter *completer() const { return m_completer; }
305 /* Note that you must set the widget for the completer separately */
306 void setCompleter(const QCompleter *c) { m_completer = const_cast<QCompleter*>(c); }
307 void complete(int key);
308#endif
309
310 int cursorPosition() const { return m_cursor; }
311 void setCursorPosition(int pos) { if (pos <= m_text.length()) moveCursor(pos: qMax(a: 0, b: pos)); }
312
313 bool hasAcceptableInput() const { return hasAcceptableInput(text: m_text); }
314 bool fixup();
315
316 QString inputMask() const
317 {
318 QString mask;
319 if (m_maskData) {
320 mask = m_inputMask;
321 if (m_blank != QLatin1Char(' ')) {
322 mask += QLatin1Char(';');
323 mask += m_blank;
324 }
325 }
326 return mask;
327 }
328 void setInputMask(const QString &mask)
329 {
330 parseInputMask(maskFields: mask);
331 if (m_maskData)
332 moveCursor(pos: nextMaskBlank(pos: 0));
333 }
334
335 // input methods
336#ifndef QT_NO_IM
337 bool composeMode() const { return !m_textLayout.preeditAreaText().isEmpty(); }
338 void setPreeditArea(int cursor, const QString &text) { m_textLayout.setPreeditArea(position: cursor, text); }
339#endif
340
341 QString preeditAreaText() const { return m_textLayout.preeditAreaText(); }
342
343 void updatePasswordEchoEditing(bool editing);
344 bool passwordEchoEditing() const {
345 if (m_passwordEchoTimer != 0)
346 return true;
347 return m_passwordEchoEditing ;
348 }
349
350 QChar passwordCharacter() const { return m_passwordCharacter; }
351 void setPasswordCharacter(QChar character) { m_passwordCharacter = character; updateDisplayText(); }
352
353 int passwordMaskDelay() const { return m_passwordMaskDelay; }
354 void setPasswordMaskDelay(int delay) { m_passwordMaskDelay = delay; }
355
356 Qt::LayoutDirection layoutDirection() const {
357 if (m_layoutDirection == Qt::LayoutDirectionAuto && !m_text.isEmpty())
358 return m_text.isRightToLeft() ? Qt::RightToLeft : Qt::LeftToRight;
359 return m_layoutDirection;
360 }
361 void setLayoutDirection(Qt::LayoutDirection direction)
362 {
363 if (direction != m_layoutDirection) {
364 m_layoutDirection = direction;
365 updateDisplayText();
366 }
367 }
368
369 void setFont(const QFont &font) { m_textLayout.setFont(font); updateDisplayText(); }
370
371 void processInputMethodEvent(QInputMethodEvent *event);
372 void processKeyEvent(QKeyEvent* ev);
373
374 void setBlinkingCursorEnabled(bool enable);
375 void updateCursorBlinking();
376 void resetCursorBlinkTimer();
377
378 bool cursorBlinkStatus() const { return m_blinkStatus; }
379
380 QString cancelText() const { return m_cancelText; }
381 void setCancelText(const QString &text) { m_cancelText = text; }
382
383 const QPalette &palette() const { return m_palette; }
384 void setPalette(const QPalette &p) { m_palette = p; }
385
386 enum DrawFlags {
387 DrawText = 0x01,
388 DrawSelections = 0x02,
389 DrawCursor = 0x04,
390 DrawAll = DrawText | DrawSelections | DrawCursor
391 };
392 void draw(QPainter *, const QPoint &, const QRect &, int flags = DrawAll);
393
394#ifndef QT_NO_SHORTCUT
395 void processShortcutOverrideEvent(QKeyEvent *ke);
396#endif
397
398 QTextLayout *textLayout() const
399 {
400 return &m_textLayout;
401 }
402
403private:
404 void init(const QString &txt);
405 void removeSelectedText();
406 void internalSetText(const QString &txt, int pos = -1, bool edited = true);
407 void updateDisplayText(bool forceUpdate = false);
408
409 void internalInsert(const QString &s);
410 void internalDelete(bool wasBackspace = false);
411 void internalRemove(int pos);
412
413 inline void internalDeselect()
414 {
415 m_selDirty |= (m_selend > m_selstart);
416 m_selstart = m_selend = 0;
417 }
418
419 void internalUndo(int until = -1);
420 void internalRedo();
421
422 QString m_text;
423 QPalette m_palette;
424 int m_cursor;
425 int m_preeditCursor;
426 int m_cursorWidth;
427 Qt::LayoutDirection m_layoutDirection;
428 uint m_hideCursor : 1; // used to hide the m_cursor inside preedit areas
429 uint m_separator : 1;
430 uint m_readOnly : 1;
431 uint m_dragEnabled : 1;
432 uint m_echoMode : 2;
433 uint m_textDirty : 1;
434 uint m_selDirty : 1;
435 uint m_validInput : 1;
436 uint m_blinkStatus : 1;
437 uint m_blinkEnabled : 1;
438 int m_blinkTimer;
439 int m_deleteAllTimer;
440 int m_ascent;
441 int m_maxLength;
442 int m_lastCursorPos;
443 QList<int> m_transactions;
444 QPoint m_tripleClick;
445 int m_tripleClickTimer;
446 QString m_cancelText;
447
448 void emitCursorPositionChanged();
449
450 bool finishChange(int validateFromState = -1, bool update = false, bool edited = true);
451
452#ifndef QT_NO_VALIDATOR
453 QPointer<QValidator> m_validator;
454#endif
455 QPointer<QCompleter> m_completer;
456#if QT_CONFIG(completer)
457 bool advanceToEnabledItem(int dir);
458#endif
459
460 struct MaskInputData {
461 enum Casemode { NoCaseMode, Upper, Lower };
462 QChar maskChar; // either the separator char or the inputmask
463 bool separator;
464 Casemode caseMode;
465 };
466 QString m_inputMask;
467 QChar m_blank;
468 MaskInputData *m_maskData;
469
470 // undo/redo handling
471 enum CommandType { Separator, Insert, Remove, Delete, RemoveSelection, DeleteSelection, SetSelection };
472 struct Command {
473 inline Command(CommandType t, int p, QChar c, int ss, int se) : type(t),uc(c),pos(p),selStart(ss),selEnd(se) {}
474 uint type : 4;
475 QChar uc;
476 int pos, selStart, selEnd;
477 };
478 int m_modifiedState;
479 int m_undoState;
480 std::vector<Command> m_history;
481 void addCommand(const Command& cmd);
482
483 inline void separate() { m_separator = true; }
484
485 // selection
486 int m_selstart;
487 int m_selend;
488
489 // masking
490 void parseInputMask(const QString &maskFields);
491 bool isValidInput(QChar key, QChar mask) const;
492 bool hasAcceptableInput(const QString &text) const;
493 QString maskString(int pos, const QString &str, bool clear = false) const;
494 QString clearString(int pos, int len) const;
495 QString stripString(const QString &str) const;
496 int findInMask(int pos, bool forward, bool findSeparator, QChar searchChar = QChar()) const;
497
498 // complex text layout (must be mutable so it can be reshaped at will)
499 mutable QTextLayout m_textLayout;
500
501 bool m_passwordEchoEditing;
502 QChar m_passwordCharacter;
503 int m_passwordEchoTimer;
504 int m_passwordMaskDelay;
505 void cancelPasswordEchoTimer()
506 {
507 if (m_passwordEchoTimer != 0) {
508 killTimer(id: m_passwordEchoTimer);
509 m_passwordEchoTimer = 0;
510 }
511 }
512
513 int redoTextLayout() const;
514
515public:
516#if defined(QT_BUILD_INTERNAL)
517 int m_passwordMaskDelayOverride;
518#endif
519
520Q_SIGNALS:
521 void cursorPositionChanged(int, int);
522 void selectionChanged();
523
524 void displayTextChanged(const QString &);
525 void textChanged(const QString &);
526 void textEdited(const QString &);
527
528 void resetInputContext();
529 void updateMicroFocus();
530
531 void accepted();
532 void editingFinished();
533 void updateNeeded(const QRect &);
534 void inputRejected();
535
536#ifdef QT_KEYPAD_NAVIGATION
537 void editFocusChange(bool);
538#endif
539protected:
540 virtual void timerEvent(QTimerEvent *event) override;
541
542private Q_SLOTS:
543 void _q_deleteSelected();
544
545private:
546 int m_keyboardScheme;
547
548 // accessibility events are sent for this object
549 QObject *m_accessibleObject;
550};
551
552QT_END_NAMESPACE
553
554#endif // QWIDGETLINECONTROL_P_H
555

source code of qtbase/src/widgets/widgets/qwidgetlinecontrol_p.h