1 /**************************************************************************** 2 ** 3 ** Copyright (C) 2020 The Qt Company Ltd. 4 ** Contact: https://www.qt.io/licensing/ 5 ** 6 ** This file is part of the QtWidgets module of the Qt Toolkit. 7 ** 8 ** $QT_BEGIN_LICENSE:LGPL$ 9 ** Commercial License Usage 10 ** Licensees holding valid commercial Qt licenses may use this file in 11 ** accordance with the commercial license agreement provided with the 12 ** Software or, alternatively, in accordance with the terms contained in 13 ** a written agreement between you and The Qt Company. For licensing terms 14 ** and conditions see https://www.qt.io/terms-conditions. For further 15 ** information use the contact form at https://www.qt.io/contact-us. 16 ** 17 ** GNU Lesser General Public License Usage 18 ** Alternatively, this file may be used under the terms of the GNU Lesser 19 ** General Public License version 3 as published by the Free Software 20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the 21 ** packaging of this file. Please review the following information to 22 ** ensure the GNU Lesser General Public License version 3 requirements 23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. 24 ** 25 ** GNU General Public License Usage 26 ** Alternatively, this file may be used under the terms of the GNU 27 ** General Public License version 2.0 or (at your option) the GNU General 28 ** Public license version 3 or any later version approved by the KDE Free 29 ** Qt Foundation. The licenses are as published by the Free Software 30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 31 ** included in the packaging of this file. Please review the following 32 ** information to ensure the GNU General Public License requirements will 33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and 34 ** https://www.gnu.org/licenses/gpl-3.0.html. 35 ** 36 ** $QT_END_LICENSE$ 37 ** 38 ****************************************************************************/ 39 40 #ifndef QWIDGETLINECONTROL_P_H 41 #define QWIDGETLINECONTROL_P_H 42 43 // 44 // W A R N I N G 45 // ------------- 46 // 47 // This file is not part of the Qt API. It exists purely as an 48 // implementation detail. This header file may change from version to 49 // version without notice, or even be removed. 50 // 51 // We mean it. 52 // 53 54 #include <QtWidgets/private/qtwidgetsglobal_p.h> 55 56 #include "private/qwidget_p.h" 57 #include "QtWidgets/qlineedit.h" 58 #include "QtGui/qtextlayout.h" 59 #include "QtWidgets/qstyleoption.h" 60 #include "QtCore/qpointer.h" 61 #include "QtGui/qclipboard.h" 62 #include "QtGui/qinputmethod.h" 63 #include "QtCore/qpoint.h" 64 #if QT_CONFIG(completer) 65 #include "QtWidgets/qcompleter.h" 66 #endif 67 #include "QtCore/qthread.h" 68 #include "QtGui/private/qinputcontrol_p.h" 69 70 #include "qplatformdefs.h" 71 72 #include <vector> 73 74 #ifdef DrawText 75 # undef DrawText 76 #endif 77 78 QT_REQUIRE_CONFIG(lineedit); 79 80 QT_BEGIN_NAMESPACE 81 82 class Q_WIDGETS_EXPORT QWidgetLineControl : public QInputControl 83 { 84 Q_OBJECT 85 86 public: 87 QWidgetLineControl(const QString &txt = QString()) QInputControl(LineEdit)88 : QInputControl(LineEdit) 89 , m_cursor(0), m_preeditCursor(0), m_cursorWidth(0), m_layoutDirection(Qt::LayoutDirectionAuto), 90 m_hideCursor(false), m_separator(0), m_readOnly(0), 91 m_dragEnabled(0), m_echoMode(0), m_textDirty(0), m_selDirty(0), 92 m_validInput(1), m_blinkStatus(0), m_blinkEnabled(false), m_blinkTimer(0), m_deleteAllTimer(0), 93 m_ascent(0), m_maxLength(32767), m_lastCursorPos(-1), 94 m_tripleClickTimer(0), m_maskData(nullptr), m_modifiedState(0), m_undoState(0), 95 m_selstart(0), m_selend(0), m_passwordEchoEditing(false) 96 , m_passwordEchoTimer(0) 97 , m_passwordMaskDelay(-1) 98 #if defined(QT_BUILD_INTERNAL) 99 , m_passwordMaskDelayOverride(-1) 100 #endif 101 , m_keyboardScheme(0) 102 , m_accessibleObject(nullptr) 103 { 104 init(txt); 105 } 106 ~QWidgetLineControl()107 ~QWidgetLineControl() 108 { 109 // If this control is used for password input, we don't want the 110 // password data to stay in the process memory, therefore we need 111 // to zero it out 112 if (m_echoMode != QLineEdit::Normal) 113 m_text.fill('\0'); 114 115 delete [] m_maskData; 116 } 117 setAccessibleObject(QObject * object)118 void setAccessibleObject(QObject *object) 119 { 120 Q_ASSERT(object); 121 m_accessibleObject = object; 122 } 123 accessibleObject()124 QObject *accessibleObject() 125 { 126 if (m_accessibleObject) 127 return m_accessibleObject; 128 return parent(); 129 } 130 nextMaskBlank(int pos)131 int nextMaskBlank(int pos) 132 { 133 int c = findInMask(pos, true, false); 134 m_separator |= (c != pos); 135 return (c != -1 ? c : m_maxLength); 136 } 137 prevMaskBlank(int pos)138 int prevMaskBlank(int pos) 139 { 140 int c = findInMask(pos, false, false); 141 m_separator |= (c != pos); 142 return (c != -1 ? c : 0); 143 } 144 145 bool isUndoAvailable() const; 146 bool isRedoAvailable() const; clearUndo()147 void clearUndo() { m_history.clear(); m_modifiedState = m_undoState = 0; } 148 isModified()149 bool isModified() const { return m_modifiedState != m_undoState; } setModified(bool modified)150 void setModified(bool modified) { m_modifiedState = modified ? -1 : m_undoState; } 151 allSelected()152 bool allSelected() const { return !m_text.isEmpty() && m_selstart == 0 && m_selend == (int)m_text.length(); } hasSelectedText()153 bool hasSelectedText() const { return !m_text.isEmpty() && m_selend > m_selstart; } 154 width()155 int width() const { return qRound(m_textLayout.lineAt(0).width()) + 1; } height()156 int height() const { return qRound(m_textLayout.lineAt(0).height()) + 1; } ascent()157 int ascent() const { return m_ascent; } naturalTextWidth()158 qreal naturalTextWidth() const { return m_textLayout.lineAt(0).naturalTextWidth(); } 159 160 void setSelection(int start, int length); 161 selectedText()162 inline QString selectedText() const { return hasSelectedText() ? m_text.mid(m_selstart, m_selend - m_selstart) : QString(); } textBeforeSelection()163 QString textBeforeSelection() const { return hasSelectedText() ? m_text.left(m_selstart) : QString(); } textAfterSelection()164 QString textAfterSelection() const { return hasSelectedText() ? m_text.mid(m_selend) : QString(); } 165 selectionStart()166 int selectionStart() const { return hasSelectedText() ? m_selstart : -1; } selectionEnd()167 int selectionEnd() const { return hasSelectedText() ? m_selend : -1; } inSelection(int x)168 bool inSelection(int x) const 169 { 170 if (m_selstart >= m_selend) 171 return false; 172 int pos = xToPos(x, QTextLine::CursorOnCharacter); 173 return pos >= m_selstart && pos < m_selend; 174 } 175 removeSelection()176 void removeSelection() 177 { 178 int priorState = m_undoState; 179 removeSelectedText(); 180 finishChange(priorState); 181 } 182 start()183 int start() const { return 0; } end()184 int end() const { return m_text.length(); } 185 186 #ifndef QT_NO_CLIPBOARD 187 void copy(QClipboard::Mode mode = QClipboard::Clipboard) const; 188 void paste(QClipboard::Mode mode = QClipboard::Clipboard); 189 #endif 190 cursor()191 int cursor() const{ return m_cursor; } preeditCursor()192 int preeditCursor() const { return m_preeditCursor; } 193 cursorWidth()194 int cursorWidth() const { return m_cursorWidth; } setCursorWidth(int value)195 void setCursorWidth(int value) { m_cursorWidth = value; } 196 cursorMoveStyle()197 Qt::CursorMoveStyle cursorMoveStyle() const { return m_textLayout.cursorMoveStyle(); } setCursorMoveStyle(Qt::CursorMoveStyle style)198 void setCursorMoveStyle(Qt::CursorMoveStyle style) { m_textLayout.setCursorMoveStyle(style); } 199 200 void moveCursor(int pos, bool mark = false); cursorForward(bool mark,int steps)201 void cursorForward(bool mark, int steps) 202 { 203 int c = m_cursor; 204 if (steps > 0) { 205 while (steps--) 206 c = cursorMoveStyle() == Qt::VisualMoveStyle ? m_textLayout.rightCursorPosition(c) 207 : m_textLayout.nextCursorPosition(c); 208 } else if (steps < 0) { 209 while (steps++) 210 c = cursorMoveStyle() == Qt::VisualMoveStyle ? m_textLayout.leftCursorPosition(c) 211 : m_textLayout.previousCursorPosition(c); 212 } 213 moveCursor(c, mark); 214 } 215 cursorWordForward(bool mark)216 void cursorWordForward(bool mark) { moveCursor(m_textLayout.nextCursorPosition(m_cursor, QTextLayout::SkipWords), mark); } cursorWordBackward(bool mark)217 void cursorWordBackward(bool mark) { moveCursor(m_textLayout.previousCursorPosition(m_cursor, QTextLayout::SkipWords), mark); } 218 home(bool mark)219 void home(bool mark) { moveCursor(0, mark); } end(bool mark)220 void end(bool mark) { moveCursor(m_text.length(), mark); } 221 222 int xToPos(int x, QTextLine::CursorPosition = QTextLine::CursorBetweenCharacters) const; 223 QRect rectForPos(int pos) const; 224 QRect cursorRect() const; 225 QRect anchorRect() const; 226 cursorToX(int cursor)227 qreal cursorToX(int cursor) const { return m_textLayout.lineAt(0).cursorToX(cursor); } cursorToX()228 qreal cursorToX() const 229 { 230 int cursor = m_cursor; 231 if (m_preeditCursor != -1) 232 cursor += m_preeditCursor; 233 return cursorToX(cursor); 234 } 235 isReadOnly()236 bool isReadOnly() const { return m_readOnly; } 237 void setReadOnly(bool enable); 238 text()239 QString text() const 240 { 241 QString content = m_text; 242 QString res = m_maskData ? stripString(content) : content; 243 return (res.isNull() ? QString::fromLatin1("") : res); 244 } setText(const QString & txt)245 void setText(const QString &txt) 246 { 247 #ifndef QT_NO_IM 248 if (composeMode()) 249 QGuiApplication::inputMethod()->reset(); 250 #endif 251 internalSetText(txt, -1, false); 252 } 253 void commitPreedit(); 254 displayText()255 QString displayText() const { return m_textLayout.text(); } 256 surroundingText()257 QString surroundingText() const 258 { 259 return m_text.isNull() ? QString::fromLatin1("") : m_text; 260 } 261 262 void backspace(); 263 void del(); deselect()264 void deselect() { internalDeselect(); finishChange(); } selectAll()265 void selectAll() { m_selstart = m_selend = m_cursor = 0; moveCursor(m_text.length(), true); } 266 267 void insert(const QString &); 268 void clear(); 269 void undo(); redo()270 void redo() { internalRedo(); finishChange(); } 271 void selectWordAtPos(int); 272 echoMode()273 uint echoMode() const { return m_echoMode; } setEchoMode(uint mode)274 void setEchoMode(uint mode) 275 { 276 cancelPasswordEchoTimer(); 277 m_echoMode = mode; 278 m_passwordEchoEditing = false; 279 280 // If this control is used for password input, we want to minimize 281 // the possibility of string reallocation not to leak (parts of) 282 // the password. 283 if (m_echoMode != QLineEdit::Normal) 284 m_text.reserve(30); 285 286 updateDisplayText(); 287 } 288 maxLength()289 int maxLength() const { return m_maxLength; } setMaxLength(int maxLength)290 void setMaxLength(int maxLength) 291 { 292 if (m_maskData) 293 return; 294 m_maxLength = maxLength; 295 setText(m_text); 296 } 297 298 #ifndef QT_NO_VALIDATOR validator()299 const QValidator *validator() const { return m_validator; } setValidator(const QValidator * v)300 void setValidator(const QValidator *v) { m_validator = const_cast<QValidator*>(v); } 301 #endif 302 303 #if QT_CONFIG(completer) completer()304 QCompleter *completer() const { return m_completer; } 305 /* Note that you must set the widget for the completer separately */ setCompleter(const QCompleter * c)306 void setCompleter(const QCompleter *c) { m_completer = const_cast<QCompleter*>(c); } 307 void complete(int key); 308 #endif 309 cursorPosition()310 int cursorPosition() const { return m_cursor; } setCursorPosition(int pos)311 void setCursorPosition(int pos) { if (pos <= m_text.length()) moveCursor(qMax(0, pos)); } 312 hasAcceptableInput()313 bool hasAcceptableInput() const { return hasAcceptableInput(m_text); } 314 bool fixup(); 315 inputMask()316 QString inputMask() const 317 { 318 QString mask; 319 if (m_maskData) { 320 mask = m_inputMask; 321 if (m_blank != QLatin1Char(' ')) { 322 mask += QLatin1Char(';'); 323 mask += m_blank; 324 } 325 } 326 return mask; 327 } setInputMask(const QString & mask)328 void setInputMask(const QString &mask) 329 { 330 parseInputMask(mask); 331 if (m_maskData) 332 moveCursor(nextMaskBlank(0)); 333 } 334 335 // input methods 336 #ifndef QT_NO_IM composeMode()337 bool composeMode() const { return !m_textLayout.preeditAreaText().isEmpty(); } setPreeditArea(int cursor,const QString & text)338 void setPreeditArea(int cursor, const QString &text) { m_textLayout.setPreeditArea(cursor, text); } 339 #endif 340 preeditAreaText()341 QString preeditAreaText() const { return m_textLayout.preeditAreaText(); } 342 343 void updatePasswordEchoEditing(bool editing); passwordEchoEditing()344 bool passwordEchoEditing() const { 345 if (m_passwordEchoTimer != 0) 346 return true; 347 return m_passwordEchoEditing ; 348 } 349 passwordCharacter()350 QChar passwordCharacter() const { return m_passwordCharacter; } setPasswordCharacter(QChar character)351 void setPasswordCharacter(QChar character) { m_passwordCharacter = character; updateDisplayText(); } 352 passwordMaskDelay()353 int passwordMaskDelay() const { return m_passwordMaskDelay; } setPasswordMaskDelay(int delay)354 void setPasswordMaskDelay(int delay) { m_passwordMaskDelay = delay; } 355 layoutDirection()356 Qt::LayoutDirection layoutDirection() const { 357 if (m_layoutDirection == Qt::LayoutDirectionAuto && !m_text.isEmpty()) 358 return m_text.isRightToLeft() ? Qt::RightToLeft : Qt::LeftToRight; 359 return m_layoutDirection; 360 } setLayoutDirection(Qt::LayoutDirection direction)361 void setLayoutDirection(Qt::LayoutDirection direction) 362 { 363 if (direction != m_layoutDirection) { 364 m_layoutDirection = direction; 365 updateDisplayText(); 366 } 367 } 368 setFont(const QFont & font)369 void setFont(const QFont &font) { m_textLayout.setFont(font); updateDisplayText(); } 370 371 void processInputMethodEvent(QInputMethodEvent *event); 372 void processKeyEvent(QKeyEvent* ev); 373 374 void setBlinkingCursorEnabled(bool enable); 375 void updateCursorBlinking(); 376 void resetCursorBlinkTimer(); 377 cursorBlinkStatus()378 bool cursorBlinkStatus() const { return m_blinkStatus; } 379 cancelText()380 QString cancelText() const { return m_cancelText; } setCancelText(const QString & text)381 void setCancelText(const QString &text) { m_cancelText = text; } 382 palette()383 const QPalette &palette() const { return m_palette; } setPalette(const QPalette & p)384 void setPalette(const QPalette &p) { m_palette = p; } 385 386 enum DrawFlags { 387 DrawText = 0x01, 388 DrawSelections = 0x02, 389 DrawCursor = 0x04, 390 DrawAll = DrawText | DrawSelections | DrawCursor 391 }; 392 void draw(QPainter *, const QPoint &, const QRect &, int flags = DrawAll); 393 394 #ifndef QT_NO_SHORTCUT 395 void processShortcutOverrideEvent(QKeyEvent *ke); 396 #endif 397 textLayout()398 QTextLayout *textLayout() const 399 { 400 return &m_textLayout; 401 } 402 403 private: 404 void init(const QString &txt); 405 void removeSelectedText(); 406 void internalSetText(const QString &txt, int pos = -1, bool edited = true); 407 void updateDisplayText(bool forceUpdate = false); 408 409 void internalInsert(const QString &s); 410 void internalDelete(bool wasBackspace = false); 411 void internalRemove(int pos); 412 internalDeselect()413 inline void internalDeselect() 414 { 415 m_selDirty |= (m_selend > m_selstart); 416 m_selstart = m_selend = 0; 417 } 418 419 void internalUndo(int until = -1); 420 void internalRedo(); 421 422 QString m_text; 423 QPalette m_palette; 424 int m_cursor; 425 int m_preeditCursor; 426 int m_cursorWidth; 427 Qt::LayoutDirection m_layoutDirection; 428 uint m_hideCursor : 1; // used to hide the m_cursor inside preedit areas 429 uint m_separator : 1; 430 uint m_readOnly : 1; 431 uint m_dragEnabled : 1; 432 uint m_echoMode : 2; 433 uint m_textDirty : 1; 434 uint m_selDirty : 1; 435 uint m_validInput : 1; 436 uint m_blinkStatus : 1; 437 uint m_blinkEnabled : 1; 438 int m_blinkTimer; 439 int m_deleteAllTimer; 440 int m_ascent; 441 int m_maxLength; 442 int m_lastCursorPos; 443 QList<int> m_transactions; 444 QPoint m_tripleClick; 445 int m_tripleClickTimer; 446 QString m_cancelText; 447 448 void emitCursorPositionChanged(); 449 450 bool finishChange(int validateFromState = -1, bool update = false, bool edited = true); 451 452 #ifndef QT_NO_VALIDATOR 453 QPointer<QValidator> m_validator; 454 #endif 455 QPointer<QCompleter> m_completer; 456 #if QT_CONFIG(completer) 457 bool advanceToEnabledItem(int dir); 458 #endif 459 460 struct MaskInputData { 461 enum Casemode { NoCaseMode, Upper, Lower }; 462 QChar maskChar; // either the separator char or the inputmask 463 bool separator; 464 Casemode caseMode; 465 }; 466 QString m_inputMask; 467 QChar m_blank; 468 MaskInputData *m_maskData; 469 470 // undo/redo handling 471 enum CommandType { Separator, Insert, Remove, Delete, RemoveSelection, DeleteSelection, SetSelection }; 472 struct Command { CommandCommand473 inline Command(CommandType t, int p, QChar c, int ss, int se) : type(t),uc(c),pos(p),selStart(ss),selEnd(se) {} 474 uint type : 4; 475 QChar uc; 476 int pos, selStart, selEnd; 477 }; 478 int m_modifiedState; 479 int m_undoState; 480 std::vector<Command> m_history; 481 void addCommand(const Command& cmd); 482 separate()483 inline void separate() { m_separator = true; } 484 485 // selection 486 int m_selstart; 487 int m_selend; 488 489 // masking 490 void parseInputMask(const QString &maskFields); 491 bool isValidInput(QChar key, QChar mask) const; 492 bool hasAcceptableInput(const QString &text) const; 493 QString maskString(int pos, const QString &str, bool clear = false) const; 494 QString clearString(int pos, int len) const; 495 QString stripString(const QString &str) const; 496 int findInMask(int pos, bool forward, bool findSeparator, QChar searchChar = QChar()) const; 497 498 // complex text layout (must be mutable so it can be reshaped at will) 499 mutable QTextLayout m_textLayout; 500 501 bool m_passwordEchoEditing; 502 QChar m_passwordCharacter; 503 int m_passwordEchoTimer; 504 int m_passwordMaskDelay; cancelPasswordEchoTimer()505 void cancelPasswordEchoTimer() 506 { 507 if (m_passwordEchoTimer != 0) { 508 killTimer(m_passwordEchoTimer); 509 m_passwordEchoTimer = 0; 510 } 511 } 512 513 int redoTextLayout() const; 514 515 public: 516 #if defined(QT_BUILD_INTERNAL) 517 int m_passwordMaskDelayOverride; 518 #endif 519 520 Q_SIGNALS: 521 void cursorPositionChanged(int, int); 522 void selectionChanged(); 523 524 void displayTextChanged(const QString &); 525 void textChanged(const QString &); 526 void textEdited(const QString &); 527 528 void resetInputContext(); 529 void updateMicroFocus(); 530 531 void accepted(); 532 void editingFinished(); 533 void updateNeeded(const QRect &); 534 void inputRejected(); 535 536 #ifdef QT_KEYPAD_NAVIGATION 537 void editFocusChange(bool); 538 #endif 539 protected: 540 virtual void timerEvent(QTimerEvent *event) override; 541 542 private Q_SLOTS: 543 void _q_deleteSelected(); 544 545 private: 546 int m_keyboardScheme; 547 548 // accessibility events are sent for this object 549 QObject *m_accessibleObject; 550 }; 551 552 QT_END_NAMESPACE 553 554 #endif // QWIDGETLINECONTROL_P_H 555