diff options
Diffstat (limited to 'src/libs/utils/checkablemessagebox.cpp')
-rw-r--r-- | src/libs/utils/checkablemessagebox.cpp | 548 |
1 files changed, 137 insertions, 411 deletions
diff --git a/src/libs/utils/checkablemessagebox.cpp b/src/libs/utils/checkablemessagebox.cpp index 4420fc66cb..d3ede7d4e7 100644 --- a/src/libs/utils/checkablemessagebox.cpp +++ b/src/libs/utils/checkablemessagebox.cpp @@ -3,6 +3,7 @@ #include "checkablemessagebox.h" +#include "hostosinfo.h" #include "qtcassert.h" #include "utilstr.h" @@ -17,10 +18,11 @@ /*! \class Utils::CheckableMessageBox + \inmodule QtCreator \brief The CheckableMessageBox class implements a message box suitable for - questions with a - "Do not ask me again" checkbox. + questions with a \uicontrol {Do not ask again} or \uicontrol {Do not show again} + checkbox. Emulates the QMessageBox API with static conveniences. The message label can open external URLs. @@ -30,435 +32,155 @@ static const char kDoNotAskAgainKey[] = "DoNotAskAgain"; namespace Utils { -class CheckableMessageBoxPrivate -{ -public: - CheckableMessageBoxPrivate(QDialog *q) - { - QSizePolicy sizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred); - - pixmapLabel = new QLabel(q); - sizePolicy.setHorizontalStretch(0); - sizePolicy.setVerticalStretch(0); - sizePolicy.setHeightForWidth(pixmapLabel->sizePolicy().hasHeightForWidth()); - pixmapLabel->setSizePolicy(sizePolicy); - pixmapLabel->setVisible(false); - pixmapLabel->setFocusPolicy(Qt::NoFocus); - - auto pixmapSpacer = - new QSpacerItem(0, 5, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding); - - messageLabel = new QLabel(q); - messageLabel->setMinimumSize(QSize(300, 0)); - messageLabel->setWordWrap(true); - messageLabel->setOpenExternalLinks(true); - messageLabel->setTextInteractionFlags(Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse); - messageLabel->setFocusPolicy(Qt::NoFocus); - messageLabel->setAlignment(Qt::AlignLeft | Qt::AlignTop); - - checkBox = new QCheckBox(q); - checkBox->setText(Tr::tr("Do not ask again")); - - const QString showText = Tr::tr("Show Details..."); - detailsButton = new QPushButton(showText, q); - detailsButton->setAutoDefault(false); - detailsButton->hide(); - detailsText = new QTextEdit(q); - detailsText->hide(); - QObject::connect(detailsButton, &QPushButton::clicked, detailsText, [this, showText] { - detailsText->setVisible(!detailsText->isVisible()); - detailsButton->setText( - detailsText->isVisible() ? Tr::tr("Hide Details...") : showText); - }); - - buttonBox = new QDialogButtonBox(q); - buttonBox->setOrientation(Qt::Horizontal); - buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok); - - auto verticalLayout = new QVBoxLayout(); - verticalLayout->addWidget(pixmapLabel); - verticalLayout->addItem(pixmapSpacer); - - auto horizontalLayout_2 = new QHBoxLayout(); - horizontalLayout_2->addLayout(verticalLayout); - horizontalLayout_2->addWidget(messageLabel, 10); - - auto horizontalLayout = new QHBoxLayout(); - horizontalLayout->addWidget(checkBox); - horizontalLayout->addStretch(10); - - auto detailsButtonLayout = new QHBoxLayout; - detailsButtonLayout->addWidget(detailsButton); - detailsButtonLayout->addStretch(10); - - auto verticalLayout_2 = new QVBoxLayout(q); - verticalLayout_2->addLayout(horizontalLayout_2); - verticalLayout_2->addLayout(horizontalLayout); - verticalLayout_2->addLayout(detailsButtonLayout); - verticalLayout_2->addWidget(detailsText, 10); - verticalLayout_2->addStretch(1); - verticalLayout_2->addWidget(buttonBox); - } - - QLabel *pixmapLabel = nullptr; - QLabel *messageLabel = nullptr; - QCheckBox *checkBox = nullptr; - QDialogButtonBox *buttonBox = nullptr; - QAbstractButton *clickedButton = nullptr; - QPushButton *detailsButton = nullptr; - QTextEdit *detailsText = nullptr; - QMessageBox::Icon icon = QMessageBox::NoIcon; -}; - -CheckableMessageBox::CheckableMessageBox(QWidget *parent) : - QDialog(parent), - d(new CheckableMessageBoxPrivate(this)) -{ - setModal(true); - connect(d->buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); - connect(d->buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); - connect(d->buttonBox, &QDialogButtonBox::clicked, - this, [this](QAbstractButton *b) { d->clickedButton = b; }); -} - -CheckableMessageBox::~CheckableMessageBox() -{ - delete d; -} - -QAbstractButton *CheckableMessageBox::clickedButton() const -{ - return d->clickedButton; -} - -QDialogButtonBox::StandardButton CheckableMessageBox::clickedStandardButton() const -{ - if (d->clickedButton) - return d->buttonBox->standardButton(d->clickedButton); - return QDialogButtonBox::NoButton; -} - -QString CheckableMessageBox::text() const -{ - return d->messageLabel->text(); -} - -void CheckableMessageBox::setText(const QString &t) -{ - d->messageLabel->setText(t); -} - -QMessageBox::Icon CheckableMessageBox::icon() const -{ - return d->icon; -} - -// See QMessageBoxPrivate::standardIcon -static QPixmap pixmapForIcon(QMessageBox::Icon icon, QWidget *w) -{ - const QStyle *style = w ? w->style() : QApplication::style(); - const int iconSize = style->pixelMetric(QStyle::PM_MessageBoxIconSize, nullptr, w); - QIcon tmpIcon; - switch (icon) { - case QMessageBox::Information: - tmpIcon = style->standardIcon(QStyle::SP_MessageBoxInformation, nullptr, w); - break; - case QMessageBox::Warning: - tmpIcon = style->standardIcon(QStyle::SP_MessageBoxWarning, nullptr, w); - break; - case QMessageBox::Critical: - tmpIcon = style->standardIcon(QStyle::SP_MessageBoxCritical, nullptr, w); - break; - case QMessageBox::Question: - tmpIcon = style->standardIcon(QStyle::SP_MessageBoxQuestion, nullptr, w); - break; - default: - break; - } - if (!tmpIcon.isNull()) { - QWindow *window = nullptr; - if (w) { - window = w->windowHandle(); - if (!window) { - if (const QWidget *nativeParent = w->nativeParentWidget()) - window = nativeParent->windowHandle(); - } +static QSettings *theSettings; + +static QMessageBox::StandardButton exec( + QWidget *parent, + QMessageBox::Icon icon, + const QString &title, + const QString &text, + const CheckableDecider &decider, + QMessageBox::StandardButtons buttons, + QMessageBox::StandardButton defaultButton, + QMessageBox::StandardButton acceptButton, + QMap<QMessageBox::StandardButton, QString> buttonTextOverrides, + const QString &msg) +{ + QMessageBox msgBox(parent); + msgBox.setWindowTitle(title); + msgBox.setIcon(icon); + msgBox.setText(text); + msgBox.setTextFormat(Qt::RichText); + msgBox.setTextInteractionFlags(Qt::LinksAccessibleByKeyboard | Qt::LinksAccessibleByMouse); + + if (HostOsInfo::isMacHost()) { + // Message boxes on macOS cannot display links. + // If the message contains a link, we need to disable native dialogs. + if (text.contains("<a ")) { +#if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0) + msgBox.setOptions(QMessageBox::Option::DontUseNativeDialog); +#endif } - return tmpIcon.pixmap(window, QSize(iconSize, iconSize)); - } - return QPixmap(); -} - -void CheckableMessageBox::setIcon(QMessageBox::Icon icon) -{ - d->icon = icon; - const QPixmap pixmap = pixmapForIcon(icon, this); - d->pixmapLabel->setPixmap(pixmap); - d->pixmapLabel->setVisible(!pixmap.isNull()); -} - -bool CheckableMessageBox::isChecked() const -{ - return d->checkBox->isChecked(); -} - -void CheckableMessageBox::setChecked(bool s) -{ - d->checkBox->setChecked(s); -} - -QString CheckableMessageBox::checkBoxText() const -{ - return d->checkBox->text(); -} - -void CheckableMessageBox::setCheckBoxText(const QString &t) -{ - d->checkBox->setText(t); -} - -bool CheckableMessageBox::isCheckBoxVisible() const -{ - return d->checkBox->isVisible(); -} - -void CheckableMessageBox::setCheckBoxVisible(bool v) -{ - d->checkBox->setVisible(v); -} - -QString CheckableMessageBox::detailedText() const -{ - return d->detailsText->toPlainText(); -} - -void CheckableMessageBox::setDetailedText(const QString &text) -{ - d->detailsText->setText(text); - if (!text.isEmpty()) - d->detailsButton->setVisible(true); -} - -QDialogButtonBox::StandardButtons CheckableMessageBox::standardButtons() const -{ - return d->buttonBox->standardButtons(); -} - -void CheckableMessageBox::setStandardButtons(QDialogButtonBox::StandardButtons s) -{ - d->buttonBox->setStandardButtons(s); -} - -QPushButton *CheckableMessageBox::button(QDialogButtonBox::StandardButton b) const -{ - return d->buttonBox->button(b); -} - -QPushButton *CheckableMessageBox::addButton(const QString &text, QDialogButtonBox::ButtonRole role) -{ - return d->buttonBox->addButton(text, role); -} - -QDialogButtonBox::StandardButton CheckableMessageBox::defaultButton() const -{ - const QList<QAbstractButton *> buttons = d->buttonBox->buttons(); - for (QAbstractButton *b : buttons) - if (auto *pb = qobject_cast<QPushButton *>(b)) - if (pb->isDefault()) - return d->buttonBox->standardButton(pb); - return QDialogButtonBox::NoButton; -} - -void CheckableMessageBox::setDefaultButton(QDialogButtonBox::StandardButton s) -{ - if (QPushButton *b = d->buttonBox->button(s)) { - b->setDefault(true); - b->setFocus(); } -} -QDialogButtonBox::StandardButton -CheckableMessageBox::question(QWidget *parent, - const QString &title, - const QString &question, - const QString &checkBoxText, - bool *checkBoxSetting, - QDialogButtonBox::StandardButtons buttons, - QDialogButtonBox::StandardButton defaultButton) -{ - CheckableMessageBox mb(parent); - mb.setWindowTitle(title); - mb.setIcon(QMessageBox::Question); - mb.setText(question); - mb.setCheckBoxText(checkBoxText); - mb.setChecked(*checkBoxSetting); - mb.setStandardButtons(buttons); - mb.setDefaultButton(defaultButton); - mb.exec(); - *checkBoxSetting = mb.isChecked(); - return mb.clickedStandardButton(); -} - -QDialogButtonBox::StandardButton -CheckableMessageBox::information(QWidget *parent, - const QString &title, - const QString &text, - const QString &checkBoxText, - bool *checkBoxSetting, - QDialogButtonBox::StandardButtons buttons, - QDialogButtonBox::StandardButton defaultButton) -{ - CheckableMessageBox mb(parent); - mb.setWindowTitle(title); - mb.setIcon(QMessageBox::Information); - mb.setText(text); - mb.setCheckBoxText(checkBoxText); - mb.setChecked(*checkBoxSetting); - mb.setStandardButtons(buttons); - mb.setDefaultButton(defaultButton); - mb.exec(); - *checkBoxSetting = mb.isChecked(); - return mb.clickedStandardButton(); -} - -QMessageBox::StandardButton CheckableMessageBox::dialogButtonBoxToMessageBoxButton(QDialogButtonBox::StandardButton db) -{ - return static_cast<QMessageBox::StandardButton>(int(db)); -} + if (decider.shouldAskAgain) { + if (!decider.shouldAskAgain()) + return acceptButton; -bool CheckableMessageBox::shouldAskAgain(QSettings *settings, const QString &settingsSubKey) -{ - if (QTC_GUARD(settings)) { - settings->beginGroup(QLatin1String(kDoNotAskAgainKey)); - bool shouldNotAsk = settings->value(settingsSubKey, false).toBool(); - settings->endGroup(); - if (shouldNotAsk) - return false; + msgBox.setCheckBox(new QCheckBox); + msgBox.checkBox()->setChecked(false); + msgBox.checkBox()->setText(msg); } - return true; -} - -enum DoNotAskAgainType{Question, Information}; - -void initDoNotAskAgainMessageBox(CheckableMessageBox &messageBox, const QString &title, - const QString &text, QDialogButtonBox::StandardButtons buttons, - QDialogButtonBox::StandardButton defaultButton, - DoNotAskAgainType type) -{ - messageBox.setWindowTitle(title); - messageBox.setIcon(type == Information ? QMessageBox::Information : QMessageBox::Question); - messageBox.setText(text); - messageBox.setCheckBoxVisible(true); - messageBox.setCheckBoxText(type == Information ? CheckableMessageBox::msgDoNotShowAgain() - : CheckableMessageBox::msgDoNotAskAgain()); - messageBox.setChecked(false); - messageBox.setStandardButtons(buttons); - messageBox.setDefaultButton(defaultButton); -} - -void CheckableMessageBox::doNotAskAgain(QSettings *settings, const QString &settingsSubKey) -{ - if (!settings) - return; - settings->beginGroup(QLatin1String(kDoNotAskAgainKey)); - settings->setValue(settingsSubKey, true); - settings->endGroup(); + msgBox.setStandardButtons(buttons); + msgBox.setDefaultButton(defaultButton); + for (auto it = buttonTextOverrides.constBegin(); it != buttonTextOverrides.constEnd(); ++it) + msgBox.button(it.key())->setText(it.value()); + msgBox.exec(); + + QMessageBox::StandardButton clickedBtn = msgBox.standardButton(msgBox.clickedButton()); + + if (decider.doNotAskAgain && msgBox.checkBox()->isChecked() + && (acceptButton == QMessageBox::NoButton || clickedBtn == acceptButton)) + decider.doNotAskAgain(); + return clickedBtn; +} + +CheckableDecider::CheckableDecider(const QString &settingsSubKey) +{ + QTC_ASSERT(theSettings, return); + shouldAskAgain = [settingsSubKey] { + theSettings->beginGroup(QLatin1String(kDoNotAskAgainKey)); + bool shouldNotAsk = theSettings->value(settingsSubKey, false).toBool(); + theSettings->endGroup(); + return !shouldNotAsk; + }; + doNotAskAgain = [settingsSubKey] { + theSettings->beginGroup(QLatin1String(kDoNotAskAgainKey)); + theSettings->setValue(settingsSubKey, true); + theSettings->endGroup(); + }; +} + +CheckableDecider::CheckableDecider(bool *storage) +{ + shouldAskAgain = [storage] { return !*storage; }; + doNotAskAgain = [storage] { *storage = true; }; +} + +QMessageBox::StandardButton CheckableMessageBox::question( + QWidget *parent, + const QString &title, + const QString &question, + const CheckableDecider &decider, + QMessageBox::StandardButtons buttons, + QMessageBox::StandardButton defaultButton, + QMessageBox::StandardButton acceptButton, + QMap<QMessageBox::StandardButton, QString> buttonTextOverrides, + const QString &msg) +{ + return exec(parent, + QMessageBox::Question, + title, + question, + decider, + buttons, + defaultButton, + acceptButton, + buttonTextOverrides, + msg.isEmpty() ? msgDoNotAskAgain() : msg); +} + +QMessageBox::StandardButton CheckableMessageBox::information( + QWidget *parent, + const QString &title, + const QString &text, + const CheckableDecider &decider, + QMessageBox::StandardButtons buttons, + QMessageBox::StandardButton defaultButton, + QMap<QMessageBox::StandardButton, QString> buttonTextOverrides, + const QString &msg) +{ + return exec(parent, + QMessageBox::Information, + title, + text, + decider, + buttons, + defaultButton, + QMessageBox::NoButton, + buttonTextOverrides, + msg.isEmpty() ? msgDoNotShowAgain() : msg); } /*! - Shows a message box with given \a title and \a text, and a \gui {Do not ask again} check box. - If the user checks the check box and accepts the dialog with the \a acceptButton, - further invocations of this function with the same \a settings and \a settingsSubKey will not - show the dialog, but instantly return \a acceptButton. - - Returns the clicked button, or QDialogButtonBox::NoButton if the user rejects the dialog - with the escape key, or \a acceptButton if the dialog is suppressed. -*/ -QDialogButtonBox::StandardButton -CheckableMessageBox::doNotAskAgainQuestion(QWidget *parent, const QString &title, - const QString &text, QSettings *settings, - const QString &settingsSubKey, - QDialogButtonBox::StandardButtons buttons, - QDialogButtonBox::StandardButton defaultButton, - QDialogButtonBox::StandardButton acceptButton) - -{ - if (!shouldAskAgain(settings, settingsSubKey)) - return acceptButton; - - CheckableMessageBox messageBox(parent); - initDoNotAskAgainMessageBox(messageBox, title, text, buttons, defaultButton, Question); - messageBox.exec(); - if (messageBox.isChecked() && (messageBox.clickedStandardButton() == acceptButton)) - doNotAskAgain(settings, settingsSubKey); - - return messageBox.clickedStandardButton(); -} - -/*! - Shows a message box with given \a title and \a text, and a \gui {Do not show again} check box. - If the user checks the check box and quits the dialog, further invocations of this - function with the same \a settings and \a settingsSubKey will not show the dialog, but instantly return. - - Returns the clicked button, or QDialogButtonBox::NoButton if the user rejects the dialog - with the escape key, or \a defaultButton if the dialog is suppressed. -*/ -QDialogButtonBox::StandardButton -CheckableMessageBox::doNotShowAgainInformation(QWidget *parent, const QString &title, - const QString &text, QSettings *settings, - const QString &settingsSubKey, - QDialogButtonBox::StandardButtons buttons, - QDialogButtonBox::StandardButton defaultButton) - -{ - if (!shouldAskAgain(settings, settingsSubKey)) - return defaultButton; - - CheckableMessageBox messageBox(parent); - initDoNotAskAgainMessageBox(messageBox, title, text, buttons, defaultButton, Information); - messageBox.exec(); - if (messageBox.isChecked()) - doNotAskAgain(settings, settingsSubKey); - - return messageBox.clickedStandardButton(); -} - -/*! - Resets all suppression settings for doNotAskAgainQuestion() found in \a settings, + Resets all suppression settings for doNotAskAgainQuestion() so all these message boxes are shown again. */ -void CheckableMessageBox::resetAllDoNotAskAgainQuestions(QSettings *settings) +void CheckableMessageBox::resetAllDoNotAskAgainQuestions() { - QTC_ASSERT(settings, return); - settings->beginGroup(QLatin1String(kDoNotAskAgainKey)); - settings->remove(QString()); - settings->endGroup(); + QTC_ASSERT(theSettings, return); + theSettings->beginGroup(QLatin1String(kDoNotAskAgainKey)); + theSettings->remove(QString()); + theSettings->endGroup(); } /*! Returns whether any message boxes from doNotAskAgainQuestion() are suppressed - in the \a settings. + in the settings. */ -bool CheckableMessageBox::hasSuppressedQuestions(QSettings *settings) +bool CheckableMessageBox::hasSuppressedQuestions() { - QTC_ASSERT(settings, return false); - bool hasSuppressed = false; - settings->beginGroup(QLatin1String(kDoNotAskAgainKey)); - const QStringList childKeys = settings->childKeys(); - for (const QString &subKey : childKeys) { - if (settings->value(subKey, false).toBool()) { - hasSuppressed = true; - break; - } - } - settings->endGroup(); + QTC_ASSERT(theSettings, return false); + theSettings->beginGroup(QLatin1String(kDoNotAskAgainKey)); + const bool hasSuppressed = !theSettings->childKeys().isEmpty() + || !theSettings->childGroups().isEmpty(); + theSettings->endGroup(); return hasSuppressed; } /*! - Returns the standard \gui {Do not ask again} check box text. - \sa doNotAskAgainQuestion() + Returns the standard \uicontrol {Do not ask again} check box text. */ QString CheckableMessageBox::msgDoNotAskAgain() { @@ -466,12 +188,16 @@ QString CheckableMessageBox::msgDoNotAskAgain() } /*! - Returns the standard \gui {Do not show again} check box text. - \sa doNotShowAgainInformation() + Returns the standard \uicontrol {Do not show again} check box text. */ QString CheckableMessageBox::msgDoNotShowAgain() { return Tr::tr("Do not &show again"); } +void CheckableMessageBox::initialize(QSettings *settings) +{ + theSettings = settings; +} + } // namespace Utils |