1 /*  KStars Asynchronous Message Box Implementation for Desktop/Android and EkosLive
2     Based on QMessageBox.
3 
4     SPDX-FileCopyrightText: 2019 Jasem Mutlaq <mutlaqja@ikarustech.com>
5 
6     SPDX-License-Identifier: GPL-2.0-or-later
7 */
8 
9 #include "ksmessagebox.h"
10 #include "config-kstars.h"
11 #include "Options.h"
12 
13 #ifdef KSTARS_LITE
14 #include "kstarslite.h"
15 #else
16 #include "kstars.h"
17 #endif
18 
19 #include <QPushButton>
20 #include <QCheckBox>
21 #include <QLayout>
22 #include <QSpacerItem>
23 #include <QLabel>
24 
25 KSMessageBox * KSMessageBox::m_Instance = nullptr;
26 
Instance()27 KSMessageBox * KSMessageBox::Instance()
28 {
29     if (!m_Instance)
30         m_Instance = new KSMessageBox();
31 
32     return m_Instance;
33 }
34 
KSMessageBox()35 KSMessageBox::KSMessageBox() : QMessageBox()
36 {
37     setDefaultButton(StandardButton::Ok);
38     setIcon(QMessageBox::Information);
39     setObjectName("KSMessageBox");
40 
41     setWindowFlags(Qt::WindowStaysOnTopHint);
42 
43     m_ProgressTimer.setInterval(1000);
44     m_ProgressTimer.setSingleShot(false);
45     connect(&m_ProgressTimer, &QTimer::timeout, this, &KSMessageBox::timerTick);
46 
47     connect(this, &KSMessageBox::rejected, [this]()
48     {
49         m_ProgressTimer.stop();
50         emit newMessage(QJsonObject());
51     });
52 
53     connect(this, &KSMessageBox::accepted, [this]()
54     {
55         m_ProgressTimer.stop();
56         emit newMessage(QJsonObject());
57     });
58 }
59 
error(const QString & message,const QString & title,quint32 timeout)60 void KSMessageBox::error(const QString &message, const QString &title, quint32 timeout)
61 {
62 #ifdef KSTARS_LITE
63     Q_UNUSED(title);
64     KStarsLite::Instance()->notificationMessage(message);
65 #else
66     //KMessageBox::error(nullptr, message, title);
67     reset();
68 
69     addOKButton();
70     setIcon(QMessageBox::Critical);
71     setText(message);
72     setWindowTitle(title);
73 
74     setupTimeout(timeout);
75     open();
76 
77     emit newMessage(createMessageObject());
78 #endif
79 }
80 
sorry(const QString & message,const QString & title,quint32 timeout)81 void KSMessageBox::sorry(const QString &message, const QString &title, quint32 timeout)
82 {
83 #ifdef KSTARS_LITE
84     Q_UNUSED(title);
85     KStarsLite::Instance()->notificationMessage(message);
86 #else
87     //KSNotification::sorry(message, title);
88     reset();
89 
90     addOKButton();
91     setIcon(QMessageBox::Warning);
92     setText(message);
93     setWindowTitle(title);
94 
95     setupTimeout(timeout);
96     open();
97 
98     emit newMessage(createMessageObject());
99 #endif
100 }
101 
info(const QString & message,const QString & title,quint32 timeout)102 void KSMessageBox::info(const QString &message, const QString &title, quint32 timeout)
103 {
104 #ifdef KSTARS_LITE
105     Q_UNUSED(title);
106     KStarsLite::Instance()->notificationMessage(message);
107 #else
108     //KMessageBox::information(nullptr, message, title);
109     reset();
110 
111     addOKButton();
112     setIcon(QMessageBox::Information);
113     setText(message);
114     setWindowTitle(title);
115 
116     setupTimeout(timeout);
117     open();
118 
119     emit newMessage(createMessageObject());
120 
121 
122 #endif
123 }
124 
setupTimeout(quint32 timeout)125 void KSMessageBox::setupTimeout(quint32 timeout)
126 {
127     m_Timeout = timeout;
128     if (m_Timeout == 0)
129         return;
130 
131     m_ProgressLabel = new QLabel(this);
132     m_ProgressLabel->setText(i18n("Auto close in ..."));
133 
134     m_ProgressIndicator = new QRoundProgressBar(this);
135     m_ProgressIndicator->setDecimals(0);
136     m_ProgressIndicator->setFormat("%v");
137     m_ProgressIndicator->setBarStyle(QRoundProgressBar::StyleLine);
138     m_ProgressIndicator->setFixedSize(QSize(32, 32));
139 
140     m_ProgressLayout = new QHBoxLayout();
141     m_ProgressLayout->setObjectName("ProgressLayout");
142     m_ProgressLayout->addWidget(m_ProgressLabel);
143     m_ProgressLayout->addWidget(m_ProgressIndicator);
144 
145     QGridLayout *gridLayout = qobject_cast<QGridLayout*>(layout());
146     if (gridLayout)
147     {
148         //gridLayout->addWidget(m_ProgressLabel.get(), 1, 2, 1, 1, Qt::AlignHCenter | Qt::AlignVCenter);
149         //gridLayout->addWidget(m_ProgressIndicator.get(), 1, 2, 1, 1, Qt::AlignHCenter | Qt::AlignVCenter);
150         gridLayout->addItem(m_ProgressLayout, 1, 2, 1, 2, Qt::AlignHCenter | Qt::AlignVCenter);
151     }
152 
153     m_ProgressTimer.start();
154     m_ProgressIndicator->setRange(0, m_Timeout);
155     m_ProgressIndicator->setValue(static_cast<int>(m_Timeout));
156 }
157 
reset()158 void KSMessageBox::reset()
159 {
160     m_ProgressTimer.stop();
161     resetTimeout();
162 
163     QList<QPushButton *> allButtons = findChildren<QPushButton*>();
164     qDeleteAll(allButtons);
165 
166     delete (m_ProgressIndicator);
167     delete (m_ProgressLabel);
168     if (m_ProgressLayout)
169     {
170         layout()->removeItem(m_ProgressLayout);
171         delete (m_ProgressLayout);
172     }
173 
174 }
175 
questionYesNo(const QString & message,const QString & title,quint32 timeout,bool defaultToYes,const QString & yesText,const QString & noText)176 void KSMessageBox::questionYesNo(const QString &message, const QString &title, quint32 timeout, bool defaultToYes,
177                                  const QString &yesText, const QString &noText)
178 {
179     reset();
180 
181     setIcon(QMessageBox::Question);
182     setText(message);
183     setWindowTitle(title);
184 
185     QPushButton *yesButton = new QPushButton(yesText, this);
186     QPushButton *noButton = new QPushButton(noText, this);
187 
188     connect(yesButton, &QPushButton::clicked, this, &KSMessageBox::accepted);
189     connect(noButton, &QPushButton::clicked, this, &KSMessageBox::rejected);
190 
191     addButton(yesButton, QMessageBox::ActionRole);
192     addButton(noButton, QMessageBox::ActionRole);
193 
194 
195     setDefaultButton(defaultToYes ? yesButton : noButton);
196     yesButton->setDefault(defaultToYes);
197     noButton->setDefault(!defaultToYes);
198 
199     setupTimeout(timeout);
200 
201     open();
202 
203     emit newMessage(createMessageObject());
204 }
205 
warningContinueCancel(const QString & message,const QString & title,quint32 timeout,bool defaultToContinue,const QString & continueText,const QString & cancelText)206 void KSMessageBox::warningContinueCancel(const QString &message, const QString &title, quint32 timeout,
207         bool defaultToContinue,
208         const QString &continueText, const QString &cancelText)
209 {
210     reset();
211 
212     setIcon(QMessageBox::Warning);
213     setText(message);
214     setWindowTitle(title);
215 
216     QPushButton *continueButton = new QPushButton(continueText, this);
217     QPushButton *cancelButton = new QPushButton(cancelText, this);
218 
219     connect(continueButton, &QPushButton::clicked, this, &KSMessageBox::accepted);
220     connect(cancelButton, &QPushButton::clicked, this, &KSMessageBox::rejected);
221 
222     addButton(continueButton, QMessageBox::ActionRole);
223     addButton(cancelButton, QMessageBox::ActionRole);
224 
225     setDefaultButton(defaultToContinue ? continueButton : cancelButton);
226     continueButton->setDefault(defaultToContinue);
227     cancelButton->setDefault(!defaultToContinue);
228 
229     setupTimeout(timeout);
230 
231     open();
232 
233     emit newMessage(createMessageObject());
234 }
235 
transient(const QString & message,const QString & title)236 void KSMessageBox::transient(const QString &message, const QString &title)
237 {
238 #ifdef KSTARS_LITE
239     Q_UNUSED(title);
240     KStarsLite::Instance()->notificationMessage(message);
241 #else
242     QPointer<QMessageBox> msgBox = new QMessageBox();
243     msgBox->setAttribute(Qt::WA_DeleteOnClose);
244     msgBox->setWindowTitle(title);
245     msgBox->setText(message);
246     msgBox->setModal(false);
247     msgBox->setIcon(QMessageBox::Warning);
248     msgBox->show();
249 #endif
250 }
251 
addOKButton()252 void KSMessageBox::addOKButton()
253 {
254     QPushButton *okButton = new QPushButton(i18n("Ok"), this);
255     connect(okButton, &QPushButton::clicked, this, &KSMessageBox::accepted);
256     addButton(okButton, QMessageBox::AcceptRole);
257     setDefaultButton(okButton);
258 }
259 
timerTick()260 void KSMessageBox::timerTick()
261 {
262     m_Timeout--;
263     m_ProgressIndicator->setValue(static_cast<int>(m_Timeout));
264 
265     if (m_Timeout == 0)
266     {
267         m_ProgressTimer.stop();
268         if (defaultButton())
269             defaultButton()->animateClick();
270         else
271             reject();
272     }
273 }
274 
createMessageObject()275 QJsonObject KSMessageBox::createMessageObject()
276 {
277     QJsonObject message;
278     QJsonArray buttons;
279 
280     message.insert("title", windowTitle());
281     message.insert("message", text());
282     message.insert("icon", icon());
283     message.insert("timeout", static_cast<int32_t>(m_Timeout));
284 
285     for (const auto oneButton : findChildren<QPushButton*>())
286     {
287         buttons.append(oneButton->text());
288         if (oneButton == defaultButton())
289             message.insert("default", oneButton->text());
290     }
291 
292     message.insert("buttons", buttons);
293 
294     return message;
295 }
296 
selectResponse(const QString & button)297 bool KSMessageBox::selectResponse(const QString &button)
298 {
299     for (const auto oneButton : findChildren<QPushButton*>())
300     {
301         const QString buttonText = oneButton->text().remove("&");
302 
303         if (button == buttonText)
304         {
305             oneButton->animateClick();
306             return true;
307         }
308     }
309 
310     return false;
311 }
312