| /**************************************************************************** |
| ** |
| ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). |
| ** All rights reserved. |
| ** Contact: Nokia Corporation (qt-info@nokia.com) |
| ** |
| ** This file is part of the QtGui module of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:LGPL$ |
| ** GNU Lesser General Public License Usage |
| ** This file may be used under the terms of the GNU Lesser General Public |
| ** License version 2.1 as published by the Free Software Foundation and |
| ** appearing in the file LICENSE.LGPL included in the packaging of this |
| ** file. Please review the following information to ensure the GNU Lesser |
| ** General Public License version 2.1 requirements will be met: |
| ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
| ** |
| ** In addition, as a special exception, Nokia gives you certain additional |
| ** rights. These rights are described in the Nokia Qt LGPL Exception |
| ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU General |
| ** Public License version 3.0 as published by the Free Software Foundation |
| ** and appearing in the file LICENSE.GPL included in the packaging of this |
| ** file. Please review the following information to ensure the GNU General |
| ** Public License version 3.0 requirements will be met: |
| ** http://www.gnu.org/copyleft/gpl.html. |
| ** |
| ** Other Usage |
| ** Alternatively, this file may be used in accordance with the terms and |
| ** conditions contained in a signed written agreement between you and Nokia. |
| ** |
| ** |
| ** |
| ** |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include "qlinecontrol_p.h" |
| |
| #ifndef QT_NO_LINEEDIT |
| |
| #include "qabstractitemview.h" |
| #include "qclipboard.h" |
| #ifndef QT_NO_ACCESSIBILITY |
| #include "qaccessible.h" |
| #endif |
| #ifndef QT_NO_IM |
| #include "qinputcontext.h" |
| #include "qlist.h" |
| #endif |
| #include "qapplication.h" |
| #ifndef QT_NO_GRAPHICSVIEW |
| #include "qgraphicssceneevent.h" |
| #endif |
| |
| QT_BEGIN_NAMESPACE |
| |
| #ifdef QT_GUI_PASSWORD_ECHO_DELAY |
| static const int qt_passwordEchoDelay = QT_GUI_PASSWORD_ECHO_DELAY; |
| #endif |
| |
| /*! |
| \macro QT_GUI_PASSWORD_ECHO_DELAY |
| |
| \internal |
| |
| Defines the amount of time in milliseconds the last entered character |
| should be displayed unmasked in the Password echo mode. |
| |
| If not defined in qplatformdefs.h there will be no delay in masking |
| password characters. |
| */ |
| |
| /*! |
| \internal |
| |
| Updates the display text based of the current edit text |
| If the text has changed will emit displayTextChanged() |
| */ |
| void QLineControl::updateDisplayText(bool forceUpdate) |
| { |
| QString orig = m_textLayout.text(); |
| QString str; |
| if (m_echoMode == QLineEdit::NoEcho) |
| str = QString::fromLatin1(""); |
| else |
| str = m_text; |
| |
| if (m_echoMode == QLineEdit::Password) { |
| str.fill(m_passwordCharacter); |
| #ifdef QT_GUI_PASSWORD_ECHO_DELAY |
| if (m_passwordEchoTimer != 0 && m_cursor > 0 && m_cursor <= m_text.length()) { |
| int cursor = m_cursor - 1; |
| QChar uc = m_text.at(cursor); |
| str[cursor] = uc; |
| if (cursor > 0 && uc.unicode() >= 0xdc00 && uc.unicode() < 0xe000) { |
| // second half of a surrogate, check if we have the first half as well, |
| // if yes restore both at once |
| uc = m_text.at(cursor - 1); |
| if (uc.unicode() >= 0xd800 && uc.unicode() < 0xdc00) |
| str[cursor - 1] = uc; |
| } |
| } |
| #endif |
| } else if (m_echoMode == QLineEdit::PasswordEchoOnEdit && !m_passwordEchoEditing) { |
| str.fill(m_passwordCharacter); |
| } |
| |
| // replace certain non-printable characters with spaces (to avoid |
| // drawing boxes when using fonts that don't have glyphs for such |
| // characters) |
| QChar* uc = str.data(); |
| for (int i = 0; i < (int)str.length(); ++i) { |
| if ((uc[i] < 0x20 && uc[i] != 0x09) |
| || uc[i] == QChar::LineSeparator |
| || uc[i] == QChar::ParagraphSeparator |
| || uc[i] == QChar::ObjectReplacementCharacter) |
| uc[i] = QChar(0x0020); |
| } |
| |
| m_textLayout.setText(str); |
| |
| QTextOption option; |
| option.setTextDirection(m_layoutDirection); |
| option.setFlags(QTextOption::IncludeTrailingSpaces); |
| m_textLayout.setTextOption(option); |
| |
| m_textLayout.beginLayout(); |
| QTextLine l = m_textLayout.createLine(); |
| m_textLayout.endLayout(); |
| m_ascent = qRound(l.ascent()); |
| |
| if (str != orig || forceUpdate) |
| emit displayTextChanged(str); |
| } |
| |
| #ifndef QT_NO_CLIPBOARD |
| /*! |
| \internal |
| |
| Copies the currently selected text into the clipboard using the given |
| \a mode. |
| |
| \note If the echo mode is set to a mode other than Normal then copy |
| will not work. This is to prevent using copy as a method of bypassing |
| password features of the line control. |
| */ |
| void QLineControl::copy(QClipboard::Mode mode) const |
| { |
| QString t = selectedText(); |
| if (!t.isEmpty() && m_echoMode == QLineEdit::Normal) { |
| disconnect(QApplication::clipboard(), SIGNAL(selectionChanged()), this, 0); |
| QApplication::clipboard()->setText(t, mode); |
| connect(QApplication::clipboard(), SIGNAL(selectionChanged()), |
| this, SLOT(_q_clipboardChanged())); |
| } |
| } |
| |
| /*! |
| \internal |
| |
| Inserts the text stored in the application clipboard into the line |
| control. |
| |
| \sa insert() |
| */ |
| void QLineControl::paste(QClipboard::Mode clipboardMode) |
| { |
| QString clip = QApplication::clipboard()->text(clipboardMode); |
| if (!clip.isEmpty() || hasSelectedText()) { |
| separate(); //make it a separate undo/redo command |
| insert(clip); |
| separate(); |
| } |
| } |
| |
| #endif // !QT_NO_CLIPBOARD |
| |
| /*! |
| \internal |
| |
| Handles the behavior for the backspace key or function. |
| Removes the current selection if there is a selection, otherwise |
| removes the character prior to the cursor position. |
| |
| \sa del() |
| */ |
| void QLineControl::backspace() |
| { |
| int priorState = m_undoState; |
| if (hasSelectedText()) { |
| removeSelectedText(); |
| } else if (m_cursor) { |
| --m_cursor; |
| if (m_maskData) |
| m_cursor = prevMaskBlank(m_cursor); |
| QChar uc = m_text.at(m_cursor); |
| if (m_cursor > 0 && uc.unicode() >= 0xdc00 && uc.unicode() < 0xe000) { |
| // second half of a surrogate, check if we have the first half as well, |
| // if yes delete both at once |
| uc = m_text.at(m_cursor - 1); |
| if (uc.unicode() >= 0xd800 && uc.unicode() < 0xdc00) { |
| internalDelete(true); |
| --m_cursor; |
| } |
| } |
| internalDelete(true); |
| } |
| finishChange(priorState); |
| } |
| |
| /*! |
| \internal |
| |
| Handles the behavior for the delete key or function. |
| Removes the current selection if there is a selection, otherwise |
| removes the character after the cursor position. |
| |
| \sa del() |
| */ |
| void QLineControl::del() |
| { |
| int priorState = m_undoState; |
| if (hasSelectedText()) { |
| removeSelectedText(); |
| } else { |
| int n = m_textLayout.nextCursorPosition(m_cursor) - m_cursor; |
| while (n--) |
| internalDelete(); |
| } |
| finishChange(priorState); |
| } |
| |
| /*! |
| \internal |
| |
| Inserts the given \a newText at the current cursor position. |
| If there is any selected text it is removed prior to insertion of |
| the new text. |
| */ |
| void QLineControl::insert(const QString &newText) |
| { |
| int priorState = m_undoState; |
| removeSelectedText(); |
| internalInsert(newText); |
| finishChange(priorState); |
| } |
| |
| /*! |
| \internal |
| |
| Clears the line control text. |
| */ |
| void QLineControl::clear() |
| { |
| int priorState = m_undoState; |
| m_selstart = 0; |
| m_selend = m_text.length(); |
| removeSelectedText(); |
| separate(); |
| finishChange(priorState, /*update*/false, /*edited*/false); |
| } |
| |
| /*! |
| \internal |
| |
| Sets \a length characters from the given \a start position as selected. |
| The given \a start position must be within the current text for |
| the line control. If \a length characters cannot be selected, then |
| the selection will extend to the end of the current text. |
| */ |
| void QLineControl::setSelection(int start, int length) |
| { |
| if(start < 0 || start > (int)m_text.length()){ |
| qWarning("QLineControl::setSelection: Invalid start position"); |
| return; |
| } |
| |
| if (length > 0) { |
| if (start == m_selstart && start + length == m_selend) |
| return; |
| m_selstart = start; |
| m_selend = qMin(start + length, (int)m_text.length()); |
| m_cursor = m_selend; |
| } else if (length < 0){ |
| if (start == m_selend && start + length == m_selstart) |
| return; |
| m_selstart = qMax(start + length, 0); |
| m_selend = start; |
| m_cursor = m_selstart; |
| } else if (m_selstart != m_selend) { |
| m_selstart = 0; |
| m_selend = 0; |
| m_cursor = start; |
| } else { |
| m_cursor = start; |
| emitCursorPositionChanged(); |
| return; |
| } |
| emit selectionChanged(); |
| emitCursorPositionChanged(); |
| } |
| |
| void QLineControl::_q_clipboardChanged() |
| { |
| } |
| |
| void QLineControl::_q_deleteSelected() |
| { |
| if (!hasSelectedText()) |
| return; |
| |
| int priorState = m_undoState; |
| emit resetInputContext(); |
| removeSelectedText(); |
| separate(); |
| finishChange(priorState); |
| } |
| |
| /*! |
| \internal |
| |
| Initializes the line control with a starting text value of \a txt. |
| */ |
| void QLineControl::init(const QString &txt) |
| { |
| m_text = txt; |
| updateDisplayText(); |
| m_cursor = m_text.length(); |
| } |
| |
| /*! |
| \internal |
| |
| Sets the password echo editing to \a editing. If password echo editing |
| is true, then the text of the password is displayed even if the echo |
| mode is set to QLineEdit::PasswordEchoOnEdit. Password echoing editing |
| does not affect other echo modes. |
| */ |
| void QLineControl::updatePasswordEchoEditing(bool editing) |
| { |
| cancelPasswordEchoTimer(); |
| m_passwordEchoEditing = editing; |
| updateDisplayText(); |
| } |
| |
| /*! |
| \internal |
| |
| Returns the cursor position of the given \a x pixel value in relation |
| to the displayed text. The given \a betweenOrOn specified what kind |
| of cursor position is requested. |
| */ |
| int QLineControl::xToPos(int x, QTextLine::CursorPosition betweenOrOn) const |
| { |
| return m_textLayout.lineAt(0).xToCursor(x, betweenOrOn); |
| } |
| |
| /*! |
| \internal |
| |
| Returns the bounds of the current cursor, as defined as a |
| between characters cursor. |
| */ |
| QRect QLineControl::cursorRect() const |
| { |
| QTextLine l = m_textLayout.lineAt(0); |
| int c = m_cursor; |
| if (m_preeditCursor != -1) |
| c += m_preeditCursor; |
| int cix = qRound(l.cursorToX(c)); |
| int w = m_cursorWidth; |
| int ch = l.height() + 1; |
| |
| return QRect(cix-5, 0, w+9, ch); |
| } |
| |
| /*! |
| \internal |
| |
| Fixes the current text so that it is valid given any set validators. |
| |
| Returns true if the text was changed. Otherwise returns false. |
| */ |
| bool QLineControl::fixup() // this function assumes that validate currently returns != Acceptable |
| { |
| #ifndef QT_NO_VALIDATOR |
| if (m_validator) { |
| QString textCopy = m_text; |
| int cursorCopy = m_cursor; |
| m_validator->fixup(textCopy); |
| if (m_validator->validate(textCopy, cursorCopy) == QValidator::Acceptable) { |
| if (textCopy != m_text || cursorCopy != m_cursor) |
| internalSetText(textCopy, cursorCopy); |
| return true; |
| } |
| } |
| #endif |
| return false; |
| } |
| |
| /*! |
| \internal |
| |
| Moves the cursor to the given position \a pos. If \a mark is true will |
| adjust the currently selected text. |
| */ |
| void QLineControl::moveCursor(int pos, bool mark) |
| { |
| if (pos != m_cursor) { |
| separate(); |
| if (m_maskData) |
| pos = pos > m_cursor ? nextMaskBlank(pos) : prevMaskBlank(pos); |
| } |
| if (mark) { |
| int anchor; |
| if (m_selend > m_selstart && m_cursor == m_selstart) |
| anchor = m_selend; |
| else if (m_selend > m_selstart && m_cursor == m_selend) |
| anchor = m_selstart; |
| else |
| anchor = m_cursor; |
| m_selstart = qMin(anchor, pos); |
| m_selend = qMax(anchor, pos); |
| updateDisplayText(); |
| } else { |
| internalDeselect(); |
| } |
| m_cursor = pos; |
| if (mark || m_selDirty) { |
| m_selDirty = false; |
| emit selectionChanged(); |
| } |
| emitCursorPositionChanged(); |
| } |
| |
| /*! |
| \internal |
| |
| Applies the given input method event \a event to the text of the line |
| control |
| */ |
| void QLineControl::processInputMethodEvent(QInputMethodEvent *event) |
| { |
| int priorState = 0; |
| bool isGettingInput = !event->commitString().isEmpty() |
| || event->preeditString() != preeditAreaText() |
| || event->replacementLength() > 0; |
| bool cursorPositionChanged = false; |
| |
| if (isGettingInput) { |
| // If any text is being input, remove selected text. |
| priorState = m_undoState; |
| if (echoMode() == QLineEdit::PasswordEchoOnEdit && !passwordEchoEditing()) { |
| updatePasswordEchoEditing(true); |
| m_selstart = 0; |
| m_selend = m_text.length(); |
| } |
| removeSelectedText(); |
| } |
| |
| int c = m_cursor; // cursor position after insertion of commit string |
| if (event->replacementStart() <= 0) |
| c += event->commitString().length() - qMin(-event->replacementStart(), event->replacementLength()); |
| |
| m_cursor += event->replacementStart(); |
| if (m_cursor < 0) |
| m_cursor = 0; |
| |
| // insert commit string |
| if (event->replacementLength()) { |
| m_selstart = m_cursor; |
| m_selend = m_selstart + event->replacementLength(); |
| removeSelectedText(); |
| } |
| if (!event->commitString().isEmpty()) { |
| internalInsert(event->commitString()); |
| cursorPositionChanged = true; |
| } |
| |
| m_cursor = qBound(0, c, m_text.length()); |
| |
| for (int i = 0; i < event->attributes().size(); ++i) { |
| const QInputMethodEvent::Attribute &a = event->attributes().at(i); |
| if (a.type == QInputMethodEvent::Selection) { |
| m_cursor = qBound(0, a.start + a.length, m_text.length()); |
| if (a.length) { |
| m_selstart = qMax(0, qMin(a.start, m_text.length())); |
| m_selend = m_cursor; |
| if (m_selend < m_selstart) { |
| qSwap(m_selstart, m_selend); |
| } |
| } else { |
| m_selstart = m_selend = 0; |
| } |
| cursorPositionChanged = true; |
| } |
| } |
| #ifndef QT_NO_IM |
| setPreeditArea(m_cursor, event->preeditString()); |
| #endif //QT_NO_IM |
| const int oldPreeditCursor = m_preeditCursor; |
| m_preeditCursor = event->preeditString().length(); |
| m_hideCursor = false; |
| QList<QTextLayout::FormatRange> formats; |
| for (int i = 0; i < event->attributes().size(); ++i) { |
| const QInputMethodEvent::Attribute &a = event->attributes().at(i); |
| if (a.type == QInputMethodEvent::Cursor) { |
| m_preeditCursor = a.start; |
| m_hideCursor = !a.length; |
| } else if (a.type == QInputMethodEvent::TextFormat) { |
| QTextCharFormat f = qvariant_cast<QTextFormat>(a.value).toCharFormat(); |
| if (f.isValid()) { |
| QTextLayout::FormatRange o; |
| o.start = a.start + m_cursor; |
| o.length = a.length; |
| o.format = f; |
| formats.append(o); |
| } |
| } |
| } |
| m_textLayout.setAdditionalFormats(formats); |
| updateDisplayText(/*force*/ true); |
| if (cursorPositionChanged) |
| emitCursorPositionChanged(); |
| else if (m_preeditCursor != oldPreeditCursor) |
| emit updateMicroFocus(); |
| if (isGettingInput) |
| finishChange(priorState); |
| } |
| |
| /*! |
| \internal |
| |
| Draws the display text for the line control using the given |
| \a painter, \a clip, and \a offset. Which aspects of the display text |
| are drawn is specified by the given \a flags. |
| |
| If the flags contain DrawSelections, then the selection or input mask |
| backgrounds and foregrounds will be applied before drawing the text. |
| |
| If the flags contain DrawCursor a cursor of the current cursorWidth() |
| will be drawn after drawing the text. |
| |
| The display text will only be drawn if the flags contain DrawText |
| */ |
| void QLineControl::draw(QPainter *painter, const QPoint &offset, const QRect &clip, int flags) |
| { |
| QVector<QTextLayout::FormatRange> selections; |
| if (flags & DrawSelections) { |
| QTextLayout::FormatRange o; |
| if (m_selstart < m_selend) { |
| o.start = m_selstart; |
| o.length = m_selend - m_selstart; |
| o.format.setBackground(m_palette.brush(QPalette::Highlight)); |
| o.format.setForeground(m_palette.brush(QPalette::HighlightedText)); |
| } else { |
| // mask selection |
| if(!m_blinkPeriod || m_blinkStatus){ |
| o.start = m_cursor; |
| o.length = 1; |
| o.format.setBackground(m_palette.brush(QPalette::Text)); |
| o.format.setForeground(m_palette.brush(QPalette::Window)); |
| } |
| } |
| selections.append(o); |
| } |
| |
| if (flags & DrawText) |
| m_textLayout.draw(painter, offset, selections, clip); |
| |
| if (flags & DrawCursor){ |
| int cursor = m_cursor; |
| if (m_preeditCursor != -1) |
| cursor += m_preeditCursor; |
| if (!m_hideCursor && (!m_blinkPeriod || m_blinkStatus)) |
| m_textLayout.drawCursor(painter, offset, cursor, m_cursorWidth); |
| } |
| } |
| |
| /*! |
| \internal |
| |
| Sets the selection to cover the word at the given cursor position. |
| The word boundaries are defined by the behavior of QTextLayout::SkipWords |
| cursor mode. |
| */ |
| void QLineControl::selectWordAtPos(int cursor) |
| { |
| int next = cursor + 1; |
| if(next > end()) |
| --next; |
| int c = m_textLayout.previousCursorPosition(next, QTextLayout::SkipWords); |
| moveCursor(c, false); |
| // ## text layout should support end of words. |
| int end = m_textLayout.nextCursorPosition(c, QTextLayout::SkipWords); |
| while (end > cursor && m_text[end-1].isSpace()) |
| --end; |
| moveCursor(end, true); |
| } |
| |
| /*! |
| \internal |
| |
| Completes a change to the line control text. If the change is not valid |
| will undo the line control state back to the given \a validateFromState. |
| |
| If \a edited is true and the change is valid, will emit textEdited() in |
| addition to textChanged(). Otherwise only emits textChanged() on a valid |
| change. |
| |
| The \a update value is currently unused. |
| */ |
| bool QLineControl::finishChange(int validateFromState, bool update, bool edited) |
| { |
| Q_UNUSED(update) |
| bool lineDirty = m_selDirty; |
| if (m_textDirty) { |
| // do validation |
| bool wasValidInput = m_validInput; |
| m_validInput = true; |
| #ifndef QT_NO_VALIDATOR |
| if (m_validator) { |
| m_validInput = false; |
| QString textCopy = m_text; |
| int cursorCopy = m_cursor; |
| m_validInput = (m_validator->validate(textCopy, cursorCopy) != QValidator::Invalid); |
| if (m_validInput) { |
| if (m_text != textCopy) { |
| internalSetText(textCopy, cursorCopy); |
| return true; |
| } |
| m_cursor = cursorCopy; |
| } |
| } |
| #endif |
| if (validateFromState >= 0 && wasValidInput && !m_validInput) { |
| if (m_transactions.count()) |
| return false; |
| internalUndo(validateFromState); |
| m_history.resize(m_undoState); |
| if (m_modifiedState > m_undoState) |
| m_modifiedState = -1; |
| m_validInput = true; |
| m_textDirty = false; |
| } |
| updateDisplayText(); |
| lineDirty |= m_textDirty; |
| if (m_textDirty) { |
| m_textDirty = false; |
| QString actualText = text(); |
| if (edited) |
| emit textEdited(actualText); |
| emit textChanged(actualText); |
| } |
| } |
| if (m_selDirty) { |
| m_selDirty = false; |
| emit selectionChanged(); |
| } |
| emitCursorPositionChanged(); |
| return true; |
| } |
| |
| /*! |
| \internal |
| |
| An internal function for setting the text of the line control. |
| */ |
| void QLineControl::internalSetText(const QString &txt, int pos, bool edited) |
| { |
| cancelPasswordEchoTimer(); |
| internalDeselect(); |
| emit resetInputContext(); |
| QString oldText = m_text; |
| if (m_maskData) { |
| m_text = maskString(0, txt, true); |
| m_text += clearString(m_text.length(), m_maxLength - m_text.length()); |
| } else { |
| m_text = txt.isEmpty() ? txt : txt.left(m_maxLength); |
| } |
| m_history.clear(); |
| m_modifiedState = m_undoState = 0; |
| m_cursor = (pos < 0 || pos > m_text.length()) ? m_text.length() : pos; |
| m_textDirty = (oldText != m_text); |
| finishChange(-1, true, edited); |
| } |
| |
| |
| /*! |
| \internal |
| |
| Adds the given \a command to the undo history |
| of the line control. Does not apply the command. |
| */ |
| void QLineControl::addCommand(const Command &cmd) |
| { |
| if (m_separator && m_undoState && m_history[m_undoState - 1].type != Separator) { |
| m_history.resize(m_undoState + 2); |
| m_history[m_undoState++] = Command(Separator, m_cursor, 0, m_selstart, m_selend); |
| } else { |
| m_history.resize(m_undoState + 1); |
| } |
| m_separator = false; |
| m_history[m_undoState++] = cmd; |
| } |
| |
| /*! |
| \internal |
| |
| Inserts the given string \a s into the line |
| control. |
| |
| Also adds the appropriate commands into the undo history. |
| This function does not call finishChange(), and may leave the text |
| in an invalid state. |
| */ |
| void QLineControl::internalInsert(const QString &s) |
| { |
| #ifdef QT_GUI_PASSWORD_ECHO_DELAY |
| if (m_echoMode == QLineEdit::Password) { |
| if (m_passwordEchoTimer != 0) |
| killTimer(m_passwordEchoTimer); |
| m_passwordEchoTimer = startTimer(qt_passwordEchoDelay); |
| } |
| #endif |
| if (hasSelectedText()) |
| addCommand(Command(SetSelection, m_cursor, 0, m_selstart, m_selend)); |
| if (m_maskData) { |
| QString ms = maskString(m_cursor, s); |
| for (int i = 0; i < (int) ms.length(); ++i) { |
| addCommand (Command(DeleteSelection, m_cursor + i, m_text.at(m_cursor + i), -1, -1)); |
| addCommand(Command(Insert, m_cursor + i, ms.at(i), -1, -1)); |
| } |
| m_text.replace(m_cursor, ms.length(), ms); |
| m_cursor += ms.length(); |
| m_cursor = nextMaskBlank(m_cursor); |
| m_textDirty = true; |
| } else { |
| int remaining = m_maxLength - m_text.length(); |
| if (remaining != 0) { |
| m_text.insert(m_cursor, s.left(remaining)); |
| for (int i = 0; i < (int) s.left(remaining).length(); ++i) |
| addCommand(Command(Insert, m_cursor++, s.at(i), -1, -1)); |
| m_textDirty = true; |
| } |
| } |
| } |
| |
| /*! |
| \internal |
| |
| deletes a single character from the current text. If \a wasBackspace, |
| the character prior to the cursor is removed. Otherwise the character |
| after the cursor is removed. |
| |
| Also adds the appropriate commands into the undo history. |
| This function does not call finishChange(), and may leave the text |
| in an invalid state. |
| */ |
| void QLineControl::internalDelete(bool wasBackspace) |
| { |
| if (m_cursor < (int) m_text.length()) { |
| cancelPasswordEchoTimer(); |
| if (hasSelectedText()) |
| addCommand(Command(SetSelection, m_cursor, 0, m_selstart, m_selend)); |
| addCommand(Command((CommandType)((m_maskData ? 2 : 0) + (wasBackspace ? Remove : Delete)), |
| m_cursor, m_text.at(m_cursor), -1, -1)); |
| if (m_maskData) { |
| m_text.replace(m_cursor, 1, clearString(m_cursor, 1)); |
| addCommand(Command(Insert, m_cursor, m_text.at(m_cursor), -1, -1)); |
| } else { |
| m_text.remove(m_cursor, 1); |
| } |
| m_textDirty = true; |
| } |
| } |
| |
| /*! |
| \internal |
| |
| removes the currently selected text from the line control. |
| |
| Also adds the appropriate commands into the undo history. |
| This function does not call finishChange(), and may leave the text |
| in an invalid state. |
| */ |
| void QLineControl::removeSelectedText() |
| { |
| if (m_selstart < m_selend && m_selend <= (int) m_text.length()) { |
| cancelPasswordEchoTimer(); |
| separate(); |
| int i ; |
| addCommand(Command(SetSelection, m_cursor, 0, m_selstart, m_selend)); |
| if (m_selstart <= m_cursor && m_cursor < m_selend) { |
| // cursor is within the selection. Split up the commands |
| // to be able to restore the correct cursor position |
| for (i = m_cursor; i >= m_selstart; --i) |
| addCommand (Command(DeleteSelection, i, m_text.at(i), -1, 1)); |
| for (i = m_selend - 1; i > m_cursor; --i) |
| addCommand (Command(DeleteSelection, i - m_cursor + m_selstart - 1, m_text.at(i), -1, -1)); |
| } else { |
| for (i = m_selend-1; i >= m_selstart; --i) |
| addCommand (Command(RemoveSelection, i, m_text.at(i), -1, -1)); |
| } |
| if (m_maskData) { |
| m_text.replace(m_selstart, m_selend - m_selstart, clearString(m_selstart, m_selend - m_selstart)); |
| for (int i = 0; i < m_selend - m_selstart; ++i) |
| addCommand(Command(Insert, m_selstart + i, m_text.at(m_selstart + i), -1, -1)); |
| } else { |
| m_text.remove(m_selstart, m_selend - m_selstart); |
| } |
| if (m_cursor > m_selstart) |
| m_cursor -= qMin(m_cursor, m_selend) - m_selstart; |
| internalDeselect(); |
| m_textDirty = true; |
| } |
| } |
| |
| /*! |
| \internal |
| |
| Parses the input mask specified by \a maskFields to generate |
| the mask data used to handle input masks. |
| */ |
| void QLineControl::parseInputMask(const QString &maskFields) |
| { |
| int delimiter = maskFields.indexOf(QLatin1Char(';')); |
| if (maskFields.isEmpty() || delimiter == 0) { |
| if (m_maskData) { |
| delete [] m_maskData; |
| m_maskData = 0; |
| m_maxLength = 32767; |
| internalSetText(QString()); |
| } |
| return; |
| } |
| |
| if (delimiter == -1) { |
| m_blank = QLatin1Char(' '); |
| m_inputMask = maskFields; |
| } else { |
| m_inputMask = maskFields.left(delimiter); |
| m_blank = (delimiter + 1 < maskFields.length()) ? maskFields[delimiter + 1] : QLatin1Char(' '); |
| } |
| |
| // calculate m_maxLength / m_maskData length |
| m_maxLength = 0; |
| QChar c = 0; |
| for (int i=0; i<m_inputMask.length(); i++) { |
| c = m_inputMask.at(i); |
| if (i > 0 && m_inputMask.at(i-1) == QLatin1Char('\\')) { |
| m_maxLength++; |
| continue; |
| } |
| if (c != QLatin1Char('\\') && c != QLatin1Char('!') && |
| c != QLatin1Char('<') && c != QLatin1Char('>') && |
| c != QLatin1Char('{') && c != QLatin1Char('}') && |
| c != QLatin1Char('[') && c != QLatin1Char(']')) |
| m_maxLength++; |
| } |
| |
| delete [] m_maskData; |
| m_maskData = new MaskInputData[m_maxLength]; |
| |
| MaskInputData::Casemode m = MaskInputData::NoCaseMode; |
| c = 0; |
| bool s; |
| bool escape = false; |
| int index = 0; |
| for (int i = 0; i < m_inputMask.length(); i++) { |
| c = m_inputMask.at(i); |
| if (escape) { |
| s = true; |
| m_maskData[index].maskChar = c; |
| m_maskData[index].separator = s; |
| m_maskData[index].caseMode = m; |
| index++; |
| escape = false; |
| } else if (c == QLatin1Char('<')) { |
| m = MaskInputData::Lower; |
| } else if (c == QLatin1Char('>')) { |
| m = MaskInputData::Upper; |
| } else if (c == QLatin1Char('!')) { |
| m = MaskInputData::NoCaseMode; |
| } else if (c != QLatin1Char('{') && c != QLatin1Char('}') && c != QLatin1Char('[') && c != QLatin1Char(']')) { |
| switch (c.unicode()) { |
| case 'A': |
| case 'a': |
| case 'N': |
| case 'n': |
| case 'X': |
| case 'x': |
| case '9': |
| case '0': |
| case 'D': |
| case 'd': |
| case '#': |
| case 'H': |
| case 'h': |
| case 'B': |
| case 'b': |
| s = false; |
| break; |
| case '\\': |
| escape = true; |
| default: |
| s = true; |
| break; |
| } |
| |
| if (!escape) { |
| m_maskData[index].maskChar = c; |
| m_maskData[index].separator = s; |
| m_maskData[index].caseMode = m; |
| index++; |
| } |
| } |
| } |
| internalSetText(m_text); |
| } |
| |
| |
| /*! |
| \internal |
| |
| checks if the key is valid compared to the inputMask |
| */ |
| bool QLineControl::isValidInput(QChar key, QChar mask) const |
| { |
| switch (mask.unicode()) { |
| case 'A': |
| if (key.isLetter()) |
| return true; |
| break; |
| case 'a': |
| if (key.isLetter() || key == m_blank) |
| return true; |
| break; |
| case 'N': |
| if (key.isLetterOrNumber()) |
| return true; |
| break; |
| case 'n': |
| if (key.isLetterOrNumber() || key == m_blank) |
| return true; |
| break; |
| case 'X': |
| if (key.isPrint()) |
| return true; |
| break; |
| case 'x': |
| if (key.isPrint() || key == m_blank) |
| return true; |
| break; |
| case '9': |
| if (key.isNumber()) |
| return true; |
| break; |
| case '0': |
| if (key.isNumber() || key == m_blank) |
| return true; |
| break; |
| case 'D': |
| if (key.isNumber() && key.digitValue() > 0) |
| return true; |
| break; |
| case 'd': |
| if ((key.isNumber() && key.digitValue() > 0) || key == m_blank) |
| return true; |
| break; |
| case '#': |
| if (key.isNumber() || key == QLatin1Char('+') || key == QLatin1Char('-') || key == m_blank) |
| return true; |
| break; |
| case 'B': |
| if (key == QLatin1Char('0') || key == QLatin1Char('1')) |
| return true; |
| break; |
| case 'b': |
| if (key == QLatin1Char('0') || key == QLatin1Char('1') || key == m_blank) |
| return true; |
| break; |
| case 'H': |
| if (key.isNumber() || (key >= QLatin1Char('a') && key <= QLatin1Char('f')) || (key >= QLatin1Char('A') && key <= QLatin1Char('F'))) |
| return true; |
| break; |
| case 'h': |
| if (key.isNumber() || (key >= QLatin1Char('a') && key <= QLatin1Char('f')) || (key >= QLatin1Char('A') && key <= QLatin1Char('F')) || key == m_blank) |
| return true; |
| break; |
| default: |
| break; |
| } |
| return false; |
| } |
| |
| /*! |
| \internal |
| |
| Returns true if the given text \a str is valid for any |
| validator or input mask set for the line control. |
| |
| Otherwise returns false |
| */ |
| bool QLineControl::hasAcceptableInput(const QString &str) const |
| { |
| #ifndef QT_NO_VALIDATOR |
| QString textCopy = str; |
| int cursorCopy = m_cursor; |
| if (m_validator && m_validator->validate(textCopy, cursorCopy) |
| != QValidator::Acceptable) |
| return false; |
| #endif |
| |
| if (!m_maskData) |
| return true; |
| |
| if (str.length() != m_maxLength) |
| return false; |
| |
| for (int i=0; i < m_maxLength; ++i) { |
| if (m_maskData[i].separator) { |
| if (str.at(i) != m_maskData[i].maskChar) |
| return false; |
| } else { |
| if (!isValidInput(str.at(i), m_maskData[i].maskChar)) |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| /*! |
| \internal |
| |
| Applies the inputMask on \a str starting from position \a pos in the mask. \a clear |
| specifies from where characters should be gotten when a separator is met in \a str - true means |
| that blanks will be used, false that previous input is used. |
| Calling this when no inputMask is set is undefined. |
| */ |
| QString QLineControl::maskString(uint pos, const QString &str, bool clear) const |
| { |
| if (pos >= (uint)m_maxLength) |
| return QString::fromLatin1(""); |
| |
| QString fill; |
| fill = clear ? clearString(0, m_maxLength) : m_text; |
| |
| int strIndex = 0; |
| QString s = QString::fromLatin1(""); |
| int i = pos; |
| while (i < m_maxLength) { |
| if (strIndex < str.length()) { |
| if (m_maskData[i].separator) { |
| s += m_maskData[i].maskChar; |
| if (str[(int)strIndex] == m_maskData[i].maskChar) |
| strIndex++; |
| ++i; |
| } else { |
| if (isValidInput(str[(int)strIndex], m_maskData[i].maskChar)) { |
| switch (m_maskData[i].caseMode) { |
| case MaskInputData::Upper: |
| s += str[(int)strIndex].toUpper(); |
| break; |
| case MaskInputData::Lower: |
| s += str[(int)strIndex].toLower(); |
| break; |
| default: |
| s += str[(int)strIndex]; |
| } |
| ++i; |
| } else { |
| // search for separator first |
| int n = findInMask(i, true, true, str[(int)strIndex]); |
| if (n != -1) { |
| if (str.length() != 1 || i == 0 || (i > 0 && (!m_maskData[i-1].separator || m_maskData[i-1].maskChar != str[(int)strIndex]))) { |
| s += fill.mid(i, n-i+1); |
| i = n + 1; // update i to find + 1 |
| } |
| } else { |
| // search for valid m_blank if not |
| n = findInMask(i, true, false, str[(int)strIndex]); |
| if (n != -1) { |
| s += fill.mid(i, n-i); |
| switch (m_maskData[n].caseMode) { |
| case MaskInputData::Upper: |
| s += str[(int)strIndex].toUpper(); |
| break; |
| case MaskInputData::Lower: |
| s += str[(int)strIndex].toLower(); |
| break; |
| default: |
| s += str[(int)strIndex]; |
| } |
| i = n + 1; // updates i to find + 1 |
| } |
| } |
| } |
| ++strIndex; |
| } |
| } else |
| break; |
| } |
| |
| return s; |
| } |
| |
| |
| |
| /*! |
| \internal |
| |
| Returns a "cleared" string with only separators and blank chars. |
| Calling this when no inputMask is set is undefined. |
| */ |
| QString QLineControl::clearString(uint pos, uint len) const |
| { |
| if (pos >= (uint)m_maxLength) |
| return QString(); |
| |
| QString s; |
| int end = qMin((uint)m_maxLength, pos + len); |
| for (int i = pos; i < end; ++i) |
| if (m_maskData[i].separator) |
| s += m_maskData[i].maskChar; |
| else |
| s += m_blank; |
| |
| return s; |
| } |
| |
| /*! |
| \internal |
| |
| Strips blank parts of the input in a QLineControl when an inputMask is set, |
| separators are still included. Typically "127.0__.0__.1__" becomes "127.0.0.1". |
| */ |
| QString QLineControl::stripString(const QString &str) const |
| { |
| if (!m_maskData) |
| return str; |
| |
| QString s; |
| int end = qMin(m_maxLength, (int)str.length()); |
| for (int i = 0; i < end; ++i) |
| if (m_maskData[i].separator) |
| s += m_maskData[i].maskChar; |
| else |
| if (str[i] != m_blank) |
| s += str[i]; |
| |
| return s; |
| } |
| |
| /*! |
| \internal |
| searches forward/backward in m_maskData for either a separator or a m_blank |
| */ |
| int QLineControl::findInMask(int pos, bool forward, bool findSeparator, QChar searchChar) const |
| { |
| if (pos >= m_maxLength || pos < 0) |
| return -1; |
| |
| int end = forward ? m_maxLength : -1; |
| int step = forward ? 1 : -1; |
| int i = pos; |
| |
| while (i != end) { |
| if (findSeparator) { |
| if (m_maskData[i].separator && m_maskData[i].maskChar == searchChar) |
| return i; |
| } else { |
| if (!m_maskData[i].separator) { |
| if (searchChar.isNull()) |
| return i; |
| else if (isValidInput(searchChar, m_maskData[i].maskChar)) |
| return i; |
| } |
| } |
| i += step; |
| } |
| return -1; |
| } |
| |
| void QLineControl::internalUndo(int until) |
| { |
| if (!isUndoAvailable()) |
| return; |
| cancelPasswordEchoTimer(); |
| internalDeselect(); |
| while (m_undoState && m_undoState > until) { |
| Command& cmd = m_history[--m_undoState]; |
| switch (cmd.type) { |
| case Insert: |
| m_text.remove(cmd.pos, 1); |
| m_cursor = cmd.pos; |
| break; |
| case SetSelection: |
| m_selstart = cmd.selStart; |
| m_selend = cmd.selEnd; |
| m_cursor = cmd.pos; |
| break; |
| case Remove: |
| case RemoveSelection: |
| m_text.insert(cmd.pos, cmd.uc); |
| m_cursor = cmd.pos + 1; |
| break; |
| case Delete: |
| case DeleteSelection: |
| m_text.insert(cmd.pos, cmd.uc); |
| m_cursor = cmd.pos; |
| break; |
| case Separator: |
| continue; |
| } |
| if (until < 0 && m_undoState) { |
| Command& next = m_history[m_undoState-1]; |
| if (next.type != cmd.type && next.type < RemoveSelection |
| && (cmd.type < RemoveSelection || next.type == Separator)) |
| break; |
| } |
| } |
| m_textDirty = true; |
| emitCursorPositionChanged(); |
| } |
| |
| void QLineControl::internalRedo() |
| { |
| if (!isRedoAvailable()) |
| return; |
| internalDeselect(); |
| while (m_undoState < (int)m_history.size()) { |
| Command& cmd = m_history[m_undoState++]; |
| switch (cmd.type) { |
| case Insert: |
| m_text.insert(cmd.pos, cmd.uc); |
| m_cursor = cmd.pos + 1; |
| break; |
| case SetSelection: |
| m_selstart = cmd.selStart; |
| m_selend = cmd.selEnd; |
| m_cursor = cmd.pos; |
| break; |
| case Remove: |
| case Delete: |
| case RemoveSelection: |
| case DeleteSelection: |
| m_text.remove(cmd.pos, 1); |
| m_selstart = cmd.selStart; |
| m_selend = cmd.selEnd; |
| m_cursor = cmd.pos; |
| break; |
| case Separator: |
| m_selstart = cmd.selStart; |
| m_selend = cmd.selEnd; |
| m_cursor = cmd.pos; |
| break; |
| } |
| if (m_undoState < (int)m_history.size()) { |
| Command& next = m_history[m_undoState]; |
| if (next.type != cmd.type && cmd.type < RemoveSelection && next.type != Separator |
| && (next.type < RemoveSelection || cmd.type == Separator)) |
| break; |
| } |
| } |
| m_textDirty = true; |
| emitCursorPositionChanged(); |
| } |
| |
| /*! |
| \internal |
| |
| If the current cursor position differs from the last emitted cursor |
| position, emits cursorPositionChanged(). |
| */ |
| void QLineControl::emitCursorPositionChanged() |
| { |
| if (m_cursor != m_lastCursorPos) { |
| const int oldLast = m_lastCursorPos; |
| m_lastCursorPos = m_cursor; |
| cursorPositionChanged(oldLast, m_cursor); |
| } |
| } |
| |
| #ifndef QT_NO_COMPLETER |
| // iterating forward(dir=1)/backward(dir=-1) from the |
| // current row based. dir=0 indicates a new completion prefix was set. |
| bool QLineControl::advanceToEnabledItem(int dir) |
| { |
| int start = m_completer->currentRow(); |
| if (start == -1) |
| return false; |
| int i = start + dir; |
| if (dir == 0) dir = 1; |
| do { |
| if (!m_completer->setCurrentRow(i)) { |
| if (!m_completer->wrapAround()) |
| break; |
| i = i > 0 ? 0 : m_completer->completionCount() - 1; |
| } else { |
| QModelIndex currentIndex = m_completer->currentIndex(); |
| if (m_completer->completionModel()->flags(currentIndex) & Qt::ItemIsEnabled) |
| return true; |
| i += dir; |
| } |
| } while (i != start); |
| |
| m_completer->setCurrentRow(start); // restore |
| return false; |
| } |
| |
| void QLineControl::complete(int key) |
| { |
| if (!m_completer || isReadOnly() || echoMode() != QLineEdit::Normal) |
| return; |
| |
| QString text = this->text(); |
| if (m_completer->completionMode() == QCompleter::InlineCompletion) { |
| if (key == Qt::Key_Backspace) |
| return; |
| int n = 0; |
| if (key == Qt::Key_Up || key == Qt::Key_Down) { |
| if (textAfterSelection().length()) |
| return; |
| QString prefix = hasSelectedText() ? textBeforeSelection() |
| : text; |
| if (text.compare(m_completer->currentCompletion(), m_completer->caseSensitivity()) != 0 |
| || prefix.compare(m_completer->completionPrefix(), m_completer->caseSensitivity()) != 0) { |
| m_completer->setCompletionPrefix(prefix); |
| } else { |
| n = (key == Qt::Key_Up) ? -1 : +1; |
| } |
| } else { |
| m_completer->setCompletionPrefix(text); |
| } |
| if (!advanceToEnabledItem(n)) |
| return; |
| } else { |
| #ifndef QT_KEYPAD_NAVIGATION |
| if (text.isEmpty()) { |
| m_completer->popup()->hide(); |
| return; |
| } |
| #endif |
| m_completer->setCompletionPrefix(text); |
| } |
| |
| m_completer->complete(); |
| } |
| #endif |
| |
| void QLineControl::setCursorBlinkPeriod(int msec) |
| { |
| if (msec == m_blinkPeriod) |
| return; |
| if (m_blinkTimer) { |
| killTimer(m_blinkTimer); |
| } |
| if (msec) { |
| m_blinkTimer = startTimer(msec / 2); |
| m_blinkStatus = 1; |
| } else { |
| m_blinkTimer = 0; |
| if (m_blinkStatus == 1) |
| emit updateNeeded(inputMask().isEmpty() ? cursorRect() : QRect()); |
| } |
| m_blinkPeriod = msec; |
| } |
| |
| void QLineControl::resetCursorBlinkTimer() |
| { |
| if (m_blinkPeriod == 0 || m_blinkTimer == 0) |
| return; |
| killTimer(m_blinkTimer); |
| m_blinkTimer = startTimer(m_blinkPeriod / 2); |
| m_blinkStatus = 1; |
| } |
| |
| void QLineControl::timerEvent(QTimerEvent *event) |
| { |
| if (event->timerId() == m_blinkTimer) { |
| m_blinkStatus = !m_blinkStatus; |
| emit updateNeeded(inputMask().isEmpty() ? cursorRect() : QRect()); |
| } else if (event->timerId() == m_deleteAllTimer) { |
| killTimer(m_deleteAllTimer); |
| m_deleteAllTimer = 0; |
| clear(); |
| } else if (event->timerId() == m_tripleClickTimer) { |
| killTimer(m_tripleClickTimer); |
| m_tripleClickTimer = 0; |
| #ifdef QT_GUI_PASSWORD_ECHO_DELAY |
| } else if (event->timerId() == m_passwordEchoTimer) { |
| killTimer(m_passwordEchoTimer); |
| m_passwordEchoTimer = 0; |
| updateDisplayText(); |
| #endif |
| } |
| } |
| |
| bool QLineControl::processEvent(QEvent* ev) |
| { |
| #ifdef QT_KEYPAD_NAVIGATION |
| if (QApplication::keypadNavigationEnabled()) { |
| if ((ev->type() == QEvent::KeyPress) || (ev->type() == QEvent::KeyRelease)) { |
| QKeyEvent *ke = (QKeyEvent *)ev; |
| if (ke->key() == Qt::Key_Back) { |
| if (ke->isAutoRepeat()) { |
| // Swallow it. We don't want back keys running amok. |
| ke->accept(); |
| return true; |
| } |
| if ((ev->type() == QEvent::KeyRelease) |
| && !isReadOnly() |
| && m_deleteAllTimer) { |
| killTimer(m_deleteAllTimer); |
| m_deleteAllTimer = 0; |
| backspace(); |
| ke->accept(); |
| return true; |
| } |
| } |
| } |
| } |
| #endif |
| switch(ev->type()){ |
| #ifndef QT_NO_GRAPHICSVIEW |
| case QEvent::GraphicsSceneMouseDoubleClick: |
| case QEvent::GraphicsSceneMouseMove: |
| case QEvent::GraphicsSceneMouseRelease: |
| case QEvent::GraphicsSceneMousePress:{ |
| QGraphicsSceneMouseEvent *gvEv = static_cast<QGraphicsSceneMouseEvent*>(ev); |
| QMouseEvent* mouse = new QMouseEvent(ev->type(), |
| gvEv->pos().toPoint(), gvEv->button(), gvEv->buttons(), gvEv->modifiers()); |
| processMouseEvent(mouse); break; |
| } |
| #endif |
| case QEvent::MouseButtonPress: |
| case QEvent::MouseButtonRelease: |
| case QEvent::MouseButtonDblClick: |
| case QEvent::MouseMove: |
| processMouseEvent(static_cast<QMouseEvent*>(ev)); break; |
| case QEvent::KeyPress: |
| case QEvent::KeyRelease: |
| processKeyEvent(static_cast<QKeyEvent*>(ev)); break; |
| case QEvent::InputMethod: |
| processInputMethodEvent(static_cast<QInputMethodEvent*>(ev)); break; |
| #ifndef QT_NO_SHORTCUT |
| case QEvent::ShortcutOverride:{ |
| if (isReadOnly()) |
| return false; |
| QKeyEvent* ke = static_cast<QKeyEvent*>(ev); |
| if (ke == QKeySequence::Copy |
| || ke == QKeySequence::Paste |
| || ke == QKeySequence::Cut |
| || ke == QKeySequence::Redo |
| || ke == QKeySequence::Undo |
| || ke == QKeySequence::MoveToNextWord |
| || ke == QKeySequence::MoveToPreviousWord |
| || ke == QKeySequence::MoveToStartOfDocument |
| || ke == QKeySequence::MoveToEndOfDocument |
| || ke == QKeySequence::SelectNextWord |
| || ke == QKeySequence::SelectPreviousWord |
| || ke == QKeySequence::SelectStartOfLine |
| || ke == QKeySequence::SelectEndOfLine |
| || ke == QKeySequence::SelectStartOfBlock |
| || ke == QKeySequence::SelectEndOfBlock |
| || ke == QKeySequence::SelectStartOfDocument |
| || ke == QKeySequence::SelectAll |
| || ke == QKeySequence::SelectEndOfDocument) { |
| ke->accept(); |
| } else if (ke->modifiers() == Qt::NoModifier || ke->modifiers() == Qt::ShiftModifier |
| || ke->modifiers() == Qt::KeypadModifier) { |
| if (ke->key() < Qt::Key_Escape) { |
| ke->accept(); |
| } else { |
| switch (ke->key()) { |
| case Qt::Key_Delete: |
| case Qt::Key_Home: |
| case Qt::Key_End: |
| case Qt::Key_Backspace: |
| case Qt::Key_Left: |
| case Qt::Key_Right: |
| ke->accept(); |
| default: |
| break; |
| } |
| } |
| } |
| } |
| #endif |
| default: |
| return false; |
| } |
| return true; |
| } |
| |
| void QLineControl::processMouseEvent(QMouseEvent* ev) |
| { |
| |
| switch (ev->type()) { |
| case QEvent::GraphicsSceneMousePress: |
| case QEvent::MouseButtonPress:{ |
| if (m_tripleClickTimer |
| && (ev->pos() - m_tripleClick).manhattanLength() |
| < QApplication::startDragDistance()) { |
| selectAll(); |
| return; |
| } |
| if (ev->button() == Qt::RightButton) |
| return; |
| |
| bool mark = ev->modifiers() & Qt::ShiftModifier; |
| int cursor = xToPos(ev->pos().x()); |
| moveCursor(cursor, mark); |
| break; |
| } |
| case QEvent::GraphicsSceneMouseDoubleClick: |
| case QEvent::MouseButtonDblClick: |
| if (ev->button() == Qt::LeftButton) { |
| selectWordAtPos(xToPos(ev->pos().x())); |
| if (m_tripleClickTimer) |
| killTimer(m_tripleClickTimer); |
| m_tripleClickTimer = startTimer(QApplication::doubleClickInterval()); |
| m_tripleClick = ev->pos(); |
| } |
| break; |
| case QEvent::GraphicsSceneMouseRelease: |
| case QEvent::MouseButtonRelease: |
| #ifndef QT_NO_CLIPBOARD |
| if (QApplication::clipboard()->supportsSelection()) { |
| if (ev->button() == Qt::LeftButton) { |
| copy(QClipboard::Selection); |
| } else if (!isReadOnly() && ev->button() == Qt::MidButton) { |
| deselect(); |
| insert(QApplication::clipboard()->text(QClipboard::Selection)); |
| } |
| } |
| #endif |
| break; |
| case QEvent::GraphicsSceneMouseMove: |
| case QEvent::MouseMove: |
| if (ev->buttons() & Qt::LeftButton) { |
| moveCursor(xToPos(ev->pos().x()), true); |
| } |
| break; |
| default: |
| break; |
| } |
| } |
| |
| void QLineControl::processKeyEvent(QKeyEvent* event) |
| { |
| bool inlineCompletionAccepted = false; |
| |
| #ifndef QT_NO_COMPLETER |
| if (m_completer) { |
| QCompleter::CompletionMode completionMode = m_completer->completionMode(); |
| if ((completionMode == QCompleter::PopupCompletion |
| || completionMode == QCompleter::UnfilteredPopupCompletion) |
| && m_completer->popup() |
| && m_completer->popup()->isVisible()) { |
| // The following keys are forwarded by the completer to the widget |
| // Ignoring the events lets the completer provide suitable default behavior |
| switch (event->key()) { |
| case Qt::Key_Escape: |
| event->ignore(); |
| return; |
| case Qt::Key_Enter: |
| case Qt::Key_Return: |
| case Qt::Key_F4: |
| #ifdef QT_KEYPAD_NAVIGATION |
| case Qt::Key_Select: |
| if (!QApplication::keypadNavigationEnabled()) |
| break; |
| #endif |
| m_completer->popup()->hide(); // just hide. will end up propagating to parent |
| default: |
| break; // normal key processing |
| } |
| } else if (completionMode == QCompleter::InlineCompletion) { |
| switch (event->key()) { |
| case Qt::Key_Enter: |
| case Qt::Key_Return: |
| case Qt::Key_F4: |
| #ifdef QT_KEYPAD_NAVIGATION |
| case Qt::Key_Select: |
| if (!QApplication::keypadNavigationEnabled()) |
| break; |
| #endif |
| if (!m_completer->currentCompletion().isEmpty() && hasSelectedText() |
| && textAfterSelection().isEmpty()) { |
| setText(m_completer->currentCompletion()); |
| inlineCompletionAccepted = true; |
| } |
| default: |
| break; // normal key processing |
| } |
| } |
| } |
| #endif // QT_NO_COMPLETER |
| |
| if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) { |
| if (hasAcceptableInput() || fixup()) { |
| emit accepted(); |
| emit editingFinished(); |
| } |
| if (inlineCompletionAccepted) |
| event->accept(); |
| else |
| event->ignore(); |
| return; |
| } |
| |
| if (echoMode() == QLineEdit::PasswordEchoOnEdit |
| && !passwordEchoEditing() |
| && !isReadOnly() |
| && !event->text().isEmpty() |
| #ifdef QT_KEYPAD_NAVIGATION |
| && event->key() != Qt::Key_Select |
| && event->key() != Qt::Key_Up |
| && event->key() != Qt::Key_Down |
| && event->key() != Qt::Key_Back |
| #endif |
| && !(event->modifiers() & Qt::ControlModifier)) { |
| // Clear the edit and reset to normal echo mode while editing; the |
| // echo mode switches back when the edit loses focus |
| // ### resets current content. dubious code; you can |
| // navigate with keys up, down, back, and select(?), but if you press |
| // "left" or "right" it clears? |
| updatePasswordEchoEditing(true); |
| clear(); |
| } |
| |
| bool unknown = false; |
| |
| if (false) { |
| } |
| #ifndef QT_NO_SHORTCUT |
| else if (event == QKeySequence::Undo) { |
| if (!isReadOnly()) |
| undo(); |
| } |
| else if (event == QKeySequence::Redo) { |
| if (!isReadOnly()) |
| redo(); |
| } |
| else if (event == QKeySequence::SelectAll) { |
| selectAll(); |
| } |
| #ifndef QT_NO_CLIPBOARD |
| else if (event == QKeySequence::Copy) { |
| copy(); |
| } |
| else if (event == QKeySequence::Paste) { |
| if (!isReadOnly()) { |
| QClipboard::Mode mode = QClipboard::Clipboard; |
| #ifdef Q_WS_X11 |
| if (event->modifiers() == (Qt::CTRL | Qt::SHIFT) && event->key() == Qt::Key_Insert) |
| mode = QClipboard::Selection; |
| #endif |
| paste(mode); |
| } |
| } |
| else if (event == QKeySequence::Cut) { |
| if (!isReadOnly()) { |
| copy(); |
| del(); |
| } |
| } |
| else if (event == QKeySequence::DeleteEndOfLine) { |
| if (!isReadOnly()) { |
| setSelection(cursor(), end()); |
| copy(); |
| del(); |
| } |
| } |
| #endif //QT_NO_CLIPBOARD |
| else if (event == QKeySequence::MoveToStartOfLine || event == QKeySequence::MoveToStartOfBlock) { |
| home(0); |
| } |
| else if (event == QKeySequence::MoveToEndOfLine || event == QKeySequence::MoveToEndOfBlock) { |
| end(0); |
| } |
| else if (event == QKeySequence::SelectStartOfLine || event == QKeySequence::SelectStartOfBlock) { |
| home(1); |
| } |
| else if (event == QKeySequence::SelectEndOfLine || event == QKeySequence::SelectEndOfBlock) { |
| end(1); |
| } |
| else if (event == QKeySequence::MoveToNextChar) { |
| #if !defined(Q_WS_WIN) || defined(QT_NO_COMPLETER) |
| if (hasSelectedText()) { |
| #else |
| if (hasSelectedText() && m_completer |
| && m_completer->completionMode() == QCompleter::InlineCompletion) { |
| #endif |
| moveCursor(selectionEnd(), false); |
| } else { |
| cursorForward(0, layoutDirection() == Qt::LeftToRight ? 1 : -1); |
| } |
| } |
| else if (event == QKeySequence::SelectNextChar) { |
| cursorForward(1, layoutDirection() == Qt::LeftToRight ? 1 : -1); |
| } |
| else if (event == QKeySequence::MoveToPreviousChar) { |
| #if !defined(Q_WS_WIN) || defined(QT_NO_COMPLETER) |
| if (hasSelectedText()) { |
| #else |
| if (hasSelectedText() && m_completer |
| && m_completer->completionMode() == QCompleter::InlineCompletion) { |
| #endif |
| moveCursor(selectionStart(), false); |
| } else { |
| cursorForward(0, layoutDirection() == Qt::LeftToRight ? -1 : 1); |
| } |
| } |
| else if (event == QKeySequence::SelectPreviousChar) { |
| cursorForward(1, layoutDirection() == Qt::LeftToRight ? -1 : 1); |
| } |
| else if (event == QKeySequence::MoveToNextWord) { |
| if (echoMode() == QLineEdit::Normal) |
| layoutDirection() == Qt::LeftToRight ? cursorWordForward(0) : cursorWordBackward(0); |
| else |
| layoutDirection() == Qt::LeftToRight ? end(0) : home(0); |
| } |
| else if (event == QKeySequence::MoveToPreviousWord) { |
| if (echoMode() == QLineEdit::Normal) |
| layoutDirection() == Qt::LeftToRight ? cursorWordBackward(0) : cursorWordForward(0); |
| else if (!isReadOnly()) { |
| layoutDirection() == Qt::LeftToRight ? home(0) : end(0); |
| } |
| } |
| else if (event == QKeySequence::SelectNextWord) { |
| if (echoMode() == QLineEdit::Normal) |
| layoutDirection() == Qt::LeftToRight ? cursorWordForward(1) : cursorWordBackward(1); |
| else |
| layoutDirection() == Qt::LeftToRight ? end(1) : home(1); |
| } |
| else if (event == QKeySequence::SelectPreviousWord) { |
| if (echoMode() == QLineEdit::Normal) |
| layoutDirection() == Qt::LeftToRight ? cursorWordBackward(1) : cursorWordForward(1); |
| else |
| layoutDirection() == Qt::LeftToRight ? home(1) : end(1); |
| } |
| else if (event == QKeySequence::Delete) { |
| if (!isReadOnly()) |
| del(); |
| } |
| else if (event == QKeySequence::DeleteEndOfWord) { |
| if (!isReadOnly()) { |
| cursorWordForward(true); |
| del(); |
| } |
| } |
| else if (event == QKeySequence::DeleteStartOfWord) { |
| if (!isReadOnly()) { |
| cursorWordBackward(true); |
| del(); |
| } |
| } |
| #endif // QT_NO_SHORTCUT |
| else { |
| bool handled = false; |
| #ifdef Q_WS_MAC |
| if (event->key() == Qt::Key_Up || event->key() == Qt::Key_Down) { |
| Qt::KeyboardModifiers myModifiers = (event->modifiers() & ~Qt::KeypadModifier); |
| if (myModifiers & Qt::ShiftModifier) { |
| if (myModifiers == (Qt::ControlModifier|Qt::ShiftModifier) |
| || myModifiers == (Qt::AltModifier|Qt::ShiftModifier) |
| || myModifiers == Qt::ShiftModifier) { |
| |
| event->key() == Qt::Key_Up ? home(1) : end(1); |
| } |
| } else { |
| if ((myModifiers == Qt::ControlModifier |
| || myModifiers == Qt::AltModifier |
| || myModifiers == Qt::NoModifier)) { |
| event->key() == Qt::Key_Up ? home(0) : end(0); |
| } |
| } |
| handled = true; |
| } |
| #endif |
| if (event->modifiers() & Qt::ControlModifier) { |
| switch (event->key()) { |
| case Qt::Key_Backspace: |
| if (!isReadOnly()) { |
| cursorWordBackward(true); |
| del(); |
| } |
| break; |
| #ifndef QT_NO_COMPLETER |
| case Qt::Key_Up: |
| case Qt::Key_Down: |
| complete(event->key()); |
| break; |
| #endif |
| #if defined(Q_WS_X11) |
| case Qt::Key_E: |
| end(0); |
| break; |
| |
| case Qt::Key_U: |
| if (!isReadOnly()) { |
| setSelection(0, text().size()); |
| #ifndef QT_NO_CLIPBOARD |
| copy(); |
| #endif |
| del(); |
| } |
| break; |
| #endif |
| default: |
| if (!handled) |
| unknown = true; |
| } |
| } else { // ### check for *no* modifier |
| switch (event->key()) { |
| case Qt::Key_Backspace: |
| if (!isReadOnly()) { |
| backspace(); |
| #ifndef QT_NO_COMPLETER |
| complete(Qt::Key_Backspace); |
| #endif |
| } |
| break; |
| #ifdef QT_KEYPAD_NAVIGATION |
| case Qt::Key_Back: |
| if (QApplication::keypadNavigationEnabled() && !event->isAutoRepeat() |
| && !isReadOnly()) { |
| if (text().length() == 0) { |
| setText(m_cancelText); |
| |
| if (passwordEchoEditing()) |
| updatePasswordEchoEditing(false); |
| |
| emit editFocusChange(false); |
| } else if (!m_deleteAllTimer) { |
| m_deleteAllTimer = startTimer(750); |
| } |
| } else { |
| unknown = true; |
| } |
| break; |
| #endif |
| default: |
| if (!handled) |
| unknown = true; |
| } |
| } |
| } |
| |
| if (event->key() == Qt::Key_Direction_L || event->key() == Qt::Key_Direction_R) { |
| setLayoutDirection((event->key() == Qt::Key_Direction_L) ? Qt::LeftToRight : Qt::RightToLeft); |
| unknown = false; |
| } |
| |
| if (unknown && !isReadOnly()) { |
| QString t = event->text(); |
| if (!t.isEmpty() && t.at(0).isPrint()) { |
| insert(t); |
| #ifndef QT_NO_COMPLETER |
| complete(event->key()); |
| #endif |
| event->accept(); |
| return; |
| } |
| } |
| |
| if (unknown) |
| event->ignore(); |
| else |
| event->accept(); |
| } |
| |
| |
| QT_END_NAMESPACE |
| |
| #endif |