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