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 
64 QT_BEGIN_NAMESPACE
65 
66 
67 /*!
68    \internal
69 
70    Updates the internal text layout. Returns the ascent of the
71    created QTextLine.
72 */
redoTextLayout() const73 int 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(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 */
updateDisplayText(bool forceUpdate)90 void QWidgetLineControl::updateDisplayText(bool forceUpdate)
91 {
92     QString orig = m_textLayout.text();
93     QString str;
94     if (m_echoMode == QLineEdit::NoEcho)
95         str = QString::fromLatin1("");
96     else
97         str = m_text;
98 
99     if (m_echoMode == QLineEdit::Password) {
100         str.fill(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(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(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(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 */
copy(QClipboard::Mode mode) const153 void 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 */
paste(QClipboard::Mode clipboardMode)169 void QWidgetLineControl::paste(QClipboard::Mode clipboardMode)
170 {
171     QString clip = QGuiApplication::clipboard()->text(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 */
commitPreedit()184 void 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(-1, QString());
196     m_textLayout.clearFormats();
197     updateDisplayText(/*force*/ 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 */
backspace()211 void 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(m_cursor);
220             QChar uc = m_text.at(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(m_cursor - 1);
225                 if (uc.isHighSurrogate()) {
226                     internalDelete(true);
227                     --m_cursor;
228                 }
229             }
230             internalDelete(true);
231     }
232     finishChange(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 */
del()244 void QWidgetLineControl::del()
245 {
246     int priorState = m_undoState;
247     if (hasSelectedText()) {
248         removeSelectedText();
249     } else {
250         int n = textLayout()->nextCursorPosition(m_cursor) - m_cursor;
251         while (n--)
252             internalDelete();
253     }
254     finishChange(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 */
insert(const QString & newText)264 void QWidgetLineControl::insert(const QString &newText)
265 {
266     int priorState = m_undoState;
267     removeSelectedText();
268     internalInsert(newText);
269     finishChange(priorState);
270 }
271 
272 /*!
273     \internal
274 
275     Clears the line control text.
276 */
clear()277 void QWidgetLineControl::clear()
278 {
279     int priorState = m_undoState;
280     m_selstart = 0;
281     m_selend = m_text.length();
282     removeSelectedText();
283     separate();
284     finishChange(priorState, /*update*/false, /*edited*/false);
285 }
286 /*!
287     \internal
288 
289     Undoes the previous operation.
290 */
291 
undo()292 void 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(-1, 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 */
setSelection(int start,int length)312 void QWidgetLineControl::setSelection(int start, int length)
313 {
314     commitPreedit();
315 
316     if (Q_UNLIKELY(start < 0 || start > m_text.size())) {
317         qWarning("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(start + length, (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(start + length, 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 
_q_deleteSelected()346 void QWidgetLineControl::_q_deleteSelected()
347 {
348     if (!hasSelectedText())
349         return;
350 
351     int priorState = m_undoState;
352     emit resetInputContext();
353     removeSelectedText();
354     separate();
355     finishChange(priorState);
356 }
357 
358 /*!
359     \internal
360 
361     Initializes the line control with a starting text value of \a txt.
362 */
init(const QString & txt)363 void 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(QPlatformTheme::KeyboardScheme).toInt();
371         m_passwordMaskDelay = theme->themeHint(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 */
updatePasswordEchoEditing(bool editing)389 void 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 */
xToPos(int x,QTextLine::CursorPosition betweenOrOn) const403 int QWidgetLineControl::xToPos(int x, QTextLine::CursorPosition betweenOrOn) const
404 {
405     return textLayout()->lineAt(0).xToCursor(x, betweenOrOn);
406 }
407 
408 /*!
409     \internal
410 
411     Returns the bounds of the given text position.
412 */
rectForPos(int pos) const413 QRect QWidgetLineControl::rectForPos(int pos) const
414 {
415     QTextLine l = textLayout()->lineAt(0);
416     if (m_preeditCursor != -1)
417         pos += m_preeditCursor;
418     int cix = qRound(l.cursorToX(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 */
cursorRect() const431 QRect QWidgetLineControl::cursorRect() const
432 {
433     return rectForPos(m_cursor);
434 }
435 
436 /*!
437     \internal
438 
439     Returns the bounds of the current anchor
440 */
anchorRect() const441 QRect QWidgetLineControl::anchorRect() const
442 {
443     if (!hasSelectedText())
444         return cursorRect();
445     return rectForPos(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 */
fixup()455 bool 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(textCopy, cursorCopy, 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 */
moveCursor(int pos,bool mark)478 void 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(anchor, pos);
496         m_selend = qMax(anchor, 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 */
processInputMethodEvent(QInputMethodEvent * event)515 void 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(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(-event->replacementStart(), 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(event->commitString());
551         cursorPositionChanged = true;
552     } else {
553         m_cursor = qBound(0, c, 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(0, a.start + a.length, m_text.length());
560             if (a.length) {
561                 m_selstart = qMax(0, qMin(a.start, m_text.length()));
562                 m_selend = m_cursor;
563                 if (m_selend < m_selstart) {
564                     qSwap(m_selstart, 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(m_cursor, 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(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>(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(o);
596             }
597         }
598     }
599     m_textLayout.setFormats(formats);
600     updateDisplayText(/*force*/ true);
601     if (cursorPositionChanged)
602         emitCursorPositionChanged();
603     else if (m_preeditCursor != oldPreeditCursor)
604         emit updateMicroFocus();
605 
606     if (isGettingInput)
607         finishChange(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 */
draw(QPainter * painter,const QPoint & offset,const QRect & clip,int flags)628 void 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(QPalette::Highlight));
637             o.format.setForeground(m_palette.brush(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(QPalette::Text));
644                 o.format.setForeground(m_palette.brush(QPalette::Window));
645             }
646         }
647         selections.append(o);
648     }
649 
650     if (flags & DrawText)
651         textLayout()->draw(painter, 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(painter, offset, cursor, 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 */
selectWordAtPos(int cursor)669 void QWidgetLineControl::selectWordAtPos(int cursor)
670 {
671     int next = cursor + 1;
672     if(next > end())
673         --next;
674     int c = textLayout()->previousCursorPosition(next, QTextLayout::SkipWords);
675     moveCursor(c, false);
676     // ## text layout should support end of words.
677     int end = textLayout()->nextCursorPosition(c, QTextLayout::SkipWords);
678     while (end > cursor && m_text[end-1].isSpace())
679         --end;
680     moveCursor(end, 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 */
finishChange(int validateFromState,bool update,bool edited)695 bool 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(textCopy, 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(validateFromState);
723             m_history.erase(m_history.begin() + m_undoState, 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 */
internalSetText(const QString & txt,int pos,bool edited)754 void 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(0, txt, true);
762         m_text += clearString(m_text.length(), m_maxLength - m_text.length());
763         if (edited && oldText == m_text)
764             emit inputRejected();
765     } else {
766         m_text = txt.isEmpty() ? txt : txt.left(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(-1, 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);
780         } else if (txt.isEmpty()) {
781             QAccessibleTextRemoveEvent event(accessibleObject(), 0, oldText);
782             event.setCursorPosition(m_cursor);
783             QAccessible::updateAccessibility(&event);
784         } else {
785             QAccessibleTextUpdateEvent event(accessibleObject(), 0, oldText, txt);
786             event.setCursorPosition(m_cursor);
787             QAccessible::updateAccessibility(&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 */
addCommand(const Command & cmd)802 void QWidgetLineControl::addCommand(const Command &cmd)
803 {
804     m_history.erase(m_history.begin() + m_undoState, m_history.end());
805 
806     if (m_separator && m_undoState && m_history[m_undoState - 1].type != Separator)
807         m_history.push_back(Command(Separator, m_cursor, 0, m_selstart, m_selend));
808 
809     m_separator = false;
810     m_history.push_back(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 */
internalInsert(const QString & s)824 void QWidgetLineControl::internalInsert(const QString &s)
825 {
826     if (m_echoMode == QLineEdit::Password) {
827         if (m_passwordEchoTimer != 0)
828             killTimer(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(delay);
837     }
838     if (hasSelectedText())
839         addCommand(Command(SetSelection, m_cursor, 0, m_selstart, m_selend));
840     if (m_maskData) {
841         QString ms = maskString(m_cursor, 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(&insertEvent);
847 #endif
848         for (int i = 0; i < (int) ms.length(); ++i) {
849             addCommand (Command(DeleteSelection, m_cursor + i, m_text.at(m_cursor + i), -1, -1));
850             addCommand(Command(Insert, m_cursor + i, ms.at(i), -1, -1));
851         }
852         m_text.replace(m_cursor, ms.length(), ms);
853         m_cursor += ms.length();
854         m_cursor = nextMaskBlank(m_cursor);
855         m_textDirty = true;
856 #ifndef QT_NO_ACCESSIBILITY
857         QAccessibleTextCursorEvent event(accessibleObject(), m_cursor);
858         QAccessible::updateAccessibility(&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(&insertEvent);
866 #endif
867             m_text.insert(m_cursor, s.left(remaining));
868             for (int i = 0; i < (int) s.left(remaining).length(); ++i)
869                addCommand(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 */
internalDelete(bool wasBackspace)888 void QWidgetLineControl::internalDelete(bool wasBackspace)
889 {
890     if (m_cursor < (int) m_text.length()) {
891         cancelPasswordEchoTimer();
892         if (hasSelectedText())
893             addCommand(Command(SetSelection, m_cursor, 0, m_selstart, m_selend));
894         addCommand(Command((CommandType)((m_maskData ? 2 : 0) + (wasBackspace ? Remove : Delete)),
895                    m_cursor, m_text.at(m_cursor), -1, -1));
896 #ifndef QT_NO_ACCESSIBILITY
897         QAccessibleTextRemoveEvent event(accessibleObject(), m_cursor, m_text.at(m_cursor));
898         QAccessible::updateAccessibility(&event);
899 #endif
900         if (m_maskData) {
901             m_text.replace(m_cursor, 1, clearString(m_cursor, 1));
902             addCommand(Command(Insert, m_cursor, m_text.at(m_cursor), -1, -1));
903         } else {
904             m_text.remove(m_cursor, 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 */
removeSelectedText()919 void QWidgetLineControl::removeSelectedText()
920 {
921     if (m_selstart < m_selend && m_selend <= (int) m_text.length()) {
922         cancelPasswordEchoTimer();
923         separate();
924         int i ;
925         addCommand(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 (Command(DeleteSelection, i, m_text.at(i), -1, 1));
931             for (i = m_selend - 1; i > m_cursor; --i)
932                 addCommand (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 (Command(RemoveSelection, i, m_text.at(i), -1, -1));
936         }
937 #ifndef QT_NO_ACCESSIBILITY
938         QAccessibleTextRemoveEvent event(accessibleObject(), m_selstart, m_text.mid(m_selstart, m_selend - m_selstart));
939         QAccessible::updateAccessibility(&event);
940 #endif
941         if (m_maskData) {
942             m_text.replace(m_selstart, m_selend - m_selstart,  clearString(m_selstart, m_selend - m_selstart));
943             for (int i = 0; i < m_selend - m_selstart; ++i)
944                 addCommand(Command(Insert, m_selstart + i, m_text.at(m_selstart + i), -1, -1));
945         } else {
946             m_text.remove(m_selstart, m_selend - m_selstart);
947         }
948         if (m_cursor > m_selstart)
949             m_cursor -= qMin(m_cursor, 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 */
parseInputMask(const QString & maskFields)961 void QWidgetLineControl::parseInputMask(const QString &maskFields)
962 {
963     int delimiter = maskFields.indexOf(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(QString(), -1, 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(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(m_text, -1, false);
1065 }
1066 
1067 
1068 /*!
1069     \internal
1070 
1071     checks if the key is valid compared to the inputMask
1072 */
isValidInput(QChar key,QChar mask) const1073 bool 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 */
hasAcceptableInput(const QString & str) const1150 bool 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(str.at(i), 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 */
maskString(int pos,const QString & str,bool clear) const1186 QString QWidgetLineControl::maskString(int pos, const QString &str, bool clear) const
1187 {
1188     if (pos >= m_maxLength)
1189         return QString::fromLatin1("");
1190 
1191     QString fill;
1192     fill = clear ? clearString(0, m_maxLength) : m_text;
1193 
1194     int strIndex = 0;
1195     QString s = QString::fromLatin1("");
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(str[(int)strIndex], 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(i, true, true, 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(i, 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(i, true, false, str[(int)strIndex]);
1228                         if (n != -1) {
1229                             s += fill.midRef(i, 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 */
clearString(int pos,int len) const1261 QString QWidgetLineControl::clearString(int pos, int len) const
1262 {
1263     if (pos >= m_maxLength)
1264         return QString();
1265 
1266     QString s;
1267     int end = qMin(m_maxLength, 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 */
stripString(const QString & str) const1283 QString QWidgetLineControl::stripString(const QString &str) const
1284 {
1285     if (!m_maskData)
1286         return str;
1287 
1288     QString s;
1289     int end = qMin(m_maxLength, (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 */
findInMask(int pos,bool forward,bool findSeparator,QChar searchChar) const1304 int 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(searchChar, m_maskData[i].maskChar))
1322                     return i;
1323             }
1324         }
1325         i += step;
1326     }
1327     return -1;
1328 }
1329 
internalUndo(int until)1330 void 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(cmd.pos, 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(cmd.pos, cmd.uc);
1352             m_cursor = cmd.pos + 1;
1353             break;
1354         case Delete:
1355         case DeleteSelection:
1356             m_text.insert(cmd.pos, 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 
internalRedo()1373 void 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(cmd.pos, 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(cmd.pos, 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 */
emitCursorPositionChanged()1422 void 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);
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.
advanceToEnabledItem(int dir)1441 bool 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(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 
complete(int key)1465 void 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(m_completer->currentCompletion(), m_completer->caseSensitivity()) != 0
1481                 || prefix.compare(m_completer->completionPrefix(), 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(n))
1490             return;
1491     } else {
1492 #ifndef QT_KEYPAD_NAVIGATION
1493         if (text.isEmpty()) {
1494             if (auto *popup = QCompleterPrivate::get(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 
setReadOnly(bool enable)1506 void QWidgetLineControl::setReadOnly(bool enable)
1507 {
1508     if (m_readOnly == enable)
1509         return;
1510 
1511     m_readOnly = enable;
1512     updateCursorBlinking();
1513 }
1514 
setBlinkingCursorEnabled(bool enable)1515 void QWidgetLineControl::setBlinkingCursorEnabled(bool enable)
1516 {
1517     if (m_blinkEnabled == enable)
1518         return;
1519 
1520     m_blinkEnabled = enable;
1521 
1522     if (enable)
1523         connect(QGuiApplication::styleHints(), &QStyleHints::cursorFlashTimeChanged, this, &QWidgetLineControl::updateCursorBlinking);
1524     else
1525         disconnect(QGuiApplication::styleHints(), &QStyleHints::cursorFlashTimeChanged, this, &QWidgetLineControl::updateCursorBlinking);
1526 
1527     updateCursorBlinking();
1528 }
1529 
updateCursorBlinking()1530 void QWidgetLineControl::updateCursorBlinking()
1531 {
1532     if (m_blinkTimer) {
1533         killTimer(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(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
resetCursorBlinkTimer()1548 void QWidgetLineControl::resetCursorBlinkTimer()
1549 {
1550     if (!m_blinkEnabled || m_blinkTimer == 0)
1551         return;
1552     killTimer(m_blinkTimer);
1553     m_blinkTimer = 0;
1554     int flashTime = QGuiApplication::styleHints()->cursorFlashTime();
1555     if (flashTime >= 2)
1556         m_blinkTimer = startTimer(flashTime / 2);
1557     m_blinkStatus = 1;
1558 }
1559 
timerEvent(QTimerEvent * event)1560 void 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(m_deleteAllTimer);
1567         m_deleteAllTimer = 0;
1568         clear();
1569     } else if (event->timerId() == m_tripleClickTimer) {
1570         killTimer(m_tripleClickTimer);
1571         m_tripleClickTimer = 0;
1572     } else if (event->timerId() == m_passwordEchoTimer) {
1573         killTimer(m_passwordEchoTimer);
1574         m_passwordEchoTimer = 0;
1575         updateDisplayText();
1576     }
1577 }
1578 
1579 #ifndef QT_NO_SHORTCUT
processShortcutOverrideEvent(QKeyEvent * ke)1580 void 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 
processKeyEvent(QKeyEvent * event)1634 void 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(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 *>(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(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(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(cursor(), end());
1758             copy();
1759             del();
1760         }
1761     }
1762 #endif //QT_NO_CLIPBOARD
1763     else if (event == QKeySequence::MoveToStartOfLine || event == QKeySequence::MoveToStartOfBlock) {
1764         home(0);
1765     }
1766     else if (event == QKeySequence::MoveToEndOfLine || event == QKeySequence::MoveToEndOfBlock) {
1767         end(0);
1768     }
1769     else if (event == QKeySequence::SelectStartOfLine || event == QKeySequence::SelectStartOfBlock) {
1770         home(1);
1771     }
1772     else if (event == QKeySequence::SelectEndOfLine || event == QKeySequence::SelectEndOfBlock) {
1773         end(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(selectionEnd(), false);
1785         } else {
1786             cursorForward(0, visual ? 1 : (layoutDirection() == Qt::LeftToRight ? 1 : -1));
1787         }
1788     }
1789     else if (event == QKeySequence::SelectNextChar) {
1790         cursorForward(1, 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(selectionStart(), false);
1802         } else {
1803             cursorForward(0, visual ? -1 : (layoutDirection() == Qt::LeftToRight ? -1 : 1));
1804         }
1805     }
1806     else if (event == QKeySequence::SelectPreviousChar) {
1807         cursorForward(1, visual ? -1 : (layoutDirection() == Qt::LeftToRight ? -1 : 1));
1808     }
1809     else if (event == QKeySequence::MoveToNextWord) {
1810         if (echoMode() == QLineEdit::Normal)
1811             layoutDirection() == Qt::LeftToRight ? cursorWordForward(0) : cursorWordBackward(0);
1812         else
1813             layoutDirection() == Qt::LeftToRight ? end(0) : home(0);
1814     }
1815     else if (event == QKeySequence::MoveToPreviousWord) {
1816         if (echoMode() == QLineEdit::Normal)
1817             layoutDirection() == Qt::LeftToRight ? cursorWordBackward(0) : cursorWordForward(0);
1818         else if (!isReadOnly()) {
1819             layoutDirection() == Qt::LeftToRight ? home(0) : end(0);
1820         }
1821     }
1822     else if (event == QKeySequence::SelectNextWord) {
1823         if (echoMode() == QLineEdit::Normal)
1824             layoutDirection() == Qt::LeftToRight ? cursorWordForward(1) : cursorWordBackward(1);
1825         else
1826             layoutDirection() == Qt::LeftToRight ? end(1) : home(1);
1827     }
1828     else if (event == QKeySequence::SelectPreviousWord) {
1829         if (echoMode() == QLineEdit::Normal)
1830             layoutDirection() == Qt::LeftToRight ? cursorWordBackward(1) : cursorWordForward(1);
1831         else
1832             layoutDirection() == Qt::LeftToRight ? home(1) : end(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(true);
1841             del();
1842         }
1843     }
1844     else if (event == QKeySequence::DeleteStartOfWord) {
1845         if (!isReadOnly()) {
1846             cursorWordBackward(true);
1847             if (hasSelectedText())
1848                 del();
1849         }
1850     } else if (event == QKeySequence::DeleteCompleteLine) {
1851         if (!isReadOnly()) {
1852             setSelection(0, 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(1) : end(1);
1871                 }
1872             } else {
1873                 if ((myModifiers == Qt::ControlModifier
1874                      || myModifiers == Qt::AltModifier
1875                      || myModifiers == Qt::NoModifier)) {
1876                     event->key() == Qt::Key_Up ? home(0) : end(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(true);
1886                     del();
1887                 }
1888                 break;
1889 #if QT_CONFIG(completer)
1890             case Qt::Key_Up:
1891             case Qt::Key_Down:
1892                 complete(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(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(event->text());
1944 #if QT_CONFIG(completer)
1945         complete(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(QClipboard::Selection);
1957 #endif
1958         event->accept();
1959     }
1960 }
1961 
isUndoAvailable() const1962 bool 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 
isRedoAvailable() const1970 bool 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 
1978 QT_END_NAMESPACE
1979 
1980 #include "moc_qwidgetlinecontrol_p.cpp"
1981