1/****************************************************************************
2**
3** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
4** Contact: http://www.qt-project.org/legal
5**
6** This file is part of the QtGui module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Digia. For licensing terms and
14** conditions see http://qt.digia.com/licensing. For further information
15** use the contact form at http://qt.digia.com/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 2.1 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 2.1 requirements
23** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24**
25** In addition, as a special exception, Digia gives you certain additional
26** rights. These rights are described in the Digia Qt LGPL Exception
27** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28**
29** GNU General Public License Usage
30** Alternatively, this file may be used under the terms of the GNU
31** General Public License version 3.0 as published by the Free Software
32** Foundation and appearing in the file LICENSE.GPL included in the
33** packaging of this file. Please review the following information to
34** ensure the GNU General Public License version 3.0 requirements will be
35** met: http://www.gnu.org/copyleft/gpl.html.
36**
37**
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qerrormessage.h"
43
44#ifndef QT_NO_ERRORMESSAGE
45
46#include "qapplication.h"
47#include "qcheckbox.h"
48#include "qlabel.h"
49#include "qlayout.h"
50#include "qmessagebox.h"
51#include "qpushbutton.h"
52#include "qstringlist.h"
53#include "qtextedit.h"
54#include "qdialog_p.h"
55#include "qpixmap.h"
56#include "qmetaobject.h"
57#include "qthread.h"
58#include "qqueue.h"
59#include "qset.h"
60
61#include <stdio.h>
62#include <stdlib.h>
63
64#ifdef Q_WS_WINCE
65extern bool qt_wince_is_mobile(); //defined in qguifunctions_wince.cpp
66extern bool qt_wince_is_high_dpi(); //defined in qguifunctions_wince.cpp
67
68#include "qguifunctions_wince.h"
69#endif
70
71#if defined(QT_SOFTKEYS_ENABLED)
72#include <qaction.h>
73#endif
74#ifdef Q_WS_S60
75#include "private/qt_s60_p.h"
76#endif
77
78QT_BEGIN_NAMESPACE
79
80class QErrorMessagePrivate : public QDialogPrivate
81{
82 Q_DECLARE_PUBLIC(QErrorMessage)
83public:
84 QPushButton * ok;
85 QCheckBox * again;
86 QTextEdit * errors;
87 QLabel * icon;
88#ifdef QT_SOFTKEYS_ENABLED
89 QAction *okAction;
90#endif
91 QQueue<QPair<QString, QString> > pending;
92 QSet<QString> doNotShow;
93 QSet<QString> doNotShowType;
94 QString currentMessage;
95 QString currentType;
96
97 bool nextPending();
98 void retranslateStrings();
99};
100
101class QErrorMessageTextView : public QTextEdit
102{
103public:
104 QErrorMessageTextView(QWidget *parent)
105 : QTextEdit(parent) { setReadOnly(true); }
106
107 virtual QSize minimumSizeHint() const;
108 virtual QSize sizeHint() const;
109};
110
111QSize QErrorMessageTextView::minimumSizeHint() const
112{
113#ifdef Q_WS_WINCE
114 if (qt_wince_is_mobile())
115 if (qt_wince_is_high_dpi())
116 return QSize(200, 200);
117 else
118 return QSize(100, 100);
119 else
120 return QSize(70, 70);
121#else
122 return QSize(50, 50);
123#endif
124}
125
126QSize QErrorMessageTextView::sizeHint() const
127{
128#ifdef Q_WS_WINCE
129 if (qt_wince_is_mobile())
130 if (qt_wince_is_high_dpi())
131 return QSize(400, 200);
132 else
133 return QSize(320, 120);
134 else
135 return QSize(300, 100);
136#else
137
138#ifdef Q_WS_S60
139 const int smallerDimension = qMin(S60->screenHeightInPixels, S60->screenWidthInPixels);
140 // In S60 layout data, error messages seem to be one third of the screen height (in portrait) minus two.
141 return QSize(smallerDimension, smallerDimension/3-2);
142#else
143 return QSize(250, 75);
144#endif //Q_WS_S60
145#endif //Q_WS_WINCE
146}
147
148/*!
149 \class QErrorMessage
150
151 \brief The QErrorMessage class provides an error message display dialog.
152
153 \ingroup standard-dialog
154
155 An error message widget consists of a text label and a checkbox. The
156 checkbox lets the user control whether the same error message will be
157 displayed again in the future, typically displaying the text,
158 "Show this message again" translated into the appropriate local
159 language.
160
161 For production applications, the class can be used to display messages which
162 the user only needs to see once. To use QErrorMessage like this, you create
163 the dialog in the usual way, and show it by calling the showMessage() slot or
164 connecting signals to it.
165
166 The static qtHandler() function installs a message handler
167 using qInstallMsgHandler() and creates a QErrorMessage that displays
168 qDebug(), qWarning() and qFatal() messages. This is most useful in
169 environments where no console is available to display warnings and
170 error messages.
171
172 In both cases QErrorMessage will queue pending messages and display
173 them in order, with each new message being shown as soon as the user
174 has accepted the previous message. Once the user has specified that a
175 message is not to be shown again it is automatically skipped, and the
176 dialog will show the next appropriate message in the queue.
177
178 The \l{dialogs/standarddialogs}{Standard Dialogs} example shows
179 how to use QErrorMessage as well as other built-in Qt dialogs.
180
181 \img qerrormessage.png
182
183 \sa QMessageBox, QStatusBar::showMessage(), {Standard Dialogs Example}
184*/
185
186static QErrorMessage * qtMessageHandler = 0;
187
188static void deleteStaticcQErrorMessage() // post-routine
189{
190 if (qtMessageHandler) {
191 delete qtMessageHandler;
192 qtMessageHandler = 0;
193 }
194}
195
196static bool metFatal = false;
197
198static void jump(QtMsgType t, const char * m)
199{
200 if (!qtMessageHandler)
201 return;
202
203 QString rich;
204
205 switch (t) {
206 case QtDebugMsg:
207 default:
208 rich = QErrorMessage::tr("Debug Message:");
209 break;
210 case QtWarningMsg:
211 rich = QErrorMessage::tr("Warning:");
212 break;
213 case QtFatalMsg:
214 rich = QErrorMessage::tr("Fatal Error:");
215 }
216 rich = QString::fromLatin1("<p><b>%1</b></p>").arg(rich);
217 rich += Qt::convertFromPlainText(QLatin1String(m), Qt::WhiteSpaceNormal);
218
219 // ### work around text engine quirk
220 if (rich.endsWith(QLatin1String("</p>")))
221 rich.chop(4);
222
223 if (!metFatal) {
224 if (QThread::currentThread() == qApp->thread()) {
225 qtMessageHandler->showMessage(rich);
226 } else {
227 QMetaObject::invokeMethod(qtMessageHandler,
228 "showMessage",
229 Qt::QueuedConnection,
230 Q_ARG(QString, rich));
231 }
232 metFatal = (t == QtFatalMsg);
233 }
234}
235
236
237/*!
238 Constructs and installs an error handler window with the given \a
239 parent.
240*/
241
242QErrorMessage::QErrorMessage(QWidget * parent)
243 : QDialog(*new QErrorMessagePrivate, parent)
244{
245 Q_D(QErrorMessage);
246 QGridLayout * grid = new QGridLayout(this);
247 d->icon = new QLabel(this);
248#ifndef QT_NO_MESSAGEBOX
249 d->icon->setPixmap(QMessageBox::standardIcon(QMessageBox::Information));
250 d->icon->setAlignment(Qt::AlignHCenter | Qt::AlignTop);
251#endif
252#ifdef Q_WS_S60
253 //In Symbian, messagebox icons are in LtR UIs on right. Thus, layout needs to switch icon and text columns.
254 const int preferredIconColumn = (QApplication::layoutDirection() == Qt::LeftToRight) ? 1 : 0;
255 const int preferredTextColumn = (QApplication::layoutDirection() == Qt::LeftToRight) ? 0 : 1;
256#else
257 const int preferredIconColumn = 0;
258 const int preferredTextColumn = 1;
259#endif
260 grid->addWidget(d->icon, 0, preferredIconColumn, Qt::AlignTop);
261 d->errors = new QErrorMessageTextView(this);
262 grid->addWidget(d->errors, 0, preferredTextColumn);
263 d->again = new QCheckBox(this);
264 d->again->setChecked(true);
265 grid->addWidget(d->again, 1, preferredTextColumn, Qt::AlignTop);
266 d->ok = new QPushButton(this);
267#ifdef QT_SOFTKEYS_ENABLED
268 d->okAction = new QAction(d->ok);
269 d->okAction->setSoftKeyRole(QAction::PositiveSoftKey);
270 connect(d->okAction, SIGNAL(triggered()), this, SLOT(accept()));
271 addAction(d->okAction);
272#endif
273
274
275#if defined(Q_WS_WINCE) || defined(Q_WS_S60)
276 d->ok->setFixedSize(0,0);
277#endif
278 connect(d->ok, SIGNAL(clicked()), this, SLOT(accept()));
279 d->ok->setFocus();
280 grid->addWidget(d->ok, 2, 0, 1, 2, Qt::AlignCenter);
281 grid->setColumnStretch(preferredTextColumn, 42);
282 grid->setRowStretch(0, 42);
283 d->retranslateStrings();
284}
285
286
287/*!
288 Destroys the error message dialog.
289*/
290
291QErrorMessage::~QErrorMessage()
292{
293 if (this == qtMessageHandler) {
294 qtMessageHandler = 0;
295 QtMsgHandler tmp = qInstallMsgHandler(0);
296 // in case someone else has later stuck in another...
297 if (tmp != jump)
298 qInstallMsgHandler(tmp);
299 }
300}
301
302
303/*! \reimp */
304
305void QErrorMessage::done(int a)
306{
307 Q_D(QErrorMessage);
308 if (!d->again->isChecked() && !d->currentMessage.isEmpty() && d->currentType.isEmpty()) {
309 d->doNotShow.insert(d->currentMessage);
310 }
311 if (!d->again->isChecked() && !d->currentType.isEmpty()) {
312 d->doNotShowType.insert(d->currentType);
313 }
314 d->currentMessage.clear();
315 d->currentType.clear();
316 if (!d->nextPending()) {
317 QDialog::done(a);
318 if (this == qtMessageHandler && metFatal)
319 exit(1);
320 }
321}
322
323
324/*!
325 Returns a pointer to a QErrorMessage object that outputs the
326 default Qt messages. This function creates such an object, if there
327 isn't one already.
328*/
329
330QErrorMessage * QErrorMessage::qtHandler()
331{
332 if (!qtMessageHandler) {
333 qtMessageHandler = new QErrorMessage(0);
334 qAddPostRoutine(deleteStaticcQErrorMessage); // clean up
335 qtMessageHandler->setWindowTitle(QApplication::applicationName());
336 qInstallMsgHandler(jump);
337 }
338 return qtMessageHandler;
339}
340
341
342/*! \internal */
343
344bool QErrorMessagePrivate::nextPending()
345{
346 while (!pending.isEmpty()) {
347 QPair<QString,QString> pendingMessage = pending.dequeue();
348 QString message = pendingMessage.first;
349 QString type = pendingMessage.second;
350 if (!message.isEmpty() && ((type.isEmpty() && !doNotShow.contains(message)) || (!type.isEmpty() && !doNotShowType.contains(type)))) {
351#ifndef QT_NO_TEXTHTMLPARSER
352 errors->setHtml(message);
353#else
354 errors->setPlainText(message);
355#endif
356 currentMessage = message;
357 currentType = type;
358 return true;
359 }
360 }
361 return false;
362}
363
364
365/*!
366 Shows the given message, \a message, and returns immediately. If the user
367 has requested for the message not to be shown again, this function does
368 nothing.
369
370 Normally, the message is displayed immediately. However, if there are
371 pending messages, it will be queued to be displayed later.
372*/
373
374void QErrorMessage::showMessage(const QString &message)
375{
376 Q_D(QErrorMessage);
377 if (d->doNotShow.contains(message))
378 return;
379 d->pending.enqueue(qMakePair(message,QString()));
380 if (!isVisible() && d->nextPending())
381 show();
382}
383
384/*!
385 \since 4.5
386 \overload
387
388 Shows the given message, \a message, and returns immediately. If the user
389 has requested for messages of type, \a type, not to be shown again, this
390 function does nothing.
391
392 Normally, the message is displayed immediately. However, if there are
393 pending messages, it will be queued to be displayed later.
394
395 \sa showMessage()
396*/
397
398void QErrorMessage::showMessage(const QString &message, const QString &type)
399{
400 Q_D(QErrorMessage);
401 if (d->doNotShow.contains(message) && d->doNotShowType.contains(type))
402 return;
403 d->pending.push_back(qMakePair(message,type));
404 if (!isVisible() && d->nextPending())
405 show();
406}
407
408/*!
409 \reimp
410*/
411void QErrorMessage::changeEvent(QEvent *e)
412{
413 Q_D(QErrorMessage);
414 if (e->type() == QEvent::LanguageChange) {
415 d->retranslateStrings();
416 }
417 QDialog::changeEvent(e);
418}
419
420void QErrorMessagePrivate::retranslateStrings()
421{
422 again->setText(QErrorMessage::tr("&Show this message again"));
423 ok->setText(QErrorMessage::tr("&OK"));
424#ifdef QT_SOFTKEYS_ENABLED
425 okAction->setText(ok->text());
426#endif
427}
428
429/*!
430 \fn void QErrorMessage::message(const QString & message)
431
432 Use showMessage(\a message) instead.
433*/
434
435QT_END_NAMESPACE
436
437#endif // QT_NO_ERRORMESSAGE
438