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

Last change on this file since 651 was 651, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.2 sources.

  • Property svn:eol-style set to native
File size: 55.2 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation (qt-info@nokia.com)
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 qt-info@nokia.com.
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()
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)
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()
140{
141 QString clip = QApplication::clipboard()->text(QClipboard::Clipboard);
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();
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_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 boundries is 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 emited 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::timerEvent(QTimerEvent *event)
1312{
1313 if (event->timerId() == m_blinkTimer) {
1314 m_blinkStatus = !m_blinkStatus;
1315 emit updateNeeded(inputMask().isEmpty() ? cursorRect() : QRect());
1316 } else if (event->timerId() == m_deleteAllTimer) {
1317 killTimer(m_deleteAllTimer);
1318 m_deleteAllTimer = 0;
1319 clear();
1320 } else if (event->timerId() == m_tripleClickTimer) {
1321 killTimer(m_tripleClickTimer);
1322 m_tripleClickTimer = 0;
1323 }
1324}
1325
1326bool QLineControl::processEvent(QEvent* ev)
1327{
1328#ifdef QT_KEYPAD_NAVIGATION
1329 if (QApplication::keypadNavigationEnabled()) {
1330 if ((ev->type() == QEvent::KeyPress) || (ev->type() == QEvent::KeyRelease)) {
1331 QKeyEvent *ke = (QKeyEvent *)ev;
1332 if (ke->key() == Qt::Key_Back) {
1333 if (ke->isAutoRepeat()) {
1334 // Swallow it. We don't want back keys running amok.
1335 ke->accept();
1336 return true;
1337 }
1338 if ((ev->type() == QEvent::KeyRelease)
1339 && !isReadOnly()
1340 && m_deleteAllTimer) {
1341 killTimer(m_deleteAllTimer);
1342 m_deleteAllTimer = 0;
1343 backspace();
1344 ke->accept();
1345 return true;
1346 }
1347 }
1348 }
1349 }
1350#endif
1351 switch(ev->type()){
1352#ifndef QT_NO_GRAPHICSVIEW
1353 case QEvent::GraphicsSceneMouseMove:
1354 case QEvent::GraphicsSceneMouseRelease:
1355 case QEvent::GraphicsSceneMousePress:{
1356 QGraphicsSceneMouseEvent *gvEv = static_cast<QGraphicsSceneMouseEvent*>(ev);
1357 QMouseEvent* mouse = new QMouseEvent(ev->type(),
1358 gvEv->pos().toPoint(), gvEv->button(), gvEv->buttons(), gvEv->modifiers());
1359 processMouseEvent(mouse); break;
1360 }
1361#endif
1362 case QEvent::MouseButtonPress:
1363 case QEvent::MouseButtonRelease:
1364 case QEvent::MouseButtonDblClick:
1365 case QEvent::MouseMove:
1366 processMouseEvent(static_cast<QMouseEvent*>(ev)); break;
1367 case QEvent::KeyPress:
1368 case QEvent::KeyRelease:
1369 processKeyEvent(static_cast<QKeyEvent*>(ev)); break;
1370 case QEvent::InputMethod:
1371 processInputMethodEvent(static_cast<QInputMethodEvent*>(ev)); break;
1372#ifndef QT_NO_SHORTCUT
1373 case QEvent::ShortcutOverride:{
1374 QKeyEvent* ke = static_cast<QKeyEvent*>(ev);
1375 if (ke == QKeySequence::Copy
1376 || ke == QKeySequence::Paste
1377 || ke == QKeySequence::Cut
1378 || ke == QKeySequence::Redo
1379 || ke == QKeySequence::Undo
1380 || ke == QKeySequence::MoveToNextWord
1381 || ke == QKeySequence::MoveToPreviousWord
1382 || ke == QKeySequence::MoveToStartOfDocument
1383 || ke == QKeySequence::MoveToEndOfDocument
1384 || ke == QKeySequence::SelectNextWord
1385 || ke == QKeySequence::SelectPreviousWord
1386 || ke == QKeySequence::SelectStartOfLine
1387 || ke == QKeySequence::SelectEndOfLine
1388 || ke == QKeySequence::SelectStartOfBlock
1389 || ke == QKeySequence::SelectEndOfBlock
1390 || ke == QKeySequence::SelectStartOfDocument
1391 || ke == QKeySequence::SelectAll
1392 || ke == QKeySequence::SelectEndOfDocument) {
1393 ke->accept();
1394 } else if (ke->modifiers() == Qt::NoModifier || ke->modifiers() == Qt::ShiftModifier
1395 || ke->modifiers() == Qt::KeypadModifier) {
1396 if (ke->key() < Qt::Key_Escape) {
1397 ke->accept();
1398 } else {
1399 switch (ke->key()) {
1400 case Qt::Key_Delete:
1401 case Qt::Key_Home:
1402 case Qt::Key_End:
1403 case Qt::Key_Backspace:
1404 case Qt::Key_Left:
1405 case Qt::Key_Right:
1406 ke->accept();
1407 default:
1408 break;
1409 }
1410 }
1411 }
1412 }
1413#endif
1414 default:
1415 return false;
1416 }
1417 return true;
1418}
1419
1420void QLineControl::processMouseEvent(QMouseEvent* ev)
1421{
1422
1423 switch (ev->type()) {
1424 case QEvent::GraphicsSceneMousePress:
1425 case QEvent::MouseButtonPress:{
1426 if (m_tripleClickTimer
1427 && (ev->pos() - m_tripleClick).manhattanLength()
1428 < QApplication::startDragDistance()) {
1429 selectAll();
1430 return;
1431 }
1432 if (ev->button() == Qt::RightButton)
1433 return;
1434
1435 bool mark = ev->modifiers() & Qt::ShiftModifier;
1436 int cursor = xToPos(ev->pos().x());
1437 moveCursor(cursor, mark);
1438 break;
1439 }
1440 case QEvent::MouseButtonDblClick:
1441 if (ev->button() == Qt::LeftButton) {
1442 selectWordAtPos(xToPos(ev->pos().x()));
1443 if (m_tripleClickTimer)
1444 killTimer(m_tripleClickTimer);
1445 m_tripleClickTimer = startTimer(QApplication::doubleClickInterval());
1446 m_tripleClick = ev->pos();
1447 }
1448 break;
1449 case QEvent::GraphicsSceneMouseRelease:
1450 case QEvent::MouseButtonRelease:
1451#ifndef QT_NO_CLIPBOARD
1452 if (QApplication::clipboard()->supportsSelection()) {
1453 if (ev->button() == Qt::LeftButton) {
1454 copy(QClipboard::Selection);
1455 } else if (!isReadOnly() && ev->button() == Qt::MidButton) {
1456 deselect();
1457 insert(QApplication::clipboard()->text(QClipboard::Selection));
1458 }
1459 }
1460#endif
1461 break;
1462 case QEvent::GraphicsSceneMouseMove:
1463 case QEvent::MouseMove:
1464 if (ev->buttons() & Qt::LeftButton) {
1465 moveCursor(xToPos(ev->pos().x()), true);
1466 }
1467 break;
1468 default:
1469 break;
1470 }
1471}
1472
1473void QLineControl::processKeyEvent(QKeyEvent* event)
1474{
1475 bool inlineCompletionAccepted = false;
1476
1477#ifndef QT_NO_COMPLETER
1478 if (m_completer) {
1479 QCompleter::CompletionMode completionMode = m_completer->completionMode();
1480 if ((completionMode == QCompleter::PopupCompletion
1481 || completionMode == QCompleter::UnfilteredPopupCompletion)
1482 && m_completer->popup()
1483 && m_completer->popup()->isVisible()) {
1484 // The following keys are forwarded by the completer to the widget
1485 // Ignoring the events lets the completer provide suitable default behavior
1486 switch (event->key()) {
1487 case Qt::Key_Escape:
1488 event->ignore();
1489 return;
1490 case Qt::Key_Enter:
1491 case Qt::Key_Return:
1492 case Qt::Key_F4:
1493#ifdef QT_KEYPAD_NAVIGATION
1494 case Qt::Key_Select:
1495 if (!QApplication::keypadNavigationEnabled())
1496 break;
1497#endif
1498 m_completer->popup()->hide(); // just hide. will end up propagating to parent
1499 default:
1500 break; // normal key processing
1501 }
1502 } else if (completionMode == QCompleter::InlineCompletion) {
1503 switch (event->key()) {
1504 case Qt::Key_Enter:
1505 case Qt::Key_Return:
1506 case Qt::Key_F4:
1507#ifdef QT_KEYPAD_NAVIGATION
1508 case Qt::Key_Select:
1509 if (!QApplication::keypadNavigationEnabled())
1510 break;
1511#endif
1512 if (!m_completer->currentCompletion().isEmpty() && hasSelectedText()
1513 && textAfterSelection().isEmpty()) {
1514 setText(m_completer->currentCompletion());
1515 inlineCompletionAccepted = true;
1516 }
1517 default:
1518 break; // normal key processing
1519 }
1520 }
1521 }
1522#endif // QT_NO_COMPLETER
1523
1524 if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) {
1525 if (hasAcceptableInput() || fixup()) {
1526 emit accepted();
1527 emit editingFinished();
1528 }
1529 if (inlineCompletionAccepted)
1530 event->accept();
1531 else
1532 event->ignore();
1533 return;
1534 }
1535
1536 if (echoMode() == QLineEdit::PasswordEchoOnEdit
1537 && !passwordEchoEditing()
1538 && !isReadOnly()
1539 && !event->text().isEmpty()
1540#ifdef QT_KEYPAD_NAVIGATION
1541 && event->key() != Qt::Key_Select
1542 && event->key() != Qt::Key_Up
1543 && event->key() != Qt::Key_Down
1544 && event->key() != Qt::Key_Back
1545#endif
1546 && !(event->modifiers() & Qt::ControlModifier)) {
1547 // Clear the edit and reset to normal echo mode while editing; the
1548 // echo mode switches back when the edit loses focus
1549 // ### resets current content. dubious code; you can
1550 // navigate with keys up, down, back, and select(?), but if you press
1551 // "left" or "right" it clears?
1552 updatePasswordEchoEditing(true);
1553 clear();
1554 }
1555
1556 bool unknown = false;
1557
1558 if (false) {
1559 }
1560#ifndef QT_NO_SHORTCUT
1561 else if (event == QKeySequence::Undo) {
1562 if (!isReadOnly())
1563 undo();
1564 }
1565 else if (event == QKeySequence::Redo) {
1566 if (!isReadOnly())
1567 redo();
1568 }
1569 else if (event == QKeySequence::SelectAll) {
1570 selectAll();
1571 }
1572#ifndef QT_NO_CLIPBOARD
1573 else if (event == QKeySequence::Copy) {
1574 copy();
1575 }
1576 else if (event == QKeySequence::Paste) {
1577 if (!isReadOnly())
1578 paste();
1579 }
1580 else if (event == QKeySequence::Cut) {
1581 if (!isReadOnly()) {
1582 copy();
1583 del();
1584 }
1585 }
1586 else if (event == QKeySequence::DeleteEndOfLine) {
1587 if (!isReadOnly()) {
1588 setSelection(cursor(), end());
1589 copy();
1590 del();
1591 }
1592 }
1593#endif //QT_NO_CLIPBOARD
1594 else if (event == QKeySequence::MoveToStartOfLine || event == QKeySequence::MoveToStartOfBlock) {
1595 home(0);
1596 }
1597 else if (event == QKeySequence::MoveToEndOfLine || event == QKeySequence::MoveToEndOfBlock) {
1598 end(0);
1599 }
1600 else if (event == QKeySequence::SelectStartOfLine || event == QKeySequence::SelectStartOfBlock) {
1601 home(1);
1602 }
1603 else if (event == QKeySequence::SelectEndOfLine || event == QKeySequence::SelectEndOfBlock) {
1604 end(1);
1605 }
1606 else if (event == QKeySequence::MoveToNextChar) {
1607#if !defined(Q_WS_WIN) || defined(QT_NO_COMPLETER)
1608 if (hasSelectedText()) {
1609#else
1610 if (hasSelectedText() && m_completer
1611 && m_completer->completionMode() == QCompleter::InlineCompletion) {
1612#endif
1613 moveCursor(selectionEnd(), false);
1614 } else {
1615 cursorForward(0, layoutDirection() == Qt::LeftToRight ? 1 : -1);
1616 }
1617 }
1618 else if (event == QKeySequence::SelectNextChar) {
1619 cursorForward(1, layoutDirection() == Qt::LeftToRight ? 1 : -1);
1620 }
1621 else if (event == QKeySequence::MoveToPreviousChar) {
1622#if !defined(Q_WS_WIN) || defined(QT_NO_COMPLETER)
1623 if (hasSelectedText()) {
1624#else
1625 if (hasSelectedText() && m_completer
1626 && m_completer->completionMode() == QCompleter::InlineCompletion) {
1627#endif
1628 moveCursor(selectionStart(), false);
1629 } else {
1630 cursorForward(0, layoutDirection() == Qt::LeftToRight ? -1 : 1);
1631 }
1632 }
1633 else if (event == QKeySequence::SelectPreviousChar) {
1634 cursorForward(1, layoutDirection() == Qt::LeftToRight ? -1 : 1);
1635 }
1636 else if (event == QKeySequence::MoveToNextWord) {
1637 if (echoMode() == QLineEdit::Normal)
1638 layoutDirection() == Qt::LeftToRight ? cursorWordForward(0) : cursorWordBackward(0);
1639 else
1640 layoutDirection() == Qt::LeftToRight ? end(0) : home(0);
1641 }
1642 else if (event == QKeySequence::MoveToPreviousWord) {
1643 if (echoMode() == QLineEdit::Normal)
1644 layoutDirection() == Qt::LeftToRight ? cursorWordBackward(0) : cursorWordForward(0);
1645 else if (!isReadOnly()) {
1646 layoutDirection() == Qt::LeftToRight ? home(0) : end(0);
1647 }
1648 }
1649 else if (event == QKeySequence::SelectNextWord) {
1650 if (echoMode() == QLineEdit::Normal)
1651 layoutDirection() == Qt::LeftToRight ? cursorWordForward(1) : cursorWordBackward(1);
1652 else
1653 layoutDirection() == Qt::LeftToRight ? end(1) : home(1);
1654 }
1655 else if (event == QKeySequence::SelectPreviousWord) {
1656 if (echoMode() == QLineEdit::Normal)
1657 layoutDirection() == Qt::LeftToRight ? cursorWordBackward(1) : cursorWordForward(1);
1658 else
1659 layoutDirection() == Qt::LeftToRight ? home(1) : end(1);
1660 }
1661 else if (event == QKeySequence::Delete) {
1662 if (!isReadOnly())
1663 del();
1664 }
1665 else if (event == QKeySequence::DeleteEndOfWord) {
1666 if (!isReadOnly()) {
1667 cursorWordForward(true);
1668 del();
1669 }
1670 }
1671 else if (event == QKeySequence::DeleteStartOfWord) {
1672 if (!isReadOnly()) {
1673 cursorWordBackward(true);
1674 del();
1675 }
1676 }
1677#endif // QT_NO_SHORTCUT
1678 else {
1679 bool handled = false;
1680#ifdef Q_WS_MAC
1681 if (event->key() == Qt::Key_Up || event->key() == Qt::Key_Down) {
1682 Qt::KeyboardModifiers myModifiers = (event->modifiers() & ~Qt::KeypadModifier);
1683 if (myModifiers & Qt::ShiftModifier) {
1684 if (myModifiers == (Qt::ControlModifier|Qt::ShiftModifier)
1685 || myModifiers == (Qt::AltModifier|Qt::ShiftModifier)
1686 || myModifiers == Qt::ShiftModifier) {
1687
1688 event->key() == Qt::Key_Up ? home(1) : end(1);
1689 }
1690 } else {
1691 if ((myModifiers == Qt::ControlModifier
1692 || myModifiers == Qt::AltModifier
1693 || myModifiers == Qt::NoModifier)) {
1694 event->key() == Qt::Key_Up ? home(0) : end(0);
1695 }
1696 }
1697 handled = true;
1698 }
1699#endif
1700 if (event->modifiers() & Qt::ControlModifier) {
1701 switch (event->key()) {
1702 case Qt::Key_Backspace:
1703 if (!isReadOnly()) {
1704 cursorWordBackward(true);
1705 del();
1706 }
1707 break;
1708#ifndef QT_NO_COMPLETER
1709 case Qt::Key_Up:
1710 case Qt::Key_Down:
1711 complete(event->key());
1712 break;
1713#endif
1714#if defined(Q_WS_X11)
1715 case Qt::Key_E:
1716 end(0);
1717 break;
1718
1719 case Qt::Key_U:
1720 if (!isReadOnly()) {
1721 setSelection(0, text().size());
1722#ifndef QT_NO_CLIPBOARD
1723 copy();
1724#endif
1725 del();
1726 }
1727 break;
1728#endif
1729 default:
1730 if (!handled)
1731 unknown = true;
1732 }
1733 } else { // ### check for *no* modifier
1734 switch (event->key()) {
1735 case Qt::Key_Backspace:
1736 if (!isReadOnly()) {
1737 backspace();
1738#ifndef QT_NO_COMPLETER
1739 complete(Qt::Key_Backspace);
1740#endif
1741 }
1742 break;
1743#ifdef QT_KEYPAD_NAVIGATION
1744 case Qt::Key_Back:
1745 if (QApplication::keypadNavigationEnabled() && !event->isAutoRepeat()
1746 && !isReadOnly()) {
1747 if (text().length() == 0) {
1748 setText(m_cancelText);
1749
1750 if (passwordEchoEditing())
1751 updatePasswordEchoEditing(false);
1752
1753 emit editFocusChange(false);
1754 } else if (!m_deleteAllTimer) {
1755 m_deleteAllTimer = startTimer(750);
1756 }
1757 } else {
1758 unknown = true;
1759 }
1760 break;
1761#endif
1762
1763 default:
1764 if (!handled)
1765 unknown = true;
1766 }
1767 }
1768 }
1769
1770 if (event->key() == Qt::Key_Direction_L || event->key() == Qt::Key_Direction_R) {
1771 setLayoutDirection((event->key() == Qt::Key_Direction_L) ? Qt::LeftToRight : Qt::RightToLeft);
1772 unknown = false;
1773 }
1774
1775 if (unknown && !isReadOnly()) {
1776 QString t = event->text();
1777 if (!t.isEmpty() && t.at(0).isPrint()) {
1778 insert(t);
1779#ifndef QT_NO_COMPLETER
1780 complete(event->key());
1781#endif
1782 event->accept();
1783 return;
1784 }
1785 }
1786
1787 if (unknown)
1788 event->ignore();
1789 else
1790 event->accept();
1791}
1792
1793
1794QT_END_NAMESPACE
1795
1796#endif
Note: See TracBrowser for help on using the repository browser.