1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtGui module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
43 #define QT_NO_ACCESSIBILITY
45 #include "qlinecontrol_p.h"
47 #ifndef QT_NO_LINEEDIT
49 #include "qclipboard.h"
50 #ifndef QT_NO_ACCESSIBILITY
51 #include "qaccessible.h"
53 #include "qguiapplication.h"
54 #include "qstylehints.h"
58 // ### these should come from QStyleHints
59 const int textCursorWidth = 1;
60 const bool fullWidthSelection = true;
65 Updates the display text based of the current edit text
66 If the text has changed will emit displayTextChanged()
68 void QLineControl::updateDisplayText(bool forceUpdate)
70 QString orig = m_textLayout.text();
72 if (m_echoMode == NoEcho)
73 str = QString::fromLatin1("");
77 if (m_echoMode == Password || (m_echoMode == PasswordEchoOnEdit
78 && !m_passwordEchoEditing))
79 str.fill(m_passwordCharacter);
81 // replace certain non-printable characters with spaces (to avoid
82 // drawing boxes when using fonts that don't have glyphs for such
84 QChar* uc = str.data();
85 for (int i = 0; i < (int)str.length(); ++i) {
86 if ((uc[i] < 0x20 && uc[i] != 0x09)
87 || uc[i] == QChar::LineSeparator
88 || uc[i] == QChar::ParagraphSeparator
89 || uc[i] == QChar::ObjectReplacementCharacter)
90 uc[i] = QChar(0x0020);
93 m_textLayout.setText(str);
96 option.setTextDirection(m_layoutDirection);
97 option.setFlags(QTextOption::IncludeTrailingSpaces);
98 m_textLayout.setTextOption(option);
100 m_textLayout.beginLayout();
101 QTextLine l = m_textLayout.createLine();
102 m_textLayout.endLayout();
103 m_ascent = qRound(l.ascent());
105 if (str != orig || forceUpdate)
106 emit displayTextChanged(str);
109 #ifndef QT_NO_CLIPBOARD
113 Copies the currently selected text into the clipboard using the given
116 \note If the echo mode is set to a mode other than Normal then copy
117 will not work. This is to prevent using copy as a method of bypassing
118 password features of the line control.
120 void QLineControl::copy(QClipboard::Mode mode) const
122 QString t = selectedText();
123 if (!t.isEmpty() && m_echoMode == Normal) {
124 disconnect(QGuiApplication::clipboard(), SIGNAL(selectionChanged()), this, 0);
125 QGuiApplication::clipboard()->setText(t, mode);
126 connect(QGuiApplication::clipboard(), SIGNAL(selectionChanged()),
127 this, SLOT(_q_clipboardChanged()));
134 Inserts the text stored in the application clipboard into the line
139 void QLineControl::paste(QClipboard::Mode clipboardMode)
141 QString clip = QGuiApplication::clipboard()->text(clipboardMode);
142 if (!clip.isEmpty() || hasSelectedText()) {
143 separate(); //make it a separate undo/redo command
149 #endif // !QT_NO_CLIPBOARD
154 Handles the behavior for the backspace key or function.
155 Removes the current selection if there is a selection, otherwise
156 removes the character prior to the cursor position.
160 void QLineControl::backspace()
162 int priorState = m_undoState;
163 if (hasSelectedText()) {
164 removeSelectedText();
165 } else if (m_cursor) {
168 m_cursor = prevMaskBlank(m_cursor);
169 QChar uc = m_text.at(m_cursor);
170 if (m_cursor > 0 && uc.unicode() >= 0xdc00 && uc.unicode() < 0xe000) {
171 // second half of a surrogate, check if we have the first half as well,
172 // if yes delete both at once
173 uc = m_text.at(m_cursor - 1);
174 if (uc.unicode() >= 0xd800 && uc.unicode() < 0xdc00) {
175 internalDelete(true);
179 internalDelete(true);
181 finishChange(priorState);
187 Handles the behavior for the delete key or function.
188 Removes the current selection if there is a selection, otherwise
189 removes the character after the cursor position.
193 void QLineControl::del()
195 int priorState = m_undoState;
196 if (hasSelectedText()) {
197 removeSelectedText();
199 int n = m_textLayout.nextCursorPosition(m_cursor) - m_cursor;
203 finishChange(priorState);
209 Inserts the given \a newText at the current cursor position.
210 If there is any selected text it is removed prior to insertion of
213 void QLineControl::insert(const QString &newText)
215 int priorState = m_undoState;
216 removeSelectedText();
217 internalInsert(newText);
218 finishChange(priorState);
224 Clears the line control text.
226 void QLineControl::clear()
228 int priorState = m_undoState;
230 m_selend = m_text.length();
231 removeSelectedText();
233 finishChange(priorState, /*update*/false, /*edited*/false);
239 Sets \a length characters from the given \a start position as selected.
240 The given \a start position must be within the current text for
241 the line control. If \a length characters cannot be selected, then
242 the selection will extend to the end of the current text.
244 void QLineControl::setSelection(int start, int length)
246 if(start < 0 || start > (int)m_text.length()){
247 qWarning("QLineControl::setSelection: Invalid start position");
252 if (start == m_selstart && start + length == m_selend)
255 m_selend = qMin(start + length, (int)m_text.length());
257 } else if (length < 0){
258 if (start == m_selend && start + length == m_selstart)
260 m_selstart = qMax(start + length, 0);
262 m_cursor = m_selstart;
263 } else if (m_selstart != m_selend) {
269 emitCursorPositionChanged();
272 emit selectionChanged();
273 emitCursorPositionChanged();
276 void QLineControl::_q_clipboardChanged()
280 void QLineControl::_q_deleteSelected()
282 if (!hasSelectedText())
285 int priorState = m_undoState;
286 emit resetInputContext();
287 removeSelectedText();
289 finishChange(priorState);
295 Initializes the line control with a starting text value of \a txt.
297 void QLineControl::init(const QString &txt)
301 m_cursor = m_text.length();
307 Sets the password echo editing to \a editing. If password echo editing
308 is true, then the text of the password is displayed even if the echo
309 mode is set to QLineEdit::PasswordEchoOnEdit. Password echoing editing
310 does not affect other echo modes.
312 void QLineControl::updatePasswordEchoEditing(bool editing)
314 m_passwordEchoEditing = editing;
321 Returns the cursor position of the given \a x pixel value in relation
322 to the displayed text. The given \a betweenOrOn specified what kind
323 of cursor position is requested.
325 int QLineControl::xToPos(int x, QTextLine::CursorPosition betweenOrOn) const
327 return m_textLayout.lineAt(0).xToCursor(x, betweenOrOn);
333 Returns the bounds of the current cursor, as defined as a
334 between characters cursor.
336 QRect QLineControl::cursorRect() const
338 QTextLine l = m_textLayout.lineAt(0);
340 if (m_preeditCursor != -1)
341 c += m_preeditCursor;
342 int cix = qRound(l.cursorToX(c));
343 int w = m_cursorWidth;
344 int ch = l.height() + 1;
346 return QRect(cix-5, 0, w+9, ch);
352 Fixes the current text so that it is valid given any set validators.
354 Returns true if the text was changed. Otherwise returns false.
356 bool QLineControl::fixup() // this function assumes that validate currently returns != Acceptable
358 #ifndef QT_NO_VALIDATOR
360 QString textCopy = m_text;
361 int cursorCopy = m_cursor;
362 m_validator->fixup(textCopy);
363 if (m_validator->validate(textCopy, cursorCopy) == QValidator::Acceptable) {
364 if (textCopy != m_text || cursorCopy != m_cursor)
365 internalSetText(textCopy, cursorCopy);
376 Moves the cursor to the given position \a pos. If \a mark is true will
377 adjust the currently selected text.
379 void QLineControl::moveCursor(int pos, bool mark)
381 if (pos != m_cursor) {
384 pos = pos > m_cursor ? nextMaskBlank(pos) : prevMaskBlank(pos);
388 if (m_selend > m_selstart && m_cursor == m_selstart)
390 else if (m_selend > m_selstart && m_cursor == m_selend)
394 m_selstart = qMin(anchor, pos);
395 m_selend = qMax(anchor, pos);
401 if (mark || m_selDirty) {
403 emit selectionChanged();
405 emitCursorPositionChanged();
411 Applies the given input method event \a event to the text of the line
414 void QLineControl::processInputMethodEvent(QInputMethodEvent *event)
417 bool isGettingInput = !event->commitString().isEmpty()
418 || event->preeditString() != preeditAreaText()
419 || event->replacementLength() > 0;
420 bool cursorPositionChanged = false;
422 if (isGettingInput) {
423 // If any text is being input, remove selected text.
424 priorState = m_undoState;
425 if (echoMode() == PasswordEchoOnEdit && !passwordEchoEditing()) {
426 updatePasswordEchoEditing(true);
428 m_selend = m_text.length();
430 removeSelectedText();
433 int c = m_cursor; // cursor position after insertion of commit string
434 if (event->replacementStart() <= 0)
435 c += event->commitString().length() - qMin(-event->replacementStart(), event->replacementLength());
437 m_cursor += event->replacementStart();
441 // insert commit string
442 if (event->replacementLength()) {
443 m_selstart = m_cursor;
444 m_selend = m_selstart + event->replacementLength();
445 removeSelectedText();
447 if (!event->commitString().isEmpty()) {
448 internalInsert(event->commitString());
449 cursorPositionChanged = true;
452 m_cursor = qBound(0, c, m_text.length());
454 for (int i = 0; i < event->attributes().size(); ++i) {
455 const QInputMethodEvent::Attribute &a = event->attributes().at(i);
456 if (a.type == QInputMethodEvent::Selection) {
457 m_cursor = qBound(0, a.start + a.length, m_text.length());
459 m_selstart = qMax(0, qMin(a.start, m_text.length()));
461 if (m_selend < m_selstart) {
462 qSwap(m_selstart, m_selend);
465 m_selstart = m_selend = 0;
467 cursorPositionChanged = true;
471 setPreeditArea(m_cursor, event->preeditString());
473 const int oldPreeditCursor = m_preeditCursor;
474 m_preeditCursor = event->preeditString().length();
475 m_hideCursor = false;
476 QList<QTextLayout::FormatRange> formats;
477 for (int i = 0; i < event->attributes().size(); ++i) {
478 const QInputMethodEvent::Attribute &a = event->attributes().at(i);
479 if (a.type == QInputMethodEvent::Cursor) {
480 m_preeditCursor = a.start;
481 m_hideCursor = !a.length;
482 } else if (a.type == QInputMethodEvent::TextFormat) {
483 QTextCharFormat f = qvariant_cast<QTextFormat>(a.value).toCharFormat();
485 QTextLayout::FormatRange o;
486 o.start = a.start + m_cursor;
493 m_textLayout.setAdditionalFormats(formats);
494 updateDisplayText(/*force*/ true);
495 if (cursorPositionChanged)
496 emitCursorPositionChanged();
497 else if (m_preeditCursor != oldPreeditCursor)
498 emit updateMicroFocus();
500 finishChange(priorState);
506 Draws the display text for the line control using the given
507 \a painter, \a clip, and \a offset. Which aspects of the display text
508 are drawn is specified by the given \a flags.
510 If the flags contain DrawSelections, then the selection or input mask
511 backgrounds and foregrounds will be applied before drawing the text.
513 If the flags contain DrawCursor a cursor of the current cursorWidth()
514 will be drawn after drawing the text.
516 The display text will only be drawn if the flags contain DrawText
518 void QLineControl::draw(QPainter *painter, const QPoint &offset, const QRect &clip, int flags)
520 QVector<QTextLayout::FormatRange> selections;
521 if (flags & DrawSelections) {
522 QTextLayout::FormatRange o;
523 if (m_selstart < m_selend) {
524 o.start = m_selstart;
525 o.length = m_selend - m_selstart;
526 o.format.setBackground(m_palette.brush(QPalette::Highlight));
527 o.format.setForeground(m_palette.brush(QPalette::HighlightedText));
530 if(!m_blinkPeriod || m_blinkStatus){
533 o.format.setBackground(m_palette.brush(QPalette::Text));
534 o.format.setForeground(m_palette.brush(QPalette::Window));
537 selections.append(o);
540 if (flags & DrawText)
541 m_textLayout.draw(painter, offset, selections, clip);
543 if (flags & DrawCursor){
544 int cursor = m_cursor;
545 if (m_preeditCursor != -1)
546 cursor += m_preeditCursor;
547 if (!m_hideCursor && (!m_blinkPeriod || m_blinkStatus))
548 m_textLayout.drawCursor(painter, offset, cursor, m_cursorWidth);
555 Sets the selection to cover the word at the given cursor position.
556 The word boundaries are defined by the behavior of QTextLayout::SkipWords
559 void QLineControl::selectWordAtPos(int cursor)
561 int next = cursor + 1;
564 int c = m_textLayout.previousCursorPosition(next, QTextLayout::SkipWords);
565 moveCursor(c, false);
566 // ## text layout should support end of words.
567 int end = m_textLayout.nextCursorPosition(c, QTextLayout::SkipWords);
568 while (end > cursor && m_text[end-1].isSpace())
570 moveCursor(end, true);
576 Completes a change to the line control text. If the change is not valid
577 will undo the line control state back to the given \a validateFromState.
579 If \a edited is true and the change is valid, will emit textEdited() in
580 addition to textChanged(). Otherwise only emits textChanged() on a valid
583 The \a update value is currently unused.
585 bool QLineControl::finishChange(int validateFromState, bool update, bool edited)
588 bool lineDirty = m_selDirty;
591 bool wasValidInput = m_validInput;
593 #ifndef QT_NO_VALIDATOR
595 m_validInput = false;
596 QString textCopy = m_text;
597 int cursorCopy = m_cursor;
598 m_validInput = (m_validator->validate(textCopy, cursorCopy) != QValidator::Invalid);
600 if (m_text != textCopy) {
601 internalSetText(textCopy, cursorCopy);
604 m_cursor = cursorCopy;
608 if (validateFromState >= 0 && wasValidInput && !m_validInput) {
609 if (m_transactions.count())
611 internalUndo(validateFromState);
612 m_history.resize(m_undoState);
613 if (m_modifiedState > m_undoState)
614 m_modifiedState = -1;
619 lineDirty |= m_textDirty;
622 QString actualText = text();
624 emit textEdited(actualText);
625 emit textChanged(actualText);
630 emit selectionChanged();
632 emitCursorPositionChanged();
639 An internal function for setting the text of the line control.
641 void QLineControl::internalSetText(const QString &txt, int pos, bool edited)
644 emit resetInputContext();
645 QString oldText = m_text;
647 m_text = maskString(0, txt, true);
648 m_text += clearString(m_text.length(), m_maxLength - m_text.length());
650 m_text = txt.isEmpty() ? txt : txt.left(m_maxLength);
653 m_modifiedState = m_undoState = 0;
654 m_cursor = (pos < 0 || pos > m_text.length()) ? m_text.length() : pos;
655 m_textDirty = (oldText != m_text);
657 #ifdef QT_NO_ACCESSIBILITY
660 bool changed = finishChange(-1, true, edited);
662 QAccessible::updateAccessibility(parent(), 0, QAccessible::TextUpdated);
670 Adds the given \a command to the undo history
671 of the line control. Does not apply the command.
673 void QLineControl::addCommand(const Command &cmd)
675 if (m_separator && m_undoState && m_history[m_undoState - 1].type != Separator) {
676 m_history.resize(m_undoState + 2);
677 m_history[m_undoState++] = Command(Separator, m_cursor, 0, m_selstart, m_selend);
679 m_history.resize(m_undoState + 1);
682 m_history[m_undoState++] = cmd;
688 Inserts the given string \a s into the line
691 Also adds the appropriate commands into the undo history.
692 This function does not call finishChange(), and may leave the text
695 void QLineControl::internalInsert(const QString &s)
697 if (hasSelectedText())
698 addCommand(Command(SetSelection, m_cursor, 0, m_selstart, m_selend));
700 QString ms = maskString(m_cursor, s);
701 for (int i = 0; i < (int) ms.length(); ++i) {
702 addCommand (Command(DeleteSelection, m_cursor + i, m_text.at(m_cursor + i), -1, -1));
703 addCommand(Command(Insert, m_cursor + i, ms.at(i), -1, -1));
705 m_text.replace(m_cursor, ms.length(), ms);
706 m_cursor += ms.length();
707 m_cursor = nextMaskBlank(m_cursor);
710 int remaining = m_maxLength - m_text.length();
711 if (remaining != 0) {
712 m_text.insert(m_cursor, s.left(remaining));
713 for (int i = 0; i < (int) s.left(remaining).length(); ++i)
714 addCommand(Command(Insert, m_cursor++, s.at(i), -1, -1));
723 deletes a single character from the current text. If \a wasBackspace,
724 the character prior to the cursor is removed. Otherwise the character
725 after the cursor is removed.
727 Also adds the appropriate commands into the undo history.
728 This function does not call finishChange(), and may leave the text
731 void QLineControl::internalDelete(bool wasBackspace)
733 if (m_cursor < (int) m_text.length()) {
734 if (hasSelectedText())
735 addCommand(Command(SetSelection, m_cursor, 0, m_selstart, m_selend));
736 addCommand(Command((CommandType)((m_maskData ? 2 : 0) + (wasBackspace ? Remove : Delete)),
737 m_cursor, m_text.at(m_cursor), -1, -1));
739 m_text.replace(m_cursor, 1, clearString(m_cursor, 1));
740 addCommand(Command(Insert, m_cursor, m_text.at(m_cursor), -1, -1));
742 m_text.remove(m_cursor, 1);
751 removes the currently selected text from the line control.
753 Also adds the appropriate commands into the undo history.
754 This function does not call finishChange(), and may leave the text
757 void QLineControl::removeSelectedText()
759 if (m_selstart < m_selend && m_selend <= (int) m_text.length()) {
762 addCommand(Command(SetSelection, m_cursor, 0, m_selstart, m_selend));
763 if (m_selstart <= m_cursor && m_cursor < m_selend) {
764 // cursor is within the selection. Split up the commands
765 // to be able to restore the correct cursor position
766 for (i = m_cursor; i >= m_selstart; --i)
767 addCommand (Command(DeleteSelection, i, m_text.at(i), -1, 1));
768 for (i = m_selend - 1; i > m_cursor; --i)
769 addCommand (Command(DeleteSelection, i - m_cursor + m_selstart - 1, m_text.at(i), -1, -1));
771 for (i = m_selend-1; i >= m_selstart; --i)
772 addCommand (Command(RemoveSelection, i, m_text.at(i), -1, -1));
775 m_text.replace(m_selstart, m_selend - m_selstart, clearString(m_selstart, m_selend - m_selstart));
776 for (int i = 0; i < m_selend - m_selstart; ++i)
777 addCommand(Command(Insert, m_selstart + i, m_text.at(m_selstart + i), -1, -1));
779 m_text.remove(m_selstart, m_selend - m_selstart);
781 if (m_cursor > m_selstart)
782 m_cursor -= qMin(m_cursor, m_selend) - m_selstart;
791 Parses the input mask specified by \a maskFields to generate
792 the mask data used to handle input masks.
794 void QLineControl::parseInputMask(const QString &maskFields)
796 int delimiter = maskFields.indexOf(QLatin1Char(';'));
797 if (maskFields.isEmpty() || delimiter == 0) {
799 delete [] m_maskData;
802 internalSetText(QString());
807 if (delimiter == -1) {
808 m_blank = QLatin1Char(' ');
809 m_inputMask = maskFields;
811 m_inputMask = maskFields.left(delimiter);
812 m_blank = (delimiter + 1 < maskFields.length()) ? maskFields[delimiter + 1] : QLatin1Char(' ');
815 // calculate m_maxLength / m_maskData length
818 for (int i=0; i<m_inputMask.length(); i++) {
819 c = m_inputMask.at(i);
820 if (i > 0 && m_inputMask.at(i-1) == QLatin1Char('\\')) {
824 if (c != QLatin1Char('\\') && c != QLatin1Char('!') &&
825 c != QLatin1Char('<') && c != QLatin1Char('>') &&
826 c != QLatin1Char('{') && c != QLatin1Char('}') &&
827 c != QLatin1Char('[') && c != QLatin1Char(']'))
831 delete [] m_maskData;
832 m_maskData = new MaskInputData[m_maxLength];
834 MaskInputData::Casemode m = MaskInputData::NoCaseMode;
839 for (int i = 0; i < m_inputMask.length(); i++) {
840 c = m_inputMask.at(i);
843 m_maskData[index].maskChar = c;
844 m_maskData[index].separator = s;
845 m_maskData[index].caseMode = m;
848 } else if (c == QLatin1Char('<')) {
849 m = MaskInputData::Lower;
850 } else if (c == QLatin1Char('>')) {
851 m = MaskInputData::Upper;
852 } else if (c == QLatin1Char('!')) {
853 m = MaskInputData::NoCaseMode;
854 } else if (c != QLatin1Char('{') && c != QLatin1Char('}') && c != QLatin1Char('[') && c != QLatin1Char(']')) {
855 switch (c.unicode()) {
881 m_maskData[index].maskChar = c;
882 m_maskData[index].separator = s;
883 m_maskData[index].caseMode = m;
888 internalSetText(m_text);
895 checks if the key is valid compared to the inputMask
897 bool QLineControl::isValidInput(QChar key, QChar mask) const
899 switch (mask.unicode()) {
905 if (key.isLetter() || key == m_blank)
909 if (key.isLetterOrNumber())
913 if (key.isLetterOrNumber() || key == m_blank)
921 if (key.isPrint() || key == m_blank)
929 if (key.isNumber() || key == m_blank)
933 if (key.isNumber() && key.digitValue() > 0)
937 if ((key.isNumber() && key.digitValue() > 0) || key == m_blank)
941 if (key.isNumber() || key == QLatin1Char('+') || key == QLatin1Char('-') || key == m_blank)
945 if (key == QLatin1Char('0') || key == QLatin1Char('1'))
949 if (key == QLatin1Char('0') || key == QLatin1Char('1') || key == m_blank)
953 if (key.isNumber() || (key >= QLatin1Char('a') && key <= QLatin1Char('f')) || (key >= QLatin1Char('A') && key <= QLatin1Char('F')))
957 if (key.isNumber() || (key >= QLatin1Char('a') && key <= QLatin1Char('f')) || (key >= QLatin1Char('A') && key <= QLatin1Char('F')) || key == m_blank)
969 Returns true if the given text \a str is valid for any
970 validator or input mask set for the line control.
972 Otherwise returns false
974 bool QLineControl::hasAcceptableInput(const QString &str) const
976 #ifndef QT_NO_VALIDATOR
977 QString textCopy = str;
978 int cursorCopy = m_cursor;
979 if (m_validator && m_validator->validate(textCopy, cursorCopy)
980 != QValidator::Acceptable)
987 if (str.length() != m_maxLength)
990 for (int i=0; i < m_maxLength; ++i) {
991 if (m_maskData[i].separator) {
992 if (str.at(i) != m_maskData[i].maskChar)
995 if (!isValidInput(str.at(i), m_maskData[i].maskChar))
1005 Applies the inputMask on \a str starting from position \a pos in the mask. \a clear
1006 specifies from where characters should be gotten when a separator is met in \a str - true means
1007 that blanks will be used, false that previous input is used.
1008 Calling this when no inputMask is set is undefined.
1010 QString QLineControl::maskString(uint pos, const QString &str, bool clear) const
1012 if (pos >= (uint)m_maxLength)
1013 return QString::fromLatin1("");
1016 fill = clear ? clearString(0, m_maxLength) : m_text;
1019 QString s = QString::fromLatin1("");
1021 while (i < m_maxLength) {
1022 if (strIndex < str.length()) {
1023 if (m_maskData[i].separator) {
1024 s += m_maskData[i].maskChar;
1025 if (str[(int)strIndex] == m_maskData[i].maskChar)
1029 if (isValidInput(str[(int)strIndex], m_maskData[i].maskChar)) {
1030 switch (m_maskData[i].caseMode) {
1031 case MaskInputData::Upper:
1032 s += str[(int)strIndex].toUpper();
1034 case MaskInputData::Lower:
1035 s += str[(int)strIndex].toLower();
1038 s += str[(int)strIndex];
1042 // search for separator first
1043 int n = findInMask(i, true, true, str[(int)strIndex]);
1045 if (str.length() != 1 || i == 0 || (i > 0 && (!m_maskData[i-1].separator || m_maskData[i-1].maskChar != str[(int)strIndex]))) {
1046 s += fill.mid(i, n-i+1);
1047 i = n + 1; // update i to find + 1
1050 // search for valid m_blank if not
1051 n = findInMask(i, true, false, str[(int)strIndex]);
1053 s += fill.mid(i, n-i);
1054 switch (m_maskData[n].caseMode) {
1055 case MaskInputData::Upper:
1056 s += str[(int)strIndex].toUpper();
1058 case MaskInputData::Lower:
1059 s += str[(int)strIndex].toLower();
1062 s += str[(int)strIndex];
1064 i = n + 1; // updates i to find + 1
1082 Returns a "cleared" string with only separators and blank chars.
1083 Calling this when no inputMask is set is undefined.
1085 QString QLineControl::clearString(uint pos, uint len) const
1087 if (pos >= (uint)m_maxLength)
1091 int end = qMin((uint)m_maxLength, pos + len);
1092 for (int i = pos; i < end; ++i)
1093 if (m_maskData[i].separator)
1094 s += m_maskData[i].maskChar;
1104 Strips blank parts of the input in a QLineControl when an inputMask is set,
1105 separators are still included. Typically "127.0__.0__.1__" becomes "127.0.0.1".
1107 QString QLineControl::stripString(const QString &str) const
1113 int end = qMin(m_maxLength, (int)str.length());
1114 for (int i = 0; i < end; ++i)
1115 if (m_maskData[i].separator)
1116 s += m_maskData[i].maskChar;
1118 if (str[i] != m_blank)
1126 searches forward/backward in m_maskData for either a separator or a m_blank
1128 int QLineControl::findInMask(int pos, bool forward, bool findSeparator, QChar searchChar) const
1130 if (pos >= m_maxLength || pos < 0)
1133 int end = forward ? m_maxLength : -1;
1134 int step = forward ? 1 : -1;
1138 if (findSeparator) {
1139 if (m_maskData[i].separator && m_maskData[i].maskChar == searchChar)
1142 if (!m_maskData[i].separator) {
1143 if (searchChar.isNull())
1145 else if (isValidInput(searchChar, m_maskData[i].maskChar))
1154 void QLineControl::internalUndo(int until)
1156 if (!isUndoAvailable())
1159 while (m_undoState && m_undoState > until) {
1160 Command& cmd = m_history[--m_undoState];
1163 m_text.remove(cmd.pos, 1);
1167 m_selstart = cmd.selStart;
1168 m_selend = cmd.selEnd;
1172 case RemoveSelection:
1173 m_text.insert(cmd.pos, cmd.uc);
1174 m_cursor = cmd.pos + 1;
1177 case DeleteSelection:
1178 m_text.insert(cmd.pos, cmd.uc);
1184 if (until < 0 && m_undoState) {
1185 Command& next = m_history[m_undoState-1];
1186 if (next.type != cmd.type && next.type < RemoveSelection
1187 && (cmd.type < RemoveSelection || next.type == Separator))
1192 emitCursorPositionChanged();
1195 void QLineControl::internalRedo()
1197 if (!isRedoAvailable())
1200 while (m_undoState < (int)m_history.size()) {
1201 Command& cmd = m_history[m_undoState++];
1204 m_text.insert(cmd.pos, cmd.uc);
1205 m_cursor = cmd.pos + 1;
1208 m_selstart = cmd.selStart;
1209 m_selend = cmd.selEnd;
1214 case RemoveSelection:
1215 case DeleteSelection:
1216 m_text.remove(cmd.pos, 1);
1217 m_selstart = cmd.selStart;
1218 m_selend = cmd.selEnd;
1222 m_selstart = cmd.selStart;
1223 m_selend = cmd.selEnd;
1227 if (m_undoState < (int)m_history.size()) {
1228 Command& next = m_history[m_undoState];
1229 if (next.type != cmd.type && cmd.type < RemoveSelection && next.type != Separator
1230 && (next.type < RemoveSelection || cmd.type == Separator))
1235 emitCursorPositionChanged();
1241 If the current cursor position differs from the last emitted cursor
1242 position, emits cursorPositionChanged().
1244 void QLineControl::emitCursorPositionChanged()
1246 if (m_cursor != m_lastCursorPos) {
1247 const int oldLast = m_lastCursorPos;
1248 m_lastCursorPos = m_cursor;
1249 cursorPositionChanged(oldLast, m_cursor);
1250 #ifndef QT_NO_ACCESSIBILITY
1251 QAccessible::updateAccessibility(parent(), 0, QAccessible::TextCaretMoved);
1257 void QLineControl::setCursorBlinkPeriod(int msec)
1259 if (msec == m_blinkPeriod)
1262 killTimer(m_blinkTimer);
1265 m_blinkTimer = startTimer(msec / 2);
1269 if (m_blinkStatus == 1)
1270 emit updateNeeded(inputMask().isEmpty() ? cursorRect() : QRect());
1272 m_blinkPeriod = msec;
1275 void QLineControl::resetCursorBlinkTimer()
1277 if (m_blinkPeriod == 0 || m_blinkTimer == 0)
1279 killTimer(m_blinkTimer);
1280 m_blinkTimer = startTimer(m_blinkPeriod / 2);
1284 void QLineControl::timerEvent(QTimerEvent *event)
1286 if (event->timerId() == m_blinkTimer) {
1287 m_blinkStatus = !m_blinkStatus;
1288 emit updateNeeded(inputMask().isEmpty() ? cursorRect() : QRect());
1289 } else if (event->timerId() == m_deleteAllTimer) {
1290 killTimer(m_deleteAllTimer);
1291 m_deleteAllTimer = 0;
1293 } else if (event->timerId() == m_tripleClickTimer) {
1294 killTimer(m_tripleClickTimer);
1295 m_tripleClickTimer = 0;
1299 bool QLineControl::processEvent(QEvent* ev)
1301 #ifdef QT_KEYPAD_NAVIGATION
1302 if (QGuiApplication::keypadNavigationEnabled()) {
1303 if ((ev->type() == QEvent::KeyPress) || (ev->type() == QEvent::KeyRelease)) {
1304 QKeyEvent *ke = (QKeyEvent *)ev;
1305 if (ke->key() == Qt::Key_Back) {
1306 if (ke->isAutoRepeat()) {
1307 // Swallow it. We don't want back keys running amok.
1311 if ((ev->type() == QEvent::KeyRelease)
1313 && m_deleteAllTimer) {
1314 killTimer(m_deleteAllTimer);
1315 m_deleteAllTimer = 0;
1325 case QEvent::MouseButtonPress:
1326 case QEvent::MouseButtonRelease:
1327 case QEvent::MouseButtonDblClick:
1328 case QEvent::MouseMove:
1329 processMouseEvent(static_cast<QMouseEvent*>(ev)); break;
1330 case QEvent::KeyPress:
1331 case QEvent::KeyRelease:
1332 processKeyEvent(static_cast<QKeyEvent*>(ev)); break;
1333 case QEvent::InputMethod:
1334 processInputMethodEvent(static_cast<QInputMethodEvent*>(ev)); break;
1335 #ifndef QT_NO_SHORTCUT
1336 case QEvent::ShortcutOverride:{
1339 QKeyEvent* ke = static_cast<QKeyEvent*>(ev);
1340 if (ke == QKeySequence::Copy
1341 || ke == QKeySequence::Paste
1342 || ke == QKeySequence::Cut
1343 || ke == QKeySequence::Redo
1344 || ke == QKeySequence::Undo
1345 || ke == QKeySequence::MoveToNextWord
1346 || ke == QKeySequence::MoveToPreviousWord
1347 || ke == QKeySequence::MoveToStartOfDocument
1348 || ke == QKeySequence::MoveToEndOfDocument
1349 || ke == QKeySequence::SelectNextWord
1350 || ke == QKeySequence::SelectPreviousWord
1351 || ke == QKeySequence::SelectStartOfLine
1352 || ke == QKeySequence::SelectEndOfLine
1353 || ke == QKeySequence::SelectStartOfBlock
1354 || ke == QKeySequence::SelectEndOfBlock
1355 || ke == QKeySequence::SelectStartOfDocument
1356 || ke == QKeySequence::SelectAll
1357 || ke == QKeySequence::SelectEndOfDocument) {
1359 } else if (ke->modifiers() == Qt::NoModifier || ke->modifiers() == Qt::ShiftModifier
1360 || ke->modifiers() == Qt::KeypadModifier) {
1361 if (ke->key() < Qt::Key_Escape) {
1364 switch (ke->key()) {
1365 case Qt::Key_Delete:
1368 case Qt::Key_Backspace:
1385 void QLineControl::processMouseEvent(QMouseEvent* ev)
1388 switch (ev->type()) {
1389 case QEvent::MouseButtonPress:{
1390 if (m_tripleClickTimer
1391 && (ev->pos() - m_tripleClick).manhattanLength() < qApp->styleHints()->startDragDistance()) {
1395 if (ev->button() == Qt::RightButton)
1398 bool mark = ev->modifiers() & Qt::ShiftModifier;
1399 int cursor = xToPos(ev->pos().x());
1400 moveCursor(cursor, mark);
1403 case QEvent::MouseButtonDblClick:
1404 if (ev->button() == Qt::LeftButton) {
1405 selectWordAtPos(xToPos(ev->pos().x()));
1406 if (m_tripleClickTimer)
1407 killTimer(m_tripleClickTimer);
1408 m_tripleClickTimer = startTimer(qApp->styleHints()->mouseDoubleClickInterval());
1409 m_tripleClick = ev->pos();
1412 case QEvent::MouseButtonRelease:
1413 #ifndef QT_NO_CLIPBOARD
1414 if (QGuiApplication::clipboard()->supportsSelection()) {
1415 if (ev->button() == Qt::LeftButton) {
1416 copy(QClipboard::Selection);
1417 } else if (!isReadOnly() && ev->button() == Qt::MidButton) {
1419 insert(QGuiApplication::clipboard()->text(QClipboard::Selection));
1424 case QEvent::MouseMove:
1425 if (ev->buttons() & Qt::LeftButton) {
1426 moveCursor(xToPos(ev->pos().x()), true);
1434 void QLineControl::processKeyEvent(QKeyEvent* event)
1436 bool inlineCompletionAccepted = false;
1438 if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) {
1439 if (hasAcceptableInput() || fixup()) {
1441 emit editingFinished();
1443 if (inlineCompletionAccepted)
1450 if (echoMode() == PasswordEchoOnEdit
1451 && !passwordEchoEditing()
1453 && !event->text().isEmpty()
1454 #ifdef QT_KEYPAD_NAVIGATION
1455 && event->key() != Qt::Key_Select
1456 && event->key() != Qt::Key_Up
1457 && event->key() != Qt::Key_Down
1458 && event->key() != Qt::Key_Back
1460 && !(event->modifiers() & Qt::ControlModifier)) {
1461 // Clear the edit and reset to normal echo mode while editing; the
1462 // echo mode switches back when the edit loses focus
1463 // ### resets current content. dubious code; you can
1464 // navigate with keys up, down, back, and select(?), but if you press
1465 // "left" or "right" it clears?
1466 updatePasswordEchoEditing(true);
1470 bool unknown = false;
1471 bool visual = cursorMoveStyle() == Qt::VisualMoveStyle;
1475 #ifndef QT_NO_SHORTCUT
1476 else if (event == QKeySequence::Undo) {
1480 else if (event == QKeySequence::Redo) {
1484 else if (event == QKeySequence::SelectAll) {
1487 #ifndef QT_NO_CLIPBOARD
1488 else if (event == QKeySequence::Copy) {
1491 else if (event == QKeySequence::Paste) {
1492 if (!isReadOnly()) {
1493 QClipboard::Mode mode = QClipboard::Clipboard;
1495 if (event->modifiers() == (Qt::CTRL | Qt::SHIFT) && event->key() == Qt::Key_Insert)
1496 mode = QClipboard::Selection;
1501 else if (event == QKeySequence::Cut) {
1502 if (!isReadOnly()) {
1507 else if (event == QKeySequence::DeleteEndOfLine) {
1508 if (!isReadOnly()) {
1509 setSelection(cursor(), end());
1514 #endif //QT_NO_CLIPBOARD
1515 else if (event == QKeySequence::MoveToStartOfLine || event == QKeySequence::MoveToStartOfBlock) {
1518 else if (event == QKeySequence::MoveToEndOfLine || event == QKeySequence::MoveToEndOfBlock) {
1521 else if (event == QKeySequence::SelectStartOfLine || event == QKeySequence::SelectStartOfBlock) {
1524 else if (event == QKeySequence::SelectEndOfLine || event == QKeySequence::SelectEndOfBlock) {
1527 else if (event == QKeySequence::MoveToNextChar) {
1528 #if !defined(Q_WS_WIN) || defined(QT_NO_COMPLETER)
1529 if (hasSelectedText()) {
1531 if (hasSelectedText() && m_completer
1532 && m_completer->completionMode() == QCompleter::InlineCompletion) {
1534 moveCursor(selectionEnd(), false);
1536 cursorForward(0, visual ? 1 : (layoutDirection() == Qt::LeftToRight ? 1 : -1));
1539 else if (event == QKeySequence::SelectNextChar) {
1540 cursorForward(1, visual ? 1 : (layoutDirection() == Qt::LeftToRight ? 1 : -1));
1542 else if (event == QKeySequence::MoveToPreviousChar) {
1543 #if !defined(Q_WS_WIN) || defined(QT_NO_COMPLETER)
1544 if (hasSelectedText()) {
1546 if (hasSelectedText() && m_completer
1547 && m_completer->completionMode() == QCompleter::InlineCompletion) {
1549 moveCursor(selectionStart(), false);
1551 cursorForward(0, visual ? -1 : (layoutDirection() == Qt::LeftToRight ? -1 : 1));
1554 else if (event == QKeySequence::SelectPreviousChar) {
1555 cursorForward(1, visual ? -1 : (layoutDirection() == Qt::LeftToRight ? -1 : 1));
1557 else if (event == QKeySequence::MoveToNextWord) {
1558 if (echoMode() == Normal)
1559 layoutDirection() == Qt::LeftToRight ? cursorWordForward(0) : cursorWordBackward(0);
1561 layoutDirection() == Qt::LeftToRight ? end(0) : home(0);
1563 else if (event == QKeySequence::MoveToPreviousWord) {
1564 if (echoMode() == Normal)
1565 layoutDirection() == Qt::LeftToRight ? cursorWordBackward(0) : cursorWordForward(0);
1566 else if (!isReadOnly()) {
1567 layoutDirection() == Qt::LeftToRight ? home(0) : end(0);
1570 else if (event == QKeySequence::SelectNextWord) {
1571 if (echoMode() == Normal)
1572 layoutDirection() == Qt::LeftToRight ? cursorWordForward(1) : cursorWordBackward(1);
1574 layoutDirection() == Qt::LeftToRight ? end(1) : home(1);
1576 else if (event == QKeySequence::SelectPreviousWord) {
1577 if (echoMode() == Normal)
1578 layoutDirection() == Qt::LeftToRight ? cursorWordBackward(1) : cursorWordForward(1);
1580 layoutDirection() == Qt::LeftToRight ? home(1) : end(1);
1582 else if (event == QKeySequence::Delete) {
1586 else if (event == QKeySequence::DeleteEndOfWord) {
1587 if (!isReadOnly()) {
1588 cursorWordForward(true);
1592 else if (event == QKeySequence::DeleteStartOfWord) {
1593 if (!isReadOnly()) {
1594 cursorWordBackward(true);
1598 #endif // QT_NO_SHORTCUT
1600 bool handled = false;
1602 if (event->key() == Qt::Key_Up || event->key() == Qt::Key_Down) {
1603 Qt::KeyboardModifiers myModifiers = (event->modifiers() & ~Qt::KeypadModifier);
1604 if (myModifiers & Qt::ShiftModifier) {
1605 if (myModifiers == (Qt::ControlModifier|Qt::ShiftModifier)
1606 || myModifiers == (Qt::AltModifier|Qt::ShiftModifier)
1607 || myModifiers == Qt::ShiftModifier) {
1609 event->key() == Qt::Key_Up ? home(1) : end(1);
1612 if ((myModifiers == Qt::ControlModifier
1613 || myModifiers == Qt::AltModifier
1614 || myModifiers == Qt::NoModifier)) {
1615 event->key() == Qt::Key_Up ? home(0) : end(0);
1621 if (event->modifiers() & Qt::ControlModifier) {
1622 switch (event->key()) {
1623 case Qt::Key_Backspace:
1624 if (!isReadOnly()) {
1625 cursorWordBackward(true);
1629 #if defined(Q_WS_X11)
1635 if (!isReadOnly()) {
1636 setSelection(0, text().size());
1637 #ifndef QT_NO_CLIPBOARD
1648 } else { // ### check for *no* modifier
1649 switch (event->key()) {
1650 case Qt::Key_Backspace:
1651 if (!isReadOnly()) {
1655 #ifdef QT_KEYPAD_NAVIGATION
1657 if (QGuiApplication::keypadNavigationEnabled() && !event->isAutoRepeat()
1659 if (text().length() == 0) {
1660 setText(m_cancelText);
1662 if (passwordEchoEditing())
1663 updatePasswordEchoEditing(false);
1665 emit editFocusChange(false);
1666 } else if (!m_deleteAllTimer) {
1667 m_deleteAllTimer = startTimer(750);
1681 if (event->key() == Qt::Key_Direction_L || event->key() == Qt::Key_Direction_R) {
1682 setLayoutDirection((event->key() == Qt::Key_Direction_L) ? Qt::LeftToRight : Qt::RightToLeft);
1686 if (unknown && !isReadOnly()) {
1687 QString t = event->text();
1688 if (!t.isEmpty() && t.at(0).isPrint()) {