/* * $Id$ * * Copyright (c) 2006-2009 by Joel Uckelman * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License (LGPL) as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, copies are available * at http://www.opensource.org. */ package VASSAL.tools.filechooser; import java.awt.Component; import java.awt.Dialog; import java.awt.FileDialog; import java.awt.Frame; import java.io.File; import javax.swing.JFileChooser; import javax.swing.JOptionPane; import javax.swing.SwingUtilities; import org.apache.commons.lang.SystemUtils; import VASSAL.configure.DirectoryConfigurer; /** * FileChooser provides a wrapper for {@link javax.swing.JFileChooser} and * {@link java.awt.FileDialog}, selecting whichever is preferred on the * user's OS. <code>FileChooser</code>'s methods mirror those of * <code>JFileChooser</code>. * * @author Joel Uckelman */ public abstract class FileChooser { protected Component parent; protected DirectoryConfigurer prefs; public final static int APPROVE_OPTION = JFileChooser.APPROVE_OPTION; public final static int CANCEL_OPTION = JFileChooser.CANCEL_OPTION; public final static int ERROR_OPTION = JFileChooser.ERROR_OPTION; public final static int FILES_ONLY = JFileChooser.FILES_ONLY; public final static int DIRECTORIES_ONLY = JFileChooser.DIRECTORIES_ONLY; protected FileChooser(Component parent, DirectoryConfigurer pref) { this.parent = parent; this.prefs = pref; } public static FileChooser createFileChooser(Component parent) { return createFileChooser(parent, null); } public static FileChooser createFileChooser(Component parent, DirectoryConfigurer prefs) { return createFileChooser(parent, prefs, FILES_ONLY); } /** * Creates a FileChooser appropriate for the user's OS. * * @param parent * The Component over which the FileChooser should appear. * @param prefs * A FileConfigure that stores the preferred starting directory of the FileChooser in preferences */ public static FileChooser createFileChooser(Component parent, DirectoryConfigurer prefs, int mode) { FileChooser fc; if (SystemUtils.IS_OS_MAC_OSX) { // Mac has a good native file dialog fc = new NativeFileChooser(parent, prefs, mode); } else if (mode == FILES_ONLY && SystemUtils.IS_OS_WINDOWS) { // Window has a good native file dialog, but it doesn't support selecting directories fc = new NativeFileChooser(parent, prefs, mode); } else { // Linux's native dialog is inferior to Swing's fc = new SwingFileChooser(parent, prefs, mode); } return fc; } public abstract File getCurrentDirectory(); public abstract void setCurrentDirectory(File dir); public abstract void rescanCurrentDirectory(); public abstract File getSelectedFile(); public abstract void setSelectedFile(File file); public abstract String getDialogTitle(); public abstract void setDialogTitle(String title); public abstract int showOpenDialog(Component parent); public abstract int showSaveDialog(Component parent); public abstract FileFilter getFileFilter(); public abstract void setFileFilter(FileFilter filter); public abstract void addChoosableFileFilter(FileFilter filter); public abstract boolean removeChoosableFileFilter(FileFilter filter); public abstract void resetChoosableFileFilters(); /** * Selects <tt>filename.sav</tt> if <tt>filename.foo</tt> is selected. */ public void selectDotSavFile() { File file = getSelectedFile(); if (file != null) { String name = file.getPath(); if (name != null) { int index = name.lastIndexOf('.'); if (index > 0) { name = name.substring(0, index) + ".vsav"; setSelectedFile(new File(name)); } } } } /** * Same as {@link #showOpenDialog(Component)}, but uses the <tt>parent</tt> set on creation of this FileDialog. */ public int showOpenDialog() { return showOpenDialog(parent); } /** * Same as {@link #showSaveDialog(Component)}, but uses the <tt>parent</tt> set on creation of this FileDialog. */ public int showSaveDialog() { return showSaveDialog(parent); } protected void updateDirectoryPreference() { if (prefs != null && getCurrentDirectory() != null && !getCurrentDirectory().equals(prefs.getFileValue())) { prefs.setValue(getCurrentDirectory()); } } private static class SwingFileChooser extends FileChooser { private JFileChooser fc = new JFileChooser(); public SwingFileChooser(Component parent, DirectoryConfigurer prefs, int mode) { super(parent, prefs); if (prefs != null && prefs.getFileValue() != null) { setCurrentDirectory(prefs.getFileValue()); } if (mode == DIRECTORIES_ONLY) { setFileFilter(new DirectoryFileFilter()); } fc.setFileSelectionMode(mode); } public File getCurrentDirectory() { return fc.getCurrentDirectory(); } public void setCurrentDirectory(File dir) { fc.setCurrentDirectory(dir); } public void rescanCurrentDirectory() { fc.rescanCurrentDirectory(); } public File getSelectedFile() { return fc.getSelectedFile(); } public void setSelectedFile(File file) { fc.setSelectedFile(file); } public int getFileSelectionMode() { return fc.getFileSelectionMode(); } public void setFileSelectionMode(int mode) { fc.setFileSelectionMode(mode); } public String getDialogTitle() { return fc.getDialogTitle(); } public void setDialogTitle(String title) { fc.setDialogTitle(title); } public int showOpenDialog(Component parent) { int value = fc.showOpenDialog(parent); updateDirectoryPreference(); return value; } public int showSaveDialog(Component parent) { int value = fc.showSaveDialog(parent); if (value == APPROVE_OPTION && getSelectedFile().exists() && JOptionPane.NO_OPTION == JOptionPane.showConfirmDialog(parent, "Overwrite " + getSelectedFile().getName() + "?", "File Exists", JOptionPane.YES_NO_OPTION)) { value = CANCEL_OPTION; } updateDirectoryPreference(); return value; } public FileFilter getFileFilter() { javax.swing.filechooser.FileFilter ff = fc.getFileFilter(); return ff instanceof FileFilter ? (FileFilter) ff : null; } public void setFileFilter(FileFilter filter) { fc.setFileFilter(filter); } public void addChoosableFileFilter(FileFilter filter) { fc.addChoosableFileFilter(filter); } public boolean removeChoosableFileFilter(FileFilter filter) { return fc.removeChoosableFileFilter(filter); } public void resetChoosableFileFilters() { fc.resetChoosableFileFilters(); } } private static class NativeFileChooser extends FileChooser { private File cur; private String title; private FileFilter filter; private int mode = FILES_ONLY; public NativeFileChooser(Component parent, DirectoryConfigurer prefs, int mode) { super(parent, prefs); if (prefs != null && prefs.getFileValue() != null) { setCurrentDirectory(prefs.getFileValue()); } this.mode = mode; if (mode == DIRECTORIES_ONLY) { setFileFilter(new DirectoryFileFilter()); } } public File getCurrentDirectory() { return cur == null ? null : cur.getParentFile(); } public void setCurrentDirectory(File dir) { cur = dir; } public void rescanCurrentDirectory() { } public File getSelectedFile() { return cur; } public void setSelectedFile(File file) { cur = file; } public int getFileSelectionMode() { return mode; } public void setFileSelectionMode(int mode) { this.mode = mode; } public String getDialogTitle() { return title; } public void setDialogTitle(String title) { this.title = title; } // // On Java 1.5, we cannot create parentless Dialogs. Instead, we make // a single global hidden Frame to be the parent of all such orphans. // This can be removed when we stop supporting Java 1.5. // protected static final boolean isJava15; protected static final Frame dummy; static { final String jvmver = System.getProperty("java.version"); isJava15 = jvmver == null || jvmver.startsWith("1.5"); dummy = isJava15 ? new Frame() : null; } protected FileDialog awt_file_dialog_init(Component parent) { final FileDialog fd; if (parent == null) { if (isJava15) { // Parentless Dialogs throw IllegalArgumentException with Java 1.5. fd = new FileDialog(dummy, title); } else { fd = new FileDialog((Frame) null, title); } } else if (parent instanceof Dialog) { fd = new FileDialog((Dialog) parent, title); } else if (parent instanceof Frame) { fd = new FileDialog((Frame) parent, title); } else { final Dialog d = (Dialog) SwingUtilities.getAncestorOfClass(Dialog.class, parent); if (d != null) { fd = new FileDialog(d, title); } else { final Frame f = (Frame) SwingUtilities.getAncestorOfClass(Frame.class, parent); if (f != null) { fd = new FileDialog(f, title); } else { // should be impossible, parent is not in a dialog or frame! throw new IllegalArgumentException( "parent is contained in neither a Dialog nor a Frame"); } } } fd.setModal(true); fd.setFilenameFilter(filter); if (cur != null) { if (cur.isDirectory()) fd.setDirectory(cur.getPath()); else { fd.setDirectory(cur.getParent()); fd.setFile(cur.getName()); } } return fd; } public int showOpenDialog(Component parent) { final FileDialog fd = awt_file_dialog_init(parent); fd.setMode(FileDialog.LOAD); System.setProperty("apple.awt.fileDialogForDirectories", String.valueOf(mode == DIRECTORIES_ONLY)); fd.setVisible(true); final int value; if (fd.getFile() != null) { cur = new File(fd.getDirectory(), fd.getFile()); value = FileChooser.APPROVE_OPTION; } else { value = FileChooser.CANCEL_OPTION; } updateDirectoryPreference(); return value; } public int showSaveDialog(Component parent) { final FileDialog fd = awt_file_dialog_init(parent); fd.setMode(FileDialog.SAVE); fd.setVisible(true); final int value; if (fd.getFile() != null) { cur = new File(fd.getDirectory(), fd.getFile()); value = FileChooser.APPROVE_OPTION; } else { value = FileChooser.CANCEL_OPTION; } updateDirectoryPreference(); return value; } public FileFilter getFileFilter() { return filter; } public void setFileFilter(FileFilter filter) { this.filter = filter; } public void addChoosableFileFilter(FileFilter filter) { } public boolean removeChoosableFileFilter(FileFilter filter) { return false; } public void resetChoosableFileFilters() { } } }