KWidgetsAddons

kpassworddialog.cpp
1 /*
2  This file is part of the KDE libraries
3  SPDX-FileCopyrightText: 2000 David Faure <[email protected]>
4  SPDX-FileCopyrightText: 2007 Olivier Goffart <ogoffart at kde.org>
5  SPDX-FileCopyrightText: 2020 Harald Sitter <[email protected]>
6 
7  SPDX-License-Identifier: LGPL-2.0-only
8 */
9 #include "kpassworddialog.h"
10 
11 #include <QCheckBox>
12 #include <QComboBox>
13 #include <QLabel>
14 #include <QLayout>
15 #include <QPushButton>
16 #include <QScreen>
17 #include <QStyleOption>
18 #include <QTimer>
19 
20 #include <ktitlewidget.h>
21 
22 #include "ui_kpassworddialog.h"
23 
24 /** @internal */
25 class KPasswordDialogPrivate
26 {
27 public:
28  KPasswordDialogPrivate(KPasswordDialog *qq)
29  : q(qq)
30  {
31  }
32 
33  void actuallyAccept();
34  void activated(const QString &userName);
35 
36  void updateFields();
37  void init();
38 
39  KPasswordDialog *const q;
40  Ui_KPasswordDialog ui;
41  QMap<QString, QString> knownLogins;
42  QComboBox *userEditCombo = nullptr;
43  QIcon icon;
45  unsigned int commentRow = 0;
46 };
47 
49  : QDialog(parent)
50  , d(new KPasswordDialogPrivate(this))
51 {
52  setWindowTitle(tr("Password", "@title:window"));
53  setWindowIcon(QIcon::fromTheme(QStringLiteral("dialog-password"), windowIcon()));
54  d->m_flags = flags;
55  d->init();
56 }
57 
59 
60 void KPasswordDialogPrivate::updateFields()
61 {
62  if (m_flags & KPasswordDialog::UsernameReadOnly) {
63  ui.userEdit->setReadOnly(true);
64  ui.credentialsGroup->setFocusProxy(ui.passEdit);
65  }
66  ui.domainEdit->setReadOnly((m_flags & KPasswordDialog::DomainReadOnly));
67  ui.credentialsGroup->setEnabled(!q->anonymousMode());
68 }
69 
70 void KPasswordDialogPrivate::init()
71 {
72  ui.setupUi(q);
73  ui.buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
74  ui.errorMessage->setHidden(true);
75 
76  ui.userEditContextHelpButton->hide();
77  ui.userEditContextHelpButton->setFlat(true);
78  ui.userEditContextHelpButton->setIcon(QIcon::fromTheme(QStringLiteral("help-contextual")));
79  ui.userEditContextHelpButton->setText(QString());
80  const QString description = QApplication::translate("KPasswordDialog", "Show Contextual Help");
81  ui.userEditContextHelpButton->setAccessibleName(description);
82  ui.userEditContextHelpButton->setToolTip(description);
83  QObject::connect(ui.userEditContextHelpButton, &QPushButton::released, q, [this] {
84  QEvent ev(QEvent::WhatsThis);
85  qApp->sendEvent(ui.userEdit, &ev);
86  });
87 
88  // Row 4: Username field
89  if (m_flags & KPasswordDialog::ShowUsernameLine) {
90  ui.userEdit->setFocus();
91  ui.credentialsGroup->setFocusProxy(ui.userEdit);
92  QObject::connect(ui.userEdit, &QLineEdit::returnPressed, ui.passEdit, qOverload<>(&QWidget::setFocus));
93  } else {
94  ui.userNameLabel->hide();
95  ui.userEdit->hide();
96  ui.domainLabel->hide();
97  ui.domainEdit->hide();
98  ui.passEdit->setFocus();
99  ui.credentialsGroup->setFocusProxy(ui.passEdit);
100  ui.prompt->setText(QApplication::translate("KPasswordDialog", "Supply a password below."));
101  }
102 
104  ui.anonymousRadioButton->hide();
105  ui.usePasswordButton->hide();
106  }
107 
108  if (!(m_flags & KPasswordDialog::ShowDomainLine)) {
109  ui.domainLabel->hide();
110  ui.domainEdit->hide();
111  }
112 
113  if (!(m_flags & KPasswordDialog::ShowKeepPassword)) {
114  ui.keepCheckBox->hide();
115  }
116 
117  updateFields();
118 
119  QRect desktop = q->topLevelWidget()->screen()->geometry();
120  q->setMinimumWidth(qMin(1000, qMax(q->sizeHint().width(), desktop.width() / 4)));
121  q->setIcon(QIcon::fromTheme(QStringLiteral("dialog-password")));
122 }
123 
125 {
126  d->icon = icon;
127 
128  QStyleOption option;
129  option.initFrom(this);
130  const int iconSize = style()->pixelMetric(QStyle::PM_MessageBoxIconSize, &option, this);
131  d->ui.pixmapLabel->setPixmap(icon.pixmap(iconSize));
132 }
133 
135 {
136  return d->icon;
137 }
138 
139 #if KWIDGETSADDONS_BUILD_DEPRECATED_SINCE(5, 63)
141 {
142  d->ui.pixmapLabel->setPixmap(pixmap);
143 }
144 
146 {
147  return d->ui.pixmapLabel->pixmap(Qt::ReturnByValue);
148 }
149 #endif
150 
152 {
153  d->ui.userEdit->setText(user);
154  if (user.isEmpty()) {
155  return;
156  }
157 
158  d->activated(user);
159  if (d->ui.userEdit->isVisibleTo(this)) {
160  d->ui.passEdit->setFocus();
161  }
162 }
163 
165 {
166  return d->ui.userEdit->text();
167 }
168 
170 {
171  return d->ui.passEdit->password();
172 }
173 
175 {
176  d->ui.domainEdit->setText(domain);
177 }
178 
180 {
181  return d->ui.domainEdit->text();
182 }
183 
185 {
186  if (anonymous && !(d->m_flags & KPasswordDialog::ShowAnonymousLoginCheckBox)) {
187  // This is an error case, but we can at least let user see what's about
188  // to happen if they proceed.
189  d->ui.anonymousRadioButton->setVisible(true);
190 
191  d->ui.usePasswordButton->setVisible(true);
192  d->ui.usePasswordButton->setEnabled(false);
193  }
194 
195  d->ui.anonymousRadioButton->setChecked(anonymous);
196 }
197 
199 {
200  return d->ui.anonymousRadioButton->isChecked();
201 }
202 
204 {
205  d->ui.keepCheckBox->setChecked(b);
206 }
207 
209 {
210  return d->ui.keepCheckBox->isChecked();
211 }
212 
213 void KPasswordDialog::addCommentLine(const QString &label, const QString &comment)
214 {
215  int gridMarginLeft;
216  int gridMarginTop;
217  int gridMarginRight;
218  int gridMarginBottom;
219  d->ui.formLayout->getContentsMargins(&gridMarginLeft, &gridMarginTop, &gridMarginRight, &gridMarginBottom);
220 
221  int spacing = d->ui.formLayout->horizontalSpacing();
222  if (spacing < 0) {
223  // same inter-column spacing for all rows, see comment in qformlayout.cpp
225  }
226 
227  QLabel *c = new QLabel(comment, this);
228  c->setWordWrap(true);
229 
230  d->ui.formLayout->insertRow(d->commentRow, label, c);
231  ++d->commentRow;
232 
233  // cycle through column 0 widgets and see the max width so we can set the minimum height of
234  // column 2 wordwrapable labels
235  int firstColumnWidth = 0;
236  for (int i = 0; i < d->ui.formLayout->rowCount(); ++i) {
237  QLayoutItem *li = d->ui.formLayout->itemAt(i, QFormLayout::LabelRole);
238  if (li) {
239  QWidget *w = li->widget();
240  if (w && !w->isHidden()) {
241  firstColumnWidth = qMax(firstColumnWidth, w->sizeHint().width());
242  }
243  }
244  }
245  for (int i = 0; i < d->ui.formLayout->rowCount(); ++i) {
246  QLayoutItem *li = d->ui.formLayout->itemAt(i, QFormLayout::FieldRole);
247  if (li) {
248  QLabel *l = qobject_cast<QLabel *>(li->widget());
249  if (l && l->wordWrap()) {
250  auto *style = this->style();
251  const int leftMargin = style->pixelMetric(QStyle::PM_LayoutLeftMargin);
252  const int rightMargin = style->pixelMetric(QStyle::PM_LayoutRightMargin);
253  int w = sizeHint().width() - firstColumnWidth - leftMargin - rightMargin - gridMarginLeft - gridMarginRight - spacing;
254  l->setMinimumSize(w, l->heightForWidth(w));
255  }
256  }
257  }
258 }
259 
261 {
262  d->ui.errorMessage->setText(message, KTitleWidget::ErrorMessage);
263 
264  QFont bold = font();
265  bold.setBold(true);
266  switch (type) {
267  case PasswordError:
268  d->ui.passwordLabel->setFont(bold);
269  d->ui.passEdit->clear();
270  d->ui.passEdit->setFocus();
271  break;
272  case UsernameError:
273  if (d->ui.userEdit->isVisibleTo(this)) {
274  d->ui.userNameLabel->setFont(bold);
275  d->ui.userEdit->setFocus();
276  }
277  break;
278  case DomainError:
279  if (d->ui.domainEdit->isVisibleTo(this)) {
280  d->ui.domainLabel->setFont(bold);
281  d->ui.domainEdit->setFocus();
282  }
283  break;
284  case FatalError:
285  d->ui.userNameLabel->setEnabled(false);
286  d->ui.userEdit->setEnabled(false);
287  d->ui.passwordLabel->setEnabled(false);
288  d->ui.passEdit->setEnabled(false);
289  d->ui.keepCheckBox->setEnabled(false);
290  d->ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
291  break;
292  default:
293  break;
294  }
295  adjustSize();
296 }
297 
299 {
300  d->ui.prompt->setText(prompt);
301  d->ui.prompt->setWordWrap(true);
302  auto *style = this->style();
303  const int leftMarginHint = style->pixelMetric(QStyle::PM_LayoutLeftMargin);
304  const int rightMarginHint = style->pixelMetric(QStyle::PM_LayoutRightMargin);
305  d->ui.prompt->setMinimumHeight(d->ui.prompt->heightForWidth(width() - leftMarginHint - rightMarginHint));
306 }
307 
309 {
310  return d->ui.prompt->text();
311 }
312 
314 {
315  d->ui.passEdit->setPassword(p);
316 }
317 
319 {
320  d->ui.userEdit->setReadOnly(readOnly);
321 
322  if (readOnly && d->ui.userEdit->hasFocus()) {
323  d->ui.passEdit->setFocus();
324  }
325 }
326 
328 {
329  const int nr = knownLogins.count();
330  if (nr == 0) {
331  return;
332  }
333 
334  if (nr == 1) {
335  d->ui.userEdit->setText(knownLogins.begin().key());
336  setPassword(knownLogins.begin().value());
337  return;
338  }
339 
340  Q_ASSERT(!d->ui.userEdit->isReadOnly());
341  if (!d->userEditCombo) {
342  int row = -1;
344 
345  d->ui.formLayout->getWidgetPosition(d->ui.userEdit, &row, &userEditRole);
346  d->ui.formLayout->removeWidget(d->ui.userEdit);
347  delete d->ui.userEdit;
348  d->userEditCombo = new QComboBox(d->ui.credentialsGroup);
349  d->userEditCombo->setEditable(true);
350  d->ui.userEdit = d->userEditCombo->lineEdit();
351  d->ui.userNameLabel->setBuddy(d->userEditCombo);
352  d->ui.formLayout->setWidget(row > -1 ? row : 0, userEditRole, d->userEditCombo);
353 
354  setTabOrder(d->ui.userEdit, d->ui.anonymousRadioButton);
355  setTabOrder(d->ui.anonymousRadioButton, d->ui.domainEdit);
356  setTabOrder(d->ui.domainEdit, d->ui.passEdit);
357  setTabOrder(d->ui.passEdit, d->ui.keepCheckBox);
358  connect(d->ui.userEdit, &QLineEdit::returnPressed, d->ui.passEdit, qOverload<>(&QWidget::setFocus));
359  }
360 
361  d->knownLogins = knownLogins;
362  d->userEditCombo->addItems(knownLogins.keys());
363  d->userEditCombo->setFocus();
364 
365  connect(d->userEditCombo, &QComboBox::textActivated, this, [this](const QString &text) {
366  d->activated(text);
367  });
368 }
369 
371 {
372  d->ui.passEdit->setRevealPasswordAvailable(reveal);
373 }
374 
376 {
377  return d->ui.passEdit->isRevealPasswordAvailable();
378 }
379 
380 void KPasswordDialogPrivate::activated(const QString &userName)
381 {
382  QMap<QString, QString>::ConstIterator it = knownLogins.constFind(userName);
383  if (it != knownLogins.constEnd()) {
384  q->setPassword(it.value());
385  }
386 }
387 
389 {
390  if (!d->ui.errorMessage->isHidden()) {
391  d->ui.errorMessage->setText(QString());
392  }
393 
394  // reset the font in case we had an error previously
395  if (!d->ui.passwordLabel->isHidden()) {
396  d->ui.passwordLabel->setFont(font());
397  d->ui.userNameLabel->setFont(font());
398  }
399 
400  // we do this to allow the error message, if any, to go away
401  // checkPassword() may block for a period of time
402  QTimer::singleShot(0, this, [this] {
403  d->actuallyAccept();
404  });
405 }
406 
407 void KPasswordDialogPrivate::actuallyAccept()
408 {
409  if (!q->checkPassword()) {
410  return;
411  }
412 
413  bool keep = ui.keepCheckBox->isVisibleTo(q) && ui.keepCheckBox->isChecked();
414  Q_EMIT q->gotPassword(q->password(), keep);
415 
416  if (ui.userEdit->isVisibleTo(q)) {
417  Q_EMIT q->gotUsernameAndPassword(q->username(), q->password(), keep);
418  }
419 
420  q->QDialog::accept();
421 }
422 
424 {
425  return true;
426 }
427 
429 {
430  return d->ui.buttonBox;
431 }
432 
434 {
435  d->ui.userEditContextHelpButton->setVisible(true);
436  d->ui.userEdit->setWhatsThis(help);
437 }
438 
439 #include "moc_kpassworddialog.cpp"
void adjustSize()
void setWindowIcon(const QIcon &icon)
void showErrorMessage(const QString &message, const ErrorType type=PasswordError)
Shows an error message in the dialog box.
QString domain() const
Returns the domain entered by the user.
@ ErrorMessage
An error message.
Definition: ktitlewidget.h:90
void setUsernameContextHelp(const QString &help)
Sets contextual help for the username input field.
@ UsernameReadOnly
If this flag is set, the login lineedit will be in read only mode.
bool keepPassword() const
Determines whether supplied authorization should persist even after the application has been closed.
QString translate(const char *context, const char *sourceText, const char *disambiguation, int n)
QIcon icon() const
Returns the icon that appears next to the prompt.
const T value(const Key &key, const T &defaultValue) const const
void setPassword(const QString &password)
Presets the password.
QPixmap pixmap() const
QCA_EXPORT void init()
QMap::iterator begin()
QDialogButtonBox * buttonBox() const
Returns the button box used in the dialog.
void setAnonymousMode(bool anonymous)
set anonymous mode (all other fields will be grayed out)
@ DomainError
A problem with the domain as entered.
QIcon fromTheme(const QString &name)
int width() const const
void initFrom(const QWidget *widget)
int width() const const
void textActivated(const QString &text)
void setWordWrap(bool on)
QMap::const_iterator constFind(const Key &key) const const
void setMinimumSize(const QSize &)
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
void setIcon(const QIcon &icon)
Set the icon that appears next to the prompt.
void setPrompt(const QString &prompt)
Sets the prompt to show to the user.
void addCommentLine(const QString &label, const QString &comment)
Adds a comment line to the dialog.
virtual QWidget * widget()
@ UsernameError
A problem with the user name as entered.
virtual int pixelMetric(QStyle::PixelMetric metric, const QStyleOption *option, const QWidget *widget) const const=0
bool isRevealPasswordAvailable() const
Whether the visibility trailing action in the line edit is visible.
int combinedLayoutSpacing(QSizePolicy::ControlTypes controls1, QSizePolicy::ControlTypes controls2, Qt::Orientation orientation, QStyleOption *option, QWidget *widget) const const
@ ShowUsernameLine
If this flag is set, there will be an additional line to let the user enter his login.
QStyle * style() const const
KPasswordDialog(QWidget *parent=nullptr, const KPasswordDialogFlags &flags=KPasswordDialog::NoFlags)
create a password dialog
@ PasswordError
Incorrect password.
void setTabOrder(QWidget *first, QWidget *second)
void accept() override
@ FatalError
Error preventing further attempts, will result in disabling most of the interface.
Horizontal
bool isEmpty() const const
~KPasswordDialog() override
Destructor.
void setWindowTitle(const QString &)
void returnPressed()
int count(const Key &key) const const
void setDomain(const QString &)
set the default domain.
virtual bool checkPassword()
Virtual function that can be overridden to provide password checking in derived classes.
PM_MessageBoxIconSize
@ DomainReadOnly
If this flag is set, the domain lineedit will be in read only mode.
bool anonymousMode() const
void setKnownLogins(const QMap< QString, QString > &knownLogins)
Presets a number of login+password pairs that the user can choose from.
QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) const const
QList< Key > keys() const const
QString prompt() const
Returns the prompt.
void setRevealPasswordAvailable(bool reveal)
Whether to show the visibility trailing action in the line edit.
@ ShowKeepPassword
If this flag is set, the "keep this password" checkbox will been shown, otherwise,...
void setPixmap(const QPixmap &)
set an image that appears next to the prompt.
void setUsername(const QString &)
set the default username.
QString password() const
Returns the password entered by the user.
void setBold(bool enable)
ReturnByValue
bool isHidden() const const
QString username() const
Returns the username entered by the user.
@ ShowAnonymousLoginCheckBox
If this flag is set, the Anonymous Login checkbox will be displayed.
QString tr(const char *sourceText, const char *disambiguation, int n)
void setFocus()
virtual int heightForWidth(int w) const const override
QString message
dialog for requesting login and password from the end user
virtual QSize sizeHint() const const override
@ ShowDomainLine
If this flag is set, there will be an additional line to let the user enter the domain.
void setUsernameReadOnly(bool readOnly)
Sets the username field read-only and sets the focus to the password field.
void setKeepPassword(bool b)
Check or uncheck the "keep password" checkbox.
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Tue Sep 26 2023 03:59:35 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.