]> git.lyx.org Git - features.git/blob - src/frontends/qt/GuiAlert.cpp
Guard against possible referencing null.
[features.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 int doPrompt(docstring const & title, docstring const & question,
79                   int default_button, int 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] = { 0, 0, 0, 0 };
112         QMessageBox msg_box(QMessageBox::Information,
113                         toqstr(title), toqstr(question),
114                         QMessageBox::NoButton, qApp->focusWidget());
115         b[0] = msg_box.addButton(b1.empty() ? "OK" : toqstr(b1),
116                                         QMessageBox::ActionRole);
117         if (!b2.empty())
118                 b[1] = msg_box.addButton(toqstr(b2), QMessageBox::ActionRole);
119         if (!b3.empty())
120                 b[2] = msg_box.addButton(toqstr(b3), QMessageBox::ActionRole);
121         if (!b4.empty())
122                 b[3] = msg_box.addButton(toqstr(b4), QMessageBox::ActionRole);
123         msg_box.setDefaultButton(b[default_button]);
124         msg_box.setEscapeButton(static_cast<QAbstractButton *>(b[cancel_button]));
125         int res = msg_box.exec();
126
127         qApp->restoreOverrideCursor();
128
129         if (long_op)
130                 theApp()->startLongOperation();
131
132         // Qt bug: can return -1 on cancel or WM close, despite the docs.
133         if (res == -1)
134                 res = cancel_button;
135         return res;
136 }
137
138 int prompt(docstring const & title, docstring const & question,
139                   int default_button, int cancel_button,
140                   docstring const & b0, docstring const & b1,
141                   docstring const & b2, docstring const & b3)
142 {
143 #ifdef EXPORT_in_THREAD
144         return InGuiThread<int>().call(&doPrompt,
145 #else
146         return doPrompt(
147 #endif
148                                 title, question, default_button,
149                                 cancel_button, b0, b1, b2, b3);
150 }
151
152 void doWarning(docstring const & title, docstring const & message,
153              bool askshowagain)
154 {
155         lyxerr << "Warning: " << toPlainText(title) << '\n'
156                << "----------------------------------------\n"
157                << toPlainText(message) << endl;
158
159         if (!use_gui)
160                 return;
161
162         if (theApp() == 0) {
163                 noAppDialog(toqstr(title), toqstr(message), QMessageBox::Warning);
164                 return;
165         }
166
167         /// Long operation in progress prevents user from Ok-ing the error dialog
168         bool long_op = theApp()->longOperationStarted();
169         if (long_op)
170                 theApp()->stopLongOperation();
171
172         // Don't use a hourglass cursor while displaying the alert
173         qApp->setOverrideCursor(Qt::ArrowCursor);
174
175         if (!askshowagain) {
176                 ProgressInterface::instance()->warning(
177                                 toqstr(title),
178                                 toqstr(message));
179         } else {
180                 ProgressInterface::instance()->toggleWarning(
181                                 toqstr(title),
182                                 toqstr(message),
183                                 toqstr(message));
184         }
185
186         qApp->restoreOverrideCursor();
187
188         if (long_op)
189                 theApp()->startLongOperation();
190 }
191
192 void warning(docstring const & title, docstring const & message,
193              bool askshowagain)
194 {
195 #ifdef EXPORT_in_THREAD
196         InGuiThread<void>().call(&doWarning,
197 #else
198         doWarning(
199 #endif
200                                 title, message, askshowagain);
201 }
202
203 void doError(docstring const & title, docstring const & message, bool backtrace)
204 {
205         lyxerr << "Error: " << toPlainText(title) << '\n'
206                << "----------------------------------------\n"
207                << toPlainText(message) << endl;
208
209         QString details;
210         if (backtrace)
211                 details = toqstr(printCallStack());
212
213         if (!use_gui)
214                 return;
215
216         if (theApp() == 0) {
217                 noAppDialog(toqstr(title), toqstr(message), QMessageBox::Critical);
218                 return;
219         }
220
221         /// Long operation in progress prevents user from Ok-ing the error dialog
222         bool long_op = theApp()->longOperationStarted();
223         if (long_op)
224                 theApp()->stopLongOperation();
225
226         // Don't use a hourglass cursor while displaying the alert
227         qApp->setOverrideCursor(Qt::ArrowCursor);
228
229         ProgressInterface::instance()->error(
230                 toqstr(title),
231                 toqstr(message),
232                 details);
233
234         qApp->restoreOverrideCursor();
235
236         if (long_op)
237                 theApp()->startLongOperation();
238 }
239
240 void error(docstring const & title, docstring const & message, bool backtrace)
241 {
242 #ifdef EXPORT_in_THREAD
243         InGuiThread<void>().call(&doError,
244 #else
245         doError(
246 #endif
247                                 title, message, backtrace);
248 }
249
250 void doInformation(docstring const & title, docstring const & message)
251 {
252         if (!use_gui || lyxerr.debugging())
253                 lyxerr << toPlainText(title) << '\n'
254                        << "----------------------------------------\n"
255                        << toPlainText(message) << endl;
256
257         if (!use_gui)
258                 return;
259
260         if (theApp() == 0) {
261                 noAppDialog(toqstr(title), toqstr(message), QMessageBox::Information);
262                 return;
263         }
264
265         /// Long operation in progress prevents user from Ok-ing the error dialog
266         bool long_op = theApp()->longOperationStarted();
267         if (long_op)
268                 theApp()->stopLongOperation();
269
270         // Don't use a hourglass cursor while displaying the alert
271         qApp->setOverrideCursor(Qt::ArrowCursor);
272
273         ProgressInterface::instance()->information(
274                 toqstr(title),
275                 toqstr(message));
276
277         qApp->restoreOverrideCursor();
278
279         if (long_op)
280                 theApp()->startLongOperation();
281 }
282
283 void information(docstring const & title, docstring const & message)
284 {
285 #ifdef EXPORT_in_THREAD
286         InGuiThread<void>().call(&doInformation,
287 #else
288         doInformation(
289 #endif
290                                 title, message);
291 }
292
293 bool doAskForText(docstring & response, docstring const & msg,
294         docstring const & dflt)
295 {
296         if (!use_gui || lyxerr.debugging()) {
297                 lyxerr << "----------------------------------------\n"
298                        << toPlainText(msg) << '\n'
299                        << "Assuming answer is " << dflt << '\n'
300                        << "----------------------------------------" << endl;
301                 if (!use_gui) {
302                         response = dflt;
303                         return true;
304                 }
305         }
306
307         docstring const title = bformat(from_utf8("%1$s"), msg);
308
309         /// Long operation in progress prevents user from Ok-ing the error dialog
310         bool long_op = theApp()->longOperationStarted();
311         if (long_op)
312                 theApp()->stopLongOperation();
313
314         bool ok;
315         QString text = QInputDialog::getText(qApp->focusWidget(),
316                 toqstr(title),
317                 toqstr(char_type('&') + msg),
318                 QLineEdit::Normal,
319                 toqstr(dflt), &ok);
320
321         if (long_op)
322                 theApp()->startLongOperation();
323
324         if (ok) {
325                 response = qstring_to_ucs4(text);
326                 return true;
327         }
328         response.clear();
329         return false;
330 }
331
332 bool askForText(docstring & response, docstring const & msg,
333         docstring const & dflt)
334 {
335 #ifdef EXPORT_in_THREAD
336         return InGuiThread<bool>().call(&doAskForText,
337 #else
338         return doAskForText(
339 #endif
340                                 response, msg, dflt);
341 }
342
343 } // namespace Alert
344 } // namespace frontend
345 } // namespace lyx