1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.xnap.commons.gui;
21
22 import java.awt.BorderLayout;
23 import java.awt.Component;
24 import java.awt.FlowLayout;
25 import java.awt.Font;
26 import java.awt.event.ActionEvent;
27 import java.io.ByteArrayOutputStream;
28 import java.io.PrintWriter;
29 import java.text.MessageFormat;
30 import java.util.Iterator;
31 import java.util.LinkedList;
32 import java.util.List;
33 import javax.swing.Action;
34 import javax.swing.Box;
35 import javax.swing.JCheckBox;
36 import javax.swing.JLabel;
37 import javax.swing.JOptionPane;
38 import javax.swing.JPanel;
39 import javax.swing.JScrollPane;
40 import javax.swing.JTextArea;
41 import javax.swing.SwingConstants;
42 import javax.swing.SwingUtilities;
43 import org.xnap.commons.gui.action.AbstractToggleAction;
44 import org.xnap.commons.gui.action.AbstractXNapAction;
45 import org.xnap.commons.gui.util.GUIHelper;
46 import org.xnap.commons.gui.util.IconHelper;
47 import org.xnap.commons.i18n.I18n;
48 import org.xnap.commons.i18n.I18nFactory;
49 import org.xnap.commons.settings.BooleanSetting;
50 import org.xnap.commons.util.UncaughtExceptionManager;
51
52 /***
53 * A dialog to display error messages. The dialog displays a prominent icon
54 * along with an error message.
55 *
56 * @author Steffen Pingel
57 */
58 public class ErrorDialog extends DefaultDialog {
59
60 private static I18n i18n = I18nFactory.getI18n(ErrorDialog.class);
61
62 private JPanel detailsPanel;
63 private JTextArea detailsTextArea;
64
65 private JCheckBox doNotShowExceptionAgainCheckBox;
66 private JCheckBox doNotShowDialogAgainCheckBox;
67
68 private ContinueAction continueAction = new ContinueAction();
69 private DetailsAction detailsAction;
70 private ExitAction exitAction = new ExitAction();
71 private SendAction sendAction;
72
73 private List<Thread> threads = new LinkedList<Thread>();
74 private List<Throwable> throwables = new LinkedList<Throwable>();
75
76 /***
77 * If true, the dialog can not handle more exceptions.
78 */
79 private boolean busy = false;
80 private UncaughtExceptionManager exceptionManager;
81 private BooleanSetting detailsVisibleSetting;
82 private BooleanSetting showDialogAgainSetting;
83
84 /***
85 * TODO clean this mess and make constructor public
86 * @param description
87 * @param exceptionManager
88 * @param showExitAction
89 * @param showDialogAgainSetting
90 * @param detailsVisibleSetting
91 */
92 ErrorDialog(String description, UncaughtExceptionManager exceptionManager,
93 boolean showExitAction,
94 BooleanSetting showDialogAgainSetting,
95 BooleanSetting detailsVisibleSetting)
96 {
97 super(BUTTON_NONE);
98
99 this.exceptionManager = exceptionManager;
100 this.showDialogAgainSetting = showDialogAgainSetting;
101 this.detailsVisibleSetting = detailsVisibleSetting;
102
103 Box mainBox = Box.createVerticalBox();
104 setMainComponent(mainBox);
105
106
107
108 JLabel infoLabel = new JLabel(GUIHelper.tt(description),
109 IconHelper.getIcon("error.png", 32),
110 SwingConstants.LEADING);
111 infoLabel.setIconTextGap(10);
112 infoLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
113 mainBox.add(infoLabel);
114 mainBox.add(Box.createVerticalStrut(10));
115
116
117 detailsPanel = new JPanel(new BorderLayout());
118 detailsPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
119 updateBorder();
120
121 detailsTextArea = new JTextArea(12, 80);
122 detailsTextArea.setEditable (false);
123 detailsTextArea.setFont(new Font("Monospaced", Font.PLAIN, 10));
124 detailsPanel.add(new JScrollPane(detailsTextArea), BorderLayout.CENTER);
125
126
127 JPanel detailsButtonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
128 detailsButtonPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
129 mainBox.add(detailsButtonPanel);
130
131 detailsAction = new DetailsAction((detailsVisibleSetting != null) ? detailsVisibleSetting.getValue() : false);
132 detailsButtonPanel.add(Builder.createButton(detailsAction));
133
134
135 mainBox.add(detailsPanel);
136
137
138 if (exceptionManager != null) {
139 doNotShowExceptionAgainCheckBox
140 = new JCheckBox(i18n.tr("Do not show this exception again"));
141 doNotShowExceptionAgainCheckBox.setAlignmentX(Component.LEFT_ALIGNMENT);
142 mainBox.add(doNotShowExceptionAgainCheckBox);
143 }
144
145 if (showDialogAgainSetting != null) {
146 doNotShowDialogAgainCheckBox
147 = new JCheckBox(i18n.tr("Do not show this dialog again"),
148 showDialogAgainSetting.getValue());
149 doNotShowDialogAgainCheckBox.setAlignmentX(Component.LEFT_ALIGNMENT);
150 mainBox.add(doNotShowDialogAgainCheckBox);
151 }
152
153 if (exceptionManager != null) {
154 sendAction = new SendAction();
155 getButtonPanel().add(Builder.createButton(sendAction));
156 }
157 GUIHelper.bindEscapeKey(getTopPanel(), continueAction);
158 getButtonPanel().add(Builder.createButton(continueAction));
159 if (showExitAction) {
160 getButtonPanel().add(Builder.createButton(exitAction));
161 }
162 }
163
164 public ErrorDialog(String description)
165 {
166 this(description, null, false, null, null);
167 }
168
169 ErrorDialog()
170 {
171
172 }
173
174 public void add(Thread t, Throwable e)
175 {
176 threads.add(t);
177 throwables.add(e);
178
179 ByteArrayOutputStream out = new ByteArrayOutputStream();
180 PrintWriter writer = new PrintWriter(out);
181 e.printStackTrace(writer);
182 writer.close();
183
184 detailsTextArea.append(out.toString() + "\n");
185
186 if (threads.size() == 1) {
187 resetCaretPosition();
188 }
189
190 updateBorder();
191 }
192
193 public void close()
194 {
195
196
197
198
199
200 if (showDialogAgainSetting != null) {
201 showDialogAgainSetting.setValue(!doNotShowDialogAgainCheckBox.isSelected());
202 }
203 if (detailsVisibleSetting != null) {
204 detailsVisibleSetting.setValue(detailsPanel.isVisible());
205 }
206
207 if (exceptionManager != null && doNotShowExceptionAgainCheckBox.isSelected()) {
208 for (Iterator<Throwable> it = throwables.iterator(); it.hasNext();) {
209 exceptionManager.addToBlacklist(it.next());
210 }
211 }
212
213 busy = true;
214 dispose();
215 }
216
217 public boolean isBusy()
218 {
219 return busy;
220 }
221
222 public void resetCaretPosition()
223 {
224 detailsTextArea.setCaretPosition(0);
225 }
226
227 public void updateBorder()
228 {
229 detailsPanel.setBorder
230 (GUIHelper.createDefaultBorder
231 (i18n.trn("Details - {0} Problem", "Details - {0} Problems", threads.size())));
232 }
233
234 public static void show(Component c, String message, String title,
235 Throwable e)
236 {
237 ErrorDialog d = new ErrorDialog(message);
238 d.setTitle(title);
239 d.pack();
240 d.setModal(true);
241 d.add(Thread.currentThread(), e);
242 d.show(c);
243 }
244
245 public static void showError(Component c, String message, String title,
246 Throwable e)
247 {
248 if (e.getLocalizedMessage() != null) {
249 show(c, MessageFormat.format("{0}:<br><br>{1}", new Object[] {
250 message, e.getLocalizedMessage()}), title, e);
251 }
252 else {
253 show(c, message, title, e);
254 }
255 }
256
257 public static void showUnexpected(Component c, String message, String title,
258 Throwable e)
259 {
260 ErrorDialog d = new ErrorDialog(message, null, true, null, null);
261 d.setTitle(title);
262 d.pack();
263 d.setModal(true);
264 d.add(Thread.currentThread(), e);
265 d.show(c);
266 }
267
268 private class DetailsAction extends AbstractToggleAction
269 {
270 public DetailsAction(boolean visible)
271 {
272 super(visible);
273
274 putValue(Action.NAME, i18n.tr("Show Details"));
275 putValue(ICON_FILENAME, "2downarrow.png");
276
277 toggled(isSelected());
278 }
279
280 public void toggled(boolean visible)
281 {
282 detailsPanel.setVisible(visible);
283 pack();
284 }
285
286 }
287
288 private class ContinueAction extends AbstractXNapAction
289 {
290 public ContinueAction()
291 {
292 putValue(Action.NAME, i18n.tr("Continue"));
293 putValue(ICON_FILENAME, "1rightarrow.png");
294 }
295
296 public void actionPerformed(ActionEvent event)
297 {
298 close();
299 }
300
301 }
302
303 private class ExitAction extends AbstractXNapAction {
304
305 public ExitAction()
306 {
307 putValue(Action.NAME, i18n.tr("Quit"));
308 putValue(ICON_FILENAME, "exit.png");
309 putValue(Action.SHORT_DESCRIPTION,
310 i18n.tr("Quits the application"));
311 }
312
313 public void actionPerformed(ActionEvent event)
314 {
315 close();
316 System.exit(1);
317 }
318
319 }
320
321 private class SendAction extends AbstractXNapAction
322 {
323 public SendAction()
324 {
325 putValue(Action.NAME, i18n.tr("Send Report"));
326 putValue(ICON_FILENAME, "mail_send.png");
327 putValue(Action.SHORT_DESCRIPTION,
328 i18n.tr("Sends the details to a remote site for analysis"));
329 }
330
331 public void actionPerformed(ActionEvent event)
332 {
333 busy = true;
334
335 Thread t = new Thread(new SendWorker(), "SendFeedback");
336 t.start();
337 }
338 }
339
340 private class SendWorker implements Runnable
341 {
342
343 public void run()
344 {
345 sendAction.setEnabled(false);
346 getCancelAction().setEnabled(false);
347
348 setTitle(i18n.tr("Sending Error Report") + "...");
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370 Runnable runner = new Runnable()
371 {
372 public void run()
373 {
374 JOptionPane.showMessageDialog
375 (ErrorDialog.this, i18n.tr("Thank you."),
376 i18n.tr("XNap Error"),
377 JOptionPane.INFORMATION_MESSAGE);
378 close();
379 }
380 };
381 SwingUtilities.invokeLater(runner);
382 }
383
384 public void failed(String response)
385 {
386 JOptionPane.showMessageDialog
387 (ErrorDialog.this,
388 i18n.tr("Could not send error report: {0}.", response),
389 i18n.tr("XNap Error"),
390 JOptionPane.ERROR_MESSAGE);
391
392 setTitle(i18n.tr("XNap Error"));
393
394 sendAction.setEnabled(true);
395 getCancelAction().setEnabled(true);
396
397 busy = false;
398 }
399 }
400
401 }
402
403
404
405
406
407
408