]> git.lyx.org Git - lyx.git/blob - src/frontends/qt/GuiAlert.cpp
Fix readability
[lyx.git] / src / frontends / qt / GuiAlert.cpp
1 /**
2  * \file qt/GuiAlert.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author John Levon
7  * \author Jürgen Spitzmüller
8  * \author Abdelrazak Younes
9  *
10  * Full author contact details are available in file CREDITS.
11  */
12
13 #include <config.h>
14
15 #include "alert.h"
16 #include "InGuiThread.h"
17
18 #include "frontends/Application.h"
19
20 #include "qt_helpers.h"
21 #include "LyX.h" // for lyx::use_gui
22
23 #include "support/gettext.h"
24 #include "support/debug.h"
25 #include "support/docstring.h"
26 #include "support/lstrings.h"
27 #include "support/lassert.h"
28 #include "support/ProgressInterface.h"
29
30 #include <QApplication>
31 #include <QCheckBox>
32 #include <QMessageBox>
33 #include <QLineEdit>
34 #include <QInputDialog>
35 #include <QPushButton>
36 #include <QSettings>
37
38 #include <iomanip>
39 #include <iostream>
40
41
42 // sync with GuiView.cpp
43 #define EXPORT_in_THREAD 1
44
45
46 using namespace std;
47 using namespace lyx::support;
48
49 namespace lyx {
50 namespace frontend {
51
52
53 void noAppDialog(QString const & title, QString const & msg, QMessageBox::Icon mode)
54 {
55         int argc = 1;
56         const char *argv[] = { "lyx", 0 };
57
58         QApplication app(argc, (char**)argv);
59         switch (mode)
60         {
61                 case QMessageBox::Information: QMessageBox::information(0, title, msg); break;
62                 case QMessageBox::Warning: QMessageBox::warning(0, title, msg); break;
63                 case QMessageBox::Critical: QMessageBox::critical(0, title, msg); break;
64                 default: break;
65         }
66 }
67
68
69 namespace Alert {
70
71
72 docstring toPlainText(docstring const & msg)
73 {
74         return qstring_to_ucs4(qtHtmlToPlainText(toqstr(msg)));
75 }
76
77
78 buttonid doPrompt(docstring const & title, docstring const & question,
79                   buttonid default_button, buttonid cancel_button,
80                   docstring const & b1, docstring const & b2,
81                   docstring const & b3, docstring const & b4)
82 {
83         //lyxerr << "PROMPT" << title << "FOCUS: " << qApp->focusWidget() << endl;
84         if (!use_gui || lyxerr.debugging()) {
85                 lyxerr << toPlainText(title) << '\n'
86                        << "----------------------------------------\n"
87                        << toPlainText(question) << endl;
88
89                 lyxerr << "Assuming answer is ";
90                 switch (default_button) {
91                 case 0: lyxerr << b1 << endl; break;
92                 case 1: lyxerr << b2 << endl; break;
93                 case 2: lyxerr << b3 << endl; break;
94                 case 3: lyxerr << b4 << endl;
95                 }
96                 if (!use_gui)
97                         return default_button;
98         }
99
100         /// Long operation in progress prevents user from Ok-ing the error dialog
101         bool long_op = theApp()->longOperationStarted();
102         if (long_op)
103                 theApp()->stopLongOperation();
104
105         // For some reason, sometimes Qt uses a hourglass or watch cursor when
106         // displaying the alert. Hence, we ask for the standard cursor shape.
107         qApp->setOverrideCursor(Qt::ArrowCursor);
108
109         // FIXME replace that with guiApp->currentView()
110         //LYXERR0("FOCUS: " << qApp->focusWidget());
111         QPushButton * b[4] = { nullptr, nullptr, nullptr, nullptr };
112         const size_t numbuttons = sizeof(b)/sizeof(b[0]);
113         QMessageBox msg_box(QMessageBox::Information,
114                         toqstr(title), toqstr(question),
115                         QMessageBox::NoButton, qApp->focusWidget());
116 #ifdef Q_OS_MAC
117         msg_box.setWindowModality(Qt::WindowModal);
118 #endif
119         b[0] = msg_box.addButton(b1.empty() ? "OK" : toqstr(b1),
120                                         QMessageBox::ActionRole);
121         if (!b2.empty())
122                 b[1] = msg_box.addButton(toqstr(b2), QMessageBox::ActionRole);
123         if (!b3.empty())
124                 b[2] = msg_box.addButton(toqstr(b3), QMessageBox::ActionRole);
125         if (!b4.empty())
126                 b[3] = msg_box.addButton(toqstr(b4), QMessageBox::ActionRole);
127         if (default_button < numbuttons && nullptr != b[default_button])
128                 msg_box.setDefaultButton(b[default_button]);
129         if (cancel_button < numbuttons && nullptr != b[cancel_button])
130                 msg_box.setEscapeButton(static_cast<QAbstractButton *>(b[cancel_button]));
131         msg_box.exec();
132         const QAbstractButton * button = msg_box.clickedButton();
133
134         qApp->restoreOverrideCursor();
135
136         if (long_op)
137                 theApp()->startLongOperation();
138
139         size_t res = cancel_button;
140
141         if (button == nullptr)
142                 return res;
143         else {
144                 // Convert selection of the button into an integer
145                 for (size_t i = 0; i < numbuttons; i++) {
146                         if (button == b[i]) {
147                                 res = i;
148                                 break;
149                         }
150                 }
151         }
152
153         return res;
154 }
155
156 buttonid prompt(docstring const & title, docstring const & question,
157                   buttonid default_button, buttonid cancel_button,
158                   docstring const & b0, docstring const & b1,
159                   docstring const & b2, docstring const & b3)
160 {
161 #ifdef EXPORT_in_THREAD
162         return InGuiThread<int>().call(&doPrompt,
163 #else
164         return doPrompt(
165 #endif
166                                 title, question, default_button,
167                                 cancel_button, b0, b1, b2, b3);
168 }
169
170 void doWarning(docstring const & title, docstring const & message,
171              bool askshowagain)
172 {
173         lyxerr << "Warning: " << toPlainText(title) << '\n'
174                << "----------------------------------------\n"
175                << toPlainText(message) << endl;
176
177         if (!use_gui)
178                 return;
179
180         if (theApp() == 0) {
181                 noAppDialog(toqstr(title), toqstr(message), QMessageBox::Warning);
182                 return;
183         }
184
185         /// Long operation in progress prevents user from Ok-ing the error dialog
186         bool long_op = theApp()->longOperationStarted();
187         if (long_op)
188                 theApp()->stopLongOperation();
189
190         // Don't use a hourglass cursor while displaying the alert
191         qApp->setOverrideCursor(Qt::ArrowCursor);
192
193         if (!askshowagain) {
194                 ProgressInterface::instance()->warning(
195                                 toqstr(title),
196                                 toqstr(message));
197         } else {
198                 ProgressInterface::instance()->toggleWarning(
199                                 toqstr(title),
200                                 toqstr(message),
201                                 toqstr(message));
202         }
203
204         qApp->restoreOverrideCursor();
205
206         if (long_op)
207                 theApp()->startLongOperation();
208 }
209
210 void warning(docstring const & title, docstring const & message,
211              bool askshowagain)
212 {
213 #ifdef EXPORT_in_THREAD
214         InGuiThread<void>().call(&doWarning,
215 #else
216         doWarning(
217 #endif
218                                 title, message, askshowagain);
219 }
220
221 void doError(docstring const & title, docstring const & message, bool backtrace)
222 {
223         lyxerr << "Error: " << toPlainText(title) << '\n'
224                << "----------------------------------------\n"
225                << toPlainText(message) << endl;
226
227         QString details;
228         if (backtrace)
229                 details = toqstr(printCallStack());
230
231         if (!use_gui)
232                 return;
233
234         if (theApp() == 0) {
235                 noAppDialog(toqstr(title), toqstr(message), QMessageBox::Critical);
236                 return;
237         }
238
239         /// Long operation in progress prevents user from Ok-ing the error dialog
240         bool long_op = theApp()->longOperationStarted();
241         if (long_op)
242                 theApp()->stopLongOperation();
243
244         // Don't use a hourglass cursor while displaying the alert
245         qApp->setOverrideCursor(Qt::ArrowCursor);
246
247         ProgressInterface::instance()->error(
248                 toqstr(title),
249                 toqstr(message),
250                 details);
251
252         qApp->restoreOverrideCursor();
253
254         if (long_op)
255                 theApp()->startLongOperation();
256 }
257
258 void error(docstring const & title, docstring const & message, bool backtrace)
259 {
260 #ifdef EXPORT_in_THREAD
261         InGuiThread<void>().call(&doError,
262 #else
263         doError(
264 #endif
265                                 title, message, backtrace);
266 }
267
268 void doInformation(docstring const & title, docstring const & message)
269 {
270         if (!use_gui || lyxerr.debugging())
271                 lyxerr << toPlainText(title) << '\n'
272                        << "----------------------------------------\n"
273                        << toPlainText(message) << endl;
274
275         if (!use_gui)
276                 return;
277
278         if (theApp() == 0) {
279                 noAppDialog(toqstr(title), toqstr(message), QMessageBox::Information);
280                 return;
281         }
282
283         /// Long operation in progress prevents user from Ok-ing the error dialog
284         bool long_op = theApp()->longOperationStarted();
285         if (long_op)
286                 theApp()->stopLongOperation();
287
288         // Don't use a hourglass cursor while displaying the alert
289         qApp->setOverrideCursor(Qt::ArrowCursor);
290
291         ProgressInterface::instance()->information(
292                 toqstr(title),
293                 toqstr(message));
294
295         qApp->restoreOverrideCursor();
296
297         if (long_op)
298                 theApp()->startLongOperation();
299 }
300
301 void information(docstring const & title, docstring const & message)
302 {
303 #ifdef EXPORT_in_THREAD
304         InGuiThread<void>().call(&doInformation,
305 #else
306         doInformation(
307 #endif
308                                 title, message);
309 }
310
311 bool doAskForText(docstring & response, docstring const & msg,
312         docstring const & dflt)
313 {
314         if (!use_gui || lyxerr.debugging()) {
315                 lyxerr << "----------------------------------------\n"
316                        << toPlainText(msg) << '\n'
317                        << "Assuming answer is " << dflt << '\n'
318                        << "----------------------------------------" << endl;
319                 if (!use_gui) {
320                         response = dflt;
321                         return true;
322                 }
323         }
324
325         docstring const title = bformat(from_utf8("%1$s"), msg);
326
327         /// Long operation in progress prevents user from Ok-ing the error dialog
328         bool long_op = theApp()->longOperationStarted();
329         if (long_op)
330                 theApp()->stopLongOperation();
331
332         bool ok;
333         QString text = QInputDialog::getText(qApp->focusWidget(),
334                 toqstr(title),
335                 toqstr(char_type('&') + msg),
336                 QLineEdit::Normal,
337                 toqstr(dflt), &ok);
338
339         if (long_op)
340                 theApp()->startLongOperation();
341
342         if (ok) {
343                 response = qstring_to_ucs4(text);
344                 return true;
345         }
346         response.clear();
347         return false;
348 }
349
350 bool askForText(docstring & response, docstring const & msg,
351         docstring const & dflt)
352 {
353 #ifdef EXPORT_in_THREAD
354         return InGuiThread<bool>().call(&doAskForText,
355 #else
356         return doAskForText(
357 #endif
358                                 response, msg, dflt);
359 }
360
361 } // namespace Alert
362 } // namespace frontend
363 } // namespace lyx