File indexing completed on 2024-04-28 03:59:11
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 void KPasswordDialog::setUsername(const QString &user) 0140 { 0141 d->ui.userEdit->setText(user); 0142 if (user.isEmpty()) { 0143 return; 0144 } 0145 0146 d->activated(user); 0147 if (d->ui.userEdit->isVisibleTo(this)) { 0148 d->ui.passEdit->setFocus(); 0149 } 0150 } 0151 0152 QString KPasswordDialog::username() const 0153 { 0154 return d->ui.userEdit->text(); 0155 } 0156 0157 QString KPasswordDialog::password() const 0158 { 0159 return d->ui.passEdit->password(); 0160 } 0161 0162 void KPasswordDialog::setDomain(const QString &domain) 0163 { 0164 d->ui.domainEdit->setText(domain); 0165 } 0166 0167 QString KPasswordDialog::domain() const 0168 { 0169 return d->ui.domainEdit->text(); 0170 } 0171 0172 void KPasswordDialog::setAnonymousMode(bool anonymous) 0173 { 0174 if (anonymous && !(d->m_flags & KPasswordDialog::ShowAnonymousLoginCheckBox)) { 0175 // This is an error case, but we can at least let user see what's about 0176 // to happen if they proceed. 0177 d->ui.anonymousRadioButton->setVisible(true); 0178 0179 d->ui.usePasswordButton->setVisible(true); 0180 d->ui.usePasswordButton->setEnabled(false); 0181 } 0182 0183 d->ui.anonymousRadioButton->setChecked(anonymous); 0184 } 0185 0186 bool KPasswordDialog::anonymousMode() const 0187 { 0188 return d->ui.anonymousRadioButton->isChecked(); 0189 } 0190 0191 void KPasswordDialog::setKeepPassword(bool b) 0192 { 0193 d->ui.keepCheckBox->setChecked(b); 0194 } 0195 0196 bool KPasswordDialog::keepPassword() const 0197 { 0198 return d->ui.keepCheckBox->isChecked(); 0199 } 0200 0201 void KPasswordDialog::addCommentLine(const QString &label, const QString &comment) 0202 { 0203 int gridMarginLeft; 0204 int gridMarginTop; 0205 int gridMarginRight; 0206 int gridMarginBottom; 0207 d->ui.formLayout->getContentsMargins(&gridMarginLeft, &gridMarginTop, &gridMarginRight, &gridMarginBottom); 0208 0209 int spacing = d->ui.formLayout->horizontalSpacing(); 0210 if (spacing < 0) { 0211 // same inter-column spacing for all rows, see comment in qformlayout.cpp 0212 spacing = style()->combinedLayoutSpacing(QSizePolicy::Label, QSizePolicy::LineEdit, Qt::Horizontal, nullptr, this); 0213 } 0214 0215 QLabel *c = new QLabel(comment, this); 0216 c->setWordWrap(true); 0217 c->setTextInteractionFlags(Qt::TextBrowserInteraction); 0218 0219 d->ui.formLayout->insertRow(d->commentRow, label, c); 0220 ++d->commentRow; 0221 0222 // cycle through column 0 widgets and see the max width so we can set the minimum height of 0223 // column 2 wordwrapable labels 0224 int firstColumnWidth = 0; 0225 for (int i = 0; i < d->ui.formLayout->rowCount(); ++i) { 0226 QLayoutItem *li = d->ui.formLayout->itemAt(i, QFormLayout::LabelRole); 0227 if (li) { 0228 QWidget *w = li->widget(); 0229 if (w && !w->isHidden()) { 0230 firstColumnWidth = qMax(firstColumnWidth, w->sizeHint().width()); 0231 } 0232 } 0233 } 0234 for (int i = 0; i < d->ui.formLayout->rowCount(); ++i) { 0235 QLayoutItem *li = d->ui.formLayout->itemAt(i, QFormLayout::FieldRole); 0236 if (li) { 0237 QLabel *l = qobject_cast<QLabel *>(li->widget()); 0238 if (l && l->wordWrap()) { 0239 auto *style = this->style(); 0240 const int leftMargin = style->pixelMetric(QStyle::PM_LayoutLeftMargin); 0241 const int rightMargin = style->pixelMetric(QStyle::PM_LayoutRightMargin); 0242 int w = sizeHint().width() - firstColumnWidth - leftMargin - rightMargin - gridMarginLeft - gridMarginRight - spacing; 0243 l->setMinimumSize(w, l->heightForWidth(w)); 0244 } 0245 } 0246 } 0247 } 0248 0249 void KPasswordDialog::showErrorMessage(const QString &message, const ErrorType type) 0250 { 0251 d->ui.errorMessage->setText(message, KTitleWidget::ErrorMessage); 0252 0253 QFont bold = font(); 0254 bold.setBold(true); 0255 switch (type) { 0256 case PasswordError: 0257 d->ui.passwordLabel->setFont(bold); 0258 d->ui.passEdit->clear(); 0259 d->ui.passEdit->setFocus(); 0260 break; 0261 case UsernameError: 0262 if (d->ui.userEdit->isVisibleTo(this)) { 0263 d->ui.userNameLabel->setFont(bold); 0264 d->ui.userEdit->setFocus(); 0265 } 0266 break; 0267 case DomainError: 0268 if (d->ui.domainEdit->isVisibleTo(this)) { 0269 d->ui.domainLabel->setFont(bold); 0270 d->ui.domainEdit->setFocus(); 0271 } 0272 break; 0273 case FatalError: 0274 d->ui.userNameLabel->setEnabled(false); 0275 d->ui.userEdit->setEnabled(false); 0276 d->ui.passwordLabel->setEnabled(false); 0277 d->ui.passEdit->setEnabled(false); 0278 d->ui.keepCheckBox->setEnabled(false); 0279 d->ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); 0280 break; 0281 default: 0282 break; 0283 } 0284 adjustSize(); 0285 } 0286 0287 void KPasswordDialog::setPrompt(const QString &prompt) 0288 { 0289 d->ui.prompt->setText(prompt); 0290 d->ui.prompt->setWordWrap(true); 0291 auto *style = this->style(); 0292 const int leftMarginHint = style->pixelMetric(QStyle::PM_LayoutLeftMargin); 0293 const int rightMarginHint = style->pixelMetric(QStyle::PM_LayoutRightMargin); 0294 d->ui.prompt->setMinimumHeight(d->ui.prompt->heightForWidth(width() - leftMarginHint - rightMarginHint)); 0295 } 0296 0297 QString KPasswordDialog::prompt() const 0298 { 0299 return d->ui.prompt->text(); 0300 } 0301 0302 void KPasswordDialog::setPassword(const QString &p) 0303 { 0304 d->ui.passEdit->setPassword(p); 0305 } 0306 0307 void KPasswordDialog::setUsernameReadOnly(bool readOnly) 0308 { 0309 d->ui.userEdit->setReadOnly(readOnly); 0310 0311 if (readOnly && d->ui.userEdit->hasFocus()) { 0312 d->ui.passEdit->setFocus(); 0313 } 0314 } 0315 0316 void KPasswordDialog::setKnownLogins(const QMap<QString, QString> &knownLogins) 0317 { 0318 const int nr = knownLogins.count(); 0319 if (nr == 0) { 0320 return; 0321 } 0322 0323 if (nr == 1) { 0324 d->ui.userEdit->setText(knownLogins.begin().key()); 0325 setPassword(knownLogins.begin().value()); 0326 return; 0327 } 0328 0329 Q_ASSERT(!d->ui.userEdit->isReadOnly()); 0330 if (!d->userEditCombo) { 0331 int row = -1; 0332 QFormLayout::ItemRole userEditRole = QFormLayout::FieldRole; 0333 0334 d->ui.formLayout->getWidgetPosition(d->ui.userEdit, &row, &userEditRole); 0335 d->ui.formLayout->removeWidget(d->ui.userEdit); 0336 delete d->ui.userEdit; 0337 d->userEditCombo = new QComboBox(d->ui.credentialsGroup); 0338 d->userEditCombo->setEditable(true); 0339 d->ui.userEdit = d->userEditCombo->lineEdit(); 0340 d->ui.userNameLabel->setBuddy(d->userEditCombo); 0341 d->ui.formLayout->setWidget(row > -1 ? row : 0, userEditRole, d->userEditCombo); 0342 0343 setTabOrder(d->ui.userEdit, d->ui.anonymousRadioButton); 0344 setTabOrder(d->ui.anonymousRadioButton, d->ui.domainEdit); 0345 setTabOrder(d->ui.domainEdit, d->ui.passEdit); 0346 setTabOrder(d->ui.passEdit, d->ui.keepCheckBox); 0347 connect(d->ui.userEdit, &QLineEdit::returnPressed, d->ui.passEdit, qOverload<>(&QWidget::setFocus)); 0348 } 0349 0350 d->knownLogins = knownLogins; 0351 d->userEditCombo->addItems(knownLogins.keys()); 0352 d->userEditCombo->setFocus(); 0353 0354 connect(d->userEditCombo, &QComboBox::textActivated, this, [this](const QString &text) { 0355 d->activated(text); 0356 }); 0357 } 0358 0359 #if KWIDGETSADDONS_ENABLE_DEPRECATED_SINCE(6, 0) 0360 #pragma GCC diagnostic push 0361 #pragma GCC diagnostic ignored "-Wdeprecated-declarations" 0362 void KPasswordDialog::setRevealPasswordAvailable(bool reveal) 0363 { 0364 d->ui.passEdit->setRevealPasswordAvailable(reveal); 0365 } 0366 0367 bool KPasswordDialog::isRevealPasswordAvailable() const 0368 { 0369 return d->ui.passEdit->isRevealPasswordAvailable(); 0370 } 0371 #pragma GCC diagnostic pop 0372 #endif 0373 0374 KPassword::RevealMode KPasswordDialog::revealPasswordMode() const 0375 { 0376 return d->ui.passEdit->revealPasswordMode(); 0377 } 0378 0379 void KPasswordDialog::setRevealPasswordMode(KPassword::RevealMode revealPasswordMode) 0380 { 0381 d->ui.passEdit->setRevealPasswordMode(revealPasswordMode); 0382 } 0383 0384 void KPasswordDialogPrivate::activated(const QString &userName) 0385 { 0386 QMap<QString, QString>::ConstIterator it = knownLogins.constFind(userName); 0387 if (it != knownLogins.constEnd()) { 0388 q->setPassword(it.value()); 0389 } 0390 } 0391 0392 void KPasswordDialog::accept() 0393 { 0394 if (!d->ui.errorMessage->isHidden()) { 0395 d->ui.errorMessage->setText(QString()); 0396 } 0397 0398 // reset the font in case we had an error previously 0399 if (!d->ui.passwordLabel->isHidden()) { 0400 d->ui.passwordLabel->setFont(font()); 0401 d->ui.userNameLabel->setFont(font()); 0402 } 0403 0404 // we do this to allow the error message, if any, to go away 0405 // checkPassword() may block for a period of time 0406 QTimer::singleShot(0, this, [this] { 0407 d->actuallyAccept(); 0408 }); 0409 } 0410 0411 void KPasswordDialogPrivate::actuallyAccept() 0412 { 0413 if (!q->checkPassword()) { 0414 return; 0415 } 0416 0417 bool keep = ui.keepCheckBox->isVisibleTo(q) && ui.keepCheckBox->isChecked(); 0418 Q_EMIT q->gotPassword(q->password(), keep); 0419 0420 if (ui.userEdit->isVisibleTo(q)) { 0421 Q_EMIT q->gotUsernameAndPassword(q->username(), q->password(), keep); 0422 } 0423 0424 q->QDialog::accept(); 0425 } 0426 0427 bool KPasswordDialog::checkPassword() 0428 { 0429 return true; 0430 } 0431 0432 QDialogButtonBox *KPasswordDialog::buttonBox() const 0433 { 0434 return d->ui.buttonBox; 0435 } 0436 0437 void KPasswordDialog::setUsernameContextHelp(const QString &help) 0438 { 0439 d->ui.userEditContextHelpButton->setVisible(true); 0440 d->ui.userEdit->setWhatsThis(help); 0441 } 0442 0443 #include "moc_kpassworddialog.cpp"