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

source code of qtbase/src/widgets/widgets/qwidgetlinecontrol.cpp