View Javadoc

1   /*
2    *  XNap Commons
3    *
4    *  Copyright (C) 2005  Steffen Pingel
5    *
6    *  This library is free software; you can redistribute it and/or
7    *  modify it under the terms of the GNU Lesser General Public
8    *  License as published by the Free Software Foundation; either
9    *  version 2.1 of the License, or (at your option) any later version.
10   *
11   *  This library is distributed in the hope that it will be useful,
12   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14   *  Lesser General Public License for more details.
15   *
16   *  You should have received a copy of the GNU Lesser General Public
17   *  License along with this library; if not, write to the Free Software
18   *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
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 		// top	
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 		// details, instantiate prior to action
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 		// details button
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 		// add details panel
135 		mainBox.add(detailsPanel);
136 		
137 		// check boxes
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     	// XXX what the hell, if we remove this, ErrorHandle gets really upset
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 		prefs.set("errorDialogWidth", getBounds().getSize().width);
197 		prefs.set("errorDialogHeight", getBounds().getSize().height);
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 			try {
351 				Iterator i1 = threads.iterator();
352 				Iterator i2 = throwables.iterator();
353 				while (i1.hasNext() && i2.hasNext()) {
354 					exceptionManager.sendProblemReport
355 						((Thread)i1.next(), (Throwable)i2.next());
356 				}
357 			}
358 			catch(final IOException e) {
359 				Runnable runner = new Runnable()
360 					{
361 						public void run()
362 						{
363 							failed(NetHelper.getErrorMessage(e));
364 						}
365 					};
366 				SwingUtilities.invokeLater(runner);
367 				return;
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