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 QDialogButtonBox;
21 
22 class KMessageDialogPrivate;
23 class KGuiItem;
24 
25 /**
26  * @class KMessageDialog kmessagedialog.h <KMessageDialog>
27  *
28  *
29  * @brief KMessageDialog creates a message box similar to the ones you get from KMessageBox,
30  * but that can be used asynchronously, i.e. you can show the dialog by using @c show()
31  * or @c open().
32  *
33  * This class contructs a dialog similar to the dialogs the KMessageBox convenience functions
34  * create. The main difference is that the KMessageBox methods typically use @c exec() to show
35  * the dialogs; one of the main disadvantages of using @c exec(), is that it starts a nested
36  * eventloop, which could lead to nasty crashes.
37  *
38  * Another difference is that the API is supposed to be slightly easier to use as it has
39  * various methods to set up the dialog, e.g. @ref setCaption(), @ref setDetails() ...etc.
40  *
41  * By default, appropriate buttons based on the dialog type are added (since 5.85) (e.g. an
42  * "OK" button is added to an Information dialog), you can set custom buttons by using the
43  * @ref setButtons() method.
44  *
45  * The @ref QDialog::done() slot is called to set the result of the dialog, which will emit the
46  * @ref QDialog::finished() signal with that result. The result is one of the
47  * QDialogButtonBox::StandardButton enum. This is useful as you can tell exactly which button
48  * was clicked by the user. E.g.:
49  * - the "No" button having been clicked, in which case you may still want to save the status
50  *   of the "Do not ask again" CheckBox
51  * - the "Cancel" button having been clicked, in which case you ideally will ignore the status
52  *   of the "Do not ask again" CheckBox
53  *
54  * For "warning" dialogs, i.e. dialogs with a potentially destructive action, the default
55  * button is set to a button with the QDialogButtonBox::RejectRole, typically either button
56  * "No" or "Cancel" depending on which one is shown in the dialog. If both of them are shown
57  * (as is the case with KMessageDialog::WarningYesNoCancel dialog Type) then the "Cancel"
58  * button will be set as the default one.
59  *
60  * This class intends to be very flexible with the buttons that can be used, since you can
61  * call the @ref setButtons() method with a KGuiItem that has custom text/icon.
62  *
63  * Example:
64  * @code
65  * auto *dlg = new KMessageDialog(KMessageDialog::QuestionYesNoCancel,
66  *                                QStringLiteral("Do you agree?"),
67  *                                nullptr);
68  *
69  * dlg->setCaption(QStringLiteral("Window Title"));
70  * dlg->setButtons(KStandardGuiItem::yes(), KStandardGuiItem::no(), KStandardGuiItem::cancel());
71  * dlg->setListWidgetItems(QStringList{QStringLiteral("file1"), QStringLiteral("file2")});
72  * dlg->setDetails(QStringLiteral("Some more details."));
73  * dlg->setDontAskAgainText(QStringLiteral("Do not ask again"));
74  * dlg->setDontAskAgainChecked(false);
75  *
76  *  // Delete the dialog when it's closed
77  *  dlg->setAttribute(Qt::WA_DeleteOnClose);
78  *  // Make the dialog window modal
79  *  dlg->setWindowModality(Qt::WindowModal);
80  *
81  *  QObject::connect(dlg, &QDialog::finished, &app, [dlg](int result) {
82  *      auto button = static_cast<QDialogButtonBox::StandardButton>(result);
83  *      switch(button) {
84  *      case QDialogButtonBox::Ok:
85  *      case QDialogButtonBox::Yes:
86  *          // The user clicked OK, handle the result...
87  *          // save the "do not ask again" box status...
88  *          break;
89  *      case QDialogButtonBox::No:
90  *          // The user clicked no, reject the changes...
91  *          // save the "do not ask again" box status...
92  *          break;
93  *      case QDialogButtonBox::Cancel:
94  *          // The user clicked cancel, reject the changes...
95  *          break;
96  *      default:
97  *          break;
98  *      }
99  *  });
100  *
101  *  dlg->show();
102  * @endcode
103  *
104  * @since 5.77
105  */
106 
107 class KWIDGETSADDONS_EXPORT KMessageDialog : public QDialog
108 {
109     Q_OBJECT
110 
111 public:
112     enum Type {
113         QuestionYesNo = 1,
114         QuestionYesNoCancel,
115         WarningYesNo,
116         WarningYesNoCancel,
117         WarningContinueCancel,
118         Information,
119         Sorry,
120         Error,
121     };
122 
123     /**
124      * Constructs a KMessageDialog.
125      *
126      * @param type the dialog Type, one of KMessageDialog::Type enum
127      * @param text the text message that is going to be displayed in the dialog
128      * @param parent a QWidget* that will be set as the dialog parent
129      */
130     explicit KMessageDialog(KMessageDialog::Type type, const QString &text, QWidget *parent = nullptr);
131 
132     /**
133      * This constructor takes the window Id of the parent window, instead of a QWidget*.
134      *
135      * @param type the dialog Type, one of KMessageDialog::Type enum
136      * @param text the text message that is going to be displayed in the dialog
137      * @param parent_id the native parent's window system identifier
138      */
139     explicit KMessageDialog(KMessageDialog::Type type, const QString &text, WId parent_id);
140     /**
141      * Destructor
142      */
143     ~KMessageDialog() override;
144 
145     /**
146      * This can be used to set the title of the dialog window. If you pass an
147      * empty QString(), a generic title will be used depending on the dialog
148      * Type. E.g. for KMessageDialog::WarningYesNo, "Warning" will be used.
149      */
150     void setCaption(const QString &caption);
151 
152     /**
153      * This can be used to set an icon that will be shown next to the main
154      * text message. If you pass a null QIcon() a generic icon based on the dialog
155      * Type will be used. E.g. for KMessageDialog::QuestionYesNo, QMessageBox::Question
156      * will be used.
157      */
158     void setIcon(const QIcon &icon);
159 
160     /**
161      * This will add a QListWidget to the dialog and populate it with @p strlist.
162      * If @p strlist is empty, the list widget will not be shown.
163      */
164     void setListWidgetItems(const QStringList &strlist);
165 
166     /**
167      * This will add a KCollapsibleGroupBox with a title "Details", as the class name
168      * implies it is collapsible (and collapsed by default); you can use it to add a
169      * more detailed explanation of what the dialog is trying to tell the user.
170      *
171      * If @p details is empty, the details widget will not be shown.
172      */
173     void setDetails(const QString &details);
174 
175     /**
176      * This will add a "Do not ask again" checkbox to the dialog with the text
177      * from @p dontAskAgainText. You can set the initial status of the checkbox
178      * by using setDontAskAgainChecked().
179      *
180      * If @p dontAskAgainText is empty, no checkbox will be shown.
181      *
182      * Typical usage of this checkbox is for recurring questions, e.g. showing
183      * a dialog to confirm moving files/directories to trash, the user can then
184      * set the checkbox so as not to be asked about that again.
185      *
186      * You can get the state of the checkbox by using isDontAskAgainChecked().
187      */
188     void setDontAskAgainText(const QString &dontAskAgainText);
189 
190     /**
191      * This can be used to set the initial status of the "Do not ask again" checkbox,
192      * checked or unchecked, by setting @p isChecked to @c true or @c false
193      * respectively.
194      *
195      * You need to call setDontAskAgainText() first to actually show a checkbox in
196      * the dialog, otherwise calling this function will have no effect.
197      */
198     void setDontAskAgainChecked(bool isChecked);
199 
200     /**
201      * This can be used to query the status of the "Do not ask again" checkbox;
202      * returns @c true if the box is checked and @c false otherwise.
203      *
204      * @note This method will return @c false if a checkbox widget isn't shown in
205      * the dialog. The dialog will not show a checkbox if setDontAskAgainText() was
206      * not used previously to add a checkbox to begin with.
207      */
208     bool isDontAskAgainChecked() const;
209 
210     /**
211      * Sets the text labels in the dialog to either allow opening external links or not.
212      */
213     void setOpenExternalLinks(bool isAllowed);
214 
215     /**
216      * Since 5.85 buttons based on the dialog type are added by default, e.g. an OK
217      * button to Information and Sorry dialogs; (before 5.85, if you didn't call this
218      * method no buttons were added to the dialog).
219      *
220      * This will add a QDialogButtonBox populated with @p buttonAccept and @p buttonNo,
221      * by default KStandardGuiItem::yes() and KStandardGuiItem::no() respectively. For
222      * dialog Types that have a Cancel button, @p buttonCancel will be used.
223      *
224      * Using this method, you can customize the behavior based on your use-case, by
225      * using a KGuiItem to get a button with custom text and icon.
226      *
227      * @note
228      * - For WarningContinueCancel dialog Type, if buttonAccept has the same text as
229      *   KStandardGuiItem::yes(), KStandardGuiItem::cont() will be used instead
230      * - For dialog Types: Information, Sorry, and Error only one button
231      *   (KStandardGuiItem::ok()) is added to the dialog.
232      */
233     void setButtons(const KGuiItem &buttonAccept = KStandardGuiItem::yes(),
234                     const KGuiItem &buttonNo = KStandardGuiItem::no(),
235                     const KGuiItem &buttonCancel = KStandardGuiItem::cancel());
236 
237 private:
238     std::unique_ptr<KMessageDialogPrivate> const d;
239 };
240 
241 #endif // KMESSAGEDIALOG_H
242