Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qwidgetlinecontrol.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
5
6#if QT_CONFIG(itemviews)
7#include "qabstractitemview.h"
8#endif
9#include "qclipboard.h"
10#include <private/qguiapplication_p.h>
11#if QT_CONFIG(completer)
12#include <private/qcompleter_p.h>
13#endif
14#include <qpa/qplatformtheme.h>
15#include <qstylehints.h>
16#if QT_CONFIG(accessibility)
17#include "qaccessible.h"
18#endif
19
20#include "qapplication.h"
21#include "private/qapplication_p.h"
22#if QT_CONFIG(graphicsview)
23#include "qgraphicssceneevent.h"
24#endif
25
26#include "qvalidator.h"
27
28using namespace std::chrono_literals;
29
30QT_BEGIN_NAMESPACE
31
32
33/*!
34 \internal
35
36 Updates the internal text layout. Returns the ascent of the
37 created QTextLine.
38*/
39int QWidgetLineControl::redoTextLayout() const
40{
41 m_textLayout.clearLayout();
42
43 m_textLayout.beginLayout();
44 QTextLine l = m_textLayout.createLine();
45 m_textLayout.endLayout();
46
47 return qRound(l.ascent());
48}
49
50/*!
51 \internal
52
53 Updates the display text based of the current edit text
54 If the text has changed will emit displayTextChanged()
55*/
56void QWidgetLineControl::updateDisplayText(bool forceUpdate)
57{
58 QString orig = m_textLayout.text();
59 QString str;
60 if (m_echoMode == QLineEdit::NoEcho)
61 str = QString::fromLatin1("");
62 else
63 str = m_text;
64
65 if (m_echoMode == QLineEdit::Password) {
66 str.fill(m_passwordCharacter);
67 if (m_passwordEchoTimer.isActive() && m_cursor > 0 && m_cursor <= m_text.size()) {
68 int cursor = m_cursor - 1;
69 QChar uc = m_text.at(cursor);
70 str[cursor] = uc;
71 if (cursor > 0 && uc.isLowSurrogate()) {
72 // second half of a surrogate, check if we have the first half as well,
73 // if yes restore both at once
74 uc = m_text.at(cursor - 1);
75 if (uc.isHighSurrogate())
76 str[cursor - 1] = uc;
77 }
78 }
79 } else if (m_echoMode == QLineEdit::PasswordEchoOnEdit && !m_passwordEchoEditing) {
80 str.fill(m_passwordCharacter);
81 }
82
83 // replace certain non-printable characters with spaces (to avoid
84 // drawing boxes when using fonts that don't have glyphs for such
85 // characters)
86 QChar* uc = str.data();
87 for (int i = 0; i < (int)str.size(); ++i) {
88 if ((uc[i].unicode() < 0x20 && uc[i].unicode() != 0x09)
89 || uc[i] == QChar::LineSeparator
90 || uc[i] == QChar::ParagraphSeparator)
91 uc[i] = QChar(0x0020);
92 }
93
94 m_textLayout.setText(str);
95
96 QTextOption option = m_textLayout.textOption();
97 option.setTextDirection(m_layoutDirection);
98 option.setFlags(QTextOption::IncludeTrailingSpaces);
99 m_textLayout.setTextOption(option);
100
101 m_ascent = redoTextLayout();
102
103 if (str != orig || forceUpdate)
104 emit displayTextChanged(str);
105}
106
107#ifndef QT_NO_CLIPBOARD
108/*!
109 \internal
110
111 Copies the currently selected text into the clipboard using the given
112 \a mode.
113
114 \note If the echo mode is set to a mode other than Normal then copy
115 will not work. This is to prevent using copy as a method of bypassing
116 password features of the line control.
117*/
118void QWidgetLineControl::copy(QClipboard::Mode mode) const
119{
120 QString t = selectedText();
121 if (!t.isEmpty() && m_echoMode == QLineEdit::Normal) {
122 QGuiApplication::clipboard()->setText(t, mode);
123 }
124}
125
126/*!
127 \internal
128
129 Inserts the text stored in the application clipboard into the line
130 control.
131
132 \sa insert()
133*/
134void QWidgetLineControl::paste(QClipboard::Mode clipboardMode)
135{
136 QString clip = QGuiApplication::clipboard()->text(clipboardMode);
137 if (!clip.isEmpty() || hasSelectedText()) {
138 separate(); //make it a separate undo/redo command
139 insert(clip);
140 separate();
141 }
142}
143
144#endif // !QT_NO_CLIPBOARD
145
146/*!
147 \internal
148*/
149void QWidgetLineControl::commitPreedit()
150{
151#ifndef QT_NO_IM
152 if (!composeMode())
153 return;
154
155 QGuiApplication::inputMethod()->commit();
156 if (!composeMode())
157 return;
158
159 m_preeditCursor = 0;
160 setPreeditArea(-1, QString());
161 m_textLayout.clearFormats();
162 updateDisplayText(/*force*/ true);
163#endif
164}
165
166
167/*!
168 \internal
169
170 Handles the behavior for the backspace key or function.
171 Removes the current selection if there is a selection, otherwise
172 removes the character prior to the cursor position.
173
174 \sa del()
175*/
176void QWidgetLineControl::backspace()
177{
178 int priorState = m_undoState;
179 if (hasSelectedText()) {
180 removeSelectedText();
181 } else if (m_cursor) {
182 --m_cursor;
183 if (m_maskData)
184 m_cursor = prevMaskBlank(m_cursor);
185 QChar uc = m_text.at(m_cursor);
186 if (m_cursor > 0 && uc.isLowSurrogate()) {
187 // second half of a surrogate, check if we have the first half as well,
188 // if yes delete both at once
189 uc = m_text.at(m_cursor - 1);
190 if (uc.isHighSurrogate()) {
191 internalDelete(true);
192 --m_cursor;
193 }
194 }
195 internalDelete(true);
196 }
197 finishChange(priorState);
198}
199
200/*!
201 \internal
202
203 Handles the behavior for the delete key or function.
204 Removes the current selection if there is a selection, otherwise
205 removes the character after the cursor position.
206
207 \sa del()
208*/
209void QWidgetLineControl::del()
210{
211 int priorState = m_undoState;
212 if (hasSelectedText()) {
213 removeSelectedText();
214 } else {
215 int n = textLayout()->nextCursorPosition(m_cursor) - m_cursor;
216 while (n--)
217 internalDelete();
218 }
219 finishChange(priorState);
220}
221
222/*!
223 \internal
224
225 Inserts the given \a newText at the current cursor position.
226 If there is any selected text it is removed prior to insertion of
227 the new text.
228*/
229void QWidgetLineControl::insert(const QString &newText)
230{
231 int priorState = m_undoState;
232 removeSelectedText();
233 internalInsert(newText);
234 finishChange(priorState);
235}
236
237/*!
238 \internal
239
240 Clears the line control text.
241*/
242void QWidgetLineControl::clear()
243{
244 int priorState = m_undoState;
245 m_selstart = 0;
246 m_selend = m_text.size();
247 removeSelectedText();
248 separate();
249 finishChange(priorState, /*update*/false, /*edited*/false);
250}
251/*!
252 \internal
253
254 Undoes the previous operation.
255*/
256
257void QWidgetLineControl::undo()
258{
259 // Undo works only for clearing the line when in any of password the modes
260 if (m_echoMode == QLineEdit::Normal) {
261 internalUndo();
262 finishChange(-1, true);
263 } else {
264 cancelPasswordEchoTimer();
265 clear();
266 }
267}
268
269/*!
270 \internal
271
272 Sets \a length characters from the given \a start position as selected.
273 The given \a start position must be within the current text for
274 the line control. If \a length characters cannot be selected, then
275 the selection will extend to the end of the current text.
276*/
277void QWidgetLineControl::setSelection(int start, int length)
278{
279 commitPreedit();
280
281 if (Q_UNLIKELY(start < 0 || start > m_text.size())) {
282 qWarning("QWidgetLineControl::setSelection: Invalid start position");
283 return;
284 }
285
286 if (length > 0) {
287 if (start == m_selstart && start + length == m_selend && m_cursor == m_selend)
288 return;
289 m_selstart = start;
290 m_selend = qMin(start + length, (int)m_text.size());
291 m_cursor = m_selend;
292 } else if (length < 0){
293 if (start == m_selend && start + length == m_selstart && m_cursor == m_selstart)
294 return;
295 m_selstart = qMax(start + length, 0);
296 m_selend = start;
297 m_cursor = m_selstart;
298 } else if (m_selstart != m_selend) {
299 m_selstart = 0;
300 m_selend = 0;
301 m_cursor = start;
302 } else {
303 m_cursor = start;
304 emitCursorPositionChanged();
305 return;
306 }
307 emit selectionChanged();
308 emitCursorPositionChanged();
309}
310
311void QWidgetLineControl::_q_deleteSelected()
312{
313 if (!hasSelectedText())
314 return;
315
316 int priorState = m_undoState;
317 emit resetInputContext();
318 removeSelectedText();
319 separate();
320 finishChange(priorState);
321}
322
323/*!
324 \internal
325
326 Initializes the line control with a starting text value of \a txt.
327*/
328void QWidgetLineControl::init(const QString &txt)
329{
330 m_textLayout.setCacheEnabled(true);
331 m_text = txt;
332 updateDisplayText();
333 m_cursor = m_text.size();
334 if (const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme()) {
335 m_keyboardScheme = theme->themeHint(QPlatformTheme::KeyboardScheme).toInt();
336 m_passwordMaskDelay = theme->themeHint(QPlatformTheme::PasswordMaskDelay).toInt();
337 }
338 // Generalize for X11
339 if (m_keyboardScheme == QPlatformTheme::KdeKeyboardScheme
340 || m_keyboardScheme == QPlatformTheme::GnomeKeyboardScheme
341 || m_keyboardScheme == QPlatformTheme::CdeKeyboardScheme) {
342 m_keyboardScheme = QPlatformTheme::X11KeyboardScheme;
343 }
344}
345
346/*!
347 \internal
348
349 Sets the password echo editing to \a editing. If password echo editing
350 is true, then the text of the password is displayed even if the echo
351 mode is set to QLineEdit::PasswordEchoOnEdit. Password echoing editing
352 does not affect other echo modes.
353*/
354void QWidgetLineControl::updatePasswordEchoEditing(bool editing)
355{
356 cancelPasswordEchoTimer();
357 m_passwordEchoEditing = editing;
358 updateDisplayText();
359}
360
361/*!
362 \internal
363
364 Returns the cursor position of the given \a x pixel value in relation
365 to the displayed text. The given \a betweenOrOn specified what kind
366 of cursor position is requested.
367*/
368int QWidgetLineControl::xToPos(int x, QTextLine::CursorPosition betweenOrOn) const
369{
370 return textLayout()->lineAt(0).xToCursor(x, betweenOrOn);
371}
372
373/*!
374 \internal
375
376 Returns the bounds of the given text position.
377*/
378QRect QWidgetLineControl::rectForPos(int pos) const
379{
380 QTextLine l = textLayout()->lineAt(0);
381 if (m_preeditCursor != -1)
382 pos += m_preeditCursor;
383 int cix = qRound(l.cursorToX(pos));
384 int w = m_cursorWidth;
385 int ch = l.height() + 1;
386
387 return QRect(cix - 5, 0, w + 9, ch);
388}
389
390/*!
391 \internal
392
393 Returns the bounds of the current cursor, as defined as a
394 between characters cursor.
395*/
396QRect QWidgetLineControl::cursorRect() const
397{
398 return rectForPos(m_cursor);
399}
400
401/*!
402 \internal
403
404 Returns the bounds of the current anchor
405*/
406QRect QWidgetLineControl::anchorRect() const
407{
408 if (!hasSelectedText())
409 return cursorRect();
410 return rectForPos(m_cursor == m_selstart ? m_selend : m_selstart);
411}
412
413/*!
414 \internal
415
416 Fixes the current text so that it is valid given any set validators.
417
418 Returns \c true if the text was changed. Otherwise returns \c false.
419*/
420bool QWidgetLineControl::fixup() // this function assumes that validate currently returns != Acceptable
421{
422#ifndef QT_NO_VALIDATOR
423 if (m_validator) {
424 QString textCopy = m_text;
425 int cursorCopy = m_cursor;
426 m_validator->fixup(textCopy);
427 if (m_validator->validate(textCopy, cursorCopy) == QValidator::Acceptable) {
428 if (textCopy != m_text || cursorCopy != m_cursor)
429 internalSetText(textCopy, cursorCopy, false);
430 return true;
431 }
432 }
433#endif
434 return false;
435}
436
437/*!
438 \internal
439
440 Moves the cursor to the given position \a pos. If \a mark is true will
441 adjust the currently selected text.
442*/
443void QWidgetLineControl::moveCursor(int pos, bool mark)
444{
445 commitPreedit();
446
447 if (pos != m_cursor) {
448 separate();
449 if (m_maskData)
450 pos = pos > m_cursor ? nextMaskBlank(pos) : prevMaskBlank(pos);
451 }
452 if (mark) {
453 int anchor;
454 if (m_selend > m_selstart && m_cursor == m_selstart)
455 anchor = m_selend;
456 else if (m_selend > m_selstart && m_cursor == m_selend)
457 anchor = m_selstart;
458 else
459 anchor = m_cursor;
460 m_selstart = qMin(anchor, pos);
461 m_selend = qMax(anchor, pos);
462 updateDisplayText();
463 } else {
464 internalDeselect();
465 }
466 m_cursor = pos;
467 if (mark || m_selDirty) {
468 m_selDirty = false;
469 emit selectionChanged();
470 }
471 emitCursorPositionChanged();
472}
473
474/*!
475 \internal
476
477 Applies the given input method event \a event to the text of the line
478 control
479*/
480void QWidgetLineControl::processInputMethodEvent(QInputMethodEvent *event)
481{
482 int priorState = -1;
483 bool isGettingInput = !event->commitString().isEmpty()
484 || event->preeditString() != preeditAreaText()
485 || event->replacementLength() > 0;
486 bool cursorPositionChanged = false;
487 bool selectionChange = false;
488
489 if (isGettingInput) {
490 // If any text is being input, remove selected text.
491 priorState = m_undoState;
492 if (echoMode() == QLineEdit::PasswordEchoOnEdit && !passwordEchoEditing()) {
493 updatePasswordEchoEditing(true);
494 m_selstart = 0;
495 m_selend = m_text.size();
496 }
497 removeSelectedText();
498 }
499
500 int c = m_cursor; // cursor position after insertion of commit string
501 if (event->replacementStart() <= 0)
502 c += event->commitString().size() - qMin(-event->replacementStart(), event->replacementLength());
503
504 m_cursor += event->replacementStart();
505 if (m_cursor < 0)
506 m_cursor = 0;
507
508 // insert commit string
509 if (event->replacementLength()) {
510 m_selstart = m_cursor;
511 m_selend = m_selstart + event->replacementLength();
512 removeSelectedText();
513 }
514 if (!event->commitString().isEmpty()) {
515 internalInsert(event->commitString());
516 cursorPositionChanged = true;
517 } else {
518 m_cursor = qBound(0, c, m_text.size());
519 }
520
521 for (int i = 0; i < event->attributes().size(); ++i) {
522 const QInputMethodEvent::Attribute &a = event->attributes().at(i);
523 if (a.type == QInputMethodEvent::Selection) {
524 m_cursor = qBound(0, a.start + a.length, m_text.size());
525 if (a.length) {
526 m_selstart = qMax(0, qMin(a.start, m_text.size()));
527 m_selend = m_cursor;
528 if (m_selend < m_selstart) {
529 qSwap(m_selstart, m_selend);
530 }
531 selectionChange = true;
532 } else {
533 if (m_selstart != m_selend)
534 selectionChange = true;
535 m_selstart = m_selend = 0;
536 }
537 cursorPositionChanged = true;
538 }
539 }
540#ifndef QT_NO_IM
541 // in NoEcho mode, the cursor is always at the beginning of the lineedit
542 switch (m_echoMode) {
543 case QLineEdit::NoEcho:
544 setPreeditArea(0, QString());
545 break;
546 case QLineEdit::Password: {
547 QString preeditString = event->preeditString();
548 preeditString.fill(m_passwordCharacter);
549 setPreeditArea(m_cursor, preeditString);
550 break;
551 }
552 default:
553 setPreeditArea(m_cursor, event->preeditString());
554 break;
555 }
556#endif //QT_NO_IM
557 const int oldPreeditCursor = m_preeditCursor;
558 m_preeditCursor = event->preeditString().size();
559 m_hideCursor = false;
560 QList<QTextLayout::FormatRange> formats;
561 formats.reserve(event->attributes().size());
562 for (int i = 0; i < event->attributes().size(); ++i) {
563 const QInputMethodEvent::Attribute &a = event->attributes().at(i);
564 if (a.type == QInputMethodEvent::Cursor) {
565 m_preeditCursor = a.start;
566 m_hideCursor = !a.length;
567 } else if (a.type == QInputMethodEvent::TextFormat) {
568 QTextCharFormat f = qvariant_cast<QTextFormat>(a.value).toCharFormat();
569 if (f.isValid()) {
570 QTextLayout::FormatRange o;
571 o.start = a.start + m_cursor;
572 o.length = a.length;
573 o.format = f;
574 formats.append(o);
575 }
576 }
577 }
578 m_textLayout.setFormats(formats);
579 updateDisplayText(/*force*/ true);
580 if (cursorPositionChanged)
581 emitCursorPositionChanged();
582 else if (m_preeditCursor != oldPreeditCursor)
583 emit updateMicroFocus();
584
585 if (isGettingInput)
586 finishChange(priorState);
587
588 if (selectionChange)
589 emit selectionChanged();
590}
591
592/*!
593 \internal
594
595 Draws the display text for the line control using the given
596 \a painter, \a clip, and \a offset. Which aspects of the display text
597 are drawn is specified by the given \a flags.
598
599 If the flags contain DrawSelections, then the selection or input mask
600 backgrounds and foregrounds will be applied before drawing the text.
601
602 If the flags contain DrawCursor a cursor of the current cursorWidth()
603 will be drawn after drawing the text.
604
605 The display text will only be drawn if the flags contain DrawText
606*/
607void QWidgetLineControl::draw(QPainter *painter, const QPoint &offset, const QRect &clip, int flags)
608{
609 QList<QTextLayout::FormatRange> selections;
610 if (flags & DrawSelections) {
611 QTextLayout::FormatRange o;
612 if (m_selstart < m_selend) {
613 o.start = m_selstart;
614 o.length = m_selend - m_selstart;
615 o.format.setBackground(m_palette.brush(QPalette::Highlight));
616 o.format.setForeground(m_palette.brush(QPalette::HighlightedText));
617 } else {
618 // mask selection
619 if (m_blinkStatus){
620 o.start = m_cursor;
621 o.length = 1;
622 o.format.setBackground(m_palette.brush(QPalette::Text));
623 o.format.setForeground(m_palette.brush(QPalette::Window));
624 }
625 }
626 selections.append(o);
627 }
628
629 if (flags & DrawText)
630 textLayout()->draw(painter, offset, selections, clip);
631
632 if (flags & DrawCursor){
633 int cursor = m_cursor;
634 if (m_preeditCursor != -1)
635 cursor += m_preeditCursor;
636 if (!m_hideCursor && m_blinkStatus)
637 textLayout()->drawCursor(painter, offset, cursor, m_cursorWidth);
638 }
639}
640
641/*!
642 \internal
643
644 Sets the selection to cover the word at the given cursor position.
645 The word boundaries are defined by the behavior of QTextLayout::SkipWords
646 cursor mode.
647*/
648void QWidgetLineControl::selectWordAtPos(int cursor)
649{
650 int next = cursor + 1;
651 if (next > end())
652 --next;
653 int c = textLayout()->previousCursorPosition(next, QTextLayout::SkipWords);
654 moveCursor(c, false);
655 // ## text layout should support end of words.
656 int end = textLayout()->nextCursorPosition(c, QTextLayout::SkipWords);
657 while (end > cursor && m_text[end-1].isSpace())
658 --end;
659 moveCursor(end, true);
660}
661
662/*!
663 \internal
664
665 Completes a change to the line control text. If the change is not valid
666 will undo the line control state back to the given \a validateFromState.
667
668 If \a edited is true and the change is valid, will emit textEdited() in
669 addition to textChanged(). Otherwise only emits textChanged() on a valid
670 change.
671
672 The \a update value is currently unused.
673*/
674bool QWidgetLineControl::finishChange(int validateFromState, bool update, bool edited)
675{
676 Q_UNUSED(update);
677
678 if (m_textDirty) {
679 // do validation
680 bool wasValidInput = m_validInput;
681 m_validInput = true;
682#ifndef QT_NO_VALIDATOR
683 if (m_validator) {
684 QString textCopy = m_text;
685 int cursorCopy = m_cursor;
686 m_validInput = (m_validator->validate(textCopy, cursorCopy) != QValidator::Invalid);
687 if (m_validInput) {
688 if (m_text != textCopy) {
689 internalSetText(textCopy, cursorCopy, edited);
690 return true;
691 }
692 m_cursor = cursorCopy;
693 } else {
694 emit inputRejected();
695 }
696 }
697#endif
698 if (validateFromState >= 0 && wasValidInput && !m_validInput) {
699 if (m_transactions.size())
700 return false;
701 internalUndo(validateFromState);
702 m_history.erase(m_history.begin() + m_undoState, m_history.end());
703 if (m_modifiedState > m_undoState)
704 m_modifiedState = -1;
705 m_validInput = true;
706 m_textDirty = false;
707 }
708 updateDisplayText();
709
710 if (m_textDirty) {
711 m_textDirty = false;
712 QString actualText = text();
713 if (edited)
714 emit textEdited(actualText);
715 emit textChanged(actualText);
716 }
717 }
718 if (m_selDirty) {
719 m_selDirty = false;
720 emit selectionChanged();
721 }
722 if (m_cursor == m_lastCursorPos)
723 emit updateMicroFocus();
724 emitCursorPositionChanged();
725 return true;
726}
727
728/*!
729 \internal
730
731 An internal function for setting the text of the line control.
732*/
733void QWidgetLineControl::internalSetText(const QString &txt, int pos, bool edited)
734{
735 cancelPasswordEchoTimer();
736 internalDeselect();
737 emit resetInputContext();
738 QString oldText = m_text;
739 if (m_maskData) {
740 m_text = maskString(0, txt, true);
741 m_text += clearString(m_text.size(), m_maxLength - m_text.size());
742 if (edited && oldText == m_text)
743 emit inputRejected();
744 } else {
745 m_text = txt.isEmpty() ? txt : txt.left(m_maxLength);
746 }
747 m_history.clear();
748 m_modifiedState = m_undoState = 0;
749 m_cursor = (pos < 0 || pos > m_text.size()) ? m_text.size() : pos;
750 m_textDirty = (oldText != m_text);
751 const bool changed = finishChange(-1, true, edited);
752
753#if QT_CONFIG(accessibility)
754 if (changed) {
755 if (oldText.isEmpty()) {
756 QAccessibleTextInsertEvent event(accessibleObject(), 0, txt);
757 event.setCursorPosition(m_cursor);
758 QAccessible::updateAccessibility(&event);
759 } else if (txt.isEmpty()) {
760 QAccessibleTextRemoveEvent event(accessibleObject(), 0, oldText);
761 event.setCursorPosition(m_cursor);
762 QAccessible::updateAccessibility(&event);
763 } else {
764 QAccessibleTextUpdateEvent event(accessibleObject(), 0, oldText, txt);
765 event.setCursorPosition(m_cursor);
766 QAccessible::updateAccessibility(&event);
767 }
768 }
769#else
770 Q_UNUSED(changed);
771#endif
772}
773
774
775/*!
776 \internal
777
778 Adds the given \a command to the undo history
779 of the line control. Does not apply the command.
780*/
781void QWidgetLineControl::addCommand(const Command &cmd)
782{
783 m_history.erase(m_history.begin() + m_undoState, m_history.end());
784
785 if (m_separator && m_undoState && m_history[m_undoState - 1].type != Separator)
786 m_history.push_back(Command(Separator, m_cursor, u'\0', m_selstart, m_selend));
787
788 m_separator = false;
789 m_history.push_back(cmd);
790 m_undoState = int(m_history.size());
791}
792
793/*!
794 \internal
795
796 Inserts the given string \a s into the line
797 control.
798
799 Also adds the appropriate commands into the undo history.
800 This function does not call finishChange(), and may leave the text
801 in an invalid state.
802*/
803void QWidgetLineControl::internalInsert(const QString &s)
804{
805 if (m_echoMode == QLineEdit::Password) {
806 if (m_passwordEchoTimer.isActive())
807 m_passwordEchoTimer.stop();
808 int delay = m_passwordMaskDelay;
809#ifdef QT_BUILD_INTERNAL
810 if (m_passwordMaskDelayOverride >= 0)
811 delay = m_passwordMaskDelayOverride;
812#endif
813
814 if (delay > 0)
815 m_passwordEchoTimer.start(delay * 1ms, this);
816 }
817 if (hasSelectedText())
818 addCommand(Command(SetSelection, m_cursor, u'\0', m_selstart, m_selend));
819 if (m_maskData) {
820 QString ms = maskString(m_cursor, s);
821 if (ms.isEmpty() && !s.isEmpty())
822 emit inputRejected();
823#if QT_CONFIG(accessibility)
824 QAccessibleTextInsertEvent insertEvent(accessibleObject(), m_cursor, ms);
825 QAccessible::updateAccessibility(&insertEvent);
826#endif
827 for (int i = 0; i < (int) ms.size(); ++i) {
828 addCommand (Command(DeleteSelection, m_cursor + i, m_text.at(m_cursor + i), -1, -1));
829 addCommand(Command(Insert, m_cursor + i, ms.at(i), -1, -1));
830 }
831 m_text.replace(m_cursor, ms.size(), ms);
832 m_cursor += ms.size();
833 m_cursor = nextMaskBlank(m_cursor);
834 m_textDirty = true;
835#if QT_CONFIG(accessibility)
836 QAccessibleTextCursorEvent event(accessibleObject(), m_cursor);
837 QAccessible::updateAccessibility(&event);
838#endif
839 } else {
840 int remaining = m_maxLength - m_text.size();
841 if (remaining != 0) {
842#if QT_CONFIG(accessibility)
843 QAccessibleTextInsertEvent insertEvent(accessibleObject(), m_cursor, s);
844 QAccessible::updateAccessibility(&insertEvent);
845#endif
846 m_text.insert(m_cursor, s.left(remaining));
847 for (int i = 0; i < (int) s.left(remaining).size(); ++i)
848 addCommand(Command(Insert, m_cursor++, s.at(i), -1, -1));
849 m_textDirty = true;
850 }
851 if (s.size() > remaining)
852 emit inputRejected();
853 }
854}
855
856/*!
857 \internal
858
859 deletes a single character from the current text. If \a wasBackspace,
860 the character prior to the cursor is removed. Otherwise the character
861 after the cursor is removed.
862
863 Also adds the appropriate commands into the undo history.
864 This function does not call finishChange(), and may leave the text
865 in an invalid state.
866*/
867void QWidgetLineControl::internalDelete(bool wasBackspace)
868{
869 if (m_cursor < (int) m_text.size()) {
870 cancelPasswordEchoTimer();
871 if (hasSelectedText())
872 addCommand(Command(SetSelection, m_cursor, u'\0', m_selstart, m_selend));
873 addCommand(Command((CommandType)((m_maskData ? 2 : 0) + (wasBackspace ? Remove : Delete)),
874 m_cursor, m_text.at(m_cursor), -1, -1));
875#if QT_CONFIG(accessibility)
876 QAccessibleTextRemoveEvent event(accessibleObject(), m_cursor, m_text.at(m_cursor));
877 QAccessible::updateAccessibility(&event);
878#endif
879 if (m_maskData) {
880 m_text.replace(m_cursor, 1, clearString(m_cursor, 1));
881 addCommand(Command(Insert, m_cursor, m_text.at(m_cursor), -1, -1));
882 } else {
883 m_text.remove(m_cursor, 1);
884 }
885 m_textDirty = true;
886 }
887}
888
889/*!
890 \internal
891
892 removes the currently selected text from the line control.
893
894 Also adds the appropriate commands into the undo history.
895 This function does not call finishChange(), and may leave the text
896 in an invalid state.
897*/
898void QWidgetLineControl::removeSelectedText()
899{
900 if (m_selstart < m_selend && m_selend <= (int) m_text.size()) {
901 cancelPasswordEchoTimer();
902 separate();
903 int i ;
904 addCommand(Command(SetSelection, m_cursor, u'\0', m_selstart, m_selend));
905 if (m_selstart <= m_cursor && m_cursor < m_selend) {
906 // cursor is within the selection. Split up the commands
907 // to be able to restore the correct cursor position
908 for (i = m_cursor; i >= m_selstart; --i)
909 addCommand (Command(DeleteSelection, i, m_text.at(i), -1, 1));
910 for (i = m_selend - 1; i > m_cursor; --i)
911 addCommand (Command(DeleteSelection, i - m_cursor + m_selstart - 1, m_text.at(i), -1, -1));
912 } else {
913 for (i = m_selend-1; i >= m_selstart; --i)
914 addCommand (Command(RemoveSelection, i, m_text.at(i), -1, -1));
915 }
916#if QT_CONFIG(accessibility)
917 QAccessibleTextRemoveEvent event(accessibleObject(), m_selstart, m_text.mid(m_selstart, m_selend - m_selstart));
918 QAccessible::updateAccessibility(&event);
919#endif
920 if (m_maskData) {
921 m_text.replace(m_selstart, m_selend - m_selstart, clearString(m_selstart, m_selend - m_selstart));
922 for (int i = 0; i < m_selend - m_selstart; ++i)
923 addCommand(Command(Insert, m_selstart + i, m_text.at(m_selstart + i), -1, -1));
924 } else {
925 m_text.remove(m_selstart, m_selend - m_selstart);
926 }
927 if (m_cursor > m_selstart)
928 m_cursor -= qMin(m_cursor, m_selend) - m_selstart;
929 internalDeselect();
930 m_textDirty = true;
931 }
932}
933
934/*!
935 \internal
936
937 Parses the input mask specified by \a maskFields to generate
938 the mask data used to handle input masks.
939*/
940void QWidgetLineControl::parseInputMask(const QString &maskFields)
941{
942 qsizetype delimiter = maskFields.indexOf(u';');
943 if (maskFields.isEmpty() || delimiter == 0) {
944 if (m_maskData) {
945 m_maskData.reset();
946 m_maxLength = 32767;
947 internalSetText(QString(), -1, false);
948 }
949 return;
950 }
951
952 if (delimiter == -1) {
953 m_blank = u' ';
954 m_inputMask = maskFields;
955 } else {
956 m_inputMask = maskFields.left(delimiter);
957 m_blank = (delimiter + 1 < maskFields.size()) ? maskFields[delimiter + 1] : u' ';
958 }
959
960 // calculate m_maxLength / m_maskData length
961 m_maxLength = 0;
962 bool escaped = false;
963 for (int i=0; i<m_inputMask.size(); i++) {
964 const auto c = m_inputMask.at(i);
965 if (escaped) {
966 ++m_maxLength;
967 escaped = false;
968 continue;
969 }
970
971 if (c == u'\\') {
972 escaped = true;
973 continue;
974 }
975
976 if (c != u'\\' && c != u'!' && c != u'<' && c != u'>' &&
977 c != u'{' && c != u'}' && c != u'[' && c != u']')
978 m_maxLength++;
979 }
980
981 m_maskData = std::make_unique<MaskInputData[]>(m_maxLength);
982
983 MaskInputData::Casemode m = MaskInputData::NoCaseMode;
984 bool s;
985 bool escape = false;
986 int index = 0;
987 for (int i = 0; i < m_inputMask.size(); i++) {
988 const auto c = m_inputMask.at(i);
989 if (escape) {
990 s = true;
991 m_maskData[index].maskChar = c;
992 m_maskData[index].separator = s;
993 m_maskData[index].caseMode = m;
994 index++;
995 escape = false;
996 } else if (c == u'<') {
997 m = MaskInputData::Lower;
998 } else if (c == u'>') {
999 m = MaskInputData::Upper;
1000 } else if (c == u'!') {
1001 m = MaskInputData::NoCaseMode;
1002 } else if (c != u'{' && c != u'}' && c != u'[' && c != u']') {
1003 switch (c.unicode()) {
1004 case 'A':
1005 case 'a':
1006 case 'N':
1007 case 'n':
1008 case 'X':
1009 case 'x':
1010 case '9':
1011 case '0':
1012 case 'D':
1013 case 'd':
1014 case '#':
1015 case 'H':
1016 case 'h':
1017 case 'B':
1018 case 'b':
1019 s = false;
1020 break;
1021 case '\\':
1022 escape = true;
1023 Q_FALLTHROUGH();
1024 default:
1025 s = true;
1026 break;
1027 }
1028
1029 if (!escape) {
1030 m_maskData[index].maskChar = c;
1031 m_maskData[index].separator = s;
1032 m_maskData[index].caseMode = m;
1033 index++;
1034 }
1035 }
1036 }
1037 internalSetText(m_text, -1, false);
1038}
1039
1040
1041/*!
1042 \internal
1043
1044 checks if the key is valid compared to the inputMask
1045*/
1046bool QWidgetLineControl::isValidInput(QChar key, QChar mask) const
1047{
1048 switch (mask.unicode()) {
1049 case 'A':
1050 if (key.isLetter())
1051 return true;
1052 break;
1053 case 'a':
1054 if (key.isLetter() || key == m_blank)
1055 return true;
1056 break;
1057 case 'N':
1058 if (key.isLetterOrNumber())
1059 return true;
1060 break;
1061 case 'n':
1062 if (key.isLetterOrNumber() || key == m_blank)
1063 return true;
1064 break;
1065 case 'X':
1066 if (key.isPrint() && key != m_blank)
1067 return true;
1068 break;
1069 case 'x':
1070 if (key.isPrint() || key == m_blank)
1071 return true;
1072 break;
1073 case '9':
1074 if (key.isNumber())
1075 return true;
1076 break;
1077 case '0':
1078 if (key.isNumber() || key == m_blank)
1079 return true;
1080 break;
1081 case 'D':
1082 if (key.isNumber() && key.digitValue() > 0)
1083 return true;
1084 break;
1085 case 'd':
1086 if ((key.isNumber() && key.digitValue() > 0) || key == m_blank)
1087 return true;
1088 break;
1089 case '#':
1090 if (key.isNumber() || key == u'+' || key == u'-' || key == m_blank)
1091 return true;
1092 break;
1093 case 'B':
1094 if (key == u'0' || key == u'1')
1095 return true;
1096 break;
1097 case 'b':
1098 if (key == u'0' || key == u'1' || key == m_blank)
1099 return true;
1100 break;
1101 case 'H':
1102 if (key.isNumber() || (key >= u'a' && key <= u'f') || (key >= u'A' && key <= u'F'))
1103 return true;
1104 break;
1105 case 'h':
1106 if (key.isNumber() || (key >= u'a' && key <= u'f') || (key >= u'A' && key <= u'F') || key == m_blank)
1107 return true;
1108 break;
1109 default:
1110 break;
1111 }
1112 return false;
1113}
1114
1115/*!
1116 \internal
1117
1118 Returns \c true if the given text \a str is valid for any
1119 validator or input mask set for the line control.
1120
1121 Otherwise returns \c false
1122*/
1123bool QWidgetLineControl::hasAcceptableInput(const QString &str) const
1124{
1125#ifndef QT_NO_VALIDATOR
1126 QString textCopy = str;
1127 int cursorCopy = m_cursor;
1128 if (m_validator && m_validator->validate(textCopy, cursorCopy)
1129 != QValidator::Acceptable)
1130 return false;
1131#endif
1132
1133 if (!m_maskData)
1134 return true;
1135
1136 if (str.size() != m_maxLength)
1137 return false;
1138
1139 for (int i=0; i < m_maxLength; ++i) {
1140 if (m_maskData[i].separator) {
1141 if (str.at(i) != m_maskData[i].maskChar)
1142 return false;
1143 } else {
1144 if (!isValidInput(str.at(i), m_maskData[i].maskChar))
1145 return false;
1146 }
1147 }
1148 return true;
1149}
1150
1151/*!
1152 \internal
1153
1154 Applies the inputMask on \a str starting from position \a pos in the mask. \a clear
1155 specifies from where characters should be gotten when a separator is met in \a str - true means
1156 that blanks will be used, false that previous input is used.
1157 Calling this when no inputMask is set is undefined.
1158*/
1159QString QWidgetLineControl::maskString(int pos, const QString &str, bool clear) const
1160{
1161 if (pos >= m_maxLength)
1162 return QString::fromLatin1("");
1163
1164 QString fill;
1165 fill = clear ? clearString(0, m_maxLength) : m_text;
1166
1167 int strIndex = 0;
1168 QString s = QString::fromLatin1("");
1169 int i = pos;
1170 while (i < m_maxLength) {
1171 if (strIndex < str.size()) {
1172 if (m_maskData[i].separator) {
1173 s += m_maskData[i].maskChar;
1174 if (str[strIndex] == m_maskData[i].maskChar)
1175 strIndex++;
1176 ++i;
1177 } else {
1178 if (isValidInput(str[strIndex], m_maskData[i].maskChar)) {
1179 switch (m_maskData[i].caseMode) {
1180 case MaskInputData::Upper:
1181 s += str[strIndex].toUpper();
1182 break;
1183 case MaskInputData::Lower:
1184 s += str[strIndex].toLower();
1185 break;
1186 default:
1187 s += str[strIndex];
1188 }
1189 ++i;
1190 } else {
1191 // search for separator first
1192 int n = findInMask(i, true, true, str[strIndex]);
1193 if (n != -1) {
1194 if (str.size() != 1 || i == 0 || (i > 0 && (!m_maskData[i-1].separator || m_maskData[i-1].maskChar != str[strIndex]))) {
1195 s += QStringView{fill}.mid(i, n - i + 1);
1196 i = n + 1; // update i to find + 1
1197 }
1198 } else {
1199 // search for valid m_blank if not
1200 n = findInMask(i, true, false, str[strIndex]);
1201 if (n != -1) {
1202 s += QStringView{fill}.mid(i, n - i);
1203 switch (m_maskData[n].caseMode) {
1204 case MaskInputData::Upper:
1205 s += str[strIndex].toUpper();
1206 break;
1207 case MaskInputData::Lower:
1208 s += str[strIndex].toLower();
1209 break;
1210 default:
1211 s += str[strIndex];
1212 }
1213 i = n + 1; // updates i to find + 1
1214 }
1215 }
1216 }
1217 ++strIndex;
1218 }
1219 } else
1220 break;
1221 }
1222
1223 return s;
1224}
1225
1226
1227
1228/*!
1229 \internal
1230
1231 Returns a "cleared" string with only separators and blank chars.
1232 Calling this when no inputMask is set is undefined.
1233*/
1234QString QWidgetLineControl::clearString(int pos, int len) const
1235{
1236 if (pos >= m_maxLength)
1237 return QString();
1238
1239 QString s;
1240 int end = qMin(m_maxLength, pos + len);
1241 for (int i = pos; i < end; ++i)
1242 if (m_maskData[i].separator)
1243 s += m_maskData[i].maskChar;
1244 else
1245 s += m_blank;
1246
1247 return s;
1248}
1249
1250/*!
1251 \internal
1252
1253 Strips blank parts of the input in a QWidgetLineControl when an inputMask is set,
1254 separators are still included. Typically "127.0__.0__.1__" becomes "127.0.0.1".
1255*/
1256QString QWidgetLineControl::stripString(const QString &str) const
1257{
1258 if (!m_maskData)
1259 return str;
1260
1261 QString s;
1262 int end = qMin(m_maxLength, (int)str.size());
1263 for (int i = 0; i < end; ++i)
1264 if (m_maskData[i].separator)
1265 s += m_maskData[i].maskChar;
1266 else
1267 if (str[i] != m_blank)
1268 s += str[i];
1269
1270 return s;
1271}
1272
1273/*!
1274 \internal
1275 searches forward/backward in m_maskData for either a separator or a m_blank
1276*/
1277int QWidgetLineControl::findInMask(int pos, bool forward, bool findSeparator, QChar searchChar) const
1278{
1279 if (pos >= m_maxLength || pos < 0)
1280 return -1;
1281
1282 int end = forward ? m_maxLength : -1;
1283 int step = forward ? 1 : -1;
1284 int i = pos;
1285
1286 while (i != end) {
1287 if (findSeparator) {
1288 if (m_maskData[i].separator && m_maskData[i].maskChar == searchChar)
1289 return i;
1290 } else {
1291 if (!m_maskData[i].separator) {
1292 if (searchChar.isNull())
1293 return i;
1294 else if (isValidInput(searchChar, m_maskData[i].maskChar))
1295 return i;
1296 }
1297 }
1298 i += step;
1299 }
1300 return -1;
1301}
1302
1303void QWidgetLineControl::internalUndo(int until)
1304{
1305 if (!isUndoAvailable())
1306 return;
1307 cancelPasswordEchoTimer();
1308 internalDeselect();
1309
1310 while (m_undoState && m_undoState > until) {
1311 Command& cmd = m_history[--m_undoState];
1312 switch (cmd.type) {
1313 case Insert:
1314 m_text.remove(cmd.pos, 1);
1315 m_cursor = cmd.pos;
1316 break;
1317 case SetSelection:
1318 m_selstart = cmd.selStart;
1319 m_selend = cmd.selEnd;
1320 m_cursor = cmd.pos;
1321 break;
1322 case Remove:
1323 case RemoveSelection:
1324 m_text.insert(cmd.pos, cmd.uc);
1325 m_cursor = cmd.pos + 1;
1326 break;
1327 case Delete:
1328 case DeleteSelection:
1329 m_text.insert(cmd.pos, cmd.uc);
1330 m_cursor = cmd.pos;
1331 break;
1332 case Separator:
1333 continue;
1334 }
1335 if (until < 0 && m_undoState) {
1336 Command& next = m_history[m_undoState-1];
1337 if (next.type != cmd.type && next.type < RemoveSelection
1338 && (cmd.type < RemoveSelection || next.type == Separator))
1339 break;
1340 }
1341 }
1342 m_textDirty = true;
1343 emitCursorPositionChanged();
1344}
1345
1346void QWidgetLineControl::internalRedo()
1347{
1348 if (!isRedoAvailable())
1349 return;
1350 internalDeselect();
1351 while (m_undoState < (int)m_history.size()) {
1352 Command& cmd = m_history[m_undoState++];
1353 switch (cmd.type) {
1354 case Insert:
1355 m_text.insert(cmd.pos, cmd.uc);
1356 m_cursor = cmd.pos + 1;
1357 break;
1358 case SetSelection:
1359 m_selstart = cmd.selStart;
1360 m_selend = cmd.selEnd;
1361 m_cursor = cmd.pos;
1362 break;
1363 case Remove:
1364 case Delete:
1365 case RemoveSelection:
1366 case DeleteSelection:
1367 m_text.remove(cmd.pos, 1);
1368 m_selstart = cmd.selStart;
1369 m_selend = cmd.selEnd;
1370 m_cursor = cmd.pos;
1371 break;
1372 case Separator:
1373 m_selstart = cmd.selStart;
1374 m_selend = cmd.selEnd;
1375 m_cursor = cmd.pos;
1376 break;
1377 }
1378 if (m_undoState < (int)m_history.size()) {
1379 Command& next = m_history[m_undoState];
1380 if (next.type != cmd.type && cmd.type < RemoveSelection && next.type != Separator
1381 && (next.type < RemoveSelection || cmd.type == Separator))
1382 break;
1383 }
1384 }
1385 m_textDirty = true;
1386 emitCursorPositionChanged();
1387}
1388
1389/*!
1390 \internal
1391
1392 If the current cursor position differs from the last emitted cursor
1393 position, emits cursorPositionChanged().
1394*/
1395void QWidgetLineControl::emitCursorPositionChanged()
1396{
1397 if (m_cursor != m_lastCursorPos) {
1398 const int oldLast = m_lastCursorPos;
1399 m_lastCursorPos = m_cursor;
1400 emit cursorPositionChanged(oldLast, m_cursor);
1401#if QT_CONFIG(accessibility)
1402 // otherwise we send a selection update which includes the cursor
1403 if (!hasSelectedText()) {
1404 QAccessibleTextCursorEvent event(accessibleObject(), m_cursor);
1405 QAccessible::updateAccessibility(&event);
1406 }
1407#endif
1408 }
1409}
1410
1411#if QT_CONFIG(completer)
1412// iterating forward(dir=1)/backward(dir=-1) from the
1413// current row based. dir=0 indicates a new completion prefix was set.
1414bool QWidgetLineControl::advanceToEnabledItem(int dir)
1415{
1416 int start = m_completer->currentRow();
1417 if (start == -1)
1418 return false;
1419 int i = start + dir;
1420 if (dir == 0) dir = 1;
1421 do {
1422 if (!m_completer->setCurrentRow(i)) {
1423 if (!m_completer->wrapAround())
1424 break;
1425 i = i > 0 ? 0 : m_completer->completionCount() - 1;
1426 } else {
1427 QModelIndex currentIndex = m_completer->currentIndex();
1428 if (m_completer->completionModel()->flags(currentIndex) & Qt::ItemIsEnabled)
1429 return true;
1430 i += dir;
1431 }
1432 } while (i != start);
1433
1434 m_completer->setCurrentRow(start); // restore
1435 return false;
1436}
1437
1438void QWidgetLineControl::complete(int key)
1439{
1440 if (!m_completer || isReadOnly() || echoMode() != QLineEdit::Normal)
1441 return;
1442
1443 QString text = this->text();
1444 if (m_completer->completionMode() == QCompleter::InlineCompletion) {
1445 if (key == Qt::Key_Backspace)
1446 return;
1447 int n = 0;
1448 if (key == Qt::Key_Up || key == Qt::Key_Down) {
1449 if (textAfterSelection().size())
1450 return;
1451 QString prefix = hasSelectedText() ? textBeforeSelection()
1452 : text;
1453 if (text.compare(m_completer->currentCompletion(), m_completer->caseSensitivity()) != 0
1454 || prefix.compare(m_completer->completionPrefix(), m_completer->caseSensitivity()) != 0) {
1455 m_completer->setCompletionPrefix(prefix);
1456 } else {
1457 n = (key == Qt::Key_Up) ? -1 : +1;
1458 }
1459 } else {
1460 m_completer->setCompletionPrefix(text);
1461 }
1462 if (!advanceToEnabledItem(n))
1463 return;
1464 } else {
1465#ifndef QT_KEYPAD_NAVIGATION
1466 if (text.isEmpty()) {
1467 if (auto *popup = QCompleterPrivate::get(m_completer)->popup)
1468 popup->hide();
1469 return;
1470 }
1471#endif
1472 m_completer->setCompletionPrefix(text);
1473 }
1474
1475 m_completer->complete();
1476}
1477#endif
1478
1479void QWidgetLineControl::setReadOnly(bool enable)
1480{
1481 if (m_readOnly == enable)
1482 return;
1483
1484 m_readOnly = enable;
1485 updateCursorBlinking();
1486}
1487
1488void QWidgetLineControl::setBlinkingCursorEnabled(bool enable)
1489{
1490 if (m_blinkEnabled == enable)
1491 return;
1492
1493 m_blinkEnabled = enable;
1494
1495 if (enable)
1496 connect(QGuiApplication::styleHints(), &QStyleHints::cursorFlashTimeChanged, this, &QWidgetLineControl::updateCursorBlinking);
1497 else
1498 disconnect(QGuiApplication::styleHints(), &QStyleHints::cursorFlashTimeChanged, this, &QWidgetLineControl::updateCursorBlinking);
1499
1500 updateCursorBlinking();
1501}
1502
1503void QWidgetLineControl::updateCursorBlinking()
1504{
1505 m_blinkTimer.stop();
1506
1507 if (m_blinkEnabled && !m_readOnly) {
1508 const auto flashTime = QGuiApplication::styleHints()->cursorFlashTime() * 1ms;
1509 if (flashTime >= 2ms)
1510 m_blinkTimer.start(flashTime / 2, this);
1511 }
1512
1513 m_blinkStatus = 1;
1514 emit updateNeeded(inputMask().isEmpty() ? cursorRect() : QRect());
1515}
1516
1517// This is still used by QDeclarativeTextInput in the qtquick1 repo
1518void QWidgetLineControl::resetCursorBlinkTimer()
1519{
1520 if (!m_blinkEnabled || !m_blinkTimer.isActive())
1521 return;
1522 m_blinkTimer.stop();
1523 const auto flashTime = QGuiApplication::styleHints()->cursorFlashTime() * 1ms;
1524 if (flashTime >= 2ms)
1525 m_blinkTimer.start(flashTime / 2, this);
1526 m_blinkStatus = 1;
1527}
1528
1529void QWidgetLineControl::timerEvent(QTimerEvent *event)
1530{
1531 const auto eventId = event->id();
1532 if (eventId == m_blinkTimer.id()) {
1533 m_blinkStatus = !m_blinkStatus;
1534 emit updateNeeded(inputMask().isEmpty() ? cursorRect() : QRect());
1535 } else if (eventId == m_deleteAllTimer.id()) {
1536 m_deleteAllTimer.stop();
1537 clear();
1538 } else if (eventId == m_tripleClickTimer.id()) {
1539 m_tripleClickTimer.stop();
1540 } else if (eventId == m_passwordEchoTimer.id()) {
1541 m_passwordEchoTimer.stop();
1542 updateDisplayText();
1543 }
1544}
1545
1546#ifndef QT_NO_SHORTCUT
1547void QWidgetLineControl::processShortcutOverrideEvent(QKeyEvent *ke)
1548{
1549 if (ke == QKeySequence::Copy
1550 || ke == QKeySequence::MoveToNextWord
1551 || ke == QKeySequence::MoveToPreviousWord
1552 || ke == QKeySequence::MoveToStartOfLine
1553 || ke == QKeySequence::MoveToEndOfLine
1554 || ke == QKeySequence::MoveToStartOfDocument
1555 || ke == QKeySequence::MoveToEndOfDocument
1556 || ke == QKeySequence::SelectNextWord
1557 || ke == QKeySequence::SelectPreviousWord
1558 || ke == QKeySequence::SelectStartOfLine
1559 || ke == QKeySequence::SelectEndOfLine
1560 || ke == QKeySequence::SelectStartOfBlock
1561 || ke == QKeySequence::SelectEndOfBlock
1562 || ke == QKeySequence::SelectStartOfDocument
1563 || ke == QKeySequence::SelectAll
1564 || ke == QKeySequence::SelectEndOfDocument) {
1565 ke->accept();
1566 } else if (ke == QKeySequence::Paste
1567 || ke == QKeySequence::Cut
1568 || ke == QKeySequence::Redo
1569 || ke == QKeySequence::Undo
1570 || ke == QKeySequence::DeleteCompleteLine) {
1571 if (!isReadOnly())
1572 ke->accept();
1573 } else if (ke->modifiers() == Qt::NoModifier || ke->modifiers() == Qt::ShiftModifier
1574 || ke->modifiers() == Qt::KeypadModifier) {
1575 if (ke->key() < Qt::Key_Escape) {
1576 if (!isReadOnly())
1577 ke->accept();
1578 } else {
1579 switch (ke->key()) {
1580 case Qt::Key_Delete:
1581 case Qt::Key_Backspace:
1582 if (!isReadOnly())
1583 ke->accept();
1584 break;
1585
1586 case Qt::Key_Home:
1587 case Qt::Key_End:
1588 case Qt::Key_Left:
1589 case Qt::Key_Right:
1590 ke->accept();
1591 break;
1592
1593 default:
1594 break;
1595 }
1596 }
1597 }
1598}
1599#endif
1600
1601void QWidgetLineControl::processKeyEvent(QKeyEvent* event)
1602{
1603 bool inlineCompletionAccepted = false;
1604
1605#if QT_CONFIG(completer)
1606 if (m_completer) {
1607 QCompleter::CompletionMode completionMode = m_completer->completionMode();
1608 auto *popup = QCompleterPrivate::get(m_completer)->popup;
1609 if ((completionMode == QCompleter::PopupCompletion
1610 || completionMode == QCompleter::UnfilteredPopupCompletion)
1611 && popup && popup->isVisible()) {
1612 // The following keys are forwarded by the completer to the widget
1613 // Ignoring the events lets the completer provide suitable default behavior
1614 switch (event->key()) {
1615 case Qt::Key_Escape:
1616 event->ignore();
1617 return;
1618 default:
1619 break; // normal key processing
1620 }
1621 } else if (completionMode == QCompleter::InlineCompletion) {
1622 switch (event->key()) {
1623 case Qt::Key_Enter:
1624 case Qt::Key_Return:
1625 case Qt::Key_F4:
1626#ifdef QT_KEYPAD_NAVIGATION
1627 case Qt::Key_Select:
1628 if (!QApplicationPrivate::keypadNavigationEnabled())
1629 break;
1630#endif
1631 if (!m_completer->currentCompletion().isEmpty() && hasSelectedText()
1632 && !m_completer->completionPrefix().isEmpty()
1633 && textAfterSelection().isEmpty()) {
1634 setText(m_completer->currentCompletion());
1635 inlineCompletionAccepted = true;
1636 }
1637 break;
1638 default:
1639 break; // normal key processing
1640 }
1641 }
1642 }
1643#endif // QT_CONFIG(completer)
1644
1645 if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) {
1646 if (hasAcceptableInput() || fixup()) {
1647
1648 QInputMethod *inputMethod = QGuiApplication::inputMethod();
1649 inputMethod->commit();
1650 QWidget *lineEdit = qobject_cast<QWidget *>(parent());
1651 if (!(lineEdit && lineEdit->inputMethodHints() & Qt::ImhMultiLine))
1652 inputMethod->hide();
1653
1654 emit accepted();
1655 emit editingFinished();
1656 }
1657 if (inlineCompletionAccepted)
1658 event->accept();
1659 else
1660 event->ignore();
1661 return;
1662 }
1663
1664 if (echoMode() == QLineEdit::PasswordEchoOnEdit
1665 && !passwordEchoEditing()
1666 && !isReadOnly()
1667 && !event->text().isEmpty()
1668#ifdef QT_KEYPAD_NAVIGATION
1669 && event->key() != Qt::Key_Select
1670 && event->key() != Qt::Key_Up
1671 && event->key() != Qt::Key_Down
1672 && event->key() != Qt::Key_Back
1673#endif
1674 && !(event->modifiers() & Qt::ControlModifier)) {
1675 // Clear the edit and reset to normal echo mode while editing; the
1676 // echo mode switches back when the edit loses focus
1677 // ### resets current content. dubious code; you can
1678 // navigate with keys up, down, back, and select(?), but if you press
1679 // "left" or "right" it clears?
1680 updatePasswordEchoEditing(true);
1681 clear();
1682 }
1683
1684 bool unknown = false;
1685#if QT_CONFIG(shortcut)
1686 bool visual = cursorMoveStyle() == Qt::VisualMoveStyle;
1687#endif
1688
1689 if (false) {
1690 }
1691#ifndef QT_NO_SHORTCUT
1692 else if (event == QKeySequence::Undo) {
1693 if (!isReadOnly())
1694 undo();
1695 }
1696 else if (event == QKeySequence::Redo) {
1697 if (!isReadOnly())
1698 redo();
1699 }
1700 else if (event == QKeySequence::SelectAll) {
1701 selectAll();
1702 }
1703#ifndef QT_NO_CLIPBOARD
1704 else if (event == QKeySequence::Copy) {
1705 copy();
1706 }
1707 else if (event == QKeySequence::Paste) {
1708 if (!isReadOnly()) {
1709 QClipboard::Mode mode = QClipboard::Clipboard;
1710 if (m_keyboardScheme == QPlatformTheme::X11KeyboardScheme
1711 && event->modifiers() == (Qt::CTRL | Qt::SHIFT)
1712 && event->key() == Qt::Key_Insert) {
1713 mode = QClipboard::Selection;
1714 }
1715 paste(mode);
1716 }
1717 }
1718 else if (event == QKeySequence::Cut) {
1719 if (!isReadOnly() && hasSelectedText()) {
1720 copy();
1721 del();
1722 }
1723 }
1724 else if (event == QKeySequence::DeleteEndOfLine) {
1725 if (!isReadOnly()) {
1726 setSelection(cursor(), end());
1727 copy();
1728 del();
1729 }
1730 }
1731#endif //QT_NO_CLIPBOARD
1732 else if (event == QKeySequence::MoveToStartOfLine || event == QKeySequence::MoveToStartOfBlock) {
1733 home(0);
1734 }
1735 else if (event == QKeySequence::MoveToEndOfLine || event == QKeySequence::MoveToEndOfBlock) {
1736 end(0);
1737 }
1738 else if (event == QKeySequence::SelectStartOfLine || event == QKeySequence::SelectStartOfBlock) {
1739 home(1);
1740 }
1741 else if (event == QKeySequence::SelectEndOfLine || event == QKeySequence::SelectEndOfBlock) {
1742 end(1);
1743 }
1744 else if (event == QKeySequence::MoveToNextChar) {
1745#if !QT_CONFIG(completer)
1746 const bool inlineCompletion = false;
1747#else
1748 const bool inlineCompletion = m_completer && m_completer->completionMode() == QCompleter::InlineCompletion;
1749#endif
1750 if (hasSelectedText()
1751 && (m_keyboardScheme != QPlatformTheme::WindowsKeyboardScheme
1752 || inlineCompletion)) {
1753 moveCursor(selectionEnd(), false);
1754 } else {
1755 cursorForward(0, visual ? 1 : (layoutDirection() == Qt::LeftToRight ? 1 : -1));
1756 }
1757 }
1758 else if (event == QKeySequence::SelectNextChar) {
1759 cursorForward(1, visual ? 1 : (layoutDirection() == Qt::LeftToRight ? 1 : -1));
1760 }
1761 else if (event == QKeySequence::MoveToPreviousChar) {
1762#if !QT_CONFIG(completer)
1763 const bool inlineCompletion = false;
1764#else
1765 const bool inlineCompletion = m_completer && m_completer->completionMode() == QCompleter::InlineCompletion;
1766#endif
1767 if (hasSelectedText()
1768 && (m_keyboardScheme != QPlatformTheme::WindowsKeyboardScheme
1769 || inlineCompletion)) {
1770 moveCursor(selectionStart(), false);
1771 } else {
1772 cursorForward(0, visual ? -1 : (layoutDirection() == Qt::LeftToRight ? -1 : 1));
1773 }
1774 }
1775 else if (event == QKeySequence::SelectPreviousChar) {
1776 cursorForward(1, visual ? -1 : (layoutDirection() == Qt::LeftToRight ? -1 : 1));
1777 }
1778 else if (event == QKeySequence::MoveToNextWord) {
1779 if (echoMode() == QLineEdit::Normal)
1780 layoutDirection() == Qt::LeftToRight ? cursorWordForward(0) : cursorWordBackward(0);
1781 else
1782 layoutDirection() == Qt::LeftToRight ? end(0) : home(0);
1783 }
1784 else if (event == QKeySequence::MoveToPreviousWord) {
1785 if (echoMode() == QLineEdit::Normal)
1786 layoutDirection() == Qt::LeftToRight ? cursorWordBackward(0) : cursorWordForward(0);
1787 else if (!isReadOnly()) {
1788 layoutDirection() == Qt::LeftToRight ? home(0) : end(0);
1789 }
1790 }
1791 else if (event == QKeySequence::SelectNextWord) {
1792 if (echoMode() == QLineEdit::Normal)
1793 layoutDirection() == Qt::LeftToRight ? cursorWordForward(1) : cursorWordBackward(1);
1794 else
1795 layoutDirection() == Qt::LeftToRight ? end(1) : home(1);
1796 }
1797 else if (event == QKeySequence::SelectPreviousWord) {
1798 if (echoMode() == QLineEdit::Normal)
1799 layoutDirection() == Qt::LeftToRight ? cursorWordBackward(1) : cursorWordForward(1);
1800 else
1801 layoutDirection() == Qt::LeftToRight ? home(1) : end(1);
1802 }
1803 else if (event == QKeySequence::Delete) {
1804 if (!isReadOnly())
1805 del();
1806 }
1807 else if (event == QKeySequence::DeleteEndOfWord) {
1808 if (!isReadOnly()) {
1809 if (!hasSelectedText())
1810 cursorWordForward(true);
1811
1812 if (hasSelectedText())
1813 del();
1814 }
1815 }
1816 else if (event == QKeySequence::DeleteStartOfWord) {
1817 if (!isReadOnly()) {
1818 if (!hasSelectedText())
1819 cursorWordBackward(true);
1820
1821 if (hasSelectedText())
1822 del();
1823 }
1824 } else if (event == QKeySequence::DeleteCompleteLine) {
1825 if (!isReadOnly()) {
1826 setSelection(0, text().size());
1827#ifndef QT_NO_CLIPBOARD
1828 copy();
1829#endif
1830 del();
1831 }
1832 }
1833#endif // QT_NO_SHORTCUT
1834 else {
1835 bool handled = false;
1836 if (m_keyboardScheme == QPlatformTheme::MacKeyboardScheme
1837 && (event->key() == Qt::Key_Up || event->key() == Qt::Key_Down)) {
1838 Qt::KeyboardModifiers myModifiers = (event->modifiers() & ~Qt::KeypadModifier);
1839 if (myModifiers & Qt::ShiftModifier) {
1840 if (myModifiers == (Qt::ControlModifier|Qt::ShiftModifier)
1841 || myModifiers == (Qt::AltModifier|Qt::ShiftModifier)
1842 || myModifiers == Qt::ShiftModifier) {
1843
1844 event->key() == Qt::Key_Up ? home(1) : end(1);
1845 }
1846 } else {
1847 if ((myModifiers == Qt::ControlModifier
1848 || myModifiers == Qt::AltModifier
1849 || myModifiers == Qt::NoModifier)) {
1850 event->key() == Qt::Key_Up ? home(0) : end(0);
1851 }
1852 }
1853 handled = true;
1854 }
1855 if (event->modifiers() & Qt::ControlModifier) {
1856 switch (event->key()) {
1857 case Qt::Key_Backspace:
1858 if (!isReadOnly()) {
1859 cursorWordBackward(true);
1860 del();
1861 }
1862 break;
1863#if QT_CONFIG(completer)
1864 case Qt::Key_Up:
1865 case Qt::Key_Down:
1866 complete(event->key());
1867 break;
1868#endif
1869 default:
1870 if (!handled)
1871 unknown = true;
1872 }
1873 } else { // ### check for *no* modifier
1874 switch (event->key()) {
1875 case Qt::Key_Backspace:
1876 if (!isReadOnly()) {
1877 backspace();
1878#if QT_CONFIG(completer)
1879 complete(Qt::Key_Backspace);
1880#endif
1881 }
1882 break;
1883#ifdef QT_KEYPAD_NAVIGATION
1884 case Qt::Key_Back:
1885 if (QApplicationPrivate::keypadNavigationEnabled() && !event->isAutoRepeat()
1886 && !isReadOnly()) {
1887 if (text().length() == 0) {
1888 setText(m_cancelText);
1889
1890 if (passwordEchoEditing())
1891 updatePasswordEchoEditing(false);
1892
1893 emit editFocusChange(false);
1894 } else if (!m_deleteAllTimer.isActive()) {
1895 m_deleteAllTimer.start(750ms, this);
1896 }
1897 } else {
1898 unknown = true;
1899 }
1900 break;
1901#endif
1902 default:
1903 if (!handled)
1904 unknown = true;
1905 }
1906 }
1907 }
1908
1909 if (event->key() == Qt::Key_Direction_L || event->key() == Qt::Key_Direction_R) {
1910 setLayoutDirection((event->key() == Qt::Key_Direction_L) ? Qt::LeftToRight : Qt::RightToLeft);
1911 unknown = false;
1912 }
1913
1914 if (unknown
1915 && !isReadOnly()
1916 && isAcceptableInput(event)) {
1917 insert(event->text());
1918#if QT_CONFIG(completer)
1919 complete(event->key());
1920#endif
1921 event->accept();
1922 return;
1923 }
1924
1925 if (unknown) {
1926 event->ignore();
1927 } else {
1928#ifndef QT_NO_CLIPBOARD
1929 if (QApplication::clipboard()->supportsSelection())
1930 copy(QClipboard::Selection);
1931#endif
1932 event->accept();
1933 }
1934}
1935
1936bool QWidgetLineControl::isUndoAvailable() const
1937{
1938 // For security reasons undo is not available in any password mode (NoEcho included)
1939 // with the exception that the user can clear the password with undo.
1940 return !m_readOnly && m_undoState
1941 && (m_echoMode == QLineEdit::Normal || m_history[m_undoState - 1].type == QWidgetLineControl::Insert);
1942}
1943
1944bool QWidgetLineControl::isRedoAvailable() const
1945{
1946 // Same as with undo. Disabled for password modes.
1947 return !m_readOnly
1948 && m_echoMode == QLineEdit::Normal
1949 && m_undoState < int(m_history.size());
1950}
1951
1952QT_END_NAMESPACE
1953
1954#include "moc_qwidgetlinecontrol_p.cpp"