QtPass 2.0.0
Developer documentation
Loading...
Searching...
No Matches
secretlineedit.cpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2026 Anne Jan Brouwer
2// SPDX-License-Identifier: GPL-3.0-or-later
8#include "secretlineedit.h"
9#include "clipboardmanager.h"
10#include <QAction>
11#include <QApplication>
12#include <QContextMenuEvent>
13#include <QKeyEvent>
14#include <QMenu>
15#include <QMouseEvent>
16
17SecretLineEdit::SecretLineEdit(QWidget *parent) : QLineEdit(parent) {}
18
20 QMenu *menu = createStandardContextMenu();
21 for (QAction *action : menu->actions()) {
22 // The standard actions call QLineEdit::copy() and cut(), which are not
23 // virtual.
24 if (action->objectName() == QLatin1String("edit-copy")) {
25 disconnect(action, &QAction::triggered, this, nullptr);
26 connect(action, &QAction::triggered, this,
27 [this] { copySelection(QClipboard::Clipboard); });
28 } else if (action->objectName() == QLatin1String("edit-cut")) {
29 disconnect(action, &QAction::triggered, this, nullptr);
30 connect(action, &QAction::triggered, this, [this] { cutSelection(); });
31 }
32 }
33 return menu;
34}
35
36void SecretLineEdit::keyPressEvent(QKeyEvent *event) {
37 if (event->matches(QKeySequence::Copy)) {
38 copySelection(QClipboard::Clipboard);
39 event->accept();
40 return;
41 }
42 if (event->matches(QKeySequence::Cut)) {
44 event->accept();
45 return;
46 }
47 // QLineEdit copies what these delete (Ctrl+K and Ctrl+U with the X11 key
48 // bindings), as a cut.
49 const bool toEnd = event->matches(QKeySequence::DeleteEndOfLine);
50 if (toEnd || event->matches(QKeySequence::DeleteCompleteLine)) {
51 if (!isReadOnly()) {
52 const int from = toEnd ? cursorPosition() : 0;
53 setSelection(from, static_cast<int>(text().size()) - from);
55 }
56 event->accept();
57 return;
58 }
59 withoutPlainSelectionCopy([this, event] { QLineEdit::keyPressEvent(event); });
60 // Where QLineEdit would have copied: after a key it handled, not after a
61 // lone modifier or another key it ignores.
62 if (event->isAccepted()) {
64 }
65}
66
67void SecretLineEdit::mouseReleaseEvent(QMouseEvent *event) {
69 [this, event] { QLineEdit::mouseReleaseEvent(event); });
70 if (event->button() == Qt::LeftButton) {
72 }
73}
74
75void SecretLineEdit::contextMenuEvent(QContextMenuEvent *event) {
76 QMenu *menu = createSecretContextMenu();
77 menu->setAttribute(Qt::WA_DeleteOnClose);
78 menu->popup(event->globalPos());
79}
80
81void SecretLineEdit::copySelection(QClipboard::Mode mode) {
82 // As QLineEdit itself: nothing leaves a field that shows dots.
83 if (echoMode() != QLineEdit::Normal || !hasSelectedText()) {
84 return;
85 }
86 const QString text = selectedText();
87 QApplication::clipboard()->setMimeData(buildClipboardMimeData(text), mode);
88 emit copied(text);
89}
90
92 // As QLineEdit::cut(): a field that shows dots deletes without copying.
93 if (isReadOnly() || !hasSelectedText()) {
94 return;
95 }
96 copySelection(QClipboard::Clipboard);
97 del();
98}
99
101 const std::function<void()> &handler) {
102 // QLineEdit fills the primary selection with plain text itself, at the end
103 // of every key it handles and on a left-button release, but only in Normal
104 // echo mode (QWidgetLineControl::copy()). Replacing that afterwards still
105 // leaves a moment in which a clipboard manager could take the unmarked
106 // text, so the base handler runs in Password mode instead: same length,
107 // so cursor and selection stay where they are, and no copy.
108 // Editable, the field keeps Normal mode (see the class description) and
109 // publishSelection() replaces the plain copy.
110 if (echoMode() != QLineEdit::Normal || !isReadOnly()) {
111 handler();
112 return;
113 }
114 setEchoMode(QLineEdit::Password);
115 handler();
116 setEchoMode(QLineEdit::Normal);
117}
118
120 if (QApplication::clipboard()->supportsSelection()) {
121 copySelection(QClipboard::Selection);
122 }
123}
void mouseReleaseEvent(QMouseEvent *event) override
Fills the primary selection with the marked selection on a left-button release, instead of QLineEdit'...
void copied(const QString &text)
Selected text was copied out of the line edit.
void cutSelection()
Copies the selection through copySelection() and then deletes it. Does nothing when read-only or with...
void withoutPlainSelectionCopy(const std::function< void()> &handler)
Runs a base-class event handler so that it cannot fill the primary selection with plain text: in a re...
auto createSecretContextMenu() -> QMenu *
The standard context menu, with Copy and Cut going through the secret copy.
void publishSelection()
Fills the X11 primary selection with the marked selection, on platforms that have one; called after a...
SecretLineEdit(QWidget *parent=nullptr)
Constructs the line edit.
void contextMenuEvent(QContextMenuEvent *event) override
Shows createSecretContextMenu().
void copySelection(QClipboard::Mode mode)
Puts the selection on the clipboard or in the primary selection as secret-marked MIME data and emits ...
void keyPressEvent(QKeyEvent *event) override
Ctrl+C copies with the hints, Ctrl+X as well before it deletes; after any other key the primary selec...
auto buildClipboardMimeData(const QString &text) -> QMimeData *
Build clipboard MIME data with platform-specific security hints.
ClipboardManager, which puts secrets on the clipboard and removes them again, and the MIME data that ...
QLineEdit whose copies, cuts and primary-selection fills carry the secret clipboard hints and are rep...