View Javadoc

1   /*
2    *  XNap Commons
3    *
4    *  Copyright (C) 2005  Felix Berger
5    *  Copyright (C) 2005  Steffen Pingel
6    *
7    *  This library is free software; you can redistribute it and/or
8    *  modify it under the terms of the GNU Lesser General Public
9    *  License as published by the Free Software Foundation; either
10   *  version 2.1 of the License, or (at your option) any later version.
11   *
12   *  This library is distributed in the hope that it will be useful,
13   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15   *  Lesser General Public License for more details.
16   *
17   *  You should have received a copy of the GNU Lesser General Public
18   *  License along with this library; if not, write to the Free Software
19   *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20   */
21  package org.xnap.commons.gui;
22  
23  import java.awt.Component;
24  import java.io.File;
25  import java.io.IOException;
26  import javax.swing.Box;
27  import javax.swing.DefaultListCellRenderer;
28  import javax.swing.JCheckBox;
29  import javax.swing.JLabel;
30  import javax.swing.JList;
31  import javax.swing.JOptionPane;
32  import javax.swing.JPasswordField;
33  import javax.swing.JScrollPane;
34  import org.xnap.commons.gui.util.GUIHelper;
35  import org.xnap.commons.i18n.I18n;
36  import org.xnap.commons.i18n.I18nFactory;
37  import org.xnap.commons.settings.BooleanSetting;
38  import org.xnap.commons.util.FileHelper;
39  
40  /***
41   * Provides a set of static methods to display common dialogs.
42   */
43  public class Dialogs {
44  
45  	private static final I18n i18n = I18nFactory.getI18n(Dialogs.class);
46  	
47      /***
48       * Copies <code>files</code> to <code>target</code>. Shows a dialog
49       * in case of an error.
50       *  
51       * @see FileHelper#copy(File, File)
52       * @param files the files to copy
53       * @param target the target directory
54       * @return true, if copy was successful; false, otherwise or if 
55       *  <code>files</code> is empty
56       *  TODO copying could be costly, show progress dialog here too, see DropFileTree..
57       */
58      public static boolean executeCopy(Component parent, File[] files, File target)
59      {
60  		if (files != null) {
61  			boolean success = true;
62  			for (int i = 0; i < files.length; i++) {
63  				try {
64  					FileHelper.copy(files[i], new File
65  						(target, files[i].getName()));
66  				}
67  				catch (IOException e) {
68  					showError
69  						(parent, 
70  						 i18n.tr("Could not copy {0} to {1}: {2}.", 
71  						 		 files[i].getAbsolutePath(), 
72  								 target.getAbsolutePath(),
73  								 e.getLocalizedMessage()),
74  						 e);
75  					success = false;
76  				}
77  			}
78  			return success;
79  		}
80  	
81  		return false;
82      }
83  
84      /***
85       * Moves <code>files</code> to <code>target</code>. Shows a dialog
86       * in case of an error. Renames each file in case it already exists
87       * in <code>target</code>. 
88       *  
89       * @param files the files to copy
90       * @param target the target directory
91       * @return true, if move was successful; false, otherwise or if 
92       *  <code>files</code> is empty
93       */
94      public static boolean executeMove(Component parent, File[] files, File target)
95      {
96  		if (files != null) {
97  			boolean success = true;
98  			for (int i = 0; i < files.length; i++) {
99  				try {
100 					FileHelper.moveUnique(files[i], target.getAbsolutePath());
101 				}
102 				catch (IOException e) {
103 					showError
104 						(parent, 
105 						 i18n.tr("Could not move {0} to {1}: {2}.", 
106 						 		 files[i].getAbsolutePath(), 
107 								 target.getAbsolutePath(),
108 								 e.getLocalizedMessage()),
109 						 e);
110 					success = false;
111 				}
112 			}
113 			return success;
114 		}
115 	
116 		return false;
117      }
118 
119     /***
120      * Shows an input dialog and returns the entered value or <code>null</code>.
121      * @param parent the parent component the input dialog is centered on
122      * @param message the message giving details about the requested input
123      * @param title the title of the dialog
124      * @return the entered value or <code>null</code> if the dialog was cancelled
125      */
126     public static String requestInput(Component parent, String message, String title)
127     {
128 		return JOptionPane.showInputDialog
129 			(parent, message, title, JOptionPane.QUESTION_MESSAGE);
130     }
131 
132     /***
133      * Shows a password input dialog and returns the entered value or <code>null</code>.
134      * @param parent the parent component the input dialog is centered on
135      * @param message the message giving details about the requested input
136      * @param title the title of the dialog
137      * @return the entered value or <code>null</code> if the dialog was cancelled
138      */
139     public static String requestPassword(Component parent, String message, String title)
140     {
141 		JPasswordField passwordField = new JPasswordField();
142 		Object[] content = new Object[] {
143 			message,
144 			passwordField,
145 		};
146 
147 		if (JOptionPane.showConfirmDialog(parent, content, title, 
148 				JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE)
149 				== JOptionPane.OK_OPTION) {
150 			return new String(passwordField.getPassword());
151 		} else {
152 			return null;
153 		}
154     }
155 
156 	 /***
157 	  * Shows a confirmation dialog asking the user if he really wants to quit
158 	  * the application.
159 	  * <p>
160 	  * It also offers a checkbox "Always quit without prompting me." If the user
161 	  * checks it, the next time this function is called it will return <code>true</code>
162 	  * without showing the dialog.
163 	  * @param parent the parent component this dialog is centered on
164 	  * @param message a message explaining what it means to quit the 
165 	  * application
166 	  * @param setting the boolean setting that stores the state of the checkbox
167 	  * @return true, if user presses okay button.
168 	  */
169 	 public static boolean showCloseDialog(Component parent, String message, BooleanSetting setting) 
170 	 {
171 		 JCheckBox alwaysQuitCheckBox 
172 			 = new JCheckBox(i18n.tr("Always quit without prompting me"));
173 		 if (setting != null) {
174 		 	alwaysQuitCheckBox.setSelected(setting.getValue());
175 		 }
176 
177 		 Object[] content = new Object[] {
178 			 message,
179 			 i18n.tr("Do you really want to quit?"), 
180 			 alwaysQuitCheckBox
181 		 };
182 
183 		 int response = JOptionPane.showConfirmDialog
184 			 (parent, content, i18n.tr("Quit"), JOptionPane.OK_CANCEL_OPTION);
185 
186 		 setting.setValue(alwaysQuitCheckBox.isSelected());
187 
188 		 return (response == JOptionPane.OK_OPTION);
189 	 }	
190 
191 	 /***
192 	  * Shows a confirmation dialog and with a message and check box that can
193 	  * be selected to not display the dialog in the future anymore.
194 	  * 
195 	  * <p>If the specified setting is not <code>null</code> and it's value is 
196 	  * <code>false</code> the dialog is not displayed and 
197 	  * <code>JOptionPane.YES_OPTION</code> is returned.
198 	  * 
199 	  * @param parent parent component used for centering the dialog
200 	  * @param message the message to show
201 	  * @param title the title of the message border
202 	  * @param optionType an int designating the options available on the 
203 	  * dialog: <code>JOptionPane.YES_NO_OPTION</code>, or 
204 	  * <code>JOptionPane.YES_NO_CANCEL_OPTION</code> 
205 	  * @param setting used to store the reverse state of the 
206 	  * <it>Do not ask me again</it> check box; if null, the state is not
207 	  * saved
208 	  * @return an int indicating the option selected by user
209 	  */
210 	 public static int showConfirmDialog(Component parent,
211 	 									 String message,
212 										 String title, 
213 										 int optionType,
214 										 BooleanSetting setting)
215 	 {
216 		 if (setting != null && !setting.getValue()) {
217 			 return JOptionPane.YES_OPTION;
218 		 }
219 
220 		 JCheckBox doNotAskAgainCheckBox = new JCheckBox(i18n.tr("Do not ask again"));
221 
222 		 JLabel messageLabel = new JLabel(GUIHelper.tt(message, 400));
223 		 //messageLabel.setBorder(GUIHelper.createDefaultBorder(title));
224 
225 		 Object[] content = new Object[] {
226 			 messageLabel, 
227 			 Box.createVerticalStrut(10), 
228 			 doNotAskAgainCheckBox,
229 			 Box.createVerticalStrut(5),
230 		 };
231 
232 		 int response = JOptionPane.showConfirmDialog
233 			 (parent, content, title, optionType);
234 
235 		 if (setting != null) {
236 		 	setting.setValue(!doNotAskAgainCheckBox.isSelected());
237 		 }
238 
239 		 return response;
240     }
241 
242 	 /***
243 	  * Displays a dialog that with a list of files asking the user to confirm 
244 	  * a copy operation to the specified target.
245 	  *
246 	  * <p>If the specified setting is not <code>null</code> and it's value is 
247 	  * <code>false</code> the dialog is not displayed and the specified
248 	  * files are returned. 
249 	  *
250 	  * @param parent the dialog parent
251 	  * @param files the files that are to be displayed in the list 
252 	  * @param target the destination path
253 	  * @param setting used to store the reverse state of the 
254 	  * <it>Do not ask me again</it> check box; if null, the state is not
255 	  * saved
256 	  * @return the selected files, i.e. the ones that should be copied; if the 
257 	  * user selected cancel an empty array is returned, never returns null
258 	  * 
259 	  * @see #showFilesActionDialog(Component, File[], String, String, BooleanSetting)
260 	  */
261     public static File[] showCopyDialog(Component parent, File files[], File target,
262     									BooleanSetting setting)
263     {
264 		return showFilesActionDialog
265 			(parent, files, 
266 			 i18n.trn("Do you really want to copy the selected file to \"{0}\"?", 
267 					 "Do you really want to copy the selected files to \"{0}\"?",
268 					 files.length,
269 			 		 target.getAbsolutePath()),
270 			 i18n.tr("Copy Files"), setting);
271     }
272 
273 	 /***
274 	  * Displays a dialog that with a list of files asking the user to confirm 
275 	  * a delete operation.
276 	  *
277 	  * <p>If the specified setting is not <code>null</code> and it's value is 
278 	  * <code>false</code> the dialog is not displayed and the specified
279 	  * files are returned. 
280 	  *
281 	  * @param parent the dialog parent
282 	  * @param files the files that are to be displayed in the list 
283 	  * @param setting used to store the reverse state of the 
284 	  * <it>Do not ask me again</it> check box; if null, the state is not
285 	  * saved
286 	  * @return the selected files, i.e. the ones that should be copied; if the 
287 	  * user selected cancel an empty array is returned, never returns null
288 	  * 
289 	  * @see #showFilesActionDialog(Component, File[], String, String, BooleanSetting)
290 	  */
291     public static File[] showDeleteDialog(Component parent, File files[], 
292     								      BooleanSetting setting)
293     {
294 		return showFilesActionDialog
295 			(parent, files, i18n.tr("Do you really want to delete the selected files?"),
296 			 i18n.tr("Delete Files"), setting);
297     }
298 	
299 	 /***
300 	  * Displays a dialog with a message indicating the cause of an error. The 
301 	  * dialog's title is set to <tt>Error</tt>.
302 	  *  
303 	  * @param parent the parent component
304 	  * @param message the error message
305 	  */
306 	public static void showError(Component parent, String message)
307 	{
308 		showError(parent, message, i18n.tr("Error"));
309 	}
310 
311 	 /***
312 	  * Displays a dialog with a message indicating the cause of an error
313 	  * a stack trace of the specified exception providing details. The dialog's
314 	  * title is set to <tt>Error</tt>.
315 	  *  
316 	  * @param parent the parent component
317 	  * @param message the error message
318 	  */
319     public static void showError(Component parent, String message, Exception e)
320     {
321     	showError(parent, message, i18n.tr("Error"), e);
322     }
323 		
324 	 /***
325 	  * Displays a dialog with a message indicating the cause of an error.
326 	  *  
327 	  * @param parent the parent component
328 	  * @param message the error message
329 	  * @param title the dialog title
330 	  */
331     public static void showError(Component parent, String message, String title)
332     {
333 		JOptionPane.showMessageDialog
334 			(parent, message, title, JOptionPane.ERROR_MESSAGE);
335     }
336     
337 	 /***
338 	  * Displays a dialog with a message indicating the cause of an error
339 	  * a stack trace of the specified exception providing details.
340 	  *  
341 	  * @param parent the parent component
342 	  * @param message the error message
343 	  * @param title the dialog title
344 	  * @param exception the exception that caused the error 
345 	  */
346     public static void showError(Component parent, String message, String title, 
347     		Exception exception)
348     {
349     	ErrorDialog.show(parent, message, title, exception
350     			
351     	);
352     }
353 
354 	 /***
355 	  * Displays a dialog with a message, a list of files and a check box that 
356 	  * can be selected to not display the dialog in the future anymore.
357 	  * 
358 	  * <p>If the specified setting is not <code>null</code> and it's value is 
359 	  * <code>false</code> the dialog is not displayed and the specified
360 	  * files are returned. 
361 	  *
362 	  * @param parent the dialog parent
363 	  * @param files the files that are to be displayed in the list
364 	  * @param title the dialog's title
365 	  * @param message the message
366 	  * @param setting used to store the reverse state of the 
367 	  * <it>Do not ask me again</it> check box; if null, the state is not
368 	  * saved
369 	  * @return the selected files; if the dialog was cancelled an empty array
370 	  * is returned, never returns null
371 	  */
372     public static File[] showFilesActionDialog
373 		(Component parent, File files[], String message, String title, 
374 		 BooleanSetting setting)
375     {
376 		if (setting != null && !setting.getValue()) {
377 			return files;
378 		}
379 
380 		JList fileList = new JList(files);
381 		fileList.setCellRenderer(new FileListCellRenderer());
382 		fileList.setVisibleRowCount(4);
383 
384 		// select all files
385 		fileList.setSelectionInterval(0, files.length - 1);
386 
387 		JCheckBox doNotAskAgainCheckBox = new JCheckBox(i18n.tr("Do not ask again"));
388 
389 		Object[] content = new Object[] {
390 			message,
391 			Box.createVerticalStrut(10),
392 			new JScrollPane(fileList), 
393 			Box.createVerticalStrut(10),
394 			doNotAskAgainCheckBox, 
395 			Box.createVerticalStrut(5),
396 		};
397 	
398 		int response = JOptionPane.showConfirmDialog
399             (parent, content, title, JOptionPane.YES_NO_OPTION);
400 	
401 		 if (setting != null) {
402 		 	setting.setValue(!doNotAskAgainCheckBox.isSelected());
403 		 }
404 	
405 		if (response == JOptionPane.YES_OPTION) {
406 			Object rows[] = fileList.getSelectedValues();
407 			File selected[] = new File[rows.length];
408 			System.arraycopy(rows, 0, selected, 0, selected.length);
409 			return selected;
410 		}
411 		
412 		return new File[0];
413     }
414     
415 	 /***
416 	  * Displays a dialog with an information message.
417 	  *  
418 	  * @param parent the parent component
419 	  * @param message the error message
420 	  * @param title the dialog title
421 	  * 
422 	  * @see JOptionPane#showMessageDialog(java.awt.Component, java.lang.Object, java.lang.String, int)
423 	  */
424     public static void showInfo(Component parent, String message, String title)
425     {
426 		JOptionPane.showMessageDialog
427 			(parent, message, title, JOptionPane.INFORMATION_MESSAGE);
428     }
429 
430 	 /***
431 	  * Displays a dialog that with a list of files asking the user to confirm 
432 	  * a delete operation.
433 	  *
434 	  * <p>If the specified setting is not <code>null</code> and it's value is 
435 	  * <code>false</code> the dialog is not displayed and the specified
436 	  * files are returned. 
437 	  *
438 	  * @param parent the dialog parent
439 	  * @param files the files that are to be displayed in the list 
440 	  * @param target the destination path
441 	  * @param setting used to store the reverse state of the 
442 	  * <it>Do not ask me again</it> check box; if null, the state is not
443 	  * saved
444 	  * @return the selected files, i.e. the ones that should be copied; if the 
445 	  * user selected cancel an empty array is returned, never returns null
446 	  * 
447 	  * @see #showFilesActionDialog(Component, File[], String, String, BooleanSetting)
448 	  */
449     public static File[] showMoveDialog(Component parent, File files[], File target, 
450     									BooleanSetting setting)
451     {
452 		return showFilesActionDialog
453 			(parent, files, 
454 			 i18n.tr("Do you really want to move the selected files to \"{0}\"?", 
455 			 		 target.getAbsolutePath()),
456 			 i18n.tr("Move Files"), setting);
457     }
458 
459     /***
460      * This class provides a list renderer for <code>File</code> objects. The
461      * path is supressed when showing filenames.
462      */
463     public static class FileListCellRenderer extends DefaultListCellRenderer
464     {
465 
466     	public FileListCellRenderer()
467         {
468         }
469 
470         public Component getListCellRendererComponent(JList list, Object value, 
471     						  int index, 
472     						  boolean isSelected, 
473     						  boolean cellHasFocus) 
474         {
475         	super.getListCellRendererComponent(list, value, index, isSelected, 
476         									   cellHasFocus);
477     	
478         	if (value instanceof File) {
479         		setText(((File)value).getName());
480         	}
481     	
482         	return this;
483         }
484         
485     }
486 
487 }
488