20#include <QDesktopServices>
32#include <QTextBrowser>
37 QBoxLayout *container,
38 QWidget *widgetParent,
44 while (
m_grid->count() > 0) {
45 QLayoutItem *item =
m_grid->takeAt(0);
46 if (QWidget *widget = item->widget()) {
55 const QList<QDrag *> drags = widget->findChildren<QDrag *>();
56 if (drags.isEmpty()) {
60 connect(drags.last(), &QObject::destroyed, widget,
61 [widget]() { widget->deleteLater(); });
71 for (
int i = 0; i <
m_grid->count(); ++i) {
72 QWidget *widget =
m_grid->itemAt(i)->widget();
73 if (widget ==
nullptr) {
86 const QString &otpConfig) {
95 addField(position, QObject::tr(
"Password"), password, s);
98 bool otpRendered =
false;
108 if (!otpRendered && !otpConfig.isEmpty()) {
115 addField(position, nv.name, nv.value, s);
119 if (!otpConfig.isEmpty() && !otpRendered) {
130const char kButtonStyle[] =
131 "QPushButton { border-style: none; background: transparent; padding: 0; "
132 "margin: 0; icon-size: 16px; color: inherit; }";
135constexpr int kFieldHeight = 26;
141auto valueBrowser(
const QString &field,
const QString &value,
144 browser->setOpenExternalLinks(
true);
145 browser->setOpenLinks(
true);
146 browser->setMaximumHeight(kFieldHeight);
147 browser->setMinimumHeight(kFieldHeight);
148 browser->setSizePolicy(
149 QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum));
150 browser->setObjectName(field);
156 browser->setHtml(linkedText);
160 browser->setPlainText(value);
162 browser->setReadOnly(
true);
163 browser->setStyleSheet(lineStyle);
164 browser->setContentsMargins(0, 0, 0, 0);
169auto urlButton(
const QString &url, QWidget *parent) -> QPushButton * {
170 auto *button =
new QPushButton(parent);
171 button->setIcon(QIcon::fromTheme(QStringLiteral(
"applications-internet"),
172 QIcon(
":/icons/open-url.svg")));
179 QStringLiteral(
"<qt style=\"white-space:nowrap\">%1</qt>")
180 .arg(QObject::tr(
"Open %1 in browser").arg(url.toHtmlEscaped())));
181 button->setStyleSheet(QString::fromLatin1(kButtonStyle));
182 button->setCursor(Qt::PointingHandCursor);
183 QObject::connect(button, &QPushButton::clicked, button, [url]() {
186 QDesktopServices::openUrl(QUrl(url));
200 return s.hidePassword &&
201 (s.hideAllFields || field == QObject::tr(
"Password") ||
207 if (!s.hidePassword) {
212 [&field](
const QString &name) {
213 return field.compare(name, Qt::CaseInsensitive) == 0;
215 return marked || (s.hideSecretFields &&
220 const QString &field,
221 const QString &value,
223 const QString buttonStyle = QString::fromLatin1(kButtonStyle);
228 copy->setStyleSheet(buttonStyle);
229 layout->addWidget(copy);
235 qr->setStyleSheet(buttonStyle);
236 layout->addWidget(qr);
241 if (field != QObject::tr(
"Password") && !
hides(field, s) &&
248 const QString &field,
249 const QString &value,
250 const QString &lineStyle) {
256 passwordLineEdit->setObjectName(field);
257 passwordLineEdit->setText(value);
258 passwordLineEdit->setReadOnly(
true);
259 passwordLineEdit->setStyleSheet(lineStyle);
260 passwordLineEdit->setContentsMargins(0, 0, 0, 0);
261 passwordLineEdit->setEchoMode(QLineEdit::Password);
264 showButton->setStyleSheet(QString::fromLatin1(kButtonStyle));
265 showButton->setContentsMargins(0, 0, 0, 0);
266 layout->addWidget(showButton);
267 layout->addWidget(passwordLineEdit);
271 const QString &value,
273 const QString trimmedField = field.trimmed();
280 QString displayValue;
281 if (trimmedField == QObject::tr(
"Password")) {
282 displayValue = value;
283 if (displayValue.endsWith(QLatin1Char(
'\r'))) {
284 displayValue.chop(1);
287 displayValue = value.trimmed();
291 const QString lineStyle =
293 ?
"QLineEdit, QTextBrowser { border-style: none; background: "
294 "transparent; font-family: monospace; }"
295 :
"QLineEdit, QTextBrowser { border-style: none; background: "
299 if (
hides(trimmedField, s)) {
303 valueBrowser(trimmedField, displayValue, lineStyle);
306 frame->layout()->addWidget(browser);
310 auto *label =
new QLabel();
311 label->setTextFormat(Qt::PlainText);
312 label->setText(trimmedField);
313 addRow(position, label, frame);
317 m_grid->addWidget(label, position, 0);
318 m_grid->addWidget(frame, position, 1);
319 label->installEventFilter(
this);
320 frame->installEventFilter(
this);
322 for (QWidget *child : frame->findChildren<QWidget *>()) {
323 child->installEventFilter(
this);
329 if (event->type() == QEvent::MouseButtonDblClick &&
330 static_cast<QMouseEvent *
>(event)->button() == Qt::LeftButton) {
334 return QObject::eventFilter(watched, event);
340 const QString borderColor =
342 frame->setStyleSheet(QStringLiteral(
".QFrame{border: 1px solid %1; "
343 "border-radius: 5px;}")
348 for (
int i = 0; i <
m_grid->count(); ++i) {
349 if (
auto *frame = qobject_cast<QFrame *>(
m_grid->itemAt(i)->widget())) {
358 auto *frame =
new QFrame();
359 auto *frameLayout =
new QHBoxLayout();
360 frameLayout->setContentsMargins(5, 2, 2, 2);
361 frameLayout->setSpacing(0);
362 frame->setLayout(frameLayout);
373 const std::optional<Totp::Settings> settings =
Totp::parse(otpConfig);
374 if (!settings.has_value()) {
381 addField(position, QObject::tr(
"OTP code"),
382 QObject::tr(
"No OTP code found in this password entry"), inert);
392 frame->layout()->addWidget(otpWidget);
394 addRow(position,
new QLabel(QObject::tr(
"OTP code")), frame);
AppSettings, the value object holding every flat QtPass setting.
static auto isOtpFieldName(const QString &name) -> bool
Whether a field name designates the one-time password field.
static auto isSecretFieldName(const QString &name, const QStringList &secrets) -> bool
Whether a field name names a secret: one of secrets occurs in it as whole words, compared case-insens...
static auto isOtpUriValue(const QString &value) -> bool
Whether a field value is an otpauth URI, and therefore a secret.
The NamedValues class is mostly a list of NamedValue but also has a method to take a specific NamedVa...
auto isSecret(const QString &field, const AppSettings &s) const -> bool
Whether a field is a secret "Hide password" hides: one a template marks (setMarkedSecrets()),...
void qrRequested(const QString &text)
Emitted when a field's QR button is clicked.
QWidget * m_widgetParent
Parent for the buttons the panel creates and source of the palette used by applyFrameStyle(); not own...
void refreshPalette()
Re-derive palette-dependent styling after a runtime theme change.
QStringList m_markedSecrets
Field names the current template marks as secret, compared case-insensitively in isSecret(); set by s...
void setMarkedSecrets(const QStringList &names)
Set the field names a template marks as secret.
void displayFields(const QString &password, const NamedValues &namedValues, const AppSettings &s, const QString &otpConfig={})
Render the password and template fields of a decrypted entry.
auto eventFilter(QObject *watched, QEvent *event) -> bool override
Turn a left double-click on a field row into editRequested().
QGridLayout * m_grid
The grid the rows go into; owned by MainWindow's .ui.
void addActionButtons(QLayout *layout, const QString &field, const QString &value, const AppSettings &s)
Add the copy, QR and open-in-browser buttons a field gets, per the settings.
QBoxLayout * m_container
Layout around the grid whose spacing is 0 while empty and 6 once rows are shown; owned by MainWindow'...
void addField(int position, const QString &field, const QString &value, const AppSettings &s)
Render one name: value row: action buttons, then the value either behind the eye button (see hides())...
void clear()
Remove all field rows and collapse the container spacing.
auto createFieldFrame() -> QFrame *
New bordered frame with a tight horizontal layout, styled by applyFrameStyle(); shared by addField() ...
PasswordDisplayPanel(QGridLayout *grid, QBoxLayout *container, QWidget *widgetParent, QObject *parent=nullptr)
Construct a panel that renders into the given layouts.
void textCopied(const QString &text)
Text was selected and copied out of a field's value (Ctrl+C, the context menu, a drag,...
void addRow(int position, QLabel *label, QFrame *frame)
Put a row's label and value frame into the grid, listening for a double-click on either (and on the f...
void copyRequested(const QString &text)
Emitted when a field's copy button is clicked.
void addOtpField(int position, const QString &otpConfig, const AppSettings &s)
Render the live OTP row: an OtpCodeWidget in a field frame under an "OTP code" label....
void addHiddenPassword(QLayout *layout, const QString &field, const QString &value, const QString &lineStyle)
Add the password, hidden, with a button that shows it.
auto currentOtpCode() const -> QString
The one-time password currently on display, if there is one.
void editRequested()
Emitted on a double-click anywhere on a field row: the user wants to change what they are looking at.
auto hides(const QString &field, const AppSettings &s) const -> bool
Whether a field is shown behind the eye button: the password, a field a template marks,...
void applyFrameStyle(QFrame *frame) const
Give a field frame its rounded border in the current QPalette::Mid colour. The colour is baked into t...
void copied(const QString &text)
Selected text was copied out of the line edit.
SecretLineEdit(QWidget *parent=nullptr)
Constructs the line edit.
A QTextBrowser for decrypted entry text: whatever is copied out of it is marked as a secret and repor...
SecretTextBrowser(QWidget *parent=nullptr)
Constructs the browser.
void copied(const QString &text)
Selected text was copied out of the browser.
static auto parse(const QString &config) -> std::optional< Settings >
Parse an OTP configuration string.
static auto isLaunchableWebUrl(const QString &value) -> bool
Check whether a value is a safe, launchable web URL.
static auto linkifyUrls(const QString &text, bool *linked=nullptr) -> QString
Turn plain text into HTML with clickable links for safe web URLs.
PasswordDisplayPanel: renders the decrypted fields of an entry (password, template fields,...
QLineEdit whose copies, cuts and primary-selection fills carry the secret clipboard hints and are rep...
QTextBrowser for showing decrypted entry text that marks everything copied out of it as a secret.
Plain value-object holding all flat QtPass configuration.
Enums::ClipBoardType clipBoardType
Clipboard copy behaviour.
bool useMonospace
Render content in a monospace font.
bool useQrencode
Enable qrencode support.
A name/value pair parsed from a password file field.
RFC 6238 TOTP: otpauth URI parsing, code generation and canonical re-serialisation.
Util: static helpers for the process environment, binary discovery, store paths, URL safety and the l...