// Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "checkablemessagebox.h" #include "guiutils.h" #include "hostosinfo.h" #include "qtcassert.h" #include "qtcsettings.h" #include "utilstr.h" #include #include #include #include #include #include #include #include /*! \class Utils::CheckableMessageBox \inmodule QtCreator \brief The CheckableMessageBox class implements a message box suitable for 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. */ static const char kDoNotAskAgainKey[] = "DoNotAskAgain"; namespace Utils { static QtcSettings *theSettings; static void prepare(QMessageBox::Icon icon, const QString &title, const QString &text, const CheckableDecider &decider, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton, QMap buttonTextOverrides, const QString &msg, QMessageBox &msgBox) { msgBox.setWindowTitle(title); msgBox.setIcon(icon); msgBox.setText(text); msgBox.setTextFormat(Qt::RichText); msgBox.setTextInteractionFlags(Qt::LinksAccessibleByKeyboard | Qt::LinksAccessibleByMouse); #if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0) 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("setChecked(false); msgBox.checkBox()->setText(msg); } msgBox.setStandardButtons(buttons); msgBox.setDefaultButton(defaultButton); for (auto it = buttonTextOverrides.constBegin(); it != buttonTextOverrides.constEnd(); ++it) msgBox.button(it.key())->setText(it.value()); } static QMessageBox::StandardButton exec( QMessageBox::Icon icon, const QString &title, const QString &text, const CheckableDecider &decider, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton, QMessageBox::StandardButton acceptButton, QMap buttonTextOverrides, const QString &msg) { if (decider.shouldAskAgain) { if (!decider.shouldAskAgain()) return acceptButton; } QMessageBox msgBox(dialogParent()); prepare(icon, title, text, decider, buttons, defaultButton, buttonTextOverrides, msg, msgBox); msgBox.exec(); QMessageBox::StandardButton clickedBtn = msgBox.standardButton(msgBox.clickedButton()); if (decider.doNotAskAgain && msgBox.checkBox()->isChecked() && (acceptButton == QMessageBox::NoButton || clickedBtn == acceptButton)) decider.doNotAskAgain(); return clickedBtn; } static void show(QMessageBox::Icon icon, const QString &title, const QString &text, CheckableDecider decider, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton, QMessageBox::StandardButton acceptButton, QMap buttonTextOverrides, const QString &msg, QObject *guard, const std::function &callback) { if (decider.shouldAskAgain && !decider.shouldAskAgain()) { if (callback) { QMetaObject::invokeMethod( guard ? guard : qApp, [callback, acceptButton] { callback(acceptButton); }, Qt::QueuedConnection); } return; } QMessageBox *msgBox = new QMessageBox(dialogParent()); prepare(icon, title, text, decider, buttons, defaultButton, buttonTextOverrides, msg, *msgBox); std::optional> guardPtr; if (guard) guardPtr = guard; QObject::connect(msgBox, &QMessageBox::finished, [guardPtr, msgBox, callback, decider, acceptButton] { QMessageBox::StandardButton clickedBtn = msgBox->standardButton( msgBox->clickedButton()); if (decider.doNotAskAgain && msgBox->checkBox()->isChecked() && (acceptButton == QMessageBox::NoButton || clickedBtn == acceptButton)) { decider.doNotAskAgain(); } if (callback && (!guardPtr || *guardPtr)) callback(clickedBtn); msgBox->deleteLater(); }); msgBox->show(); } CheckableDecider::CheckableDecider(const Key &settingsSubKey) { QTC_ASSERT(theSettings, return); shouldAskAgain = [settingsSubKey] { theSettings->beginGroup(kDoNotAskAgainKey); bool shouldNotAsk = theSettings->value(settingsSubKey, false).toBool(); theSettings->endGroup(); return !shouldNotAsk; }; doNotAskAgain = [settingsSubKey] { theSettings->beginGroup(kDoNotAskAgainKey); theSettings->setValue(settingsSubKey, true); theSettings->endGroup(); }; } CheckableDecider::CheckableDecider(bool *storage) { shouldAskAgain = [storage] { return !*storage; }; doNotAskAgain = [storage] { *storage = true; }; } QMessageBox::StandardButton CheckableMessageBox::question( const QString &title, const QString &question, const CheckableDecider &decider, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton, QMessageBox::StandardButton acceptButton, QMap buttonTextOverrides, const QString &msg) { return exec(QMessageBox::Question, title, question, decider, buttons, defaultButton, acceptButton, buttonTextOverrides, msg.isEmpty() ? msgDoNotAskAgain() : msg); } void CheckableMessageBox::question_async( const QString &title, const QString &question, const CheckableDecider &decider, QObject *guard, std::function callback, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton, QMessageBox::StandardButton acceptButton, QMap buttonTextOverrides, const QString &msg) { show(QMessageBox::Question, title, question, decider, buttons, defaultButton, acceptButton, buttonTextOverrides, msg.isEmpty() ? msgDoNotAskAgain() : msg, guard, callback); } QMessageBox::StandardButton CheckableMessageBox::information( const QString &title, const QString &text, const CheckableDecider &decider, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton, QMap buttonTextOverrides, const QString &msg) { return exec(QMessageBox::Information, title, text, decider, buttons, defaultButton, defaultButton, buttonTextOverrides, msg.isEmpty() ? msgDoNotShowAgain() : msg); } void CheckableMessageBox::information_async( const QString &title, const QString &text, const CheckableDecider &decider, QObject *guard, std::function callback, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton, QMap buttonTextOverrides, const QString &msg) { show(QMessageBox::Information, title, text, decider, buttons, defaultButton, defaultButton, buttonTextOverrides, msg.isEmpty() ? msgDoNotShowAgain() : msg, guard, callback); } /*! Resets all suppression settings for doNotAskAgainQuestion() so all these message boxes are shown again. */ void CheckableMessageBox::resetAllDoNotAskAgainQuestions() { QTC_ASSERT(theSettings, return); theSettings->beginGroup(kDoNotAskAgainKey); theSettings->remove(Key()); theSettings->endGroup(); } /*! Returns whether any message boxes from doNotAskAgainQuestion() are suppressed in the settings. */ bool CheckableMessageBox::hasSuppressedQuestions() { QTC_ASSERT(theSettings, return false); theSettings->beginGroup(kDoNotAskAgainKey); const bool hasSuppressed = !theSettings->childKeys().isEmpty() || !theSettings->childGroups().isEmpty(); theSettings->endGroup(); return hasSuppressed; } /*! Returns the standard \uicontrol {Do not ask again} check box text. */ QString CheckableMessageBox::msgDoNotAskAgain() { return Tr::tr("Do not &ask again"); } /*! Returns the standard \uicontrol {Do not show again} check box text. */ QString CheckableMessageBox::msgDoNotShowAgain() { return Tr::tr("Do not &show again"); } void CheckableMessageBox::initialize(QtcSettings *settings) { theSettings = settings; } } // namespace Utils