• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

IJHack / QtPass / 27692393037

17 Jun 2026 01:26PM UTC coverage: 57.221% (-0.2%) from 57.47%
27692393037

Pull #1553

github

web-flow
Merge 9051e6041 into 311601814
Pull Request #1553: refactor(#1511): inject Pass*/AppSettings into dialog layer

11 of 31 new or added lines in 5 files covered. (35.48%)

19 existing lines in 4 files now uncovered.

3966 of 6931 relevant lines covered (57.22%)

23.3 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

71.59
/src/passworddisplaypanel.cpp
1
// SPDX-FileCopyrightText: 2014 Anne Jan Brouwer
2
// SPDX-License-Identifier: GPL-3.0-or-later
3

4
/**
5
 * @class PasswordDisplayPanel
6
 * @brief Password-field rendering implementation.
7
 *
8
 * @see passworddisplaypanel.h
9
 */
10

11
#include "passworddisplaypanel.h"
12
#include "appsettings.h"
13
#include "qpushbuttonasqrcode.h"
14
#include "qpushbuttonshowpassword.h"
15
#include "qpushbuttonwithclipboard.h"
16
#include "util.h"
17

18
#include <QBoxLayout>
19
#include <QDesktopServices>
20
#include <QFrame>
21
#include <QGridLayout>
22
#include <QHBoxLayout>
23
#include <QIcon>
24
#include <QLabel>
25
#include <QLineEdit>
26
#include <QPalette>
27
#include <QPushButton>
28
#include <QTextBrowser>
29
#include <QUrl>
30

31
PasswordDisplayPanel::PasswordDisplayPanel(QGridLayout *grid,
16✔
32
                                           QBoxLayout *container,
33
                                           QWidget *widgetParent,
34
                                           QObject *parent)
16✔
35
    : QObject(parent), m_grid(grid), m_container(container),
16✔
36
      m_widgetParent(widgetParent) {}
16✔
37

38
void PasswordDisplayPanel::clear() {
2✔
39
  while (m_grid->count() > 0) {
4✔
40
    QLayoutItem *item = m_grid->takeAt(0);
2✔
41
    delete item->widget();
2✔
42
    delete item;
2✔
43
  }
44
  m_container->setSpacing(0);
2✔
45
}
2✔
46

47
void PasswordDisplayPanel::displayFields(const QString &password,
3✔
48
                                         const NamedValues &namedValues,
49
                                         const AppSettings &s) {
50
  if (!password.isEmpty()) {
3✔
51
    // The password is hidden in addField when needed.
52
    addField(0, QObject::tr("Password"), password, s);
4✔
53
  }
54
  for (int j = 0; j < namedValues.length(); ++j) {
4✔
55
    const NamedValue &nv = namedValues.at(j);
56
    addField(j + 1, nv.name, nv.value, s);
1✔
57
  }
58
  m_container->setSpacing(m_grid->count() == 0 ? 0 : 6);
5✔
59
}
3✔
60

61
void PasswordDisplayPanel::appendField(const QString &field,
1✔
62
                                       const QString &value,
63
                                       const AppSettings &s) {
64
  // Each row is two grid items (label + value frame), so the next free row is
65
  // count() / 2 — the same sequential scheme displayFields() uses.
66
  addField(m_grid->count() / 2, field, value, s);
1✔
67
}
1✔
68

