| 1 | /* |
| 2 | This file is part of the KDE libraries |
| 3 | SPDX-FileCopyrightText: 2020 Ahmad Samir <a.samirh78@gmail.com> |
| 4 | |
| 5 | SPDX-License-Identifier: LGPL-2.0-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL |
| 6 | */ |
| 7 | |
| 8 | #ifndef KMESSAGEDIALOG_H |
| 9 | #define KMESSAGEDIALOG_H |
| 10 | |
| 11 | #include <kwidgetsaddons_export.h> |
| 12 | |
| 13 | #include <KStandardGuiItem> |
| 14 | |
| 15 | #include <QDialog> |
| 16 | #include <QMessageBox> |
| 17 | |
| 18 | #include <memory> |
| 19 | |
| 20 | class KMessageDialogPrivate; |
| 21 | class KGuiItem; |
| 22 | class QWidget; |
| 23 | |
| 24 | /*! |
| 25 | * \class KMessageDialog |
| 26 | * \inmodule KWidgetsAddons |
| 27 | * |
| 28 | * \brief KMessageDialog creates a message box similar to the ones you get from KMessageBox, |
| 29 | * but that can be used asynchronously, i.e. you can show the dialog by using show() |
| 30 | * or open(). |
| 31 | * |
| 32 | * This class contructs a dialog similar to the dialogs the KMessageBox convenience functions |
| 33 | * create. The main difference is that the KMessageBox methods typically use exec() to show |
| 34 | * the dialogs; one of the main disadvantages of using exec(), is that it starts a nested |
| 35 | * eventloop, which could lead to nasty crashes. |
| 36 | * |
| 37 | * Another difference is that the API is supposed to be slightly easier to use as it has |
| 38 | * various methods to set up the dialog, e.g. setCaption(), setDetails() ...etc. |
| 39 | * |
| 40 | * By default, appropriate buttons based on the dialog type are added (since 5.85) (e.g. an |
| 41 | * "OK" button is added to an Information dialog), you can set custom buttons by using the |
| 42 | * setButtons() method. |
| 43 | * |
| 44 | * The QDialog::done() slot is called to set the result of the dialog, which will emit the |
| 45 | * QDialog::finished() signal with that result. The result is one of the |
| 46 | * KMessageDialog::ButtonType enum. This is useful as you can tell exactly which button |
| 47 | * was clicked by the user. E.g.: |
| 48 | * \list |
| 49 | * \li the secondary action button having been clicked, in which case you may still want to save the status |
| 50 | * of the "Do not ask again" CheckBox |
| 51 | * \li the "Cancel" button having been clicked, in which case you ideally will ignore the status |
| 52 | * of the "Do not ask again" CheckBox |
| 53 | * \endlist |
| 54 | * |
| 55 | * For "warning" dialogs, i.e. dialogs with a potentially destructive action, the default |
| 56 | * button is set to a button with the QDialogButtonBox::RejectRole. If the "Cancel" button |
| 57 | * is used, it will be the default, otherwise the secondary action button. |
| 58 | * |
| 59 | * This class intends to be very flexible with the buttons that can be used, since you can |
| 60 | * call the setButtons() method with a KGuiItem that has custom text/icon. |
| 61 | * |
| 62 | * Since Frameworks 5.97 a notification sound is played when the dialog opens like KMessageBox |
| 63 | * does, this can be controlled using the setNotifyEnabled() method. |
| 64 | * |
| 65 | * Example: |
| 66 | * \code |
| 67 | * auto *dlg = new KMessageDialog(KMessageDialog::QuestionTwoActionsCancel, |
| 68 | * QStringLiteral("Back or forward?"), |
| 69 | * nullptr); |
| 70 | * |
| 71 | * dlg->setCaption(QStringLiteral("Window Title")); |
| 72 | * dlg->setButtons(KStandardGuiItem::back(), KStandardGuiItem::forward(), KStandardGuiItem::cancel()); |
| 73 | * dlg->setListWidgetItems(QStringList{QStringLiteral("file1"), QStringLiteral("file2")}); |
| 74 | * dlg->setDetails(QStringLiteral("Some more details.")); |
| 75 | * dlg->setDontAskAgainText(QStringLiteral("Do not ask again")); |
| 76 | * dlg->setDontAskAgainChecked(false); |
| 77 | * |
| 78 | * // Delete the dialog when it's closed |
| 79 | * dlg->setAttribute(Qt::WA_DeleteOnClose); |
| 80 | * // Make the dialog window modal |
| 81 | * dlg->setWindowModality(Qt::WindowModal); |
| 82 | * |
| 83 | * QObject::connect(dlg, &QDialog::finished, &app, [dlg](int result) { |
| 84 | * auto button = static_cast<KMessageDialog::ButtonType>(result); |
| 85 | * switch(button) { |
| 86 | * case KMessageDialog::PrimaryAction: |
| 87 | * // The user clicked the primary action, handle the result... |
| 88 | * // save the "do not ask again" box status... |
| 89 | * break; |
| 90 | * case KMessageDialog::SecondaryAction: |
| 91 | * // The user clicked the secondary action, handle the result... |
| 92 | * // save the "do not ask again" box status... |
| 93 | * break; |
| 94 | * case KMessageDialog::Cancel: |
| 95 | * // The user clicked cancel, reject the changes... |
| 96 | * break; |
| 97 | * default: |
| 98 | * break; |
| 99 | * } |
| 100 | * }); |
| 101 | * |
| 102 | * dlg->show(); |
| 103 | * \endcode |
| 104 | * |
| 105 | * \since 5.77 |
| 106 | */ |
| 107 | class KWIDGETSADDONS_EXPORT KMessageDialog : public QDialog |
| 108 | { |
| 109 | Q_OBJECT |
| 110 | |
| 111 | public: |
| 112 | /*! |
| 113 | * Button types |
| 114 | * \since 5.100 |
| 115 | * \value Ok Ok button |
| 116 | * \value Cancel Cancel button |
| 117 | * \value PrimaryAction Primary action button |
| 118 | * \value SecondaryAction Secondary action button |
| 119 | */ |
| 120 | enum ButtonType { |
| 121 | Ok = 1, |
| 122 | Cancel = 2, |
| 123 | PrimaryAction = 3, |
| 124 | SecondaryAction = 4, |
| 125 | }; |
| 126 | |
| 127 | /*! |
| 128 | * \value[since 5.100] QuestionTwoActions Question dialog with two buttons |
| 129 | * \value[since 5.100] QuestionTwoActionsCancel Question dialog with two buttons and Cancel |
| 130 | * \value[since 5.100] WarningTwoActions Warning dialog with two buttons |
| 131 | * \value[since 5.100] WarningTwoActionsCancel Warning dialog with two buttons and Cancel |
| 132 | * \value WarningContinueCancel Warning dialog with Continue and Cancel |
| 133 | * \value Information Information dialog |
| 134 | * \value Error Error dialog |
| 135 | */ |
| 136 | enum Type { |
| 137 | QuestionTwoActions = 1, |
| 138 | QuestionTwoActionsCancel = 2, |
| 139 | WarningTwoActions = 3, |
| 140 | WarningTwoActionsCancel = 4, |
| 141 | WarningContinueCancel = 5, |
| 142 | Information = 6, |
| 143 | Error = 8, |
| 144 | }; |
| 145 | |
| 146 | /*! |
| 147 | * Constructs a KMessageDialog. |
| 148 | * |
| 149 | * Buttons based on the dialog type are set by default in some cases, |
| 150 | * using KStandardGuiItem instances. For the dialog types Information and Error |
| 151 | * the button is set to KStandardGuiItem::ok(). For the type WarningContinueCancel |
| 152 | * the buttons are set to KStandardGuiItem::cont() & KStandardGuiItem::cancel(). |
| 153 | * |
| 154 | * For the other Quesion* and Warning* types the buttons are to be set explicitly. |
| 155 | * |
| 156 | * \a type the dialog Type, one of KMessageDialog::Type enum |
| 157 | * |
| 158 | * \a text the text message that is going to be displayed in the dialog |
| 159 | * |
| 160 | * \a parent a QWidget* that will be set as the dialog parent |
| 161 | */ |
| 162 | explicit KMessageDialog(KMessageDialog::Type type, const QString &text, QWidget *parent = nullptr); |
| 163 | |
| 164 | /*! |
| 165 | * This constructor takes the window Id of the parent window, instead of a QWidget*. |
| 166 | * |
| 167 | * \a type the dialog Type, one of KMessageDialog::Type enum |
| 168 | * |
| 169 | * \a text the text message that is going to be displayed in the dialog |
| 170 | * |
| 171 | * \a parent_id the native parent's window system identifier |
| 172 | */ |
| 173 | explicit KMessageDialog(KMessageDialog::Type type, const QString &text, WId parent_id); |
| 174 | /*! |
| 175 | * Destructor |
| 176 | */ |
| 177 | ~KMessageDialog() override; |
| 178 | |
| 179 | /*! |
| 180 | * This can be used to set the title of the dialog window. If you pass an |
| 181 | * empty QString(), a generic title will be used depending on the dialog |
| 182 | * Type. E.g. for KMessageDialog::WarningTwoActions, "Warning" will be used. |
| 183 | */ |
| 184 | void setCaption(const QString &caption); |
| 185 | |
| 186 | /*! |
| 187 | * This can be used to set an icon that will be shown next to the main |
| 188 | * text message. If you pass a null QIcon() a generic icon based on the dialog |
| 189 | * Type will be used. E.g. for KMessageDialog::QuestionTwoActions, QMessageBox::Question |
| 190 | * will be used. |
| 191 | */ |
| 192 | void setIcon(const QIcon &icon); |
| 193 | |
| 194 | /*! |
| 195 | * This will add a QListWidget to the dialog and populate it with \a strlist. |
| 196 | * If \a strlist is empty, the list widget will not be shown. |
| 197 | */ |
| 198 | void setListWidgetItems(const QStringList &strlist); |
| 199 | |
| 200 | /*! |
| 201 | * This will add a KCollapsibleGroupBox with a title "Details", as the class name |
| 202 | * implies it is collapsible (and collapsed by default); you can use it to add a |
| 203 | * more detailed explanation of what the dialog is trying to tell the user. |
| 204 | * |
| 205 | * If \a details is empty, the details widget will not be shown. |
| 206 | */ |
| 207 | void setDetails(const QString &details); |
| 208 | |
| 209 | /*! |
| 210 | * This will add a "Do not ask again" checkbox to the dialog with the text |
| 211 | * from \a dontAskAgainText. You can set the initial status of the checkbox |
| 212 | * by using setDontAskAgainChecked(). |
| 213 | * |
| 214 | * If \a dontAskAgainText is empty, no checkbox will be shown. |
| 215 | * |
| 216 | * Typical usage of this checkbox is for recurring questions, e.g. showing |
| 217 | * a dialog to confirm moving files/directories to trash, the user can then |
| 218 | * set the checkbox so as not to be asked about that again. |
| 219 | * |
| 220 | * You can get the state of the checkbox by using isDontAskAgainChecked(). |
| 221 | */ |
| 222 | void setDontAskAgainText(const QString &dontAskAgainText); |
| 223 | |
| 224 | /*! |
| 225 | * This can be used to set the initial status of the "Do not ask again" checkbox, |
| 226 | * checked or unchecked, by setting \a isChecked to \c true or \c false |
| 227 | * respectively. |
| 228 | * |
| 229 | * You need to call setDontAskAgainText() first to actually show a checkbox in |
| 230 | * the dialog, otherwise calling this function will have no effect. |
| 231 | */ |
| 232 | void setDontAskAgainChecked(bool isChecked); |
| 233 | |
| 234 | /*! |
| 235 | * This can be used to query the status of the "Do not ask again" checkbox; |
| 236 | * returns \c true if the box is checked and \c false otherwise. |
| 237 | * |
| 238 | * \note This method will return \c false if a checkbox widget isn't shown in |
| 239 | * the dialog. The dialog will not show a checkbox if setDontAskAgainText() was |
| 240 | * not used previously to add a checkbox to begin with. |
| 241 | */ |
| 242 | bool isDontAskAgainChecked() const; |
| 243 | |
| 244 | /*! |
| 245 | * Sets the text labels in the dialog to either allow opening external links or not. |
| 246 | */ |
| 247 | void setOpenExternalLinks(bool isAllowed); |
| 248 | |
| 249 | /*! |
| 250 | * Whether a KNotification is emitted when the dialog is shown. |
| 251 | * |
| 252 | * This typically plays a notification sound. Default is true. |
| 253 | * |
| 254 | * \since 5.97 |
| 255 | * \sa KMessageBox::Notify |
| 256 | */ |
| 257 | bool isNotifyEnabled() const; |
| 258 | |
| 259 | /*! |
| 260 | * Whether to emit a KNotification when the dialog is shown. |
| 261 | * |
| 262 | * This typically plays a notification sound. |
| 263 | * |
| 264 | * \since 5.97 |
| 265 | * \sa KMessageBox::Notify |
| 266 | */ |
| 267 | void setNotifyEnabled(bool enable); |
| 268 | |
| 269 | /*! |
| 270 | * Sets the buttons in the buttom box. |
| 271 | * |
| 272 | * Using this method, you can customize the behavior based on your use-case, by |
| 273 | * using a KGuiItem to get a button with custom text and icon. |
| 274 | * |
| 275 | * Since 5.85 buttons based on the dialog type are added by default (see |
| 276 | * KMessageDialog(KMessageDialog::Type, const QString &, QWidget *) for details). |
| 277 | * Before, this method had to be called explicitly to have any buttons added to the dialog. |
| 278 | * |
| 279 | * \note For dialog types Information and Error only one button |
| 280 | * (KStandardGuiItem::ok()) is added to the dialog. |
| 281 | * |
| 282 | * \a primaryAction the action for the primary button. |
| 283 | * Reported in the result for dialog types Information and Error |
| 284 | * as KMessageDialog::Ok enum value, otherwise as KMessageDialog::PrimaryAction. |
| 285 | * |
| 286 | * \a secondaryAction the action for the secondary button. |
| 287 | * Reported in the result as KMessageDialog::SecondaryAction enum value. |
| 288 | * Ignored with all dialog types without a "secondary" action. |
| 289 | * |
| 290 | * \a cancelAction the action for the cancel button. |
| 291 | * Reported in the result as KMessageDialog::Cancel enum value. |
| 292 | * Ignored with all dialog types without a Cancel button. |
| 293 | */ |
| 294 | void setButtons(const KGuiItem &primaryAction = KGuiItem(), const KGuiItem &secondaryAction = KGuiItem(), const KGuiItem &cancelAction = KGuiItem()); |
| 295 | |
| 296 | /*! |
| 297 | * Manually play the notification sound |
| 298 | * |
| 299 | * When implementing your entirely own message box, not using KMessageDialog, |
| 300 | * you can call this function to play the relevant notification sound (if enabled). |
| 301 | * |
| 302 | * \note You don't need to call this when using KMessageDialog, it plays the sound automatically. |
| 303 | * |
| 304 | * \a type The message box type |
| 305 | * |
| 306 | * \a text The message box contents, for accessibility purposes. |
| 307 | * |
| 308 | * \a dialog The dialog that was displayed |
| 309 | * |
| 310 | * \since 6.0 |
| 311 | * \sa setNotifyEnabled |
| 312 | */ |
| 313 | static void beep(KMessageDialog::Type type, const QString &text = QString(), QWidget *dialog = nullptr); |
| 314 | |
| 315 | protected: |
| 316 | void showEvent(QShowEvent *event) override; |
| 317 | |
| 318 | private: |
| 319 | std::unique_ptr<KMessageDialogPrivate> const d; |
| 320 | }; |
| 321 | |
| 322 | #endif // KMESSAGEDIALOG_H |
| 323 | |