1 /* ============================================================
2  *
3  * This file is a part of digiKam project
4  * https://www.digikam.org
5  *
6  * Date        : 2015-01-19
7  * Description : message box notification settings
8  *
9  * Copyright (C) 2015-2021 by Gilles Caulier <caulier dot gilles at gmail dot com>
10  *
11  * This program is free software; you can redistribute it
12  * and/or modify it under the terms of the GNU General
13  * Public License as published by the Free Software Foundation;
14  * either version 2, or (at your option)
15  * any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * ============================================================ */
23 
24 #include "dmessagebox.h"
25 
26 // Qt includes
27 
28 #include <QPointer>
29 #include <QCheckBox>
30 #include <QGroupBox>
31 #include <QLayout>
32 #include <QLabel>
33 #include <QHeaderView>
34 #include <QApplication>
35 #include <QStyle>
36 #include <QUrl>
37 #include <QIcon>
38 #include <QDialog>
39 #include <QPushButton>
40 #include <QDialogButtonBox>
41 
42 // KDE includes
43 
44 #include <ksharedconfig.h>
45 #include <kconfiggroup.h>
46 #include <klocalizedstring.h>
47 
48 namespace Digikam
49 {
50 
readMsgBoxShouldBeShown(const QString & dontShowAgainName)51 bool DMessageBox::readMsgBoxShouldBeShown(const QString& dontShowAgainName)
52 {
53     if (dontShowAgainName.isEmpty())
54     {
55         return false;
56     }
57 
58     KSharedConfigPtr config = KSharedConfig::openConfig();
59     KConfigGroup group      = config->group("Notification Messages");
60     bool value              = group.readEntry(dontShowAgainName, false);
61 
62     return value;
63 }
64 
saveMsgBoxShouldBeShown(const QString & dontShowAgainName,bool value)65 void DMessageBox::saveMsgBoxShouldBeShown(const QString& dontShowAgainName, bool value)
66 {
67     if (dontShowAgainName.isEmpty())
68     {
69         return;
70     }
71 
72     KSharedConfigPtr config = KSharedConfig::openConfig();
73     KConfigGroup group      = config->group("Notification Messages");
74 
75     group.writeEntry(dontShowAgainName, value);
76     config->sync();
77 }
78 
79 // --------------------------------------------------------------------------------------------------------
80 
showInformationList(QMessageBox::Icon icon,QWidget * const parent,const QString & caption,const QString & text,const QStringList & items,const QString & dontShowAgainName)81 void DMessageBox::showInformationList(QMessageBox::Icon icon,
82                                       QWidget* const parent,
83                                       const QString& caption,
84                                       const QString& text,
85                                       const QStringList& items,
86                                       const QString& dontShowAgainName)
87 {
88     showInformationWidget(icon, parent, caption, text, createWidgetList(items), dontShowAgainName);
89 }
90 
showInformationWidget(QMessageBox::Icon icon,QWidget * const parent,const QString & caption,const QString & text,QWidget * const listWidget,const QString & dontShowAgainName)91 void DMessageBox::showInformationWidget(QMessageBox::Icon icon,
92                                         QWidget* const parent,
93                                         const QString& caption,
94                                         const QString& text,
95                                         QWidget* const listWidget,
96                                         const QString& dontShowAgainName)
97 {
98     if (readMsgBoxShouldBeShown(dontShowAgainName))
99     {
100         return;
101     }
102 
103     QDialog* const dialog = new QDialog(parent, Qt::Dialog);
104     dialog->setWindowTitle(caption);
105     dialog->setObjectName(QLatin1String("showInformation"));
106     dialog->setModal(true);
107 
108     QDialogButtonBox* const buttons = new QDialogButtonBox(QDialogButtonBox::Ok, dialog);
109     buttons->button(QDialogButtonBox::Ok)->setDefault(true);
110     buttons->button(QDialogButtonBox::Ok)->setShortcut(Qt::Key_Escape);
111 
112     QObject::connect(buttons->button(QDialogButtonBox::Ok), SIGNAL(clicked()),
113                      dialog, SLOT(accept()));
114 
115     bool  checkboxResult = false;
116 
117     createMessageBox(dialog, buttons, createIcon(icon), text, listWidget,
118                      dontShowAgainName.isEmpty() ? QString() : i18n("Do not show this message again"),
119                      &checkboxResult);
120 
121     saveMsgBoxShouldBeShown(dontShowAgainName, checkboxResult);
122 }
123 
124 // --------------------------------------------------------------------------------------------------------
125 
showContinueCancel(QMessageBox::Icon icon,QWidget * const parent,const QString & caption,const QString & text,const QString & dontAskAgainName)126 int DMessageBox::showContinueCancel(QMessageBox::Icon icon,
127                                     QWidget* const parent,
128                                     const QString& caption,
129                                     const QString& text,
130                                     const QString& dontAskAgainName)
131 {
132     return (showContinueCancelList(icon, parent, caption, text, QStringList(), dontAskAgainName));
133 }
134 
showContinueCancelList(QMessageBox::Icon icon,QWidget * const parent,const QString & caption,const QString & text,const QStringList & items,const QString & dontAskAgainName)135 int DMessageBox::showContinueCancelList(QMessageBox::Icon icon,
136                                         QWidget* const parent,
137                                         const QString& caption,
138                                         const QString& text,
139                                         const QStringList& items,
140                                         const QString& dontAskAgainName)
141 {
142     return (showContinueCancelWidget(icon, parent, caption, text, createWidgetList(items), dontAskAgainName));
143 }
144 
showContinueCancelWidget(QMessageBox::Icon icon,QWidget * const parent,const QString & caption,const QString & text,QWidget * const listWidget,const QString & dontAskAgainName)145 int DMessageBox::showContinueCancelWidget(QMessageBox::Icon icon,
146                                           QWidget* const parent,
147                                           const QString& caption,
148                                           const QString& text,
149                                           QWidget* const listWidget,
150                                           const QString& dontAskAgainName)
151 {
152     if (readMsgBoxShouldBeShown(dontAskAgainName))
153     {
154         return QMessageBox::Yes;
155     }
156 
157     QDialog* const dialog = new QDialog(parent, Qt::Dialog);
158     dialog->setWindowTitle(caption);
159     dialog->setObjectName(QLatin1String("showContinueCancel"));
160     dialog->setModal(true);
161 
162     QDialogButtonBox* const buttons = new QDialogButtonBox(QDialogButtonBox::Yes | QDialogButtonBox::Cancel, dialog);
163     buttons->button(QDialogButtonBox::Yes)->setDefault(true);
164     buttons->button(QDialogButtonBox::Yes)->setText(i18n("Continue"));
165     buttons->button(QDialogButtonBox::Cancel)->setShortcut(Qt::Key_Escape);
166 
167     QObject::connect(buttons->button(QDialogButtonBox::Yes), SIGNAL(clicked()),
168                      dialog, SLOT(accept()));
169 
170     QObject::connect(buttons->button(QDialogButtonBox::Cancel), SIGNAL(clicked()),
171                      dialog, SLOT(reject()));
172 
173     bool checkboxResult = false;
174     const int result    = createMessageBox(dialog, buttons, createIcon(icon), text, listWidget,
175                                            dontAskAgainName.isEmpty() ? QString() : i18n("Do not ask again"),
176                                            &checkboxResult);
177 
178     if (result != QDialog::Accepted)
179     {
180         return QMessageBox::Cancel;
181     }
182 
183     saveMsgBoxShouldBeShown(dontAskAgainName, checkboxResult);
184 
185     return QMessageBox::Yes;
186 }
187 
188 // --------------------------------------------------------------------------------------------------------
189 
showYesNo(QMessageBox::Icon icon,QWidget * const parent,const QString & caption,const QString & text,const QString & dontAskAgainName)190 int DMessageBox::showYesNo(QMessageBox::Icon icon,
191                            QWidget* const parent,
192                            const QString& caption,
193                            const QString& text,
194                            const QString& dontAskAgainName)
195 {
196     return (showYesNoList(icon, parent, caption, text, QStringList(), dontAskAgainName));
197 }
198 
showYesNoList(QMessageBox::Icon icon,QWidget * const parent,const QString & caption,const QString & text,const QStringList & items,const QString & dontAskAgainName)199 int DMessageBox::showYesNoList(QMessageBox::Icon icon,
200                                QWidget* const parent,
201                                const QString& caption,
202                                const QString& text,
203                                const QStringList& items,
204                                const QString& dontAskAgainName)
205 {
206     return (showYesNoWidget(icon, parent, caption, text, createWidgetList(items), dontAskAgainName));
207 }
208 
showYesNoWidget(QMessageBox::Icon icon,QWidget * const parent,const QString & caption,const QString & text,QWidget * const listWidget,const QString & dontAskAgainName)209 int DMessageBox::showYesNoWidget(QMessageBox::Icon icon,
210                                  QWidget* const parent,
211                                  const QString& caption,
212                                  const QString& text,
213                                  QWidget* const listWidget,
214                                  const QString& dontAskAgainName)
215 {
216     if (readMsgBoxShouldBeShown(dontAskAgainName))
217     {
218         return QMessageBox::Yes;
219     }
220 
221     QDialog* const dialog = new QDialog(parent, Qt::Dialog);
222     dialog->setWindowTitle(caption);
223     dialog->setObjectName(QLatin1String("showYesNo"));
224     dialog->setModal(true);
225 
226     QDialogButtonBox* const buttons = new QDialogButtonBox(QDialogButtonBox::Yes | QDialogButtonBox::No, dialog);
227     buttons->button(QDialogButtonBox::No)->setDefault(true);
228     buttons->button(QDialogButtonBox::No)->setShortcut(Qt::Key_Escape);
229 
230     QObject::connect(buttons->button(QDialogButtonBox::Yes), &QAbstractButton::clicked,
231                      dialog, [dialog]() { dialog->done(QDialogButtonBox::Yes); });
232 
233     QObject::connect(buttons->button(QDialogButtonBox::No), &QAbstractButton::clicked,
234                      dialog, [dialog]() { dialog->done(QDialogButtonBox::No); });
235 
236     bool checkboxResult = false;
237     int result          = createMessageBox(dialog, buttons, createIcon(icon), text, listWidget,
238                                            dontAskAgainName.isEmpty() ? QString() : i18n("Do not ask again"),
239                                            &checkboxResult);
240 
241     if      (result == QDialogButtonBox::Yes)
242     {
243         saveMsgBoxShouldBeShown(dontAskAgainName, checkboxResult);
244 
245         return QMessageBox::Yes;
246     }
247     else if (result == QDialogButtonBox::No)
248     {
249         saveMsgBoxShouldBeShown(dontAskAgainName, checkboxResult);
250 
251         return QMessageBox::No;
252     }
253 
254     return QMessageBox::Cancel;
255 }
256 
257 // --------------------------------------------------------------------------------------------------------
258 
createMessageBox(QDialog * const dialog,QDialogButtonBox * const buttons,const QIcon & icon,const QString & text,QWidget * const listWidget,const QString & ask,bool * checkboxReturn)259 int DMessageBox::createMessageBox(QDialog* const dialog,
260                                   QDialogButtonBox* const buttons,
261                                   const QIcon& icon,
262                                   const QString& text,
263                                   QWidget* const listWidget,
264                                   const QString& ask,
265                                   bool* checkboxReturn)
266 {
267     const int spacing = QApplication::style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing);
268 
269     QWidget* const mainWidget     = new QWidget(dialog);
270     QVBoxLayout* const mainLayout = new QVBoxLayout(mainWidget);
271     mainLayout->setContentsMargins(spacing, spacing, spacing, spacing);
272     mainLayout->setSpacing(spacing);
273 
274     QHBoxLayout* const hLayout    = new QHBoxLayout();
275     hLayout->setContentsMargins(QMargins());
276     hLayout->setSpacing(-1); // use default spacing
277     mainLayout->addLayout(hLayout, 5);
278 
279     //--------------------------------------------------------------------------------
280 
281     QLabel* const iconLabel       = new QLabel(mainWidget);
282     QStyleOption option;
283     option.initFrom(mainWidget);
284     iconLabel->setPixmap(icon.pixmap(mainWidget->style()->pixelMetric(QStyle::PM_MessageBoxIconSize, &option, mainWidget)));
285 
286     QVBoxLayout* const iconLayout = new QVBoxLayout();
287     iconLayout->addStretch(1);
288     iconLayout->addWidget(iconLabel);
289     iconLayout->addStretch(5);
290     hLayout->addLayout(iconLayout, 0);
291     hLayout->addSpacing(spacing);
292 
293     //--------------------------------------------------------------------------------
294 
295     QLabel* const messageLabel    = new QLabel(text, mainWidget);
296     messageLabel->setOpenExternalLinks(true);
297     messageLabel->setWordWrap(true);
298     messageLabel->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum);
299     QPalette messagePal(messageLabel->palette());
300     messagePal.setColor(QPalette::Window, Qt::transparent);
301     messageLabel->setPalette(messagePal);
302     hLayout->addWidget(messageLabel, 5);
303 
304     //--------------------------------------------------------------------------------
305 
306     if (listWidget)
307     {
308         listWidget->setParent(mainWidget);
309         mainLayout->addWidget(listWidget, 50);
310     }
311 
312     //--------------------------------------------------------------------------------
313 
314     QPointer<QCheckBox> checkbox = nullptr;
315 
316     if (!ask.isEmpty())
317     {
318         checkbox = new QCheckBox(ask, mainWidget);
319         mainLayout->addWidget(checkbox);
320 
321         if (checkboxReturn)
322         {
323             checkbox->setChecked(*checkboxReturn);
324         }
325     }
326 
327     //--------------------------------------------------------------------------------
328 
329     mainLayout->addWidget(buttons);
330     dialog->setLayout(mainLayout);
331 
332     //--------------------------------------------------------------------------------
333 
334     // We use a QPointer because the dialog may get deleted
335     // during exec() if the parent of the dialog gets deleted.
336     // In that case the QPointer will reset to 0.
337 
338     QPointer<QDialog> guardedDialog = dialog;
339 
340     const int result = guardedDialog->exec();
341 
342     if (checkbox && checkboxReturn)
343     {
344         *checkboxReturn = checkbox->isChecked();
345     }
346 
347     delete guardedDialog;
348 
349     return result;
350 }
351 
createIcon(QMessageBox::Icon icon)352 QIcon DMessageBox::createIcon(QMessageBox::Icon icon)
353 {
354     QIcon tmpIcon;
355 
356     switch (icon)
357     {
358         case QMessageBox::Warning:
359             tmpIcon = qApp->style()->standardIcon(QStyle::SP_MessageBoxWarning, nullptr, qApp->activeWindow());
360             break;
361 
362         case QMessageBox::Critical:
363             tmpIcon = qApp->style()->standardIcon(QStyle::SP_MessageBoxCritical, nullptr, qApp->activeWindow());
364             break;
365 
366         case QMessageBox::Question:
367             tmpIcon = qApp->style()->standardIcon(QStyle::SP_MessageBoxQuestion, nullptr, qApp->activeWindow());
368             break;
369 
370         case QMessageBox::Information:
371         default:
372             tmpIcon = qApp->style()->standardIcon(QStyle::SP_MessageBoxInformation, nullptr, qApp->activeWindow());
373             break;
374     }
375 
376     int iconSize = qApp->style()->pixelMetric(QStyle::PM_MessageBoxIconSize, nullptr, qApp->activeWindow());
377 
378     return tmpIcon.pixmap(iconSize);
379 }
380 
381 
createWidgetList(const QStringList & items)382 QListWidget* DMessageBox::createWidgetList(const QStringList& items)
383 {
384     QListWidget* listWidget = nullptr;
385 
386     if (!items.isEmpty())
387     {
388         listWidget = new QListWidget();
389         listWidget->addItems(items);
390     }
391 
392     return listWidget;
393 }
394 
395 } // namespace Digikam
396