File indexing completed on 2025-02-02 14:20:08
0001 /* 0002 This file is part of the KDE libraries 0003 SPDX-FileCopyrightText: 2000 David Faure <faure@kde.org> 0004 SPDX-FileCopyrightText: 2007 Olivier Goffart <ogoffart at kde.org> 0005 SPDX-FileCopyrightText: 2020 Harald Sitter <sitter@kde.org> 0006 0007 SPDX-License-Identifier: LGPL-2.0-only 0008 */ 0009 #include "kpassworddialog.h" 0010 0011 #include <QCheckBox> 0012 #include <QComboBox> 0013 #include <QLabel> 0014 #include <QLayout> 0015 #include <QPushButton> 0016 #include <QScreen> 0017 #include <QStyleOption> 0018 #include <QTimer> 0019 0020 #include <ktitlewidget.h> 0021 0022 #include "ui_kpassworddialog.h" 0023 0024 /** @internal */ 0025 class KPasswordDialogPrivate 0026 { 0027 public: 0028 KPasswordDialogPrivate(KPasswordDialog *qq) 0029 : q(qq) 0030 { 0031 } 0032 0033 void actuallyAccept(); 0034 void activated(const QString &userName); 0035 0036 void updateFields(); 0037 void init(); 0038 0039 KPasswordDialog *const q; 0040 Ui_KPasswordDialog ui; 0041 QMap<QString, QString> knownLogins; 0042 QComboBox *userEditCombo = nullptr; 0043 QIcon icon; 0044 KPasswordDialog::KPasswordDialogFlags m_flags; 0045 unsigned int commentRow = 0; 0046 }; 0047 0048 KPasswordDialog::KPasswordDialog(QWidget *parent, const KPasswordDialogFlags &flags) 0049 : QDialog(parent) 0050 , d(new KPasswordDialogPrivate(this)) 0051 { 0052 setWindowTitle(tr("Password", "@title:window")); 0053 setWindowIcon(QIcon::fromTheme(QStringLiteral("dialog-password"), windowIcon())); 0054 d->m_flags = flags; 0055 d->init(); 0056 } 0057 0058 KPasswordDialog::~KPasswordDialog() = default; 0059 0060 void KPasswordDialogPrivate::updateFields() 0061 { 0062 if (m_flags & KPasswordDialog::UsernameReadOnly) { 0063 ui.userEdit->setReadOnly(true); 0064 ui.credentialsGroup->setFocusProxy(ui.passEdit); 0065 } 0066 ui.domainEdit->setReadOnly((m_flags & KPasswordDialog::DomainReadOnly)); 0067 ui.credentialsGroup->setEnabled(!q->anonymousMode()); 0068 } 0069 0070 void KPasswordDialogPrivate::init() 0071 { 0072 ui.setupUi(q); 0073 ui.buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); 0074 ui.errorMessage->setHidden(true); 0075 0076 ui.userEditContextHelpButton->hide(); 0077 ui.userEditContextHelpButton->setFlat(true); 0078 ui.userEditContextHelpButton->setIcon(QIcon::fromTheme(QStringLiteral("help-contextual"))); 0079 ui.userEditContextHelpButton->setText(QString()); 0080 const QString description = QApplication::translate("KPasswordDialog", "Show Contextual Help"); 0081 ui.userEditContextHelpButton->setAccessibleName(description); 0082 ui.userEditContextHelpButton->setToolTip(description); 0083 QObject::connect(ui.userEditContextHelpButton, &QPushButton::released, q, [this] { 0084 QEvent ev(QEvent::WhatsThis); 0085 qApp->sendEvent(ui.userEdit, &ev); 0086 }); 0087 0088 // Row 4: Username field 0089 if (m_flags & KPasswordDialog::ShowUsernameLine) { 0090 ui.userEdit->setFocus(); 0091 ui.credentialsGroup->setFocusProxy(ui.userEdit); 0092 QObject::connect(ui.userEdit, &QLineEdit::returnPressed, ui.passEdit, qOverload<>(&QWidget::setFocus)); 0093 } else { 0094 ui.userNameLabel->hide(); 0095 ui.userEdit->hide(); 0096 ui.domainLabel->hide(); 0097 ui.domainEdit->hide(); 0098 ui.passEdit->setFocus(); 0099 ui.credentialsGroup->setFocusProxy(ui.passEdit); 0100 ui.prompt->setText(QApplication::translate("KPasswordDialog", "Supply a password below.")); 0101 } 0102 0103 if (!(m_flags & KPasswordDialog::ShowAnonymousLoginCheckBox)) { 0104 ui.anonymousRadioButton->hide(); 0105 ui.usePasswordButton->hide(); 0106 } 0107 0108 if (!(m_flags & KPasswordDialog::ShowDomainLine)) { 0109 ui.domainLabel->hide(); 0110 ui.domainEdit->hide(); 0111 } 0112 0113 if (!(m_flags & KPasswordDialog::ShowKeepPassword)) { 0114 ui.keepCheckBox->hide(); 0115 } 0116 0117 updateFields(); 0118 0119 QRect desktop = q->topLevelWidget()->screen()->geometry(); 0120 q->setMinimumWidth(qMin(1000, qMax(q->sizeHint().width(), desktop.width() / 4))); 0121 q->setIcon(QIcon::fromTheme(QStringLiteral("dialog-password"))); 0122 } 0123 0124 void KPasswordDialog::setIcon(const QIcon &icon) 0125 { 0126 d->icon = icon; 0127 0128 QStyleOption option; 0129 option.initFrom(this); 0130 const int iconSize = style()->pixelMetric(QStyle::PM_MessageBoxIconSize, &option, this); 0131 d->ui.pixmapLabel->setPixmap(icon.pixmap(iconSize)); 0132 } 0133 0134 QIcon KPasswordDialog::icon() const 0135 { 0136 return d->icon; 0137 } 0138 0139 #if KWIDGETSADDONS_BUILD_DEPRECATED_SINCE(5, 63) 0140 void KPasswordDialog::setPixmap(const QPixmap &pixmap) 0141 { 0142 d->ui.pixmapLabel->setPixmap(pixmap); 0143 } 0144 0145 QPixmap KPasswordDialog::pixmap() const 0146 { 0147 return d->ui.pixmapLabel->pixmap(Qt::ReturnByValue); 0148 } 0149 #endif 0150 0151 void KPasswordDialog::setUsername(const QString &user) 0152 { 0153 d->ui.userEdit->setText(user); 0154 if (user.isEmpty()) { 0155 return; 0156 } 0157 0158 d->activated(user); 0159 if (d->ui.userEdit->isVisibleTo(this)) { 0160 d->ui.passEdit->setFocus(); 0161 } 0162 } 0163 0164 QString KPasswordDialog::username() const 0165 { 0166 return d->ui.userEdit->text(); 0167 } 0168 0169 QString KPasswordDialog::password() const 0170 { 0171 return d->ui.passEdit->password(); 0172 } 0173 0174 void KPasswordDialog::setDomain(const QString &domain) 0175 { 0176 d->ui.domainEdit->setText(domain); 0177 } 0178 0179 QString KPasswordDialog::domain() const 0180 { 0181 return d->ui.domainEdit->text(); 0182 } 0183 0184 void KPasswordDialog::setAnonymousMode(bool anonymous) 0185 { 0186 if (anonymous && !(d->m_flags & KPasswordDialog::ShowAnonymousLoginCheckBox)) { 0187 // This is an error case, but we can at least let user see what's about 0188 // to happen if they proceed. 0189 d->ui.anonymousRadioButton->setVisible(true); 0190 0191 d->ui.usePasswordButton->setVisible(true); 0192 d->ui.usePasswordButton->setEnabled(false); 0193 } 0194 0195 d->ui.anonymousRadioButton->setChecked(anonymous); 0196 } 0197 0198 bool KPasswordDialog::anonymousMode() const 0199 { 0200 return d->ui.anonymousRadioButton->isChecked(); 0201 } 0202 0203 void KPasswordDialog::setKeepPassword(bool b) 0204 { 0205 d->ui.keepCheckBox->setChecked(b); 0206 } 0207 0208 bool KPasswordDialog::keepPassword() const 0209 { 0210 return d->ui.keepCheckBox->isChecked(); 0211 } 0212 0213 void KPasswordDialog::addCommentLine(const QString &label, const QString &comment) 0214 { 0215 int gridMarginLeft; 0216 int gridMarginTop; 0217 int gridMarginRight; 0218 int gridMarginBottom; 0219 d->ui.formLayout->getContentsMargins(&gridMarginLeft, &gridMarginTop, &gridMarginRight, &gridMarginBottom); 0220 0221 int spacing = d->ui.formLayout->horizontalSpacing(); 0222 if (spacing < 0) { 0223 // same inter-column spacing for all rows, see comment in qformlayout.cpp 0224 spacing = style()->combinedLayoutSpacing(QSizePolicy::Label, QSizePolicy::LineEdit, Qt::Horizontal, nullptr, this); 0225 } 0226 0227 QLabel *c = new QLabel(comment, this); 0228 c->setWordWrap(true); 0229 0230 d->ui.formLayout->insertRow(d->commentRow, label, c); 0231 ++d->commentRow; 0232 0233 // cycle through column 0 widgets and see the max width so we can set the minimum height of 0234 // column 2 wordwrapable labels 0235 int firstColumnWidth = 0; 0236 for (int i = 0; i < d->ui.formLayout->rowCount(); ++i) { 0237 QLayoutItem *li = d->ui.formLayout->itemAt(i, QFormLayout::LabelRole); 0238 if (li) { 0239 QWidget *w = li->widget(); 0240 if (w && !w->isHidden()) { 0241 firstColumnWidth = qMax(firstColumnWidth, w->sizeHint().width()); 0242 } 0243 } 0244 } 0245 for (int i = 0; i < d->ui.formLayout->rowCount(); ++i) { 0246 QLayoutItem *li = d->ui.formLayout->itemAt(i, QFormLayout::FieldRole); 0247 if (li) { 0248 QLabel *l = qobject_cast<QLabel *>(li->widget()); 0249 if (l && l->wordWrap()) { 0250 auto *style = this->style(); 0251 const int leftMargin = style->pixelMetric(QStyle::PM_LayoutLeftMargin); 0252 const int rightMargin = style->pixelMetric(QStyle::PM_LayoutRightMargin); 0253 int w = sizeHint().width() - firstColumnWidth - leftMargin - rightMargin - gridMarginLeft - gridMarginRight - spacing; 0254 l->setMinimumSize(w, l->heightForWidth(w)); 0255 } 0256 } 0257 } 0258 } 0259 0260 void KPasswordDialog::showErrorMessage(const QString &message, const ErrorType type) 0261 { 0262 d->ui.errorMessage->setText(message, KTitleWidget::ErrorMessage); 0263 0264 QFont bold = font(); 0265 bold.setBold(true); 0266 switch (type) { 0267 case PasswordError: 0268 d->ui.passwordLabel->setFont(bold); 0269 d->ui.passEdit->clear(); 0270 d->ui.passEdit->setFocus(); 0271 break; 0272 case UsernameError: 0273 if (d->ui.userEdit->isVisibleTo(this)) { 0274 d->ui.userNameLabel->setFont(bold); 0275 d->ui.userEdit->setFocus(); 0276 } 0277 break; 0278 case DomainError: 0279 if (d->ui.domainEdit->isVisibleTo(this)) { 0280 d->ui.domainLabel->setFont(bold); 0281 d->ui.domainEdit->setFocus(); 0282 } 0283 break; 0284 case FatalError: 0285 d->ui.userNameLabel->setEnabled(false); 0286 d->ui.userEdit->setEnabled(false); 0287 d->ui.passwordLabel->setEnabled(false); 0288 d->ui.passEdit->setEnabled(false); 0289 d->ui.keepCheckBox->setEnabled(false); 0290 d->ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); 0291 break; 0292 default: 0293 break; 0294 } 0295 adjustSize(); 0296 } 0297 0298 void KPasswordDialog::setPrompt(const QString &prompt) 0299 { 0300 d->ui.prompt->setText(prompt); 0301 d->ui.prompt->setWordWrap(true); 0302 auto *style = this->style(); 0303 const int leftMarginHint = style->pixelMetric(QStyle::PM_LayoutLeftMargin); 0304 const int rightMarginHint = style->pixelMetric(QStyle::PM_LayoutRightMargin); 0305 d->ui.prompt->setMinimumHeight(d->ui.prompt->heightForWidth(width() - leftMarginHint - rightMarginHint)); 0306 } 0307 0308 QString KPasswordDialog::prompt() const 0309 { 0310 return d->ui.prompt->text(); 0311 } 0312 0313 void KPasswordDialog::setPassword(const QString &p) 0314 { 0315 d->ui.passEdit->setPassword(p); 0316 } 0317 0318 void KPasswordDialog::setUsernameReadOnly(bool readOnly) 0319 { 0320 d->ui.userEdit->setReadOnly(readOnly); 0321 0322 if (readOnly && d->ui.userEdit->hasFocus()) { 0323 d->ui.passEdit->setFocus(); 0324 } 0325 } 0326 0327 void KPasswordDialog::setKnownLogins(const QMap<QString, QString> &knownLogins) 0328 { 0329 const int nr = knownLogins.count(); 0330 if (nr == 0) { 0331 return; 0332 } 0333 0334 if (nr == 1) { 0335 d->ui.userEdit->setText(knownLogins.begin().key()); 0336 setPassword(knownLogins.begin().value()); 0337 return; 0338 } 0339 0340 Q_ASSERT(!d->ui.userEdit->isReadOnly()); 0341 if (!d->userEditCombo) { 0342 int row = -1; 0343 QFormLayout::ItemRole userEditRole = QFormLayout::FieldRole; 0344 0345 d->ui.formLayout->getWidgetPosition(d->ui.userEdit, &row, &userEditRole); 0346 d->ui.formLayout->removeWidget(d->ui.userEdit); 0347 delete d->ui.userEdit; 0348 d->userEditCombo = new QComboBox(d->ui.credentialsGroup); 0349 d->userEditCombo->setEditable(true); 0350 d->ui.userEdit = d->userEditCombo->lineEdit(); 0351 d->ui.userNameLabel->setBuddy(d->userEditCombo); 0352 d->ui.formLayout->setWidget(row > -1 ? row : 0, userEditRole, d->userEditCombo); 0353 0354 setTabOrder(d->ui.userEdit, d->ui.anonymousRadioButton); 0355 setTabOrder(d->ui.anonymousRadioButton, d->ui.domainEdit); 0356 setTabOrder(d->ui.domainEdit, d->ui.passEdit); 0357 setTabOrder(d->ui.passEdit, d->ui.keepCheckBox); 0358 connect(d->ui.userEdit, &QLineEdit::returnPressed, d->ui.passEdit, qOverload<>(&QWidget::setFocus)); 0359 } 0360 0361 d->knownLogins = knownLogins; 0362 d->userEditCombo->addItems(knownLogins.keys()); 0363 d->userEditCombo->setFocus(); 0364 0365 connect(d->userEditCombo, &QComboBox::textActivated, this, [this](const QString &text) { 0366 d->activated(text); 0367 }); 0368 } 0369 0370 void KPasswordDialog::setRevealPasswordAvailable(bool reveal) 0371 { 0372 d->ui.passEdit->setRevealPasswordAvailable(reveal); 0373 } 0374 0375 bool KPasswordDialog::isRevealPasswordAvailable() const 0376 { 0377 return d->ui.passEdit->isRevealPasswordAvailable(); 0378 } 0379 0380 void KPasswordDialogPrivate::activated(const QString &userName) 0381 { 0382 QMap<QString, QString>::ConstIterator it = knownLogins.constFind(userName); 0383 if (it != knownLogins.constEnd()) { 0384 q->setPassword(it.value()); 0385 } 0386 } 0387 0388 void KPasswordDialog::accept() 0389 { 0390 if (!d->ui.errorMessage->isHidden()) { 0391 d->ui.errorMessage->setText(QString()); 0392 } 0393 0394 // reset the font in case we had an error previously 0395 if (!d->ui.passwordLabel->isHidden()) { 0396 d->ui.passwordLabel->setFont(font()); 0397 d->ui.userNameLabel->setFont(font()); 0398 } 0399 0400 // we do this to allow the error message, if any, to go away 0401 // checkPassword() may block for a period of time 0402 QTimer::singleShot(0, this, [this] { 0403 d->actuallyAccept(); 0404 }); 0405 } 0406 0407 void KPasswordDialogPrivate::actuallyAccept() 0408 { 0409 if (!q->checkPassword()) { 0410 return; 0411 } 0412 0413 bool keep = ui.keepCheckBox->isVisibleTo(q) && ui.keepCheckBox->isChecked(); 0414 Q_EMIT q->gotPassword(q->password(), keep); 0415 0416 if (ui.userEdit->isVisibleTo(q)) { 0417 Q_EMIT q->gotUsernameAndPassword(q->username(), q->password(), keep); 0418 } 0419 0420 q->QDialog::accept(); 0421 } 0422 0423 bool KPasswordDialog::checkPassword() 0424 { 0425 return true; 0426 } 0427 0428 QDialogButtonBox *KPasswordDialog::buttonBox() const 0429 { 0430 return d->ui.buttonBox; 0431 } 0432 0433 void KPasswordDialog::setUsernameContextHelp(const QString &help) 0434 { 0435 d->ui.userEditContextHelpButton->setVisible(true); 0436 d->ui.userEdit->setWhatsThis(help); 0437 } 0438 0439 #include "moc_kpassworddialog.cpp"