source: trunk/src/gui/widgets/qlinecontrol.cpp

Last change on this file was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

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