1/****************************************************************************
2**
3** Copyright (C) 2016 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 0 // Used to be included in Qt4 for Q_WS_MAC
99 , m_threadChecks(false)
100 , m_textLayoutThread(0)
101 #endif
102#if defined(QT_BUILD_INTERNAL)
103 , m_passwordMaskDelayOverride(-1)
104#endif
105 , m_keyboardScheme(0)
106 , m_accessibleObject(nullptr)
107 {
108 init(txt);
109 }
110
111 ~QWidgetLineControl()
112 {
113 // If this control is used for password input, we don't want the
114 // password data to stay in the process memory, therefore we need
115 // to zero it out
116 if (m_echoMode != QLineEdit::Normal)
117 m_text.fill('\0');
118
119 delete [] m_maskData;
120 }
121
122 void setAccessibleObject(QObject *object)
123 {
124 Q_ASSERT(object);
125 m_accessibleObject = object;
126 }
127
128 QObject *accessibleObject()
129 {
130 if (m_accessibleObject)
131 return m_accessibleObject;
132 return parent();
133 }
134
135 int nextMaskBlank(int pos)
136 {
137 int c = findInMask(pos, true, false);
138 m_separator |= (c != pos);
139 return (c != -1 ? c : m_maxLength);
140 }
141
142 int prevMaskBlank(int pos)
143 {
144 int c = findInMask(pos, false, false);
145 m_separator |= (c != pos);
146 return (c != -1 ? c : 0);
147 }
148
149 bool isUndoAvailable() const;
150 bool isRedoAvailable() const;
151 void clearUndo() { m_history.clear(); m_modifiedState = m_undoState = 0; }
152
153 bool isModified() const { return m_modifiedState != m_undoState; }
154 void setModified(bool modified) { m_modifiedState = modified ? -1 : m_undoState; }
155
156 bool allSelected() const { return !m_text.isEmpty() && m_selstart == 0 && m_selend == (int)m_text.length(); }
157 bool hasSelectedText() const { return !m_text.isEmpty() && m_selend > m_selstart; }
158
159 int width() const { return qRound(m_textLayout.lineAt(0).width()) + 1; }
160 int height() const { return qRound(m_textLayout.lineAt(0).height()) + 1; }
161 int ascent() const { return m_ascent; }
162 qreal naturalTextWidth() const { return m_textLayout.lineAt(0).naturalTextWidth(); }
163
164 void setSelection(int start, int length);
165
166 inline QString selectedText() const { return hasSelectedText() ? m_text.mid(m_selstart, m_selend - m_selstart) : QString(); }
167 QString textBeforeSelection() const { return hasSelectedText() ? m_text.left(m_selstart) : QString(); }
168 QString textAfterSelection() const { return hasSelectedText() ? m_text.mid(m_selend) : QString(); }
169
170 int selectionStart() const { return hasSelectedText() ? m_selstart : -1; }
171 int selectionEnd() const { return hasSelectedText() ? m_selend : -1; }
172 bool inSelection(int x) const
173 {
174 if (m_selstart >= m_selend)
175 return false;
176 int pos = xToPos(x, QTextLine::CursorOnCharacter);
177 return pos >= m_selstart && pos < m_selend;
178 }
179
180 void removeSelection()
181 {
182 int priorState = m_undoState;
183 removeSelectedText();
184 finishChange(priorState);
185 }
186
187 int start() const { return 0; }
188 int end() const { return m_text.length(); }
189
190#ifndef QT_NO_CLIPBOARD
191 void copy(QClipboard::Mode mode = QClipboard::Clipboard) const;
192 void paste(QClipboard::Mode mode = QClipboard::Clipboard);
193#endif
194
195 int cursor() const{ return m_cursor; }
196 int preeditCursor() const { return m_preeditCursor; }
197
198 int cursorWidth() const { return m_cursorWidth; }
199 void setCursorWidth(int value) { m_cursorWidth = value; }
200
201 Qt::CursorMoveStyle cursorMoveStyle() const { return m_textLayout.cursorMoveStyle(); }
202 void setCursorMoveStyle(Qt::CursorMoveStyle style) { m_textLayout.setCursorMoveStyle(style); }
203
204 void moveCursor(int pos, bool mark = false);
205 void cursorForward(bool mark, int steps)
206 {
207 int c = m_cursor;
208 if (steps > 0) {
209 while (steps--)
210 c = cursorMoveStyle() == Qt::VisualMoveStyle ? m_textLayout.rightCursorPosition(c)
211 : m_textLayout.nextCursorPosition(c);
212 } else if (steps < 0) {
213 while (steps++)
214 c = cursorMoveStyle() == Qt::VisualMoveStyle ? m_textLayout.leftCursorPosition(c)
215 : m_textLayout.previousCursorPosition(c);
216 }
217 moveCursor(c, mark);
218 }
219
220 void cursorWordForward(bool mark) { moveCursor(m_textLayout.nextCursorPosition(m_cursor, QTextLayout::SkipWords), mark); }
221 void cursorWordBackward(bool mark) { moveCursor(m_textLayout.previousCursorPosition(m_cursor, QTextLayout::SkipWords), mark); }
222
223 void home(bool mark) { moveCursor(0, mark); }
224 void end(bool mark) { moveCursor(text().length(), mark); }
225
226 int xToPos(int x, QTextLine::CursorPosition = QTextLine::CursorBetweenCharacters) const;
227 QRect rectForPos(int pos) const;
228 QRect cursorRect() const;
229 QRect anchorRect() const;
230
231 qreal cursorToX(int cursor) const { return m_textLayout.lineAt(0).cursorToX(cursor); }
232 qreal cursorToX() const
233 {
234 int cursor = m_cursor;
235 if (m_preeditCursor != -1)
236 cursor += m_preeditCursor;
237 return cursorToX(cursor);
238 }
239
240 bool isReadOnly() const { return m_readOnly; }
241 void setReadOnly(bool enable);
242
243 QString text() const
244 {
245 QString content = m_text;
246 QString res = m_maskData ? stripString(content) : content;
247 return (res.isNull() ? QString::fromLatin1("") : res);
248 }
249 void setText(const QString &txt)
250 {
251#ifndef QT_NO_IM
252 if (composeMode())
253 QGuiApplication::inputMethod()->reset();
254#endif
255 internalSetText(txt, -1, false);
256 }
257 void commitPreedit();
258
259 QString displayText() const { return m_textLayout.text(); }
260
261 QString surroundingText() const
262 {
263 return m_text.isNull() ? QString::fromLatin1("") : m_text;
264 }
265
266 void backspace();
267 void del();
268 void deselect() { internalDeselect(); finishChange(); }
269 void selectAll() { m_selstart = m_selend = m_cursor = 0; moveCursor(m_text.length(), true); }
270
271 void insert(const QString &);
272 void clear();
273 void undo();
274 void redo() { internalRedo(); finishChange(); }
275 void selectWordAtPos(int);
276
277 uint echoMode() const { return m_echoMode; }
278 void setEchoMode(uint mode)
279 {
280 cancelPasswordEchoTimer();
281 m_echoMode = mode;
282 m_passwordEchoEditing = false;
283
284 // If this control is used for password input, we want to minimize
285 // the possibility of string reallocation not to leak (parts of)
286 // the password.
287 if (m_echoMode != QLineEdit::Normal)
288 m_text.reserve(30);
289
290 updateDisplayText();
291 }
292
293 int maxLength() const { return m_maxLength; }
294 void setMaxLength(int maxLength)
295 {
296 if (m_maskData)
297 return;
298 m_maxLength = maxLength;
299 setText(m_text);
300 }
301
302#ifndef QT_NO_VALIDATOR
303 const QValidator *validator() const { return m_validator; }
304 void setValidator(const QValidator *v) { m_validator = const_cast<QValidator*>(v); }
305#endif
306
307#if QT_CONFIG(completer)
308 QCompleter *completer() const { return m_completer; }
309 /* Note that you must set the widget for the completer separately */
310 void setCompleter(const QCompleter *c) { m_completer = const_cast<QCompleter*>(c); }
311 void complete(int key);
312#endif
313
314 int cursorPosition() const { return m_cursor; }
315 void setCursorPosition(int pos) { if (pos <= m_text.length()) moveCursor(qMax(0, pos)); }
316
317 bool hasAcceptableInput() const { return hasAcceptableInput(m_text); }
318 bool fixup();
319
320 QString inputMask() const
321 {
322 QString mask;
323 if (m_maskData) {
324 mask = m_inputMask;
325 if (m_blank != QLatin1Char(' ')) {
326 mask += QLatin1Char(';');
327 mask += m_blank;
328 }
329 }
330 return mask;
331 }
332 void setInputMask(const QString &mask)
333 {
334 parseInputMask(mask);
335 if (m_maskData)
336 moveCursor(nextMaskBlank(0));
337 }
338
339 // input methods
340#ifndef QT_NO_IM
341 bool composeMode() const { return !m_textLayout.preeditAreaText().isEmpty(); }
342 void setPreeditArea(int cursor, const QString &text) { m_textLayout.setPreeditArea(cursor, text); }
343#endif
344
345 QString preeditAreaText() const { return m_textLayout.preeditAreaText(); }
346
347 void updatePasswordEchoEditing(bool editing);
348 bool passwordEchoEditing() const {
349 if (m_passwordEchoTimer != 0)
350 return true;
351 return m_passwordEchoEditing ;
352 }
353
354 QChar passwordCharacter() const { return m_passwordCharacter; }
355 void setPasswordCharacter(QChar character) { m_passwordCharacter = character; updateDisplayText(); }
356
357 int passwordMaskDelay() const { return m_passwordMaskDelay; }
358 void setPasswordMaskDelay(int delay) { m_passwordMaskDelay = delay; }
359
360 Qt::LayoutDirection layoutDirection() const {
361 if (m_layoutDirection == Qt::LayoutDirectionAuto) {
362 if (m_text.isEmpty())
363 return QGuiApplication::inputMethod()->inputDirection();
364 return m_text.isRightToLeft() ? Qt::RightToLeft : Qt::LeftToRight;
365 }
366 return m_layoutDirection;
367 }
368 void setLayoutDirection(Qt::LayoutDirection direction)
369 {
370 if (direction != m_layoutDirection) {
371 m_layoutDirection = direction;
372 updateDisplayText();
373 }
374 }
375
376 void setFont(const QFont &font) { m_textLayout.setFont(font); updateDisplayText(); }
377
378 void processInputMethodEvent(QInputMethodEvent *event);
379 void processKeyEvent(QKeyEvent* ev);
380
381 void setBlinkingCursorEnabled(bool enable);
382 void updateCursorBlinking();
383 void resetCursorBlinkTimer();
384
385 bool cursorBlinkStatus() const { return m_blinkStatus; }
386
387 QString cancelText() const { return m_cancelText; }
388 void setCancelText(const QString &text) { m_cancelText = text; }
389
390 const QPalette &palette() const { return m_palette; }
391 void setPalette(const QPalette &p) { m_palette = p; }
392
393 enum DrawFlags {
394 DrawText = 0x01,
395 DrawSelections = 0x02,
396 DrawCursor = 0x04,
397 DrawAll = DrawText | DrawSelections | DrawCursor
398 };
399 void draw(QPainter *, const QPoint &, const QRect &, int flags = DrawAll);
400
401#ifndef QT_NO_SHORTCUT
402 void processShortcutOverrideEvent(QKeyEvent *ke);
403#endif
404
405 QTextLayout *textLayout() const
406 {
407#if 0 // Used to be included in Qt4 for Q_WS_MAC
408 if (m_threadChecks && QThread::currentThread() != m_textLayoutThread)
409 redoTextLayout();
410#endif
411 return &m_textLayout;
412 }
413
414#if 0 // Used to be included in Qt4 for Q_WS_MAC
415 void setThreadChecks(bool threadChecks)
416 {
417 m_threadChecks = threadChecks;
418 }
419
420 bool threadChecks() const
421 {
422 return m_threadChecks;
423 }
424#endif
425
426private:
427 void init(const QString &txt);
428 void removeSelectedText();
429 void internalSetText(const QString &txt, int pos = -1, bool edited = true);
430 void updateDisplayText(bool forceUpdate = false);
431
432 void internalInsert(const QString &s);
433 void internalDelete(bool wasBackspace = false);
434 void internalRemove(int pos);
435
436 inline void internalDeselect()
437 {
438 m_selDirty |= (m_selend > m_selstart);
439 m_selstart = m_selend = 0;
440 }
441
442 void internalUndo(int until = -1);
443 void internalRedo();
444
445 QString m_text;
446 QPalette m_palette;
447 int m_cursor;
448 int m_preeditCursor;
449 int m_cursorWidth;
450 Qt::LayoutDirection m_layoutDirection;
451 uint m_hideCursor : 1; // used to hide the m_cursor inside preedit areas
452 uint m_separator : 1;
453 uint m_readOnly : 1;
454 uint m_dragEnabled : 1;
455 uint m_echoMode : 2;
456 uint m_textDirty : 1;
457 uint m_selDirty : 1;
458 uint m_validInput : 1;
459 uint m_blinkStatus : 1;
460 uint m_blinkEnabled : 1;
461 int m_blinkTimer;
462 int m_deleteAllTimer;
463 int m_ascent;
464 int m_maxLength;
465 int m_lastCursorPos;
466 QList<int> m_transactions;
467 QPoint m_tripleClick;
468 int m_tripleClickTimer;
469 QString m_cancelText;
470
471 void emitCursorPositionChanged();
472
473 bool finishChange(int validateFromState = -1, bool update = false, bool edited = true);
474
475#ifndef QT_NO_VALIDATOR
476 QPointer<QValidator> m_validator;
477#endif
478 QPointer<QCompleter> m_completer;
479#if QT_CONFIG(completer)
480 bool advanceToEnabledItem(int dir);
481#endif
482
483 struct MaskInputData {
484 enum Casemode { NoCaseMode, Upper, Lower };
485 QChar maskChar; // either the separator char or the inputmask
486 bool separator;
487 Casemode caseMode;
488 };
489 QString m_inputMask;
490 QChar m_blank;
491 MaskInputData *m_maskData;
492
493 // undo/redo handling
494 enum CommandType { Separator, Insert, Remove, Delete, RemoveSelection, DeleteSelection, SetSelection };
495 struct Command {
496 inline Command(CommandType t, int p, QChar c, int ss, int se) : type(t),uc(c),pos(p),selStart(ss),selEnd(se) {}
497 uint type : 4;
498 QChar uc;
499 int pos, selStart, selEnd;
500 };
501 int m_modifiedState;
502 int m_undoState;
503 std::vector<Command> m_history;
504 void addCommand(const Command& cmd);
505
506 inline void separate() { m_separator = true; }
507
508 // selection
509 int m_selstart;
510 int m_selend;
511
512 // masking
513 void parseInputMask(const QString &maskFields);
514 bool isValidInput(QChar key, QChar mask) const;
515 bool hasAcceptableInput(const QString &text) const;
516 QString maskString(int pos, const QString &str, bool clear = false) const;
517 QString clearString(int pos, int len) const;
518 QString stripString(const QString &str) const;
519 int findInMask(int pos, bool forward, bool findSeparator, QChar searchChar = QChar()) const;
520
521 // complex text layout (must be mutable so it can be reshaped at will)
522 mutable QTextLayout m_textLayout;
523
524 bool m_passwordEchoEditing;
525 QChar m_passwordCharacter;
526 int m_passwordEchoTimer;
527 int m_passwordMaskDelay;
528 void cancelPasswordEchoTimer()
529 {
530 if (m_passwordEchoTimer != 0) {
531 killTimer(m_passwordEchoTimer);
532 m_passwordEchoTimer = 0;
533 }
534 }
535
536 int redoTextLayout() const;
537#if 0 // Used to be included in Qt4 for Q_WS_MAC
538 bool m_threadChecks;
539 mutable QThread *m_textLayoutThread;
540#endif
541
542public:
543#if defined(QT_BUILD_INTERNAL)
544 int m_passwordMaskDelayOverride;
545#endif
546
547Q_SIGNALS:
548 void cursorPositionChanged(int, int);
549 void selectionChanged();
550
551 void displayTextChanged(const QString &);
552 void textChanged(const QString &);
553 void textEdited(const QString &);
554
555 void resetInputContext();
556 void updateMicroFocus();
557
558 void accepted();
559 void editingFinished();
560 void updateNeeded(const QRect &);
561 void inputRejected();
562
563#ifdef QT_KEYPAD_NAVIGATION
564 void editFocusChange(bool);
565#endif
566protected:
567 virtual void timerEvent(QTimerEvent *event) override;
568
569private Q_SLOTS:
570 void _q_deleteSelected();
571
572private:
573 int m_keyboardScheme;
574
575 // accessibility events are sent for this object
576 QObject *m_accessibleObject;
577};
578
579QT_END_NAMESPACE
580
581#endif // QWIDGETLINECONTROL_P_H
582