|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 ** |
|
7 ** This file is part of the QtGui module of the Qt Toolkit. |
|
8 ** |
|
9 ** $QT_BEGIN_LICENSE:LGPL$ |
|
10 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
15 ** |
|
16 ** GNU Lesser General Public License Usage |
|
17 ** Alternatively, this file may be used under the terms of the GNU Lesser |
|
18 ** General Public License version 2.1 as published by the Free Software |
|
19 ** Foundation and appearing in the file LICENSE.LGPL included in the |
|
20 ** packaging of this file. Please review the following information to |
|
21 ** ensure the GNU Lesser General Public License version 2.1 requirements |
|
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
|
23 ** |
|
24 ** In addition, as a special exception, Nokia gives you certain additional |
|
25 ** rights. These rights are described in the Nokia Qt LGPL Exception |
|
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
|
27 ** |
|
28 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
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 |
|
65 extern bool qt_wince_is_mobile(); //defined in qguifunctions_wince.cpp |
|
66 extern 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 #ifdef Q_WS_S60 |
|
74 #include "private/qt_s60_p.h" |
|
75 #endif |
|
76 #endif |
|
77 |
|
78 QT_BEGIN_NAMESPACE |
|
79 |
|
80 class QErrorMessagePrivate : public QDialogPrivate |
|
81 { |
|
82 Q_DECLARE_PUBLIC(QErrorMessage) |
|
83 public: |
|
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 |
|
101 class QErrorMessageTextView : public QTextEdit |
|
102 { |
|
103 public: |
|
104 QErrorMessageTextView(QWidget *parent) |
|
105 : QTextEdit(parent) { setReadOnly(true); } |
|
106 |
|
107 virtual QSize minimumSizeHint() const; |
|
108 virtual QSize sizeHint() const; |
|
109 }; |
|
110 |
|
111 QSize 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 |
|
126 QSize 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 |
|
186 static QErrorMessage * qtMessageHandler = 0; |
|
187 |
|
188 static void deleteStaticcQErrorMessage() // post-routine |
|
189 { |
|
190 if (qtMessageHandler) { |
|
191 delete qtMessageHandler; |
|
192 qtMessageHandler = 0; |
|
193 } |
|
194 } |
|
195 |
|
196 static bool metFatal = false; |
|
197 |
|
198 static 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 |
|
242 QErrorMessage::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 grid->addWidget(d->icon, 0, 0, Qt::AlignTop); |
|
253 d->errors = new QErrorMessageTextView(this); |
|
254 grid->addWidget(d->errors, 0, 1); |
|
255 d->again = new QCheckBox(this); |
|
256 d->again->setChecked(true); |
|
257 grid->addWidget(d->again, 1, 1, Qt::AlignTop); |
|
258 d->ok = new QPushButton(this); |
|
259 #ifdef QT_SOFTKEYS_ENABLED |
|
260 d->okAction = new QAction(d->ok); |
|
261 d->okAction->setSoftKeyRole(QAction::PositiveSoftKey); |
|
262 connect(d->okAction, SIGNAL(triggered()), this, SLOT(accept())); |
|
263 addAction(d->okAction); |
|
264 #endif |
|
265 |
|
266 |
|
267 #if defined(Q_WS_WINCE) || defined(Q_WS_S60) |
|
268 d->ok->setFixedSize(0,0); |
|
269 #endif |
|
270 connect(d->ok, SIGNAL(clicked()), this, SLOT(accept())); |
|
271 d->ok->setFocus(); |
|
272 grid->addWidget(d->ok, 2, 0, 1, 2, Qt::AlignCenter); |
|
273 grid->setColumnStretch(1, 42); |
|
274 grid->setRowStretch(0, 42); |
|
275 d->retranslateStrings(); |
|
276 } |
|
277 |
|
278 |
|
279 /*! |
|
280 Destroys the error message dialog. |
|
281 */ |
|
282 |
|
283 QErrorMessage::~QErrorMessage() |
|
284 { |
|
285 if (this == qtMessageHandler) { |
|
286 qtMessageHandler = 0; |
|
287 QtMsgHandler tmp = qInstallMsgHandler(0); |
|
288 // in case someone else has later stuck in another... |
|
289 if (tmp != jump) |
|
290 qInstallMsgHandler(tmp); |
|
291 } |
|
292 } |
|
293 |
|
294 |
|
295 /*! \reimp */ |
|
296 |
|
297 void QErrorMessage::done(int a) |
|
298 { |
|
299 Q_D(QErrorMessage); |
|
300 if (!d->again->isChecked() && !d->currentMessage.isEmpty() && d->currentType.isEmpty()) { |
|
301 d->doNotShow.insert(d->currentMessage); |
|
302 } |
|
303 if (!d->again->isChecked() && !d->currentType.isEmpty()) { |
|
304 d->doNotShowType.insert(d->currentType); |
|
305 } |
|
306 d->currentMessage.clear(); |
|
307 d->currentType.clear(); |
|
308 if (!d->nextPending()) { |
|
309 QDialog::done(a); |
|
310 if (this == qtMessageHandler && metFatal) |
|
311 exit(1); |
|
312 } |
|
313 } |
|
314 |
|
315 |
|
316 /*! |
|
317 Returns a pointer to a QErrorMessage object that outputs the |
|
318 default Qt messages. This function creates such an object, if there |
|
319 isn't one already. |
|
320 */ |
|
321 |
|
322 QErrorMessage * QErrorMessage::qtHandler() |
|
323 { |
|
324 if (!qtMessageHandler) { |
|
325 qtMessageHandler = new QErrorMessage(0); |
|
326 qAddPostRoutine(deleteStaticcQErrorMessage); // clean up |
|
327 qtMessageHandler->setWindowTitle(QApplication::applicationName()); |
|
328 qInstallMsgHandler(jump); |
|
329 } |
|
330 return qtMessageHandler; |
|
331 } |
|
332 |
|
333 |
|
334 /*! \internal */ |
|
335 |
|
336 bool QErrorMessagePrivate::nextPending() |
|
337 { |
|
338 while (!pending.isEmpty()) { |
|
339 QPair<QString,QString> pendingMessage = pending.dequeue(); |
|
340 QString message = pendingMessage.first; |
|
341 QString type = pendingMessage.second; |
|
342 if (!message.isEmpty() && ((type.isEmpty() && !doNotShow.contains(message)) || (!type.isEmpty() && !doNotShowType.contains(type)))) { |
|
343 #ifndef QT_NO_TEXTHTMLPARSER |
|
344 errors->setHtml(message); |
|
345 #else |
|
346 errors->setPlainText(message); |
|
347 #endif |
|
348 currentMessage = message; |
|
349 currentType = type; |
|
350 return true; |
|
351 } |
|
352 } |
|
353 return false; |
|
354 } |
|
355 |
|
356 |
|
357 /*! |
|
358 Shows the given message, \a message, and returns immediately. If the user |
|
359 has requested for the message not to be shown again, this function does |
|
360 nothing. |
|
361 |
|
362 Normally, the message is displayed immediately. However, if there are |
|
363 pending messages, it will be queued to be displayed later. |
|
364 */ |
|
365 |
|
366 void QErrorMessage::showMessage(const QString &message) |
|
367 { |
|
368 Q_D(QErrorMessage); |
|
369 if (d->doNotShow.contains(message)) |
|
370 return; |
|
371 d->pending.enqueue(qMakePair(message,QString())); |
|
372 if (!isVisible() && d->nextPending()) |
|
373 show(); |
|
374 } |
|
375 |
|
376 /*! |
|
377 \since 4.5 |
|
378 \overload |
|
379 |
|
380 Shows the given message, \a message, and returns immediately. If the user |
|
381 has requested for messages of type, \a type, not to be shown again, this |
|
382 function does nothing. |
|
383 |
|
384 Normally, the message is displayed immediately. However, if there are |
|
385 pending messages, it will be queued to be displayed later. |
|
386 |
|
387 \sa showMessage() |
|
388 */ |
|
389 |
|
390 void QErrorMessage::showMessage(const QString &message, const QString &type) |
|
391 { |
|
392 Q_D(QErrorMessage); |
|
393 if (d->doNotShow.contains(message) && d->doNotShowType.contains(type)) |
|
394 return; |
|
395 d->pending.push_back(qMakePair(message,type)); |
|
396 if (!isVisible() && d->nextPending()) |
|
397 show(); |
|
398 } |
|
399 |
|
400 /*! |
|
401 \reimp |
|
402 */ |
|
403 void QErrorMessage::changeEvent(QEvent *e) |
|
404 { |
|
405 Q_D(QErrorMessage); |
|
406 if (e->type() == QEvent::LanguageChange) { |
|
407 d->retranslateStrings(); |
|
408 } |
|
409 QDialog::changeEvent(e); |
|
410 } |
|
411 |
|
412 void QErrorMessagePrivate::retranslateStrings() |
|
413 { |
|
414 again->setText(QErrorMessage::tr("&Show this message again")); |
|
415 ok->setText(QErrorMessage::tr("&OK")); |
|
416 #ifdef QT_SOFTKEYS_ENABLED |
|
417 okAction->setText(ok->text()); |
|
418 #endif |
|
419 } |
|
420 |
|
421 /*! |
|
422 \fn void QErrorMessage::message(const QString & message) |
|
423 |
|
424 Use showMessage(\a message) instead. |
|
425 */ |
|
426 |
|
427 QT_END_NAMESPACE |
|
428 |
|
429 #endif // QT_NO_ERRORMESSAGE |