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
****************************************************************************/
42
#include "qlinecontrol_p.h"
44
#ifndef QT_NO_LINEEDIT
46
#include "qabstractitemview.h"
47
#include "qclipboard.h"
48
#ifndef QT_NO_ACCESSIBILITY
49
#include "qaccessible.h"
52
#include "qinputcontext.h"
55
#include "qapplication.h"
56
#ifndef QT_NO_GRAPHICSVIEW
57
#include "qgraphicssceneevent.h"
62
#ifdef QT_GUI_PASSWORD_ECHO_DELAY
63
static const int qt_passwordEchoDelay = QT_GUI_PASSWORD_ECHO_DELAY;
67
\macro QT_GUI_PASSWORD_ECHO_DELAY
71
Defines the amount of time in milliseconds the last entered character
72
should be displayed unmasked in the Password echo mode.
74
If not defined in qplatformdefs.h there will be no delay in masking
81
Updates the display text based of the current edit text
82
If the text has changed will emit displayTextChanged()
84
void QLineControl::updateDisplayText(bool forceUpdate)
86
QString orig = m_textLayout.text();
88
if (m_echoMode == QLineEdit::NoEcho)
89
str = QString::fromLatin1("");
93
if (m_echoMode == QLineEdit::Password) {
94
str.fill(m_passwordCharacter);
95
#ifdef QT_GUI_PASSWORD_ECHO_DELAY
96
if (m_passwordEchoTimer != 0 && m_cursor > 0 && m_cursor <= m_text.length()) {
97
int cursor = m_cursor - 1;
98
QChar uc = m_text.at(cursor);
100
if (cursor > 0 && uc.unicode() >= 0xdc00 && uc.unicode() < 0xe000) {
101
// second half of a surrogate, check if we have the first half as well,
102
// if yes restore both at once
103
uc = m_text.at(cursor - 1);
104
if (uc.unicode() >= 0xd800 && uc.unicode() < 0xdc00)
105
str[cursor - 1] = uc;
109
} else if (m_echoMode == QLineEdit::PasswordEchoOnEdit && !m_passwordEchoEditing) {
110
str.fill(m_passwordCharacter);
113
// replace certain non-printable characters with spaces (to avoid
114
// drawing boxes when using fonts that don't have glyphs for such
116
QChar* uc = str.data();
117
for (int i = 0; i < (int)str.length(); ++i) {
118
if ((uc[i] < 0x20 && uc[i] != 0x09)
119
|| uc[i] == QChar::LineSeparator
120
|| uc[i] == QChar::ParagraphSeparator
121
|| uc[i] == QChar::ObjectReplacementCharacter)
122
uc[i] = QChar(0x0020);
125
m_textLayout.setText(str);
128
option.setTextDirection(m_layoutDirection);
129
option.setFlags(QTextOption::IncludeTrailingSpaces);
130
m_textLayout.setTextOption(option);
132
m_textLayout.beginLayout();
133
QTextLine l = m_textLayout.createLine();
134
m_textLayout.endLayout();
135
m_ascent = qRound(l.ascent());
137
if (str != orig || forceUpdate)
138
emit displayTextChanged(str);
141
#ifndef QT_NO_CLIPBOARD
145
Copies the currently selected text into the clipboard using the given
148
\note If the echo mode is set to a mode other than Normal then copy
149
will not work. This is to prevent using copy as a method of bypassing
150
password features of the line control.
152
void QLineControl::copy(QClipboard::Mode mode) const
154
QString t = selectedText();
155
if (!t.isEmpty() && m_echoMode == QLineEdit::Normal) {
156
disconnect(QApplication::clipboard(), SIGNAL(selectionChanged()), this, 0);
157
QApplication::clipboard()->setText(t, mode);
158
connect(QApplication::clipboard(), SIGNAL(selectionChanged()),
159
this, SLOT(_q_clipboardChanged()));
166
Inserts the text stored in the application clipboard into the line
171
void QLineControl::paste(QClipboard::Mode clipboardMode)
173
QString clip = QApplication::clipboard()->text(clipboardMode);
174
if (!clip.isEmpty() || hasSelectedText()) {
175
separate(); //make it a separate undo/redo command
181
#endif // !QT_NO_CLIPBOARD
186
Handles the behavior for the backspace key or function.
187
Removes the current selection if there is a selection, otherwise
188
removes the character prior to the cursor position.
192
void QLineControl::backspace()
194
int priorState = m_undoState;
195
if (hasSelectedText()) {
196
removeSelectedText();
197
} else if (m_cursor) {
200
m_cursor = prevMaskBlank(m_cursor);
201
QChar uc = m_text.at(m_cursor);
202
if (m_cursor > 0 && uc.unicode() >= 0xdc00 && uc.unicode() < 0xe000) {
203
// second half of a surrogate, check if we have the first half as well,
204
// if yes delete both at once
205
uc = m_text.at(m_cursor - 1);
206
if (uc.unicode() >= 0xd800 && uc.unicode() < 0xdc00) {
207
internalDelete(true);
211
internalDelete(true);
213
finishChange(priorState);
219
Handles the behavior for the delete key or function.
220
Removes the current selection if there is a selection, otherwise
221
removes the character after the cursor position.
225
void QLineControl::del()
227
int priorState = m_undoState;
228
if (hasSelectedText()) {
229
removeSelectedText();
231
int n = m_textLayout.nextCursorPosition(m_cursor) - m_cursor;
235
finishChange(priorState);
241
Inserts the given \a newText at the current cursor position.
242
If there is any selected text it is removed prior to insertion of
245
void QLineControl::insert(const QString &newText)
247
int priorState = m_undoState;
248
removeSelectedText();
249
internalInsert(newText);
250
finishChange(priorState);
256
Clears the line control text.
258
void QLineControl::clear()
260
int priorState = m_undoState;
262
m_selend = m_text.length();
263
removeSelectedText();
265
finishChange(priorState, /*update*/false, /*edited*/false);
271
Sets \a length characters from the given \a start position as selected.
272
The given \a start position must be within the current text for
273
the line control. If \a length characters cannot be selected, then
274
the selection will extend to the end of the current text.
276
void QLineControl::setSelection(int start, int length)
278
if(start < 0 || start > (int)m_text.length()){
279
qWarning("QLineControl::setSelection: Invalid start position");
284
if (start == m_selstart && start + length == m_selend)
287
m_selend = qMin(start + length, (int)m_text.length());
289
} else if (length < 0){
290
if (start == m_selend && start + length == m_selstart)
292
m_selstart = qMax(start + length, 0);
294
m_cursor = m_selstart;
295
} else if (m_selstart != m_selend) {
301
emitCursorPositionChanged();
304
emit selectionChanged();
305
emitCursorPositionChanged();
308
void QLineControl::_q_clipboardChanged()
312
void QLineControl::_q_deleteSelected()
314
if (!hasSelectedText())
317
int priorState = m_undoState;
318
emit resetInputContext();
319
removeSelectedText();
321
finishChange(priorState);
327
Initializes the line control with a starting text value of \a txt.
329
void QLineControl::init(const QString &txt)
333
m_cursor = m_text.length();
339
Sets the password echo editing to \a editing. If password echo editing
340
is true, then the text of the password is displayed even if the echo
341
mode is set to QLineEdit::PasswordEchoOnEdit. Password echoing editing
342
does not affect other echo modes.
344
void QLineControl::updatePasswordEchoEditing(bool editing)
346
cancelPasswordEchoTimer();
347
m_passwordEchoEditing = editing;
354
Returns the cursor position of the given \a x pixel value in relation
355
to the displayed text. The given \a betweenOrOn specified what kind
356
of cursor position is requested.
358
int QLineControl::xToPos(int x, QTextLine::CursorPosition betweenOrOn) const
360
return m_textLayout.lineAt(0).xToCursor(x, betweenOrOn);
366
Returns the bounds of the current cursor, as defined as a
367
between characters cursor.
369
QRect QLineControl::cursorRect() const
371
QTextLine l = m_textLayout.lineAt(0);
373
if (m_preeditCursor != -1)
374
c += m_preeditCursor;
375
int cix = qRound(l.cursorToX(c));
376
int w = m_cursorWidth;
377
int ch = l.height() + 1;
379
return QRect(cix-5, 0, w+9, ch);
385
Fixes the current text so that it is valid given any set validators.
387
Returns true if the text was changed. Otherwise returns false.
389
bool QLineControl::fixup() // this function assumes that validate currently returns != Acceptable
391
#ifndef QT_NO_VALIDATOR
393
QString textCopy = m_text;
394
int cursorCopy = m_cursor;
395
m_validator->fixup(textCopy);
396
if (m_validator->validate(textCopy, cursorCopy) == QValidator::Acceptable) {
397
if (textCopy != m_text || cursorCopy != m_cursor)
398
internalSetText(textCopy, cursorCopy);
409
Moves the cursor to the given position \a pos. If \a mark is true will
410
adjust the currently selected text.
412
void QLineControl::moveCursor(int pos, bool mark)
414
if (pos != m_cursor) {
417
pos = pos > m_cursor ? nextMaskBlank(pos) : prevMaskBlank(pos);
421
if (m_selend > m_selstart && m_cursor == m_selstart)
423
else if (m_selend > m_selstart && m_cursor == m_selend)
427
m_selstart = qMin(anchor, pos);
428
m_selend = qMax(anchor, pos);
434
if (mark || m_selDirty) {
436
emit selectionChanged();
438
emitCursorPositionChanged();
444
Applies the given input method event \a event to the text of the line
447
void QLineControl::processInputMethodEvent(QInputMethodEvent *event)
450
bool isGettingInput = !event->commitString().isEmpty()
451
|| event->preeditString() != preeditAreaText()
452
|| event->replacementLength() > 0;
453
bool cursorPositionChanged = false;
455
if (isGettingInput) {
456
// If any text is being input, remove selected text.
457
priorState = m_undoState;
458
if (echoMode() == QLineEdit::PasswordEchoOnEdit && !passwordEchoEditing()) {
459
updatePasswordEchoEditing(true);
461
m_selend = m_text.length();
463
removeSelectedText();
466
int c = m_cursor; // cursor position after insertion of commit string
467
if (event->replacementStart() <= 0)
468
c += event->commitString().length() - qMin(-event->replacementStart(), event->replacementLength());
470
m_cursor += event->replacementStart();
474
// insert commit string
475
if (event->replacementLength()) {
476
m_selstart = m_cursor;
477
m_selend = m_selstart + event->replacementLength();
478
removeSelectedText();
480
if (!event->commitString().isEmpty()) {
481
internalInsert(event->commitString());
482
cursorPositionChanged = true;
485
m_cursor = qBound(0, c, m_text.length());
487
for (int i = 0; i < event->attributes().size(); ++i) {
488
const QInputMethodEvent::Attribute &a = event->attributes().at(i);
489
if (a.type == QInputMethodEvent::Selection) {
490
m_cursor = qBound(0, a.start + a.length, m_text.length());
492
m_selstart = qMax(0, qMin(a.start, m_text.length()));
494
if (m_selend < m_selstart) {
495
qSwap(m_selstart, m_selend);
498
m_selstart = m_selend = 0;
500
cursorPositionChanged = true;
504
setPreeditArea(m_cursor, event->preeditString());
506
const int oldPreeditCursor = m_preeditCursor;
507
m_preeditCursor = event->preeditString().length();
508
m_hideCursor = false;
509
QList<QTextLayout::FormatRange> formats;
510
for (int i = 0; i < event->attributes().size(); ++i) {
511
const QInputMethodEvent::Attribute &a = event->attributes().at(i);
512
if (a.type == QInputMethodEvent::Cursor) {
513
m_preeditCursor = a.start;
514
m_hideCursor = !a.length;
515
} else if (a.type == QInputMethodEvent::TextFormat) {
516
QTextCharFormat f = qvariant_cast<QTextFormat>(a.value).toCharFormat();
518
QTextLayout::FormatRange o;
519
o.start = a.start + m_cursor;
526
m_textLayout.setAdditionalFormats(formats);
527
updateDisplayText(/*force*/ true);
528
if (cursorPositionChanged)
529
emitCursorPositionChanged();
530
else if (m_preeditCursor != oldPreeditCursor)
531
emit updateMicroFocus();
533
finishChange(priorState);
539
Draws the display text for the line control using the given
540
\a painter, \a clip, and \a offset. Which aspects of the display text
541
are drawn is specified by the given \a flags.
543
If the flags contain DrawSelections, then the selection or input mask
544
backgrounds and foregrounds will be applied before drawing the text.
546
If the flags contain DrawCursor a cursor of the current cursorWidth()
547
will be drawn after drawing the text.
549
The display text will only be drawn if the flags contain DrawText
551
void QLineControl::draw(QPainter *painter, const QPoint &offset, const QRect &clip, int flags)
553
QVector<QTextLayout::FormatRange> selections;
554
if (flags & DrawSelections) {
555
QTextLayout::FormatRange o;
556
if (m_selstart < m_selend) {
557
o.start = m_selstart;
558
o.length = m_selend - m_selstart;
559
o.format.setBackground(m_palette.brush(QPalette::Highlight));
560
o.format.setForeground(m_palette.brush(QPalette::HighlightedText));
563
if(!m_blinkPeriod || m_blinkStatus){
566
o.format.setBackground(m_palette.brush(QPalette::Text));
567
o.format.setForeground(m_palette.brush(QPalette::Window));
570
selections.append(o);
573
if (flags & DrawText)
574
m_textLayout.draw(painter, offset, selections, clip);
576
if (flags & DrawCursor){
577
int cursor = m_cursor;
578
if (m_preeditCursor != -1)
579
cursor += m_preeditCursor;
580
if (!m_hideCursor && (!m_blinkPeriod || m_blinkStatus))
581
m_textLayout.drawCursor(painter, offset, cursor, m_cursorWidth);
588
Sets the selection to cover the word at the given cursor position.
589
The word boundaries are defined by the behavior of QTextLayout::SkipWords
592
void QLineControl::selectWordAtPos(int cursor)
594
int next = cursor + 1;
597
int c = m_textLayout.previousCursorPosition(next, QTextLayout::SkipWords);
598
moveCursor(c, false);
599
// ## text layout should support end of words.
600
int end = m_textLayout.nextCursorPosition(c, QTextLayout::SkipWords);
601
while (end > cursor && m_text[end-1].isSpace())
603
moveCursor(end, true);
609
Completes a change to the line control text. If the change is not valid
610
will undo the line control state back to the given \a validateFromState.
612
If \a edited is true and the change is valid, will emit textEdited() in
613
addition to textChanged(). Otherwise only emits textChanged() on a valid
616
The \a update value is currently unused.
618
bool QLineControl::finishChange(int validateFromState, bool update, bool edited)
621
bool lineDirty = m_selDirty;
624
bool wasValidInput = m_validInput;
626
#ifndef QT_NO_VALIDATOR
628
m_validInput = false;
629
QString textCopy = m_text;
630
int cursorCopy = m_cursor;
631
m_validInput = (m_validator->validate(textCopy, cursorCopy) != QValidator::Invalid);
633
if (m_text != textCopy) {
634
internalSetText(textCopy, cursorCopy);
637
m_cursor = cursorCopy;
641
if (validateFromState >= 0 && wasValidInput && !m_validInput) {
642
if (m_transactions.count())
644
internalUndo(validateFromState);
645
m_history.resize(m_undoState);
646
if (m_modifiedState > m_undoState)
647
m_modifiedState = -1;
652
lineDirty |= m_textDirty;
655
QString actualText = text();
657
emit textEdited(actualText);
658
emit textChanged(actualText);
663
emit selectionChanged();
665
emitCursorPositionChanged();
672
An internal function for setting the text of the line control.
674
void QLineControl::internalSetText(const QString &txt, int pos, bool edited)
676
cancelPasswordEchoTimer();
678
emit resetInputContext();
679
QString oldText = m_text;
681
m_text = maskString(0, txt, true);
682
m_text += clearString(m_text.length(), m_maxLength - m_text.length());
684
m_text = txt.isEmpty() ? txt : txt.left(m_maxLength);
687
m_modifiedState = m_undoState = 0;
688
m_cursor = (pos < 0 || pos > m_text.length()) ? m_text.length() : pos;
689
m_textDirty = (oldText != m_text);
690
finishChange(-1, true, edited);
697
Adds the given \a command to the undo history
698
of the line control. Does not apply the command.
700
void QLineControl::addCommand(const Command &cmd)
702
if (m_separator && m_undoState && m_history[m_undoState - 1].type != Separator) {
703
m_history.resize(m_undoState + 2);
704
m_history[m_undoState++] = Command(Separator, m_cursor, 0, m_selstart, m_selend);
706
m_history.resize(m_undoState + 1);
709
m_history[m_undoState++] = cmd;
715
Inserts the given string \a s into the line
718
Also adds the appropriate commands into the undo history.
719
This function does not call finishChange(), and may leave the text
722
void QLineControl::internalInsert(const QString &s)
724
#ifdef QT_GUI_PASSWORD_ECHO_DELAY
725
if (m_echoMode == QLineEdit::Password) {
726
if (m_passwordEchoTimer != 0)
727
killTimer(m_passwordEchoTimer);
728
m_passwordEchoTimer = startTimer(qt_passwordEchoDelay);
731
if (hasSelectedText())
732
addCommand(Command(SetSelection, m_cursor, 0, m_selstart, m_selend));
734
QString ms = maskString(m_cursor, s);
735
for (int i = 0; i < (int) ms.length(); ++i) {
736
addCommand (Command(DeleteSelection, m_cursor + i, m_text.at(m_cursor + i), -1, -1));
737
addCommand(Command(Insert, m_cursor + i, ms.at(i), -1, -1));
739
m_text.replace(m_cursor, ms.length(), ms);
740
m_cursor += ms.length();
741
m_cursor = nextMaskBlank(m_cursor);
744
int remaining = m_maxLength - m_text.length();
745
if (remaining != 0) {
746
m_text.insert(m_cursor, s.left(remaining));
747
for (int i = 0; i < (int) s.left(remaining).length(); ++i)
748
addCommand(Command(Insert, m_cursor++, s.at(i), -1, -1));
757
deletes a single character from the current text. If \a wasBackspace,
758
the character prior to the cursor is removed. Otherwise the character
759
after the cursor is removed.
761
Also adds the appropriate commands into the undo history.
762
This function does not call finishChange(), and may leave the text
765
void QLineControl::internalDelete(bool wasBackspace)
767
if (m_cursor < (int) m_text.length()) {
768
cancelPasswordEchoTimer();
769
if (hasSelectedText())
770
addCommand(Command(SetSelection, m_cursor, 0, m_selstart, m_selend));
771
addCommand(Command((CommandType)((m_maskData ? 2 : 0) + (wasBackspace ? Remove : Delete)),
772
m_cursor, m_text.at(m_cursor), -1, -1));
774
m_text.replace(m_cursor, 1, clearString(m_cursor, 1));
775
addCommand(Command(Insert, m_cursor, m_text.at(m_cursor), -1, -1));
777
m_text.remove(m_cursor, 1);
786
removes the currently selected text from the line control.
788
Also adds the appropriate commands into the undo history.
789
This function does not call finishChange(), and may leave the text
792
void QLineControl::removeSelectedText()
794
if (m_selstart < m_selend && m_selend <= (int) m_text.length()) {
795
cancelPasswordEchoTimer();
798
addCommand(Command(SetSelection, m_cursor, 0, m_selstart, m_selend));
799
if (m_selstart <= m_cursor && m_cursor < m_selend) {
800
// cursor is within the selection. Split up the commands
801
// to be able to restore the correct cursor position
802
for (i = m_cursor; i >= m_selstart; --i)
803
addCommand (Command(DeleteSelection, i, m_text.at(i), -1, 1));
804
for (i = m_selend - 1; i > m_cursor; --i)
805
addCommand (Command(DeleteSelection, i - m_cursor + m_selstart - 1, m_text.at(i), -1, -1));
807
for (i = m_selend-1; i >= m_selstart; --i)
808
addCommand (Command(RemoveSelection, i, m_text.at(i), -1, -1));
811
m_text.replace(m_selstart, m_selend - m_selstart, clearString(m_selstart, m_selend - m_selstart));
812
for (int i = 0; i < m_selend - m_selstart; ++i)
813
addCommand(Command(Insert, m_selstart + i, m_text.at(m_selstart + i), -1, -1));
815
m_text.remove(m_selstart, m_selend - m_selstart);
817
if (m_cursor > m_selstart)
818
m_cursor -= qMin(m_cursor, m_selend) - m_selstart;
827
Parses the input mask specified by \a maskFields to generate
828
the mask data used to handle input masks.
830
void QLineControl::parseInputMask(const QString &maskFields)
832
int delimiter = maskFields.indexOf(QLatin1Char(';'));
833
if (maskFields.isEmpty() || delimiter == 0) {
835
delete [] m_maskData;
838
internalSetText(QString());
843
if (delimiter == -1) {
844
m_blank = QLatin1Char(' ');
845
m_inputMask = maskFields;
847
m_inputMask = maskFields.left(delimiter);
848
m_blank = (delimiter + 1 < maskFields.length()) ? maskFields[delimiter + 1] : QLatin1Char(' ');
851
// calculate m_maxLength / m_maskData length
854
for (int i=0; i<m_inputMask.length(); i++) {
855
c = m_inputMask.at(i);
856
if (i > 0 && m_inputMask.at(i-1) == QLatin1Char('\\')) {
860
if (c != QLatin1Char('\\') && c != QLatin1Char('!') &&
861
c != QLatin1Char('<') && c != QLatin1Char('>') &&
862
c != QLatin1Char('{') && c != QLatin1Char('}') &&
863
c != QLatin1Char('[') && c != QLatin1Char(']'))
867
delete [] m_maskData;
868
m_maskData = new MaskInputData[m_maxLength];
870
MaskInputData::Casemode m = MaskInputData::NoCaseMode;
875
for (int i = 0; i < m_inputMask.length(); i++) {
876
c = m_inputMask.at(i);
879
m_maskData[index].maskChar = c;
880
m_maskData[index].separator = s;
881
m_maskData[index].caseMode = m;
884
} else if (c == QLatin1Char('<')) {
885
m = MaskInputData::Lower;
886
} else if (c == QLatin1Char('>')) {
887
m = MaskInputData::Upper;
888
} else if (c == QLatin1Char('!')) {
889
m = MaskInputData::NoCaseMode;
890
} else if (c != QLatin1Char('{') && c != QLatin1Char('}') && c != QLatin1Char('[') && c != QLatin1Char(']')) {
891
switch (c.unicode()) {
917
m_maskData[index].maskChar = c;
918
m_maskData[index].separator = s;
919
m_maskData[index].caseMode = m;
924
internalSetText(m_text);
931
checks if the key is valid compared to the inputMask
933
bool QLineControl::isValidInput(QChar key, QChar mask) const
935
switch (mask.unicode()) {
941
if (key.isLetter() || key == m_blank)
945
if (key.isLetterOrNumber())
949
if (key.isLetterOrNumber() || key == m_blank)
957
if (key.isPrint() || key == m_blank)
965
if (key.isNumber() || key == m_blank)
969
if (key.isNumber() && key.digitValue() > 0)
973
if ((key.isNumber() && key.digitValue() > 0) || key == m_blank)
977
if (key.isNumber() || key == QLatin1Char('+') || key == QLatin1Char('-') || key == m_blank)
981
if (key == QLatin1Char('0') || key == QLatin1Char('1'))
985
if (key == QLatin1Char('0') || key == QLatin1Char('1') || key == m_blank)
989
if (key.isNumber() || (key >= QLatin1Char('a') && key <= QLatin1Char('f')) || (key >= QLatin1Char('A') && key <= QLatin1Char('F')))
993
if (key.isNumber() || (key >= QLatin1Char('a') && key <= QLatin1Char('f')) || (key >= QLatin1Char('A') && key <= QLatin1Char('F')) || key == m_blank)
1005
Returns true if the given text \a str is valid for any
1006
validator or input mask set for the line control.
1008
Otherwise returns false
1010
bool QLineControl::hasAcceptableInput(const QString &str) const
1012
#ifndef QT_NO_VALIDATOR
1013
QString textCopy = str;
1014
int cursorCopy = m_cursor;
1015
if (m_validator && m_validator->validate(textCopy, cursorCopy)
1016
!= QValidator::Acceptable)
1023
if (str.length() != m_maxLength)
1026
for (int i=0; i < m_maxLength; ++i) {
1027
if (m_maskData[i].separator) {
1028
if (str.at(i) != m_maskData[i].maskChar)
1031
if (!isValidInput(str.at(i), m_maskData[i].maskChar))
1041
Applies the inputMask on \a str starting from position \a pos in the mask. \a clear
1042
specifies from where characters should be gotten when a separator is met in \a str - true means
1043
that blanks will be used, false that previous input is used.
1044
Calling this when no inputMask is set is undefined.
1046
QString QLineControl::maskString(uint pos, const QString &str, bool clear) const
1048
if (pos >= (uint)m_maxLength)
1049
return QString::fromLatin1("");
1052
fill = clear ? clearString(0, m_maxLength) : m_text;
1055
QString s = QString::fromLatin1("");
1057
while (i < m_maxLength) {
1058
if (strIndex < str.length()) {
1059
if (m_maskData[i].separator) {
1060
s += m_maskData[i].maskChar;
1061
if (str[(int)strIndex] == m_maskData[i].maskChar)
1065
if (isValidInput(str[(int)strIndex], m_maskData[i].maskChar)) {
1066
switch (m_maskData[i].caseMode) {
1067
case MaskInputData::Upper:
1068
s += str[(int)strIndex].toUpper();
1070
case MaskInputData::Lower:
1071
s += str[(int)strIndex].toLower();
1074
s += str[(int)strIndex];
1078
// search for separator first
1079
int n = findInMask(i, true, true, str[(int)strIndex]);
1081
if (str.length() != 1 || i == 0 || (i > 0 && (!m_maskData[i-1].separator || m_maskData[i-1].maskChar != str[(int)strIndex]))) {
1082
s += fill.mid(i, n-i+1);
1083
i = n + 1; // update i to find + 1
1086
// search for valid m_blank if not
1087
n = findInMask(i, true, false, str[(int)strIndex]);
1089
s += fill.mid(i, n-i);
1090
switch (m_maskData[n].caseMode) {
1091
case MaskInputData::Upper:
1092
s += str[(int)strIndex].toUpper();
1094
case MaskInputData::Lower:
1095
s += str[(int)strIndex].toLower();
1098
s += str[(int)strIndex];
1100
i = n + 1; // updates i to find + 1
1118
Returns a "cleared" string with only separators and blank chars.
1119
Calling this when no inputMask is set is undefined.
1121
QString QLineControl::clearString(uint pos, uint len) const
1123
if (pos >= (uint)m_maxLength)
1127
int end = qMin((uint)m_maxLength, pos + len);
1128
for (int i = pos; i < end; ++i)
1129
if (m_maskData[i].separator)
1130
s += m_maskData[i].maskChar;
1140
Strips blank parts of the input in a QLineControl when an inputMask is set,
1141
separators are still included. Typically "127.0__.0__.1__" becomes "127.0.0.1".
1143
QString QLineControl::stripString(const QString &str) const
1149
int end = qMin(m_maxLength, (int)str.length());
1150
for (int i = 0; i < end; ++i)
1151
if (m_maskData[i].separator)
1152
s += m_maskData[i].maskChar;
1154
if (str[i] != m_blank)
1162
searches forward/backward in m_maskData for either a separator or a m_blank
1164
int QLineControl::findInMask(int pos, bool forward, bool findSeparator, QChar searchChar) const
1166
if (pos >= m_maxLength || pos < 0)
1169
int end = forward ? m_maxLength : -1;
1170
int step = forward ? 1 : -1;
1174
if (findSeparator) {
1175
if (m_maskData[i].separator && m_maskData[i].maskChar == searchChar)
1178
if (!m_maskData[i].separator) {
1179
if (searchChar.isNull())
1181
else if (isValidInput(searchChar, m_maskData[i].maskChar))
1190
void QLineControl::internalUndo(int until)
1192
if (!isUndoAvailable())
1194
cancelPasswordEchoTimer();
1196
while (m_undoState && m_undoState > until) {
1197
Command& cmd = m_history[--m_undoState];
1200
m_text.remove(cmd.pos, 1);
1204
m_selstart = cmd.selStart;
1205
m_selend = cmd.selEnd;
1209
case RemoveSelection:
1210
m_text.insert(cmd.pos, cmd.uc);
1211
m_cursor = cmd.pos + 1;
1214
case DeleteSelection:
1215
m_text.insert(cmd.pos, cmd.uc);
1221
if (until < 0 && m_undoState) {
1222
Command& next = m_history[m_undoState-1];
1223
if (next.type != cmd.type && next.type < RemoveSelection
1224
&& (cmd.type < RemoveSelection || next.type == Separator))
1229
emitCursorPositionChanged();
1232
void QLineControl::internalRedo()
1234
if (!isRedoAvailable())
1237
while (m_undoState < (int)m_history.size()) {
1238
Command& cmd = m_history[m_undoState++];
1241
m_text.insert(cmd.pos, cmd.uc);
1242
m_cursor = cmd.pos + 1;
1245
m_selstart = cmd.selStart;
1246
m_selend = cmd.selEnd;
1251
case RemoveSelection:
1252
case DeleteSelection:
1253
m_text.remove(cmd.pos, 1);
1254
m_selstart = cmd.selStart;
1255
m_selend = cmd.selEnd;
1259
m_selstart = cmd.selStart;
1260
m_selend = cmd.selEnd;
1264
if (m_undoState < (int)m_history.size()) {
1265
Command& next = m_history[m_undoState];
1266
if (next.type != cmd.type && cmd.type < RemoveSelection && next.type != Separator
1267
&& (next.type < RemoveSelection || cmd.type == Separator))
1272
emitCursorPositionChanged();
1278
If the current cursor position differs from the last emitted cursor
1279
position, emits cursorPositionChanged().
1281
void QLineControl::emitCursorPositionChanged()
1283
if (m_cursor != m_lastCursorPos) {
1284
const int oldLast = m_lastCursorPos;
1285
m_lastCursorPos = m_cursor;
1286
cursorPositionChanged(oldLast, m_cursor);
1290
#ifndef QT_NO_COMPLETER
1291
// iterating forward(dir=1)/backward(dir=-1) from the
1292
// current row based. dir=0 indicates a new completion prefix was set.
1293
bool QLineControl::advanceToEnabledItem(int dir)
1295
int start = m_completer->currentRow();
1298
int i = start + dir;
1299
if (dir == 0) dir = 1;
1301
if (!m_completer->setCurrentRow(i)) {
1302
if (!m_completer->wrapAround())
1304
i = i > 0 ? 0 : m_completer->completionCount() - 1;
1306
QModelIndex currentIndex = m_completer->currentIndex();
1307
if (m_completer->completionModel()->flags(currentIndex) & Qt::ItemIsEnabled)
1311
} while (i != start);
1313
m_completer->setCurrentRow(start); // restore
1317
void QLineControl::complete(int key)
1319
if (!m_completer || isReadOnly() || echoMode() != QLineEdit::Normal)
1322
QString text = this->text();
1323
if (m_completer->completionMode() == QCompleter::InlineCompletion) {
1324
if (key == Qt::Key_Backspace)
1327
if (key == Qt::Key_Up || key == Qt::Key_Down) {
1328
if (textAfterSelection().length())
1330
QString prefix = hasSelectedText() ? textBeforeSelection()
1332
if (text.compare(m_completer->currentCompletion(), m_completer->caseSensitivity()) != 0
1333
|| prefix.compare(m_completer->completionPrefix(), m_completer->caseSensitivity()) != 0) {
1334
m_completer->setCompletionPrefix(prefix);
1336
n = (key == Qt::Key_Up) ? -1 : +1;
1339
m_completer->setCompletionPrefix(text);
1341
if (!advanceToEnabledItem(n))
1344
#ifndef QT_KEYPAD_NAVIGATION
1345
if (text.isEmpty()) {
1346
m_completer->popup()->hide();
1350
m_completer->setCompletionPrefix(text);
1353
m_completer->complete();
1357
void QLineControl::setCursorBlinkPeriod(int msec)
1359
if (msec == m_blinkPeriod)
1362
killTimer(m_blinkTimer);
1365
m_blinkTimer = startTimer(msec / 2);
1369
if (m_blinkStatus == 1)
1370
emit updateNeeded(inputMask().isEmpty() ? cursorRect() : QRect());
1372
m_blinkPeriod = msec;
1375
void QLineControl::resetCursorBlinkTimer()
1377
if (m_blinkPeriod == 0 || m_blinkTimer == 0)
1379
killTimer(m_blinkTimer);
1380
m_blinkTimer = startTimer(m_blinkPeriod / 2);
1384
void QLineControl::timerEvent(QTimerEvent *event)
1386
if (event->timerId() == m_blinkTimer) {
1387
m_blinkStatus = !m_blinkStatus;
1388
emit updateNeeded(inputMask().isEmpty() ? cursorRect() : QRect());
1389
} else if (event->timerId() == m_deleteAllTimer) {
1390
killTimer(m_deleteAllTimer);
1391
m_deleteAllTimer = 0;
1393
} else if (event->timerId() == m_tripleClickTimer) {
1394
killTimer(m_tripleClickTimer);
1395
m_tripleClickTimer = 0;
1396
#ifdef QT_GUI_PASSWORD_ECHO_DELAY
1397
} else if (event->timerId() == m_passwordEchoTimer) {
1398
killTimer(m_passwordEchoTimer);
1399
m_passwordEchoTimer = 0;
1400
updateDisplayText();
1405
bool QLineControl::processEvent(QEvent* ev)
1407
#ifdef QT_KEYPAD_NAVIGATION
1408
if (QApplication::keypadNavigationEnabled()) {
1409
if ((ev->type() == QEvent::KeyPress) || (ev->type() == QEvent::KeyRelease)) {
1410
QKeyEvent *ke = (QKeyEvent *)ev;
1411
if (ke->key() == Qt::Key_Back) {
1412
if (ke->isAutoRepeat()) {
1413
// Swallow it. We don't want back keys running amok.
1417
if ((ev->type() == QEvent::KeyRelease)
1419
&& m_deleteAllTimer) {
1420
killTimer(m_deleteAllTimer);
1421
m_deleteAllTimer = 0;
1431
#ifndef QT_NO_GRAPHICSVIEW
1432
case QEvent::GraphicsSceneMouseDoubleClick:
1433
case QEvent::GraphicsSceneMouseMove:
1434
case QEvent::GraphicsSceneMouseRelease:
1435
case QEvent::GraphicsSceneMousePress:{
1436
QGraphicsSceneMouseEvent *gvEv = static_cast<QGraphicsSceneMouseEvent*>(ev);
1437
QMouseEvent* mouse = new QMouseEvent(ev->type(),
1438
gvEv->pos().toPoint(), gvEv->button(), gvEv->buttons(), gvEv->modifiers());
1439
processMouseEvent(mouse); break;
1442
case QEvent::MouseButtonPress:
1443
case QEvent::MouseButtonRelease:
1444
case QEvent::MouseButtonDblClick:
1445
case QEvent::MouseMove:
1446
processMouseEvent(static_cast<QMouseEvent*>(ev)); break;
1447
case QEvent::KeyPress:
1448
case QEvent::KeyRelease:
1449
processKeyEvent(static_cast<QKeyEvent*>(ev)); break;
1450
case QEvent::InputMethod:
1451
processInputMethodEvent(static_cast<QInputMethodEvent*>(ev)); break;
1452
#ifndef QT_NO_SHORTCUT
1453
case QEvent::ShortcutOverride:{
1456
QKeyEvent* ke = static_cast<QKeyEvent*>(ev);
1457
if (ke == QKeySequence::Copy
1458
|| ke == QKeySequence::Paste
1459
|| ke == QKeySequence::Cut
1460
|| ke == QKeySequence::Redo
1461
|| ke == QKeySequence::Undo
1462
|| ke == QKeySequence::MoveToNextWord
1463
|| ke == QKeySequence::MoveToPreviousWord
1464
|| ke == QKeySequence::MoveToStartOfDocument
1465
|| ke == QKeySequence::MoveToEndOfDocument
1466
|| ke == QKeySequence::SelectNextWord
1467
|| ke == QKeySequence::SelectPreviousWord
1468
|| ke == QKeySequence::SelectStartOfLine
1469
|| ke == QKeySequence::SelectEndOfLine
1470
|| ke == QKeySequence::SelectStartOfBlock
1471
|| ke == QKeySequence::SelectEndOfBlock
1472
|| ke == QKeySequence::SelectStartOfDocument
1473
|| ke == QKeySequence::SelectAll
1474
|| ke == QKeySequence::SelectEndOfDocument) {
1476
} else if (ke->modifiers() == Qt::NoModifier || ke->modifiers() == Qt::ShiftModifier
1477
|| ke->modifiers() == Qt::KeypadModifier) {
1478
if (ke->key() < Qt::Key_Escape) {
1481
switch (ke->key()) {
1482
case Qt::Key_Delete:
1485
case Qt::Key_Backspace:
1502
void QLineControl::processMouseEvent(QMouseEvent* ev)
1505
switch (ev->type()) {
1506
case QEvent::GraphicsSceneMousePress:
1507
case QEvent::MouseButtonPress:{
1508
if (m_tripleClickTimer
1509
&& (ev->pos() - m_tripleClick).manhattanLength()
1510
< QApplication::startDragDistance()) {
1514
if (ev->button() == Qt::RightButton)
1517
bool mark = ev->modifiers() & Qt::ShiftModifier;
1518
int cursor = xToPos(ev->pos().x());
1519
moveCursor(cursor, mark);
1522
case QEvent::GraphicsSceneMouseDoubleClick:
1523
case QEvent::MouseButtonDblClick:
1524
if (ev->button() == Qt::LeftButton) {
1525
selectWordAtPos(xToPos(ev->pos().x()));
1526
if (m_tripleClickTimer)
1527
killTimer(m_tripleClickTimer);
1528
m_tripleClickTimer = startTimer(QApplication::doubleClickInterval());
1529
m_tripleClick = ev->pos();
1532
case QEvent::GraphicsSceneMouseRelease:
1533
case QEvent::MouseButtonRelease:
1534
#ifndef QT_NO_CLIPBOARD
1535
if (QApplication::clipboard()->supportsSelection()) {
1536
if (ev->button() == Qt::LeftButton) {
1537
copy(QClipboard::Selection);
1538
} else if (!isReadOnly() && ev->button() == Qt::MidButton) {
1540
insert(QApplication::clipboard()->text(QClipboard::Selection));
1545
case QEvent::GraphicsSceneMouseMove:
1546
case QEvent::MouseMove:
1547
if (ev->buttons() & Qt::LeftButton) {
1548
moveCursor(xToPos(ev->pos().x()), true);
1556
void QLineControl::processKeyEvent(QKeyEvent* event)
1558
bool inlineCompletionAccepted = false;
1560
#ifndef QT_NO_COMPLETER
1562
QCompleter::CompletionMode completionMode = m_completer->completionMode();
1563
if ((completionMode == QCompleter::PopupCompletion
1564
|| completionMode == QCompleter::UnfilteredPopupCompletion)
1565
&& m_completer->popup()
1566
&& m_completer->popup()->isVisible()) {
1567
// The following keys are forwarded by the completer to the widget
1568
// Ignoring the events lets the completer provide suitable default behavior
1569
switch (event->key()) {
1570
case Qt::Key_Escape:
1574
case Qt::Key_Return:
1576
#ifdef QT_KEYPAD_NAVIGATION
1577
case Qt::Key_Select:
1578
if (!QApplication::keypadNavigationEnabled())
1581
m_completer->popup()->hide(); // just hide. will end up propagating to parent
1583
break; // normal key processing
1585
} else if (completionMode == QCompleter::InlineCompletion) {
1586
switch (event->key()) {
1588
case Qt::Key_Return:
1590
#ifdef QT_KEYPAD_NAVIGATION
1591
case Qt::Key_Select:
1592
if (!QApplication::keypadNavigationEnabled())
1595
if (!m_completer->currentCompletion().isEmpty() && hasSelectedText()
1596
&& textAfterSelection().isEmpty()) {
1597
setText(m_completer->currentCompletion());
1598
inlineCompletionAccepted = true;
1601
break; // normal key processing
1605
#endif // QT_NO_COMPLETER
1607
if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) {
1608
if (hasAcceptableInput() || fixup()) {
1610
emit editingFinished();
1612
if (inlineCompletionAccepted)
1619
if (echoMode() == QLineEdit::PasswordEchoOnEdit
1620
&& !passwordEchoEditing()
1622
&& !event->text().isEmpty()
1623
#ifdef QT_KEYPAD_NAVIGATION
1624
&& event->key() != Qt::Key_Select
1625
&& event->key() != Qt::Key_Up
1626
&& event->key() != Qt::Key_Down
1627
&& event->key() != Qt::Key_Back
1629
&& !(event->modifiers() & Qt::ControlModifier)) {
1630
// Clear the edit and reset to normal echo mode while editing; the
1631
// echo mode switches back when the edit loses focus
1632
// ### resets current content. dubious code; you can
1633
// navigate with keys up, down, back, and select(?), but if you press
1634
// "left" or "right" it clears?
1635
updatePasswordEchoEditing(true);
1639
bool unknown = false;
1643
#ifndef QT_NO_SHORTCUT
1644
else if (event == QKeySequence::Undo) {
1648
else if (event == QKeySequence::Redo) {
1652
else if (event == QKeySequence::SelectAll) {
1655
#ifndef QT_NO_CLIPBOARD
1656
else if (event == QKeySequence::Copy) {
1659
else if (event == QKeySequence::Paste) {
1660
if (!isReadOnly()) {
1661
QClipboard::Mode mode = QClipboard::Clipboard;
1663
if (event->modifiers() == (Qt::CTRL | Qt::SHIFT) && event->key() == Qt::Key_Insert)
1664
mode = QClipboard::Selection;
1669
else if (event == QKeySequence::Cut) {
1670
if (!isReadOnly()) {
1675
else if (event == QKeySequence::DeleteEndOfLine) {
1676
if (!isReadOnly()) {
1677
setSelection(cursor(), end());
1682
#endif //QT_NO_CLIPBOARD
1683
else if (event == QKeySequence::MoveToStartOfLine || event == QKeySequence::MoveToStartOfBlock) {
1686
else if (event == QKeySequence::MoveToEndOfLine || event == QKeySequence::MoveToEndOfBlock) {
1689
else if (event == QKeySequence::SelectStartOfLine || event == QKeySequence::SelectStartOfBlock) {
1692
else if (event == QKeySequence::SelectEndOfLine || event == QKeySequence::SelectEndOfBlock) {
1695
else if (event == QKeySequence::MoveToNextChar) {
1696
#if !defined(Q_WS_WIN) || defined(QT_NO_COMPLETER)
1697
if (hasSelectedText()) {
1699
if (hasSelectedText() && m_completer
1700
&& m_completer->completionMode() == QCompleter::InlineCompletion) {
1702
moveCursor(selectionEnd(), false);
1704
cursorForward(0, layoutDirection() == Qt::LeftToRight ? 1 : -1);
1707
else if (event == QKeySequence::SelectNextChar) {
1708
cursorForward(1, layoutDirection() == Qt::LeftToRight ? 1 : -1);
1710
else if (event == QKeySequence::MoveToPreviousChar) {
1711
#if !defined(Q_WS_WIN) || defined(QT_NO_COMPLETER)
1712
if (hasSelectedText()) {
1714
if (hasSelectedText() && m_completer
1715
&& m_completer->completionMode() == QCompleter::InlineCompletion) {
1717
moveCursor(selectionStart(), false);
1719
cursorForward(0, layoutDirection() == Qt::LeftToRight ? -1 : 1);
1722
else if (event == QKeySequence::SelectPreviousChar) {
1723
cursorForward(1, layoutDirection() == Qt::LeftToRight ? -1 : 1);
1725
else if (event == QKeySequence::MoveToNextWord) {
1726
if (echoMode() == QLineEdit::Normal)
1727
layoutDirection() == Qt::LeftToRight ? cursorWordForward(0) : cursorWordBackward(0);
1729
layoutDirection() == Qt::LeftToRight ? end(0) : home(0);
1731
else if (event == QKeySequence::MoveToPreviousWord) {
1732
if (echoMode() == QLineEdit::Normal)
1733
layoutDirection() == Qt::LeftToRight ? cursorWordBackward(0) : cursorWordForward(0);
1734
else if (!isReadOnly()) {
1735
layoutDirection() == Qt::LeftToRight ? home(0) : end(0);
1738
else if (event == QKeySequence::SelectNextWord) {
1739
if (echoMode() == QLineEdit::Normal)
1740
layoutDirection() == Qt::LeftToRight ? cursorWordForward(1) : cursorWordBackward(1);
1742
layoutDirection() == Qt::LeftToRight ? end(1) : home(1);
1744
else if (event == QKeySequence::SelectPreviousWord) {
1745
if (echoMode() == QLineEdit::Normal)
1746
layoutDirection() == Qt::LeftToRight ? cursorWordBackward(1) : cursorWordForward(1);
1748
layoutDirection() == Qt::LeftToRight ? home(1) : end(1);
1750
else if (event == QKeySequence::Delete) {
1754
else if (event == QKeySequence::DeleteEndOfWord) {
1755
if (!isReadOnly()) {
1756
cursorWordForward(true);
1760
else if (event == QKeySequence::DeleteStartOfWord) {
1761
if (!isReadOnly()) {
1762
cursorWordBackward(true);
1766
#endif // QT_NO_SHORTCUT
1768
bool handled = false;
1770
if (event->key() == Qt::Key_Up || event->key() == Qt::Key_Down) {
1771
Qt::KeyboardModifiers myModifiers = (event->modifiers() & ~Qt::KeypadModifier);
1772
if (myModifiers & Qt::ShiftModifier) {
1773
if (myModifiers == (Qt::ControlModifier|Qt::ShiftModifier)
1774
|| myModifiers == (Qt::AltModifier|Qt::ShiftModifier)
1775
|| myModifiers == Qt::ShiftModifier) {
1777
event->key() == Qt::Key_Up ? home(1) : end(1);
1780
if ((myModifiers == Qt::ControlModifier
1781
|| myModifiers == Qt::AltModifier
1782
|| myModifiers == Qt::NoModifier)) {
1783
event->key() == Qt::Key_Up ? home(0) : end(0);
1789
if (event->modifiers() & Qt::ControlModifier) {
1790
switch (event->key()) {
1791
case Qt::Key_Backspace:
1792
if (!isReadOnly()) {
1793
cursorWordBackward(true);
1797
#ifndef QT_NO_COMPLETER
1800
complete(event->key());
1803
#if defined(Q_WS_X11)
1809
if (!isReadOnly()) {
1810
setSelection(0, text().size());
1811
#ifndef QT_NO_CLIPBOARD
1822
} else { // ### check for *no* modifier
1823
switch (event->key()) {
1824
case Qt::Key_Backspace:
1825
if (!isReadOnly()) {
1827
#ifndef QT_NO_COMPLETER
1828
complete(Qt::Key_Backspace);
1832
#ifdef QT_KEYPAD_NAVIGATION
1834
if (QApplication::keypadNavigationEnabled() && !event->isAutoRepeat()
1836
if (text().length() == 0) {
1837
setText(m_cancelText);
1839
if (passwordEchoEditing())
1840
updatePasswordEchoEditing(false);
1842
emit editFocusChange(false);
1843
} else if (!m_deleteAllTimer) {
1844
m_deleteAllTimer = startTimer(750);
1858
if (event->key() == Qt::Key_Direction_L || event->key() == Qt::Key_Direction_R) {
1859
setLayoutDirection((event->key() == Qt::Key_Direction_L) ? Qt::LeftToRight : Qt::RightToLeft);
1863
if (unknown && !isReadOnly()) {
1864
QString t = event->text();
1865
if (!t.isEmpty() && t.at(0).isPrint()) {
1867
#ifndef QT_NO_COMPLETER
1868
complete(event->key());