diff --git a/src/conf/groupsconfigdialog.cpp b/src/conf/groupsconfigdialog.cpp index 02581592e..60fe198e2 100644 --- a/src/conf/groupsconfigdialog.cpp +++ b/src/conf/groupsconfigdialog.cpp @@ -1,134 +1,147 @@ /* conf/groupsconfigdialog.h This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2021 g10 Code GmbH SPDX-FileContributor: Ingo Klöcker SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "groupsconfigdialog.h" #include "groupsconfigpage.h" +#include "utils/gui-helper.h" + #include #include #include #include #include #include #include #include #include class GroupsConfigDialog::Private { friend class ::GroupsConfigDialog; GroupsConfigDialog *const q; GroupsConfigPage *configPage = nullptr; public: Private(GroupsConfigDialog *qq) : q(qq) , configPage(new GroupsConfigPage(qq)) { restoreLayout(); } ~Private() { saveLayout(); } private: void saveLayout() { KConfigGroup configGroup(KSharedConfig::openStateConfig(), "GroupsConfigDialog"); configGroup.writeEntry("Size", q->size()); configGroup.sync(); } void restoreLayout(const QSize &defaultSize = QSize()) { const KConfigGroup configGroup(KSharedConfig::openStateConfig(), "GroupsConfigDialog"); const QSize size = configGroup.readEntry("Size", defaultSize); if (size.isValid()) { q->resize(size); } } }; GroupsConfigDialog::GroupsConfigDialog(QWidget *parent) : KConfigDialog(parent, GroupsConfigDialog::dialogName(), /*config=*/ nullptr) , d(new Private(this)) { setWindowTitle(i18nc("@title:window", "Configure Groups")); setFaceType(KPageDialog::Plain); const auto *const item = addPage(d->configPage, i18n("Groups"), /*pixmapName=*/ QString(), /*header=*/ QString(), /*manage=*/ false); // prevent scroll area embedding the config page from receiving focus for (const auto scrollAreas = item->widget()->findChildren(); auto sa : scrollAreas) { sa->setFocusPolicy(Qt::NoFocus); } // there are no defaults to restore buttonBox()->removeButton(buttonBox()->button(QDialogButtonBox::RestoreDefaults)); QPushButton *resetButton = buttonBox()->addButton(QDialogButtonBox::Reset); KGuiItem::assign(resetButton, KStandardGuiItem::reset()); resetButton->setEnabled(false); const auto helpAction = new Kleo::DocAction(QIcon::fromTheme(QStringLiteral("help")), i18n("Help"), i18nc("Only available in German and English. Leave to English for other languages.", "handout_group-feature_gnupg_en.pdf"), QStringLiteral("../share/doc/gnupg-vsd")); if (helpAction->isEnabled()) { auto helpButton = buttonBox()->button(QDialogButtonBox::Help); if (helpButton) { disconnect(helpButton, &QAbstractButton::clicked, nullptr, nullptr); connect(helpButton, &QAbstractButton::clicked, helpAction, &QAction::trigger); connect(helpButton, &QObject::destroyed, helpAction, &QObject::deleteLater); } } else { delete helpAction; } + // prevent accidental closing of dialog when pressing Enter while the search field has focus + Kleo::unsetAutoDefaultButtons(this); + connect(buttonBox()->button(QDialogButtonBox::Reset), &QAbstractButton::clicked, this, &GroupsConfigDialog::updateWidgets); connect(d->configPage, &GroupsConfigPage::changed, this, [this]() { updateButtons(); if (QPushButton *button = buttonBox()->button(QDialogButtonBox::Reset)) { button->setEnabled(d->configPage->hasChanged()); } }); } GroupsConfigDialog::~GroupsConfigDialog() = default; QString GroupsConfigDialog::dialogName() { return QStringLiteral("Group Settings"); } +void GroupsConfigDialog::showEvent(QShowEvent *event) +{ + KConfigDialog::showEvent(event); + + // prevent accidental closing of dialog when pressing Enter while the search field has focus + Kleo::unsetDefaultButtons(buttonBox()); +} + void GroupsConfigDialog::updateSettings() { d->configPage->save(); } void GroupsConfigDialog::updateWidgets() { d->configPage->load(); } bool GroupsConfigDialog::hasChanged() { return d->configPage->hasChanged(); } diff --git a/src/conf/groupsconfigdialog.h b/src/conf/groupsconfigdialog.h index b2811e587..95c8c29d8 100644 --- a/src/conf/groupsconfigdialog.h +++ b/src/conf/groupsconfigdialog.h @@ -1,37 +1,40 @@ /* conf/groupsconfigdialog.h This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2021 g10 Code GmbH SPDX-FileContributor: Ingo Klöcker SPDX-License-Identifier: GPL-2.0-or-later */ #pragma once #include #include class GroupsConfigDialog : public KConfigDialog { Q_OBJECT public: explicit GroupsConfigDialog(QWidget *parent = nullptr); ~GroupsConfigDialog() override; static QString dialogName(); +protected: + void showEvent(QShowEvent *event) override; + private Q_SLOTS: void updateSettings() override; void updateWidgets() override; private: bool hasChanged() override; private: class Private; const std::unique_ptr d; }; diff --git a/src/dialogs/editgroupdialog.cpp b/src/dialogs/editgroupdialog.cpp index b1c5f5ef5..63b9e057b 100644 --- a/src/dialogs/editgroupdialog.cpp +++ b/src/dialogs/editgroupdialog.cpp @@ -1,330 +1,342 @@ /* dialogs/editgroupdialog.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2021 g10 Code GmbH SPDX-FileContributor: Ingo Klöcker SPDX-License-Identifier: GPL-2.0-or-later */ #include "editgroupdialog.h" #include "commands/detailscommand.h" +#include "utils/gui-helper.h" #include "view/keytreeview.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kleopatra_debug.h" using namespace Kleo; using namespace Kleo::Commands; using namespace Kleo::Dialogs; using namespace GpgME; Q_DECLARE_METATYPE(GpgME::Key) namespace { auto createOpenPGPOnlyKeyFilter() { auto filter = std::make_shared(); filter->setIsOpenPGP(DefaultKeyFilter::Set); return filter; } } class EditGroupDialog::Private { friend class ::Kleo::Dialogs::EditGroupDialog; EditGroupDialog *const q; struct { QLineEdit *groupNameEdit = nullptr; QLineEdit *availableKeysFilter = nullptr; KeyTreeView *availableKeysList = nullptr; QLineEdit *groupKeysFilter = nullptr; KeyTreeView *groupKeysList = nullptr; QDialogButtonBox *buttonBox = nullptr; } ui; AbstractKeyListModel *availableKeysModel = nullptr; AbstractKeyListModel *groupKeysModel = nullptr; public: Private(EditGroupDialog *qq) : q(qq) { auto mainLayout = new QVBoxLayout(q); auto groupNameLayout = new QHBoxLayout(); groupNameLayout->addWidget(new QLabel(i18nc("Name of a group of keys", "Name:"), q)); ui.groupNameEdit = new QLineEdit(q); groupNameLayout->addWidget(ui.groupNameEdit); mainLayout->addLayout(groupNameLayout); mainLayout->addWidget(new KSeparator(Qt::Horizontal, q)); auto centerLayout = new QVBoxLayout; auto availableKeysLayout = new QVBoxLayout; availableKeysLayout->addWidget(new QLabel(i18n("Available keys:"), q)); { auto hbox = new QHBoxLayout; auto label = new QLabel{i18nc("@label", "Search:")}; label->setToolTip(i18nc("@info:tooltip", "Search the list for keys matching the search term.")); hbox->addWidget(label); ui.availableKeysFilter = new QLineEdit(q); ui.availableKeysFilter->setClearButtonEnabled(true); ui.availableKeysFilter->setToolTip(i18nc("@info:tooltip", "Search the list for keys matching the search term.")); ui.availableKeysFilter->setPlaceholderText(i18nc("@info::placeholder", "Enter search term")); label->setBuddy(ui.availableKeysFilter); hbox->addWidget(ui.availableKeysFilter, 1); availableKeysLayout->addLayout(hbox); } availableKeysModel = AbstractKeyListModel::createFlatKeyListModel(q); availableKeysModel->useKeyCache(true, KeyList::AllKeys); ui.availableKeysList = new KeyTreeView(q); ui.availableKeysList->view()->setRootIsDecorated(false); ui.availableKeysList->setFlatModel(availableKeysModel); ui.availableKeysList->setHierarchicalView(false); if (!Settings{}.cmsEnabled()) { ui.availableKeysList->setKeyFilter(createOpenPGPOnlyKeyFilter()); } availableKeysLayout->addWidget(ui.availableKeysList, /*stretch=*/ 1); centerLayout->addLayout(availableKeysLayout, /*stretch=*/ 1); auto buttonsLayout = new QHBoxLayout; buttonsLayout->addStretch(1); auto addButton = new QPushButton(q); addButton->setIcon(QIcon::fromTheme(QStringLiteral("arrow-down"))); addButton->setAccessibleName(i18nc("@action:button", "Add Selected Keys")); addButton->setToolTip(i18n("Add the selected keys to the group")); addButton->setEnabled(false); buttonsLayout->addWidget(addButton); auto removeButton = new QPushButton(q); removeButton->setIcon(QIcon::fromTheme(QStringLiteral("arrow-up"))); addButton->setAccessibleName(i18nc("@action:button", "Remove Selected Keys")); removeButton->setToolTip(i18n("Remove the selected keys from the group")); removeButton->setEnabled(false); buttonsLayout->addWidget(removeButton); buttonsLayout->addStretch(1); centerLayout->addLayout(buttonsLayout); auto groupKeysLayout = new QVBoxLayout; groupKeysLayout->addWidget(new QLabel(i18n("Group keys:"), q)); { auto hbox = new QHBoxLayout; auto label = new QLabel{i18nc("@label", "Search:")}; label->setToolTip(i18nc("@info:tooltip", "Search the list for keys matching the search term.")); hbox->addWidget(label); ui.groupKeysFilter = new QLineEdit(q); ui.groupKeysFilter->setClearButtonEnabled(true); ui.groupKeysFilter->setToolTip(i18nc("@info:tooltip", "Search the list for keys matching the search term.")); ui.groupKeysFilter->setPlaceholderText(i18nc("@info::placeholder", "Enter search term")); label->setBuddy(ui.groupKeysFilter); hbox->addWidget(ui.groupKeysFilter, 1); groupKeysLayout->addLayout(hbox); } groupKeysModel = AbstractKeyListModel::createFlatKeyListModel(q); ui.groupKeysList = new KeyTreeView(q); ui.groupKeysList->view()->setRootIsDecorated(false); ui.groupKeysList->setFlatModel(groupKeysModel); ui.groupKeysList->setHierarchicalView(false); groupKeysLayout->addWidget(ui.groupKeysList, /*stretch=*/ 1); centerLayout->addLayout(groupKeysLayout, /*stretch=*/ 1); mainLayout->addLayout(centerLayout); mainLayout->addWidget(new KSeparator(Qt::Horizontal, q)); ui.buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, q); QPushButton *okButton = ui.buttonBox->button(QDialogButtonBox::Ok); KGuiItem::assign(okButton, KStandardGuiItem::ok()); KGuiItem::assign(ui.buttonBox->button(QDialogButtonBox::Cancel), KStandardGuiItem::cancel()); okButton->setEnabled(false); mainLayout->addWidget(ui.buttonBox); + // prevent accidental closing of dialog when pressing Enter while a search field has focus + Kleo::unsetAutoDefaultButtons(q); + connect(ui.groupNameEdit, &QLineEdit::textChanged, q, [okButton] (const QString &text) { okButton->setEnabled(!text.trimmed().isEmpty()); }); connect(ui.availableKeysFilter, &QLineEdit::textChanged, ui.availableKeysList, &KeyTreeView::setStringFilter); connect(ui.availableKeysList->view()->selectionModel(), &QItemSelectionModel::selectionChanged, q, [addButton] (const QItemSelection &selected, const QItemSelection &) { addButton->setEnabled(!selected.isEmpty()); }); connect(ui.availableKeysList->view(), &QAbstractItemView::doubleClicked, q, [this] (const QModelIndex &index) { showKeyDetails(index); }); connect(ui.groupKeysFilter, &QLineEdit::textChanged, ui.groupKeysList, &KeyTreeView::setStringFilter); connect(ui.groupKeysList->view()->selectionModel(), &QItemSelectionModel::selectionChanged, q, [removeButton] (const QItemSelection &selected, const QItemSelection &) { removeButton->setEnabled(!selected.isEmpty()); }); connect(ui.groupKeysList->view(), &QAbstractItemView::doubleClicked, q, [this] (const QModelIndex &index) { showKeyDetails(index); }); connect(addButton, &QPushButton::clicked, q, [this] () { addKeysToGroup(); }); connect(removeButton, &QPushButton::clicked, q, [this] () { removeKeysFromGroup(); }); connect(ui.buttonBox, &QDialogButtonBox::accepted, q, &EditGroupDialog::accept); connect(ui.buttonBox, &QDialogButtonBox::rejected, q, &EditGroupDialog::reject); // calculate default size with enough space for the key list const auto fm = q->fontMetrics(); const QSize sizeHint = q->sizeHint(); const QSize defaultSize = QSize(qMax(sizeHint.width(), 150 * fm.horizontalAdvance(QLatin1Char('x'))), sizeHint.height()); restoreLayout(defaultSize); } ~Private() { saveLayout(); } private: void saveLayout() { KConfigGroup configGroup(KSharedConfig::openConfig(), "EditGroupDialog"); configGroup.writeEntry("Size", q->size()); KConfigGroup availableKeysConfig = configGroup.group("AvailableKeysView"); ui.availableKeysList->saveLayout(availableKeysConfig); KConfigGroup groupKeysConfig = configGroup.group("GroupKeysView"); ui.groupKeysList->saveLayout(groupKeysConfig); configGroup.sync(); } void restoreLayout(const QSize &defaultSize) { const KConfigGroup configGroup(KSharedConfig::openConfig(), "EditGroupDialog"); const KConfigGroup availableKeysConfig = configGroup.group("AvailableKeysView"); ui.availableKeysList->restoreLayout(availableKeysConfig); const KConfigGroup groupKeysConfig = configGroup.group("GroupKeysView"); ui.groupKeysList->restoreLayout(groupKeysConfig); const QSize size = configGroup.readEntry("Size", defaultSize); if (size.isValid()) { q->resize(size); } } void showKeyDetails(const QModelIndex &index) { if (!index.isValid()) { return; } const auto key = index.model()->data(index, KeyList::KeyRole).value(); if (!key.isNull()) { auto cmd = new DetailsCommand(key, nullptr); cmd->setParentWidget(q); cmd->start(); } } void addKeysToGroup(); void removeKeysFromGroup(); }; void EditGroupDialog::Private::addKeysToGroup() { const std::vector selectedGroupKeys = ui.groupKeysList->selectedKeys(); const std::vector selectedKeys = ui.availableKeysList->selectedKeys(); groupKeysModel->addKeys(selectedKeys); ui.groupKeysList->selectKeys(selectedGroupKeys); } void EditGroupDialog::Private::removeKeysFromGroup() { const std::vector selectedKeys = ui.groupKeysList->selectedKeys(); for (const Key &key : selectedKeys) { groupKeysModel->removeKey(key); } } EditGroupDialog::EditGroupDialog(QWidget *parent) : QDialog(parent) , d(new Private(this)) { setWindowTitle(i18nc("@title:window", "Edit Group")); } EditGroupDialog::~EditGroupDialog() = default; void EditGroupDialog::setInitialFocus(FocusWidget widget) { switch (widget) { case GroupName: d->ui.groupNameEdit->setFocus(); break; case KeysFilter: d->ui.availableKeysFilter->setFocus(); break; default: qCDebug(KLEOPATRA_LOG) << "EditGroupDialog::setInitialFocus - invalid focus widget:" << widget; } } void EditGroupDialog::setGroupName(const QString &name) { d->ui.groupNameEdit->setText(name); } QString EditGroupDialog::groupName() const { return d->ui.groupNameEdit->text().trimmed(); } void EditGroupDialog::setGroupKeys(const std::vector &keys) { d->groupKeysModel->setKeys(keys); } std::vector EditGroupDialog::groupKeys() const { std::vector keys; keys.reserve(d->groupKeysModel->rowCount()); for (int row = 0; row < d->groupKeysModel->rowCount(); ++row) { const QModelIndex index = d->groupKeysModel->index(row, 0); keys.push_back(d->groupKeysModel->key(index)); } return keys; } + +void EditGroupDialog::showEvent(QShowEvent *event) +{ + QDialog::showEvent(event); + + // prevent accidental closing of dialog when pressing Enter while a search field has focus + Kleo::unsetDefaultButtons(d->ui.buttonBox); +} diff --git a/src/dialogs/editgroupdialog.h b/src/dialogs/editgroupdialog.h index 0d9722018..0475d9096 100644 --- a/src/dialogs/editgroupdialog.h +++ b/src/dialogs/editgroupdialog.h @@ -1,55 +1,58 @@ /* dialogs/editgroupdialog.h This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2021 g10 Code GmbH SPDX-FileContributor: Ingo Klöcker SPDX-License-Identifier: GPL-2.0-or-later */ #pragma once #include #include #include namespace GpgME { class Key; } namespace Kleo { namespace Dialogs { class EditGroupDialog : public QDialog { Q_OBJECT public: enum FocusWidget { GroupName, KeysFilter }; explicit EditGroupDialog(QWidget *parent = nullptr); ~EditGroupDialog() override; void setInitialFocus(FocusWidget widget); void setGroupName(const QString &name); QString groupName() const; void setGroupKeys(const std::vector &keys); std::vector groupKeys() const; +protected: + void showEvent(QShowEvent *event) override; + private: class Private; const std::unique_ptr d; }; } } diff --git a/src/utils/gui-helper.cpp b/src/utils/gui-helper.cpp index 626d7dd39..964dd7002 100644 --- a/src/utils/gui-helper.cpp +++ b/src/utils/gui-helper.cpp @@ -1,134 +1,159 @@ /* crypto/gui/signencryptemailconflictdialog.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2017 Intevation GmbH SPDX-License-Identifier: GPL-2.0-or-later */ #include "gui-helper.h" +#include +#include +#include #include #ifdef Q_OS_WIN #include #endif /* This is a Hack to workaround the fact that Foregrounding a Window is so restricted that it AllowSetForegroundWindow does not always work e.g. when the ForegroundWindow timeout has not expired. This Hack is semi official but may stop working in future windows versions. This is similar to what pinentry-qt does on Windows. */ #ifdef Q_OS_WIN WINBOOL SetForegroundWindowEx(HWND hWnd) { //Attach foreground window thread to our thread const DWORD ForeGroundID = GetWindowThreadProcessId(::GetForegroundWindow(), NULL); const DWORD CurrentID = GetCurrentThreadId(); WINBOOL retval; AttachThreadInput(ForeGroundID, CurrentID, TRUE); //Do our stuff here HWND hLastActivePopupWnd = GetLastActivePopup(hWnd); retval = SetForegroundWindow(hLastActivePopupWnd); //Detach the attached thread AttachThreadInput(ForeGroundID, CurrentID, FALSE); return retval; }// End SetForegroundWindowEx #endif void Kleo::aggressive_raise(QWidget *w, bool stayOnTop) { /* Maybe Qt will become aggressive enough one day that * this is enough on windows too*/ w->raise(); w->setWindowState(Qt::WindowActive); w->activateWindow(); #ifdef Q_OS_WIN HWND wid = (HWND)w->effectiveWinId(); /* In the meantime we do our own attention grabbing */ if (!SetForegroundWindow(wid) && !SetForegroundWindowEx(wid)) { OutputDebugStringA("SetForegroundWindow (ex) failed"); /* Yet another fallback which will not work on some * versions and is not recommended by msdn */ if (!ShowWindow(wid, SW_SHOWNORMAL)) { OutputDebugStringA("ShowWindow failed."); } } /* Even if SetForgeoundWindow / SetForegroundWinowEx don't fail * we sometimes are still not in the foreground. So we try yet * another hack by using SetWindowPos */ if (!SetWindowPos(wid, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW)) { OutputDebugStringA("SetWindowPos failed."); } /* sometimes we want to stay on top even if the user * changes focus because we are _aggressive_ and otherwise * Outlook might show the "Help I'm unresponsive so I must have * crashed" Popup if the user clicks into Outlook while a dialog * from us is active. */ else if (!stayOnTop) { // Without moving back to NOTOPMOST we just stay on top. // Even if the user changes focus. SetWindowPos(wid, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); } #else Q_UNUSED(stayOnTop) #endif } void Kleo::forceSetTabOrder(QWidget *first, QWidget *second) { if (!first || !second || first == second) { return; } // temporarily change the focus policy of the two widgets to something // other than Qt::NoFocus because QWidget::setTabOrder() does nothing // if either widget has focus policy Qt::NoFocus const auto firstFocusPolicy = first->focusPolicy(); const auto secondFocusPolicy = second->focusPolicy(); if (firstFocusPolicy == Qt::NoFocus) { first->setFocusPolicy(Qt::StrongFocus); } if (secondFocusPolicy == Qt::NoFocus) { second->setFocusPolicy(Qt::StrongFocus); } QWidget::setTabOrder(first, second); if (first->focusPolicy() != firstFocusPolicy) { first->setFocusPolicy(firstFocusPolicy); } if (second->focusPolicy() != secondFocusPolicy) { second->setFocusPolicy(secondFocusPolicy); } } template bool focusFirstButtonIf(const std::vector &buttons, UnaryPredicate p) { auto it = std::find_if(std::begin(buttons), std::end(buttons), p); if (it != std::end(buttons)) { (*it)->setFocus(); return true; } return false; } bool Kleo::focusFirstCheckedButton(const std::vector &buttons) { return focusFirstButtonIf(buttons, [](auto btn) { return btn && btn->isEnabled() && btn->isChecked(); }); } bool Kleo::focusFirstEnabledButton(const std::vector &buttons) { return focusFirstButtonIf(buttons, [](auto btn) { return btn && btn->isEnabled(); }); } + +void Kleo::unsetDefaultButtons(const QDialogButtonBox *buttonBox) +{ + if (!buttonBox) { + return; + } + for (const auto buttons = buttonBox->buttons(); auto button : buttons) { + if (auto pushButton = qobject_cast(button)) { + pushButton->setDefault(false); + } + } +} + +void Kleo::unsetAutoDefaultButtons(const QDialog *dialog) +{ + if (!dialog) { + return; + } + for (const auto pushButtons = dialog->findChildren(); auto pushButton : pushButtons) { + pushButton->setAutoDefault(false); + } +} diff --git a/src/utils/gui-helper.h b/src/utils/gui-helper.h index 0039858b2..e5bc1ab39 100644 --- a/src/utils/gui-helper.h +++ b/src/utils/gui-helper.h @@ -1,69 +1,92 @@ /* -*- mode: c++; c-basic-offset:4 -*- utils/gui-helper.h This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2009 Klarälvdalens Datakonsult AB SPDX-License-Identifier: GPL-2.0-or-later */ #pragma once #include +class QDialog; +class QDialogButtonBox; class QWidget; namespace Kleo { static inline void really_check(QAbstractButton &b, bool on) { const bool excl = b.autoExclusive(); b.setAutoExclusive(false); b.setChecked(on); b.setAutoExclusive(excl); } static inline bool xconnect(const QObject *a, const char *signal, const QObject *b, const char *slot) { return QObject::connect(a, signal, b, slot) && QObject::connect(b, signal, a, slot); } /** Aggressively raise a window to foreground. May be platform * specific. */ void aggressive_raise(QWidget *w, bool stayOnTop); /** * Puts the second widget after the first widget in the focus order. * * In contrast to QWidget::setTabOrder(), this function also changes the * focus order if the first widget or the second widget has focus policy * Qt::NoFocus. * * Note: After calling this function all widgets in the focus proxy chain * of the first widget have focus policy Qt::NoFocus if the first widget * has this focus policy. Correspondingly, for the second widget. */ void forceSetTabOrder(QWidget *first, QWidget *second); /** * Gives the keyboard input focus to the first of the \p buttons, that is * enabled and checked. * * Returns true, if a button was given focus. Returns false, if no button was * found that is enabled and checked. */ bool focusFirstCheckedButton(const std::vector &buttons); /** * Gives the keyboard input focus to the first of the \p buttons, that is * enabled. * * Returns true, if a button was given focus. Returns false, if no button was * found that is enabled. */ bool focusFirstEnabledButton(const std::vector &buttons); +/** + * Unsets the default property of all push buttons in the button box. + * + * This function needs to be called after the button box received the show event + * because QDialogButtonBox automatically sets a default button when it is shown. + * + * \sa unsetAutoDefaultButtons + */ +void unsetDefaultButtons(const QDialogButtonBox *buttonBox); + +/** + * Unsets the auto-default property of all push buttons in the dialog. + * + * This can be useful if you want to prevent the accidental closing of the dialog + * when the user presses Enter while another UI element, e.g. a text input field + * has focus. + * + * \sa unsetDefaultButtons + */ +void unsetAutoDefaultButtons(const QDialog *dialog); + }