JFileChooser

Swing provides a much more sophisticated and useful file chooser component written in pure Java, javax.swing.JFileChooser:

public class JFileChooser extends JComponent implements Accessible

JFileChooser is not an independent, free-standing window like FileDialog. Instead, it is a component you can add to your own frame, dialog, or other container or window. You can, however, ask the JFileChooser class to create a modal dialog just for your file chooser. Figure 18-3 shows a file chooser embedded in a JFrame window with the Metal look and feel. Of course, like all Swing components, the exact appearance depends on the current look and feel.

Figure 18-3. A JFileChooser with the Metal look and feel

For the most part, the file chooser works as you expect, especially if you're accustomed to Windows. (On Mac OS X, it's much more obviously a nonnative dialog, in fact, so much so that you're probably better off using a java.awt.FileDialog on that platform instead.) You select a file with the mouse. Double-clicking the filename or pressing the Open button returns the currently selected file. You can change which files are displayed by selecting different filters from the pop-up list of choosable file filters. All the components have tooltips to help users who are a little thrown by an unfamiliar look and feel.

The JFileChooser class relies on support from several classes in the javax.swing.filechooser package, including:

public abstract class FileFilter public abstract class FileSystemView public abstract class FileView

There are three basic steps for asking the user to choose a file with a JFileChooser:

  1. Construct the file chooser.
  2. Display the file chooser.
  3. Get the files the user selected.

You can also set a lot of options for how files are displayed and chosen, which directory and file are selected when the file chooser first appears, which files are and are not shown in the choosers, and several other options. However, these three are your basic operations.

18.2.1. Constructing File Choosers

The JFileChooser class has six constructors. These specify the initial directory and file that appear when the chooser is shown and the view of the filesystem:

public JFileChooser( ) public JFileChooser(String initialDirectoryPath) public JFileChooser(File initialDirectory) public JFileChooser(FileSystemView fileSystemView) public JFileChooser(File initialDirectory, FileSystemView fileSystemView) public JFileChooser(String initialDirectoryPath, FileSystemView fileSystemView)

Most of the time the no-argument constructor is sufficient. The first time a particular JFileChooser object is shown, it brings up the user's home directory. If you'd like it to appear somewhere else, you can pass the directory to the constructor. For example, the following two lines construct a file chooser that appears with the Java home directory shown:

String javahome = System.getProperty("java.home"); JFileChooser chooser = new JFileChooser(javahome);

If you reuse the same JFileChooser object repeatedly by showing and hiding it, it initially displays the last directory where the user chose a file.

18.2.2. Displaying File Choosers

Although JFileChooser is a component, not a window, you usually want to display a modal dialog containing a JFileChooser component that asks the user to save or open a file. Three methods do this without requiring you to construct a dialog or frame explicitly:

public int showOpenDialog(Component parent) public int showSaveDialog(Component parent) public int showDialog(Component parent, String approveButtonText)

You use all three methods the same way. The only difference is the text shown in the dialog's title bar and on its approve button. For showOpenDialog( ), it is usually the word Open, possibly translated for the local environment. For showSaveDialog( ), it is usually the word Save, possibly translated for the local environment, and for showDialog( ), it is whatever string is passed as the second argument.

All three methods display a modal dialog that blocks input to the dialog's parent and blocks the current thread until the user either selects a file or cancels the dialog. If the user does choose a file, both these methods return JFileChooser.APPROVE_OPTION. If the user does not choose a file, both these methods return JFileChooser.CANCEL_OPTION.

18.2.3. Getting the User's Selection

If showOpenDialog( ) or showSaveDialog( ) returns JFileChooser.APPROVE_OPTION, the getSelectedFile( ) method returns a File object pointing to the file the user chose; otherwise, it returns null:

public File getSelectedFile( )

If the file chooser allows multiple selections, getSelectedFiles( ) returns an array of all the files the user chose:

public File[] getSelectedFiles( )

You can get a File object for the directory in which the selected file lives by calling getCurrentDirectory( ):

public File getCurrentDirectory( )

Example 18-4 is a program that uses JFileChooser to ask the user to select a file and then prints the file's contents on System.out. This example is essentially the same as Example 18-1, except that it uses JFileChooser instead of FileDialog.

Example 18-4. JFileTyper

import java.io.*; import java.lang.reflect.InvocationTargetException; import javax.swing.*; public class JFileTyper { public static void main(String[] args) throws InterruptedException, InvocationTargetException { SwingUtilities.invokeAndWait( new Runnable( ) { public void run( ) { JFileChooser fc = new JFileChooser( ); int result = fc.showOpenDialog(new JFrame( )); if (result == JFileChooser.APPROVE_OPTION) { InputStream in = null; try { File f = fc.getSelectedFile( ); if (f != null) { // Make sure the user didn't choose a directory. in = new FileInputStream(f); for (int c = in.read(); c != -1; c = in.read( )) { System.out.write(c); } } in.close( ); } catch (IOException e) {System.err.println(e);} } System.exit(0); } } ); } }

The dialogs shown by JFileChooser.showOpenDialog( ) and JFileChooser.showSaveDialog( ) are still Swing dialogs, and they are still subject to the usual constraints on Swing dialogs. One of those is that dialogs should only be shown from the AWT thread and then only by calling SwingUtilities.invokeLater( ), SwingUtilities.invokeAndWait( ), or the equivalent methods in EventQueue.

18.2.4. Manipulating the JFileChooser

The JFileChooser class includes several methods to specify which files and directories are selected and displayed when the chooser is shown. These include:

public void changeToParentDirectory( ) public void rescanCurrentDirectory( ) public void ensureFileIsVisible(File f)

The changeToParentDirectory( ) method simply displays the parent directory of the directory currently displayed; that is, it moves one level up in the directory hierarchy. The rescanCurrentDirectory( ) method refreshes the list of files shown. Use it when you have reason to believe a file may have been added to or deleted from the directory. ensureFileIsVisible( ) scrolls the list up or down until the specified file is shown.

Three methods allow you to specify which directory and file are selected in the file chooser:

public void setSelectedFile(File selectedFile) public void setSelectedFiles(File[] selectedFiles) public void setCurrentDirectory(File dir)

You can use these methods to point the user at a particular file. For instance, a Java source code editor might like to set the filename to the title of the class being edited plus the customary .java extension. Another common example: if the user opens a file, edits it, and selects Save As... from the File menu, it's customary to bring up the save dialog with the previous location of the file already selected. The user can change this if desired.

18.2.5. Custom Dialogs

File choosers support three dialog types: open, save, and custom. The type is indicated by one of these three mnemonic constants:

FileChooser.OPEN_DIALOG FileChooser.SAVE_DIALOG FileChooser.CUSTOM_DIALOG

You set the type with the setDialogType( ) method or, less commonly, retrieve it with getdialogType( ):

public int getDialogType( ) public void setDialogType(int dialogType)

If you use a custom dialog, you should also set the dialog title, the text of the approve button's label, the text of the approve button's tool tip, and the approve button mnemonic (shortcut key). Setting the approve button's text automatically sets the dialog to custom type. Five setter and four getter methods handle these tasks:

public void setDialogTitle(String dialogTitle) public String getDialogTitle( ) public void setApproveButtonToolTipText(String toolTipText) public String getApproveButtonToolTipText( ) public int getApproveButtonMnemonic( ) public void setApproveButtonMnemonic(int mnemonic) public void setApproveButtonMnemonic(char mnemonic) public void setApproveButtonText(String approveButtonText) public String getApproveButtonText( )

Use these methods sparingly. If you use them, you'll probably want to store the exact strings you use in a resource bundle so that your code is easily localizable.

When you're showing a custom dialog, you'll simply use the showDialog( ) method rather than showOpenDialog( ) or showSaveDialog( ) (since a custom dialog is neither):

public int showDialog(Component parent, String approveButtonText)

Suppose you want a file chooser that allows you to gzip files and exit when the user presses the Cancel button. You can set the Approve button text to "GZIP," the approve button tooltip to "Select a file, then press this button to gzip it," the approve button mnemonic to the letter "g" (for gzip), and the dialog title to "Please choose a file to gzip:," as Example 18-5 demonstrates. The chosen file is read from a file input stream and copied onto a file output stream chained to a gzip output stream that compresses the data. After both input and output streams are closed, the directory is rescanned so the compressed file appears in the list.

Example 18-5. GUIGZipper

import java.io.*; import java.lang.reflect.InvocationTargetException; import java.util.zip.*; import javax.swing.*; public class GUIGZipper { public final static String GZIP_SUFFIX = ".gz"; public static void main(String[] args) throws InterruptedException, InvocationTargetException { SwingUtilities.invokeAndWait( new Runnable( ) { public void run( ) { JFrame parent = new JFrame( ); JFileChooser fc = new JFileChooser( ); fc.setDialogTitle("Please choose a file to gzip: "); fc.setApproveButtonMnemonic('g'); while (true) { int result = fc.showDialog(parent, "Select a file, then press this button to gzip it"); if (result == JFileChooser.APPROVE_OPTION) { try { File f = fc.getSelectedFile( ); if (f == null) { JOptionPane.showMessageDialog(parent, "Can only gzip files, not directories"); } else { InputStream in = new FileInputStream(f); FileOutputStream fout = new FileOutputStream(f.getAbsolutePath( ) + GZIP_SUFFIX); OutputStream gzout = new GZIPOutputStream(fout); for (int c = in.read(); c != -1; c = in.read( )) { gzout.write(c); } // These next two should be in a finally block; but the multiple // nested try-catch blocks just got way too complicated for a // simple example in.close( ); gzout.close( ); } } catch (IOException ex) { ex.printStackTrace( ); } } else { System.exit(0); } // end else } // end while } // end run } // end Runnable ); // end invokeAndWait } // end main } // end class

To be honest, this interface is a little funny (though not nearly as strange as WinZip). If I were really tasked with writing such an application, I probably wouldn't design it like this. At a minimum, the cancel button text needs to change to "Exit" or "Quit." There's no setCancelButtonText( ) method corresponding to setApproveButtonText( ). However, you can ask for no buttons at all by passing false to setControlButtonsAreShown( ):

public void setControlButtonsAreShown(boolean b)

This would enable you to manage the chooser through your own buttons in your own frame. However, some look and feels do not respect this setting and show the approve and cancel buttons whether you turn this property off or not.

If you do want to manage the file chooser more directly, the action listeners for your buttons need to control it. They can do this by calling the approveSelection( ) and cancelSelection( ) methods:

public void approveSelection( ) public void cancelSelection( )

These methods have the same effect as pushing the regular approve and cancel buttons.

18.2.6. Filters

A FilenameFilter determines which files a file dialog shows to the user. The user cannot change this list. For instance, a user can't switch from displaying HTML files to displaying Java source code. However, a FileFilter in combination with a JFileChooser allows programmers to give users a choice about which files are filtered by providing users with a series of different file filters. By choosing a file filter from the pop-up menu in a file chooser dialog, the user can adjust which files are and are not shown. Figure 18-4 shows a file chooser that allows the user to select text files, all files, C and C++ files, Perl files, HTML files, or Java source code files.

Figure 18-4. The choosable file filters pop-up in a file chooser

Annoyingly, these file filters are not instances of the java.io.FileFilter interface you're already familiar with. Instead, they're instances of a new abstract class in the javax.swing.filechooser package. Because of name conflicts with the java.io.FileFilter interface, any file that imports both packages has to use the fully qualified name.

public abstract class javax.swing.filechooser.FileFilter

This class declares two methods, both abstract:

public abstract boolean accept(File f); public abstract String getDescription( );

The accept( ) method returns TRue if the file passes the filter and should be displayed in the chooser or false if it shouldn't be. Unlike the accept( ) method in java.io.FilenameFilter, this accept( ) method is called to filter directories as well as files. Most filters accept all directories to allow the user to navigate between directories. The geTDescription( ) method returns a string describing the filter to be shown to the user in the chooser's pop-up menu, for example, Text files (*.txt, *.text). Example 18-6 is a simple file filter that only passes Java source code files:

Example 18-6. JavaFilter

import java.io.*; public class JavaFilter extends javax.swing.filechooser.FileFilter { public boolean accept(File f) { if (f.getName( ).endsWith(".java")) return true; else if (f.getName( ).endsWith(".jav")) return true; else if (f.isDirectory( )) return true; return false; } public String getDescription( ) { return "Java source code (*.java)"; } }

Each file chooser stores a list of javax.swing.filechooser.FileFilter objects. The JFileChooser class has methods for setting and getting the list of file filters:

public void addChoosableFileFilter(FileFilter filter) public boolean removeChoosableFileFilter(FileFilter f) public FileFilter[] getChoosableFileFilters( )

You can add a file filter to the list with addChoosableFileFilter( ). You can remove a file filter from the list with removeChoosableFileFilter( ). You can retrieve the current list of file filters with getChoosableFileFilters( ).

At any given time, exactly one file filter is selected and active. In Figure 18-4, the Java filter is active. That one file filter is returned by the getFileFilter( ) method and can be changed by the setFileFilter( ) method:

public void setFileFilter(FileFilter filter) public FileFilter getFileFilter( )

By default, a JFileChooser object includes a file filter that accepts all files (*.*). A reference to this object is returned by the getAcceptAllFileFilter( ) method:

public FileFilter getAcceptAllFileFilter( )

The resetChoosableFileFilters( ) method removes all file filters from the list, except the *.* filter:

public void resetChoosableFileFilters( )

To remove the *.* filter from the list, pass false to setAcceptAllFileFilterUsed( ):

public void setAcceptAllFileFilterUsed(boolean b)

To remove a specific filter from the list, pass it to removeChoosableFileFilter( ):

public boolean removeChoosableFileFilter(FileFilter f)

Example 18-7 uses the JavaFilter class of Example 18-6 to set up a file chooser that passes Java source code files or all files.

Example 18-7. JavaChooser

import java.io.*; import java.lang.reflect.InvocationTargetException; import javax.swing.*; public class JavaChooser { public static void main(String[] args) throws InterruptedException, InvocationTargetException { SwingUtilities.invokeAndWait( new Runnable( ) { public void run( ) { JFileChooser fc = new JFileChooser( ); fc.addChoosableFileFilter(new JavaFilter( )); int result = fc.showOpenDialog(new JFrame( )); if (result == JFileChooser.APPROVE_OPTION) { try { File f = fc.getSelectedFile( ); if (f != null) { InputStream in = new FileInputStream(f); for (int c = in.read(); c != -1; c = in.read( )) { System.out.write(c); } in.close( ); } } catch (IOException ex) {System.err.println(ex);} } System.exit(0); } // end run } // end Runnable ); // end invokeAndWait } // end main } // end class

You do not need to construct a new subclass of FileFilter to create a new filter. Often it's more convenient to encapsulate some algorithm in a subclass than to parameterize the algorithm in particular objects. For instance, Example 18-8 is an ExtensionFilter that extends FileFilter. It's similar to the ExtensionFilenameFilter of Example 18-3. However, this class also needs to store a description for each extension. Furthermore, the extensions are used one at a time, not all at once. This reflects the difference between JFileChooser and FileDialog.

Example 18-8. ExtensionFilter

package com.elharo.swing.filechooser; import java.io.*; import javax.swing.filechooser.*; import javax.swing.*; public class ExtensionFilter extends javax.swing.filechooser.FileFilter { private String extension; private String description; public ExtensionFilter(String extension, String description) { if (extension.indexOf('.') == -1) { extension = "." + extension; } this.extension = extension; this.description = description; } public boolean accept(File f) { if (f.getName( ).endsWith(extension)) { return true; } else if (f.isDirectory( )) { return true; } return false; } public String getDescription( ) { return this.description + "(*" + extension + ")"; } }

ExtensionFilter is used in several upcoming examples.

18.2.7. Selecting Directories

FileDialog doesn't provide a good way to select directories instead of files. JFileChooser, by contrast, can have a selection mode that allows the user to select files, directories, or both. The selection mode is set by setFileSelectionMode( ) and returned by getFileSelectionMode( ):

public void setFileSelectionMode(int mode) public int getFileSelectionMode( )

The selection mode should be one of the three mnemonic constants JFileChooser.FILES_ONLY, JFileChooser.DIRECTORIES_ONLY, or JFileChooser.FILES_AND_DIRECTORIES:

public static final int FILES_ONLY = 0; public static final int DIRECTORIES_ONLY = 1; public static final int FILES_AND_DIRECTORIES = 2;

For example:

JFileChooser fc = new JFileChooser( ); fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);

The isFileSelectionEnabled( ) method returns TRue if the selection mode allows files to be selectedthat is, the selection mode is either FILES_ONLY or FILES_AND_DIRECTORIES.

public boolean isFileSelectionEnabled( )

The isDirectorySelectionEnabled( ) method returns TRue if the selection mode allows directories to be selectedthat is, the selection mode is either DIRECTORIES_ONLY or FILES_AND_DIRECTORIES.

public boolean isDirectorySelectionEnabled( )

Example 18-9 is a simple program that lets the user pick a directory from the file chooser. The contents of that directory are then listed.

Example 18-9. DirectoryChooser

import java.io.*; import java.lang.reflect.InvocationTargetException; import javax.swing.*; public class DirectoryLister { public static void main(String[] args) throws InterruptedException, InvocationTargetException { SwingUtilities.invokeAndWait( new Runnable( ) { public void run( ) { JFileChooser fc = new JFileChooser( ); fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); int result = fc.showOpenDialog(new JFrame( )); if (result == JFileChooser.APPROVE_OPTION) { File dir = fc.getSelectedFile( ); String[] contents = dir.list( ); for (int i = 0; i < contents.length; i++) { System.out.println(contents[i]); } } System.exit(0); } } ); } }

18.2.8. Multiple Selections

JFileChooser also enables you to allow users to choose more than one file. Just pass true to setMultiSelectionEnabled( ):

public void setMultiSelectionEnabled(boolean b)

Typically, the user shift-clicks or command-clicks on the different files he wants to select.

The isMultiSelectionEnabled( ) method returns true if the file chooser allows multiple files to be selected at one time or false otherwise:

public boolean isMultiSelectionEnabled( )

 

18.2.9. Hidden Files

Most operating systems have ways of hiding a file. By default, hidden files are not shown in file choosers. However, you can change this by passing false to the setFileHidingEnabled( ) method. You can check whether or not hidden files are shown with the isFileHidingEnabled( ) method:

public boolean isFileHidingEnabled( ) public void setFileHidingEnabled(boolean b)

 

18.2.10. File Views

The file view determines how information about files is interpreted and displayed to the user. For instance, you can use a file view to display names but not extensions, icons for files, last-modified dates of files, file sizes, and more. In general, the more information you choose to display in the file chooser, the slower the choosers are to appear and the longer it takes to switch directories. This information is encapsulated in a javax.swing.filechooser.FileView object. This class has five methods:

public String getName(File f) public String getDescription(File f) public String getTypeDescription(File f) public Icon getIcon(File f) public boolean isTraversable(File f)

You can get the current view with the getFileView( ) method:

public FileView getFileView( )

Most of the time the default file view is enough. However, you can write your own subclass of FileView that implements all five of these methods and install it in the file chooser with setFileView( ):

public void setFileView(fileView)

The getName( ) method should return the name of the file to be displayed to the user. The getdescription( ) method returns a short description of the file, generally not shown to the user. getTypeDescription( ) should return a short description of the general kind of file, also generally not shown to the user. The getIcon( ) method returns a javax.swing.ImageIcon object for the type of file, which is generally shown to the user to the left of the filename. Finally, isTraversable( ) should return Boolean.TRUE for directories the user can enter and Boolean.FALSE for a directory the user can't open. Example 18-10 is a FileView class that describes compressed files.

Example 18-10. CompressedFileView

import java.io.*; import javax.swing.*; import javax.swing.filechooser.*; public class CompressedFileView extends FileView { ImageIcon zipIcon = new ImageIcon("images/zipIcon.gif"); ImageIcon gzipIcon = new ImageIcon("images/gzipIcon.gif"); ImageIcon deflateIcon = new ImageIcon("images/deflateIcon.gif"); public String getName(File f) { return f.getName( ); } public String getTypeDescription(File f) { if (f.getName( ).endsWith(".zip")) return "Zip archive"; if (f.getName( ).endsWith(".gz")) return "Gzipped file"; if (f.getName( ).endsWith(".dfl")) return "Deflated file"; return null; } public Icon getIcon(File f) { if (f.getName( ).endsWith(".zip")) return zipIcon; if (f.getName( ).endsWith(".gz")) return gzipIcon; if (f.getName( ).endsWith(".dfl")) return deflateIcon; return null; } public String getDescription(File f) { return null; } public Boolean isTraversable(File f) { return null; } }

Two methods in this class, geTDescription( ) and isTraversable( ), always return null. The other three methods can return null if they don't recognize the file's extension. Returning null in this context means that the look and feel should figure out the details for itself. Using this class is easy once you've written it. Simply pass an instance of it to the file chooser's setFileView( ) method like this:

fc.setFileView(new CompressedFileView( ));

You also need to make sure that the GIF files images/zipIcon.gif, images/gzipIcon.gif, and images/deflateIcon.gif exist in the current working directory. In practice, it would probably be more reliable to place these files in a JAR archive and load them from there using System.getResource( ).

18.2.11. FileSystem Views

javax.swing.FileSystemView is an abstract class that connects the filesystem abstraction the programmer works with to the GUI abstraction an end user works with. For instance, on Windows, the File class shows a multiply rooted filesystem: C:, D:, etc. However, a file chooser shows a single root that is actually the C:Documents and SettingsUsernameDesktop directory. FileSystemView represents this user's view of the filesystem rather than the programmer's view of the File class.

The details depend on the platform. For example, the FileSystemView has to tell which files are hidden and which aren't. On Unix, a file whose name begins with a period is hidden. On Windows, it's not. The getFileSystemView( ) method returns a FileSystemView object configured for the local system:

public FileSystemView getFileSystemView( )

This class is mostly designed for the internal use of Swing and JFileChooser. However, it contains some generally useful methods, even if your application has no GUI at all. It's sometimes worth creating a JFileChooser just to get a FileSystemView:

JFileChooser chooser = new JFileChooser( ); FileSystemView view = chooser.getFileSystemView( );

However, this launches the AWT thread with the usual consequences, so you may not want to use it in a non-GUI application.

Once you have such an object, you can learn a lot of details about files and the filesystem that the regular java.io.File class won't tell you. This is not the intended use of this class, and it's definitely a hack, but it is useful. For example, the isRoot( ) method tells you whether a given file appears to be the root of the filesystem to a user:

public boolean isRoot(File f)

On Windows, this returns TRue for real roots such as C: but also returns TRue for the user's Desktop folder.

The isTraversable( ) method returns Boolean.TRUE if the user can enter the directory and Boolean.FALSE if she can't:

public Boolean isTraversable(File f)

I have no idea why this method returns a java.lang.Boolean object instead of a boolean primitive, but that's what it does.

The isHidden( ) method returns true if the file is not normally shown to the user or false if it is normally shown:

public boolean isHiddenFile(File f)

The getFiles( ) method returns an array of the files within a directory. The second argument controls whether or not hidden files are included in the list:

public File[] getFiles(File dir, boolean useFileHiding)

The getSystemDisplayName( ) method returns the name of the file as it should be shown to the user.

public String getSystemDisplayName(File f)

Mostly this is the same as the filename. However, occasionally it's something different. For example, on Windows, the name of the primary hard disk is C:, but the display name is Local Disk (C:).

The getSystemTypeDescription( ) method returns a short description of the file you might also want to show to the user:

public String getSystemTypeDescription(File f)

The getSystemIcon( ) method returns the file's icon that would typically be shown in the GUI shell:

public Icon getSystemIcon(File f)

The isParent( ) method returns TRue if the GUI shows the folder as the parent of the specified file:

public boolean isParent(File folder, File file)

Again, think of the Desktop on Windows whose parent is really the C:Documents and SettingsUsername folder but which appears to be the parent of everything else, including all the disks and network mounts.

The getParentDirectory( ) method returns the apparent parent directory of a specified file:

public File getParentDirectory(File dir)

For example, on Windows the parent of the C: directory is My Computer. (Of course, this isn't the real name of the directory. That's usually something incomprehensible like ::{20D04FE0-3AEA-1069-A2D8-08002B30309D})

The getChild( ) method is the reverse of this:

public File getChild(File parent, String fileName)

This method returns a File object for an apparent child of a specified directory. Almost all the time this is the same File object you'd get with the usual constructors. However, there are a few special cases where the GUI shows the parent and child somewhere other than where they actually are. In this case, getChild( ) returns the actual location of the child.

The isFileSystemRoot( ) method returns true if the file is the root of a filesystem or false if it isn't:

public boolean isFileSystemRoot(File f)

For instance, on Windows, C:, D:, and the like are treated as roots.

The isFileSystem( ) method returns true if the specified file appears to the user as a file or folder:

public boolean isFileSystem(File f)

It returns false if the file is one of the special files such as the Desktop on Windows that is shown differently in the GUI than in the filesystem.

The isDrive( ) method returns TRue if the file represents an entire disk. This usually means it has a special disk icon of some kind in the GUI shell.

public boolean isDrive(File dir)

On Windows, this returns true for A:, B:, C:, and so on and false for pretty much everything else. On Mac OS X, this method is more or less broken and can't be relied on.

The isFloppyDrive( ) method returns true if the file represents a floppy disk:

public boolean isFloppyDrive(File dir)

On Windows, this is normally true of the A: and B: directories.

The isComputerNode( ) method returns true if and only the file represents an entire computer:

public boolean isComputerNode(File dir)

This is normally true of any mounted network servers and false of all other directories.

Example 18-11 is a simple class that calls FileSystemView to list various information about a file as it might appear to a user.

Example 18-11. User-level file info

import java.io.*; import javax.swing.JFileChooser; import javax.swing.filechooser.*; public class GUIFileSpy { public static void main(String[] args) { File f = new File(args[0]); JFileChooser chooser = new JFileChooser( ); FileSystemView view = chooser.getFileSystemView( ); String name = view.getSystemDisplayName(f); if (view.isHiddenFile(f)) System.out.println(name + " is hidden."); if (view.isRoot(f)) System.out.println(name + " is a root."); if (view.isTraversable(f).booleanValue( )) { System.out.println(name + " is traversable."); } System.out.println("The parent of " + name + " is " + view.getParentDirectory(f)); if (view.isFileSystem(f)) System.out.println(name + " is a regular file."); if (view.isFileSystemRoot(f)) System.out.println(name + " is the root."); if (view.isComputerNode(f)) System.out.println(name + " is the computer."); if (view.isDrive(f)) System.out.println(name + " is a disk."); if (view.isFloppyDrive(f)) System.out.println(name + " is a floppy disk."); } }

Here's the output when run on the Mac OS X Desktop folder:

Desktop is traversable. The parent of Desktop is /Users/elharo Desktop is a regular file.

Here's the output when run on the Windows Desktop folder:

Desktop is a root. Desktop is traversable. The parent of Desktop is C:Documents and SettingsAdministrator Desktop is a regular file.

Compare this output to that from the FileSpy class in Example 17-4. That class listed information about a file as a Java program sees it. This class lists information about the file as an end user sees it. The information is related, but is not the same.

Three methods create files in a specified directory. The createNewFolder( ) method makes a new directory:

public abstract File createNewFolder(File containingDir) throws IOException

Typically, this uses whatever name is common for new directories on the host platform, untitled folder, untitled folder 2, untitled folder 3, etc. on the Mac; New Folder, New Folder (2), etc. on Windows. (Actually, the current Mac VM gets this wrong. It uses the names NewFolder, NewFolder.1, etc.)

You can also create a file. However, for this you have to provide a name as well as a directory, or a full path to the file:

public File createFileObject(File dir, String filename) public File createFileObject(String path)

However, these two methods only create the File object. If the corresponding file does not already exist, these methods do not create it. To create the file, you have to invoke createNewFile( ) on the File object these methods return.

Several methods return information about the entire filesystem rather a particular file. The geTRoots( ) method returns an array containing all the filesystem roots:

public File[] getRoots( )

This is not the same as File.listRoots( ). The getroots( ) method returns the apparent root while File.listRoots( ) returns the actual roots. For example, on Windows getroots( ) typically returns a length one array containing C:Documents and SettingAdministratorDesktop. On the same system, File.listRoots( )returns a longer array containing A:, C:, D:, and all other mapped drive letters.

The getHomeDirectory( ) method returns the user's home directory:

public File getHomeDirectory( )

This is an alternative to System.getProperty("user.home").

The geTDefaultDirectory( ) method returns the directory that the file chooser lists by default unless told otherwise:

public File getDefaultDirectory( )

On most platforms, the default directory is either the user's home directory or the current working directory. Example 18-12 is a simple class that calls FileSystemView to list various information about the local filesystem as it might appear to a user.

Example 18-12. Filesystem info

import java.io.*; import javax.swing.JFileChooser; import javax.swing.filechooser.*; public class FileSystemViewer { public static void main(String[] args) { JFileChooser chooser = new JFileChooser( ); FileSystemView view = chooser.getFileSystemView( ); System.out.println("The home directory is " + view.getHomeDirectory( )); System.out.println("The default directory is " + view.getDefaultDirectory( )); System.out.println("The roots of this filesystem are: "); File[] roots = view.getRoots( ); for (int i = 0; i < roots.length; i++) { System.out.println(" " + roots[i]); } } }

Here's the output when run on Windows while logged in as Administrator:

The home directory is C:Documents and SettingsAdministratorDesktop The default directory is C:Documents and SettingsAdministratorMy Documents The roots of this filesystem are: C:Documents and SettingsAdministratorDesktop

 

18.2.12. Handling Events

FileDialog is difficult to work with because of its synchronous nature. When a file dialog is shown, it blocks execution of the calling thread and all input to the parent frame. A raw JFileChooser, by contrast (not a JFileChooser embedded in a modal dialog by showOpenDialog( ), showSaveDialog( ), or showDialog( )), is asynchronous. It follows the standard AWT event model and can fire action and property change events.

18.2.12.1. Action events

When the user hits the Approve button, the chooser fires an action event with the action command JFileChooser.APPROVE_SELECTION. When the user hits the Cancel button, the chooser fires an action event with the action command JFileChooser.CANCEL_SELECTION.

public static final String CANCEL_SELECTION = "CancelSelection"; public static final String APPROVE_SELECTION = "ApproveSelection";

You register and remove action listeners with the file chooser in the usual fashion using addActionListener( ) and removeActionListener( ):

public void addActionListener(ActionListener l) public void removeActionListener(ActionListener l)

The approveSelection( ) and cancelSelection( ) methods are called by the user interface when the user hits the Approve or Cancel button, respectively. You can call them yourself if you're driving the selection directly:

public void approveSelection( ) public void cancelSelection( )

Each of these methods fires an action event to all the registered action listeners by invoking the fireActionPerformed( ) method:

protected void fireActionPerformed(String command)

 

18.2.12.2. Property change events

When the state of a file chooser changes, the file chooser fires a property change event (an instance of java.beans.PropertyChangeEvent). Property changes are triggered by file selections, changing directories, hitting the Approve or Cancel button, and many more actions. The event fired has its own name property set to one of the following constants in the JFileChooser class:

public static final String CANCEL_SELECTION = "CancelSelection"; public static final String APPROVE_SELECTION = "ApproveSelection"; public static final String APPROVE_BUTTON_TEXT_CHANGED_PROPERTY = "ApproveButtonTextChangedProperty"; public static final String APPROVE_BUTTON_TOOL_TIP_TEXT_CHANGED_PROPERTY = "ApproveButtonToolTipTextChangedProperty"; public static final String APPROVE_BUTTON_MNEMONIC_CHANGED_PROPERTY = "ApproveButtonMnemonicChangedProperty"; public static final String DIRECTORY_CHANGED_PROPERTY = "directoryChanged"; public static final String SELECTED_FILE_CHANGED_PROPERTY = "ApproveSelection"; public static final String MULTI_SELECTION_ENABLED_CHANGED_PROPERTY = "fileFilterChanged"; public static final String FILE_SYSTEM_VIEW_CHANGED_PROPERTY = "FileSystemViewChanged"; public static final String FILE_VIEW_CHANGED_PROPERTY = "fileViewChanged"; public static final String FILE_HIDING_CHANGED_PROPERTY = "FileHidingChanged"; public static final String FILE_FILTER_CHANGED_PROPERTY = "fileFilterChanged"; public static final String FILE_SELECTION_MODE_CHANGED_PROPERTY = "fileSelectionChanged"; public static final String ACCESSORY_CHANGED_PROPERTY = "AccessoryChangedProperty"; public static final String DIALOG_TYPE_CHANGED_PROPERTY = "DialogTypeChangedProperty"; public static final String CHOOSABLE_FILE_FILTER_CHANGED_PROPERTY = "ChoosableFileFilterChangedProperty";

You listen for and respond to property change events through an instance of the java.beans.PropertyChangeListener interface. This interface declares a single method, propertyChange( ). However, it's relatively rare to use a property change listener with a file chooser. Most of the time, you don't need to do anything as a result of a state change in the file chooser. You might want to respond to a property change event fired by a file chooser if you're using an accessory to preview the selected file. In this case, you'll watch for changes in the SELECTED_FILE_CHANGED_PROPERTY, as demonstrated in the next section.

18.2.13. Accessory

An accessory is an optional component you can add to the JFileChooser. The most common use of an accessory is to show a preview of the file. For example, a file chooser for selecting an image file might provide an accessory that shows a thumbnail of the picture. The setAccessory( ) method adds an accessory to the file chooser while the getAccessory( ) method returns a reference to it:

public JComponent getAccessory( ) public void setAccessory(JComponent newAccessory)

A JFileChooser object can have at most one accessory and does not need to have any.

Example 18-13 is a chooser that uses a JTextArea as an accessory to show the first few lines of the selected text file. This TextFilePreview class extends JTextArea so that it can easily display text. It implements the PropertyChangeListener interface so that it can be notified through its propertyChange( ) method when the user changes the selected file and the preview needs to be changed. The loadText( ) method reads in the first few hundred bytes of the selected file and stores that data in the preview field. Finally, the main( ) method tests this class by displaying a file chooser with this accessory. Figure 18-5 shows the result.

Figure 18-5. A JFileChooser with a TextFilePreview accessory

 

Example 18-13. TextFilePreview

import javax.swing.*; import java.beans.*; import java.io.*; import java.lang.reflect.InvocationTargetException; import java.awt.*; public class TextFilePreview extends JTextArea implements PropertyChangeListener { private File selectedFile = null; private String preview = ""; private int previewLength = 250; public TextFilePreview(JFileChooser fc) { super(10, 20); this.setEditable(false); this.setPreferredSize(new Dimension(150, 150)); this.setLineWrap(true); fc.addPropertyChangeListener(this); } private void loadText( ) { if (selectedFile != null) { try { FileInputStream fin = new FileInputStream(selectedFile); byte[] data = new byte[previewLength]; int bytesRead = 0; for (int i = 0; i < previewLength; i++) { int b = fin.read( ); if (b == -1) break; bytesRead++; data[i] = (byte) b; } preview = new String(data, 0, bytesRead); fin.close( ); } catch (IOException ex) { // File preview is not an essential operation so // we'll simply ignore the exception and return. } } } public void propertyChange(PropertyChangeEvent evt) { if (evt.getPropertyName( ).equals( JFileChooser.SELECTED_FILE_CHANGED_PROPERTY)) { selectedFile = (File) evt.getNewValue( ); if(isShowing( )) { loadText( ); this.setText(preview); } } } public static void main(String[] args) throws InterruptedException, InvocationTargetException { SwingUtilities.invokeAndWait( new Runnable( ) { public void run( ) { JFileChooser fc = new JFileChooser( ); fc.setAccessory(new TextFilePreview(fc)); int result = fc.showOpenDialog(new JFrame( )); if (result == JFileChooser.APPROVE_OPTION) { try { File f = fc.getSelectedFile( ); if (f != null) { InputStream in = new FileInputStream(f); for (int c = in.read(); c != -1; c = in.read( )) { System.out.write(c); } in.close( ); } } catch (IOException ex) {System.err.println(ex);} } System.exit(0); } // end run } // end Runnable ); // end invokeAndWait } }

Категории