69
void PasswordDisplayPanel::addField(int position, const QString &field,
4✔
70
                                    const QString &value,
71
                                    const AppSettings &s) {
72
  QString trimmedField = field.trimmed();
73
  QString trimmedValue = value.trimmed();
74

75
  const QString buttonStyle =
76
      "border-style: none; background: transparent; padding: 0; margin: 0; "
77
      "icon-size: 16px; color: inherit;";
4✔
78

79
  // Combine the Copy button and the line edit in one widget
80
  auto *frame = new QFrame();
4✔
81
  QLayout *ly = new QHBoxLayout();
4✔
82
  ly->setContentsMargins(5, 2, 2, 2);
4✔
83
  ly->setSpacing(0);
4✔
84
  frame->setLayout(ly);
4✔
85
  if (s.clipBoardType != Enums::CLIPBOARD_NEVER) {
4✔
86
    auto *fieldLabel =
UNCOV
87
        new QPushButtonWithClipboard(trimmedValue, m_widgetParent);
×
UNCOV
88
    connect(fieldLabel, &QPushButtonWithClipboard::clicked, this,
×
UNCOV
89
            &PasswordDisplayPanel::copyRequested);
×
90

UNCOV
91
    fieldLabel->setStyleSheet(buttonStyle);
×
UNCOV
92
    frame->layout()->addWidget(fieldLabel);
×
93
  }
94

95
  if (s.useQrencode) {
4✔
96
    auto *qrbutton = new QPushButtonAsQRCode(trimmedValue, m_widgetParent);
×
97
    connect(qrbutton, &QPushButtonAsQRCode::clicked, this,
×
98
            &PasswordDisplayPanel::qrRequested);
×
99
    qrbutton->setStyleSheet(buttonStyle);
×
100
    frame->layout()->addWidget(qrbutton);
×
101
  }
102

103
  // Show an explicit "open in browser" button when the value is a safe
104
  // http(s) URL. The inline clickable link still works for URLs embedded in
105
  // prose; this button is the discoverable affordance for url fields.
106
  // Never on the password field: its value is a secret and must not be
107
  // surfaced in a tooltip or handed to the browser.
108
  if (trimmedField != QObject::tr("Password") &&
10✔
109
      Util::isLaunchableWebUrl(trimmedValue)) {
2✔
110
    auto *urlButton = new QPushButton(m_widgetParent);
1✔
111
    urlButton->setIcon(QIcon::fromTheme(QStringLiteral("applications-internet"),
2✔
112
                                        QIcon(":/icons/open-url.svg")));
2✔
113
    urlButton->setToolTip(
2✔
114
        QObject::tr("Open %1 in browser").arg(trimmedValue.toHtmlEscaped()));
2✔
115
    urlButton->setStyleSheet(buttonStyle);
1✔
116
    urlButton->setCursor(Qt::PointingHandCursor);
1✔
117
    connect(urlButton, &QPushButton::clicked, this, [trimmedValue]() {
1✔
118
      // Re-validate before launching (defence in depth: the value is
119
      // immutable here, but never hand an unvalidated string to the OS
120
      // URL handler).
121
      if (Util::isLaunchableWebUrl(trimmedValue)) {
×
122
        QDesktopServices::openUrl(QUrl(trimmedValue));
×
123
      }
124
    });
×
125
    frame->layout()->addWidget(urlButton);
1✔
126
  }
127

128
  // set the echo mode to password, if the field is "password"
129
  const QString lineStyle =
130
      s.useMonospace
4✔
131
          ? "border-style: none; background: transparent; font-family: "
132
            "monospace;"
133
          : "border-style: none; background: transparent;";
8✔
134

135
  if (s.hidePassword && trimmedField == QObject::tr("Password")) {
4✔
UNCOV
136
    auto *line = new QLineEdit();
×
137
    line->setObjectName(trimmedField);
×
138
    line->setText(trimmedValue);
×
139
    line->setReadOnly(true);
×
140
    line->setStyleSheet(lineStyle);
×
141
    line->setContentsMargins(0, 0, 0, 0);
×
142
    line->setEchoMode(QLineEdit::Password);
×
143
    auto *showButton = new QPushButtonShowPassword(line, m_widgetParent);
×
144
    showButton->setStyleSheet(buttonStyle);
×
145
    showButton->setContentsMargins(0, 0, 0, 0);
×
146
    frame->layout()->addWidget(showButton);
×
147
    frame->layout()->addWidget(line);
×
148
  } else {
149
    auto *line = new QTextBrowser();
4✔
150
    line->setOpenExternalLinks(true);
4✔
151
    line->setOpenLinks(true);
4✔
152
    line->setMaximumHeight(26);
4✔
153
    line->setMinimumHeight(26);
4✔
154
    line->setSizePolicy(
4✔
155
        QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum));
156
    line->setObjectName(trimmedField);
4✔
157
    trimmedValue.replace(Util::protocolRegex(), R"(<a href="\1">\1</a>)");
4✔
158
    line->setText(trimmedValue);
4✔
159
    line->setReadOnly(true);
4✔
160
    line->setStyleSheet(lineStyle);
4✔
161
    line->setContentsMargins(0, 0, 0, 0);
4✔
162
    frame->layout()->addWidget(line);
4✔
163
  }
164

165
  // Derive the border colour from the palette so it adapts to light/dark
166
  // themes instead of a hardcoded light grey.
167
  const QString borderColor =
168
      m_widgetParent->palette().color(QPalette::Mid).name();
8✔
169
  frame->setStyleSheet(QStringLiteral(".QFrame{border: 1px solid %1; "
8✔
170
                                      "border-radius: 5px;}")
171
                           .arg(borderColor));
8✔
172

173
  // set into the layout
174
  m_grid->addWidget(new QLabel(trimmedField), position, 0);
4✔
175
  m_grid->addWidget(frame, position, 1);
4✔
176
}
4✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc