Java Tips Weblog

  • Blog Stats

    • 2,571,044 hits
  • Categories

  • Archives

Single Root File Chooser

Posted by Rob Camick on January 28, 2009

The JFileChooser component allows a user to select a file from anywhere in the File System. The “Look In” combo box allows you to move around the File System easily to find the file you are looking for. However, there may be times when you want to restrict the user to searching a specific directory tree. Therefore, we need to control the entries displayed in the “Look In” combo box.

As the API says, FileSystemView is JFileChooser’s gateway to the file system. The SingleRootFileSystemView class is a customized view of the file system that lets the file chooser only see the files contained in the specified directory (and its sub directories). The “Look In” combo box will now only display the specifed directory, and its children as you navigate the directory tree. In addition, the “Up One Level” button on the file chooser will be disabled when viewing files at the specified root directory to prevent navigation to the parent of the directory.

To limit the file chooser to search the files in my Java JDK I used:

File root = new File("c:/java/jdk6.7");
FileSystemView fsv = new SingleRootFileSystemView( root );
JFileChooser chooser = new JFileChooser(fsv);

After selecting the “docs” directory, the file chooser would look like:

single-root-file-chooser

This class was designed to be used once. That is, if you ever need to change the root directory of the view you should create a new SingleRootFileSystemView class and a new JFileChooser. I recommend this approach because the setFileSystemView(…) does not work as expected. However, it appears that the following work around will allow you to dynamically change the file system view:


File root2 = new File("c:/java/blog");
FileSystemView fsv2 = new SingleRootFileSystemView( root2 );
chooser.setFileSystemView(fsv2);
chooser.updateUI();
chooser.setCurrentDirectory(root2);

I normally don’t ever recommend using the updateUI() method as this should only be used for a Look and Feel change. Use this approach at your own risk.

Get The Code

SingleRootFileSystemView.java

Related Reading

How to Use File Choosers

18 Responses to “Single Root File Chooser”

  1. jon80 said

    Looks fine, however, I get a few squibbly error messages when utilizing the code as follows:

    import java.awt.*;
    import java.awt.event.*;
    import java.beans.*;
    import java.io.*;
    import javax.swing.*;
    import javax.swing.filechooser.*;
    import javax.swing.filechooser.FileFilter;
    
    /**
     * @version 1.23 2007-06-12
     * @author Cay Horstmann
     */
    public class FileChooserTest
    {
       public static void main(String[] args)
       {
          EventQueue.invokeLater(new Runnable()
             {
                public void run()
                {
                   ImageViewerFrame frame = new ImageViewerFrame();
                   frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                   frame.setVisible(true);
                }
             });
       }
    }
    
    /**
     * A frame that has a menu for loading an image and a display area for the loaded image.
     */
    class ImageViewerFrame extends JFrame
    {
       public ImageViewerFrame()
       {
          setTitle("FileChooserTest");
          setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
    
          // set up menu bar
          JMenuBar menuBar = new JMenuBar();
          setJMenuBar(menuBar);
    
          JMenu menu = new JMenu("File");
          menuBar.add(menu);
    
          JMenuItem openItem = new JMenuItem("Open");
          menu.add(openItem);
          openItem.addActionListener(new FileOpenListener());
    
          JMenuItem exitItem = new JMenuItem("Exit");
          menu.add(exitItem);
          exitItem.addActionListener(new ActionListener()
             {
                public void actionPerformed(ActionEvent event)
                {
                   System.exit(0);
                }
             });
    
          // use a label to display the images
          label = new JLabel();
          add(label);
    
          // set up file chooser
          File root = new File (".");
          FileSystemView fsv = new SingleRootFileSystemView(root);
          chooser = new JFileChooser(fsv);
    
          // accept all image files ending with .jpg, .jpeg, .gif
          /*
          final ExtensionFileFilter filter = new ExtensionFileFilter();
          filter.addExtension("jpg");
          filter.addExtension("jpeg");
          filter.addExtension("gif");
          filter.setDescription("Image files");
          */
          FileNameExtensionFilter filter = new FileNameExtensionFilter("Image files", "jpg", "jpeg", "gif");
          chooser.setFileFilter(filter);
    
          chooser.setAccessory(new ImagePreviewer(chooser));
    
          chooser.setFileView(new FileIconView(filter, new ImageIcon("palette.gif")));
       }
    
       /**
        * This is the listener for the File->Open menu item.
        */
       private class FileOpenListener implements ActionListener
       {
          public void actionPerformed(ActionEvent event)
          {
             chooser.setCurrentDirectory(new File("."));
    
             // show file chooser dialog
             int result = chooser.showOpenDialog(ImageViewerFrame.this);
    
             // if image file accepted, set it as icon of the label
             if (result == JFileChooser.APPROVE_OPTION)
             {
                String name = chooser.getSelectedFile().getPath();
                label.setIcon(new ImageIcon(name));
             }
          }
       }
    
       public static final int DEFAULT_WIDTH = 300;
       public static final int DEFAULT_HEIGHT = 400;
    
       private JLabel label;
       private JFileChooser chooser;
    }
    
    /**
     * A file view that displays an icon for all files that match a file filter.
     */
    class FileIconView extends FileView
    {
       /**
        * Constructs a FileIconView.
        * @param aFilter a file filter--all files that this filter accepts will be shown with the icon.
        * @param anIcon--the icon shown with all accepted files.
        */
       public FileIconView(FileFilter aFilter, Icon anIcon)
       {
          filter = aFilter;
          icon = anIcon;
       }
    
       public Icon getIcon(File f)
       {
          if (!f.isDirectory() && filter.accept(f)) return icon;
          else return null;
       }
    
       private FileFilter filter;
       private Icon icon;
    }
    
    /**
     * A file chooser accessory that previews images.
     */
    class ImagePreviewer extends JLabel
    {
       /**
        * Constructs an ImagePreviewer.
        * @param chooser the file chooser whose property changes trigger an image change in this
        * previewer
        */
       public ImagePreviewer(JFileChooser chooser)
       {
          setPreferredSize(new Dimension(100, 100));
          setBorder(BorderFactory.createEtchedBorder());
    
          chooser.addPropertyChangeListener(new PropertyChangeListener()
             {
                public void propertyChange(PropertyChangeEvent event)
                {
                   if (event.getPropertyName() == JFileChooser.SELECTED_FILE_CHANGED_PROPERTY)
                   {
                      // the user has selected a new file
                      File f = (File) event.getNewValue();
                      if (f == null)
                      {
                         setIcon(null);
                         return;
                      }
    
                      // read the image into an icon
                      ImageIcon icon = new ImageIcon(f.getPath());
    
                      // if the icon is too large to fit, scale it
                      if (icon.getIconWidth() > getWidth()) icon = new ImageIcon(icon.getImage()
                            .getScaledInstance(getWidth(), -1, Image.SCALE_DEFAULT));
    
                      setIcon(icon);
                   }
                }
             });
       }
    }
    Sourced from Core Java Vol 1, Chapter 9.
    
    import java.io.*;
    import javax.swing.filechooser.*;
    
    /**
     *  A FileSystemView class that limits the file selections to a single root.
     *
     *  When used with the JFileChooser component the user will only be able to
     *  traverse the directories contained within the specified root fill.
     *
     *  The "Look In" combo box will only display the specified root.
     *
     *  The "Up One Level" button will be disable when at the root.
     *
     */
    public class SingleRootFileSystemView extends FileSystemView
    {
    	File root;
    	File[] roots = new File[1];
    
    	public SingleRootFileSystemView(File root)
    	{
    		super();
    		this.root = root;
    		roots[0] = root;
    	}
    
    	@Override
    	public File createNewFolder(File containingDir)
    	{
    		File folder = new File(containingDir, "New Folder");
    		folder.mkdir();
    		return folder;
    	}
    
    	@Override
    	public File getDefaultDirectory()
    	{
    		return root;
    	}
    
    	@Override
    	public File getHomeDirectory()
    	{
    		return root;
    	}
    
    	@Override
    	public File[] getRoots()
    	{
    		return roots;
    	}
    }
    

    Error (cannot display it all):
    r2.java:418)
    at sun.awt.shell.Win32ShellFolder2$4.call(Win32ShellFolder2.java:389)
    at sun.awt.shell.Win32ShellFolder2$4.call(Win32ShellFolder2.java:385)
    at sun.awt.shell.Win32ShellFolderManager2$ComInvoker.invoke(Win32ShellFo
    lderManager2.java:523)
    at sun.awt.shell.ShellFolder.invoke(ShellFolder.java:399)
    at sun.awt.shell.ShellFolder.invoke(ShellFolder.java:385)
    at sun.awt.shell.Win32ShellFolder2.getIShellFolder(Win32ShellFolder2.jav
    a:384)
    at sun.awt.shell.Win32ShellFolder2.getParentIShellFolder(Win32ShellFolde
    r2.java:418)
    at sun.awt.shell.Win32ShellFolder2$4.call(Win32ShellFolder2.java:389)
    at sun.awt.shell.Win32ShellFolder2$4.call(Win32ShellFolder2.java:385)
    at sun.awt.shell.Win32ShellFolderManager2$ComInvoker.invoke(Win32ShellFo
    lderManager2.java:523)

    • Rob Camick said

      Code works fine for me. Maybe its a version or platform difference. One thing you can try changing:

      //File root = new File (".");
      File root = new File ("").getAbsoluteFile();

      And when you click the menu item you don’t need to set the current directory:

      //chooser.setCurrentDirectory(new File("."));

      • Adhisheshu said

        1)How to add all folders like C,D into the Look in combo box of jfile chooser.
        2)when click on perticular drive its can be display jtree model along checkboxes in side jfilechooser?
        3)And click on jtree check box it can be display folder name label of jfilechooser?

    • bonechilla said

      Yeah I had the same problem I found that if you want to start from a directory don’t bother with ./ just start from the path as though your in the base Directory and then use .getAbsoluteFile() and it should work

  2. thnks anyway…. work perfectly…. :)

  3. Jörg said

    Hello Rob,

    I just implemented your code in an existing application. Thank you very much.
    I only wondered why in my case the “Up One Level”-button was always enabled, thus making all the code futile.
    After an hour of testing in shutting down my code line by line,it turned out that it happened due to a “myFileChooser.updateUI()” – and I found no workaround. You may want to mention this fact in your description (and also that it’s no way to specify SingleRootFileSystemView AFTER (a parameter-less) creation of JFileChooser with myFileChooser.setFileSystemView(…)).

    Kind regards

    Jörg

    • Rob Camick said

      Jorg,

      As a general rule updateUI() should not be invoked manually on any component. If you use this method it is usually in indication of improper design. Swing will invoke this method automatically on a LAF change. Thanks for pointing out that the setFileSystemView() method does not work. I added some code for a workaround, although I recommend that this code should not be used. As I say any time you need to invoke updateUI() manually I think there is a problem.

      Rob

  4. I found that I had to put in a couple of additional overrides to disable the “up button” at the root.

    	@Override
    	public boolean isRoot(File file)
    	{
    		try
    		{
    		   if (root.getCanonicalFile().equals(file.getCanonicalFile())) return true;
    		   else return false;
    	        }
    	        catch (Exception e)
    	        {
    			Messages.exceptionHandler("SingleRootFileSystemView", e);
    			return false;
    		}
    	}
    
    	@Override
    	public boolean isFileSystemRoot(File file)
    	{
    		try
    	        {
    		   if (root.getCanonicalFile().equals(file.getCanonicalFile())) return true;
    		   else return false;
    	        }
    	        catch (Exception e)
    	        {
    			Messages.exceptionHandler("SingleRootFileSystemView", e);
    			return false;
    	        }
    	}
    
    • Rob Camick said

      Code works fine for me using JDK8 on Windows 7. I just use:

      File root = new File("c://");
      FileSystemView fsv = new SingleRootFileSystemView( root );
      JFileChooser chooser = new JFileChooser(fsv);
      

      Maybe this is a version/platform issue?

      • I am using Oracle’s JRE 8 on Linux Ubuntu 16.04 with my application packaged as a ‘SNAP’. This is a sandboxed environment which keeps the application isolated from the rest of the computer.

        Here is the code I use:

        FileSystemView fsv = new SingleRootFileSystemView(new File("."));
        JFileChooser chooser = new JFileChooser(new File("XML"), fsv);
        
      • Rob Camick said

        Yes I can duplicate the problem. I was testing with absolute paths not relative paths. I fixed the problem by converting the File to its canonical File in the constructor. New version has been uploaded. Thanks for the feedback.

      • Your new version is now working in my application. Many thanks for your help.

  5. Flavio Menezes dos Reis said

    Hi, Rob

    Your class was exactly what I was looking for. Thank you very much

  6. Jerome said

    thanks !

  7. Christof said

    Works like a charm, thank you!

    Nevertheless I have a question: If I enter an absolute path into the filechoosers textarea I am still able to “break out” of my root directory. How can I fix this issue?

    • Rob Camick said

      That is not functionality of the FileSystemView. That would be functionality of the JFileChooser which determines whether a file can be selected. You can override the “approveSelection(…)” method of the JFileChooser. Your logic might to be make sure the the selected files “startsWith(…)” the root directory. See: https://stackoverflow.com/a/4234018/131872 for a basic example of overriding this method.

  8. Ti said

    Still working wonderfully!

    One tweak I found I needed was to add an override for ‘isTraversable(…)’. The inherited method boils down to “is a directory”, but I found that the JFileChooser (especially under Windows 10) would still let me explore all kinds of places — probably similar to what Christof found in comment #7. In any case, having ‘isTraversable’ additionally test for whether its argument is contained under the ‘this.root’ path results in all such extraneous locations being forbidden, including entering an absolute path into the text field of the chooser.

    There’s probably still some way to escape such a restriction, so overriding ‘approveSelection(…)’ is still a good idea. Belt *and* bracers, and so forth.

    Thank you again for this writeup and code!

Leave a comment