Forum Stats

  • 3,757,497 Users
  • 2,251,236 Discussions
  • 7,869,845 Comments

Discussions

How does the JFileChooser "readOnly" property work?

camickr
camickr Member Posts: 24,931
edited Nov 3, 2013 2:44PM in Swing
The "normal" behaviour for a file chooser usiing the UIManager "FileChooser.readOnly" default value of Boolean.FALSE is:

1) A "New Folder" button is displayed
2) When a File in the list is selected you can use the F2 key to rename the file
3) Or, if you click on an already selected file you will also be able to rename the file

When you use:
UIManager.put("FileChooser.readOnly", Boolean.TRUE);
then the behaviour changes:

1) The "New Folder" button is not displayed
2) You can't rename the file by using F2
3) You can't rename the file by clicking on a selected file

I am curious how the "readOnly" property works?

Looking through the source code of BasicFileChooserUI and MetalFileChooserUI it was easy to see that the "New Folder" button is not created when the "readOnly" property is true.

However, I can't figure out how "file name editing" works. Using Key Bindings I was able to find the binding between the F2 key and an Action named "editFileName". However, I can't find where this Action is actually created in the UI. I also can't figure out how clicking on a selected file name invokes this Action.

Does anybody know how to prevent the mouse click from allowing editing of the file name? Does anybody know where the "editFileName" Action is created and how this Action gets invoked by a mouse click?

This is only a curiosity question. Just when I think I understand Actions and Key Bindings I find something like this that I don't understand.

The following SSCCE shows:

1) the normal behaviour of a file chooser
2) the "readOnly" behaviour of a file chooser
3) my attempt to simulate the "readOnly" behaviour of a file chooser. It works except a mouse click still allows editing of the file name.
import java.awt.*;
import java.beans.*;
import java.awt.event.*;
import javax.swing.*;

public class FileChooserSSCCE extends JPanel
{
    public FileChooserSSCCE()
    {
        //  Demonstrate default file chooser functionality

        JButton normal = new JButton("Normal");
        normal.addActionListener( new ActionListener()
        {
            public void actionPerformed(ActionEvent e)
            {
                UIManager.put("FileChooser.readOnly", Boolean.FALSE);

                JFileChooser fc = new JFileChooser(".");
                fc.showSaveDialog(null);
            }
        });
        add( normal );

        //  Demonstrate "read only" property

        JButton readOnly = new JButton("Read Only");
        readOnly.addActionListener( new ActionListener()
        {
            public void actionPerformed(ActionEvent e)
            {
                UIManager.put("FileChooser.readOnly", Boolean.TRUE);

                JFileChooser fc = new JFileChooser(".");
                fc.showSaveDialog(null);
            }
        });
        add( readOnly );

        //  Simulate "read only" property

        JButton simulate = new JButton("Simulate Read Only");
        simulate.addActionListener( new ActionListener()
        {
            public void actionPerformed(ActionEvent e)
            {
                UIManager.put("FileChooser.readOnly", Boolean.FALSE);

                JFileChooser fc = new JFileChooser(".");
                ActionMap am = fc.getActionMap();

                //  Disable the New Folder action. The action gets re-enabled every time
                //  a new directory is selected so we need the PropertyChangeListener

                final Action newFolder = am.get("New Folder");
                newFolder.setEnabled( false );
                newFolder.addPropertyChangeListener( new PropertyChangeListener()
                {
                   public void propertyChange(PropertyChangeEvent e)
                   {
                        newFolder.setEnabled( false );
                   }
                });

                //  The other solution is to remove the button then you don't need to
                //  disable the action. This solution requires Darryl's SwingUtils
                //  class: http://tips4java.wordpress.com/2008/11/13/swing-utils/
                //  Comment the above code and uncomment below if you want to test this.
/*
                Icon icon = UIManager.getIcon("FileChooser.newFolderIcon");
                JButton button =
                    SwingUtils.getDescendantOfType(JButton.class, fc, "Icon", icon);
                button.getParent().remove(button);
*/
                //  Disable editing of the file name by using F2

                InputMap im = fc.getInputMap(JFileChooser.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
                im.put(KeyStroke.getKeyStroke("F2"), "none");

                fc.showSaveDialog(null);
            }
        });
        add( simulate );
    }

    private static void createAndShowUI()
    {
        JFrame frame = new JFrame("FileChooserSSCCE");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add( new FileChooserSSCCE() );
        frame.pack();
        frame.setLocationRelativeTo( null );
        frame.setVisible( true );
    }

    public static void main(String[] args)
    {
        EventQueue.invokeLater(new Runnable()
        {
            public void run()
            {
                createAndShowUI();
            }
        });
    }
}
This question was inspired by by my attemtp to help with this question: http://www.coderanch.com/t/555535/GUI/java/FileChooser-readOnly

Best Answer

  • darrylburke
    darrylburke Member Posts: 18,007
    edited Oct 14, 2011 3:11AM Accepted Answer
    However, I can't figure out how "file name editing" works. Using Key Bindings I was able to find the binding between the F2 key and an Action named "editFileName". However, I can't find where this Action is actually created in the UI. I also can't figure out how clicking on a selected file name invokes this Action.
    I guess that would be in the proprietary <tt>sun.swing.FilePane</tt> class. EDIT: Also, disabling the <tt>Action</tt> showed that clicking doesn't invoke the <tt>Action</tt>. It's possible that both clicking and the <tt>Action</tt>'s <tt>actionPerformed(...)</tt> invoke the same method, which starts the editing.
    Does anybody know how to prevent the mouse click from allowing editing of the file name?
    Removing the associated <tt>MouseListener</tt> (in an unacceptably implementation dependent way) seems to do it. The detail view, which is an anonymous <tt>JTable</tt> subclass, is a bigger problem as the API doesn't provide a means to block editing, and removing the <tt>MouseListener</tt> also blocks cell selection events.

    Here's a working example I wouldn't recommend anyone to use in a real-world application. For other members reading this, the <tt>SwingUtils</tt> class is available here
    import darrylbu.util.SwingUtils;
    import java.awt.Container;
    import java.awt.event.*;
    import java.beans.PropertyChangeEvent;
    import java.beans.PropertyChangeListener;
    import javax.swing.*;
    
    public class ReadOnlyFileChooser {
    
      public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
    
          @Override
          public void run() {
            new ReadOnlyFileChooser().makeUI();
          }
        });
      }
    
      public void makeUI() {
        final JFileChooser chooser = new JFileChooser();
        InputMap inputMap = chooser.getInputMap(JFileChooser.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
        inputMap.put(KeyStroke.getKeyStroke("F2"), "none");
    
        final JList list = SwingUtils.getDescendantOfType(JList.class, chooser, "Enabled", true);
        final MouseListener mouseListener = list.getMouseListeners()[2];
        list.removeMouseListener(mouseListener);
        list.addMouseListener(new MouseAdapter() {
    
          @Override
          public void mouseClicked(MouseEvent e) {
            if (e.getClickCount() == 2) {
              mouseListener.mouseClicked(e);
            }
          }
        });
    
        final Container filePane = SwingUtilities.getAncestorOfClass(sun.swing.FilePane.class, list);
        filePane.addContainerListener(new ContainerAdapter() {
    
          @Override
          public void componentAdded(ContainerEvent e) {
            final JTable table = SwingUtils.getDescendantOfType(JTable.class, chooser, "Enabled", true);
            if (table != null && table.getPropertyChangeListeners("tableCellEditor").length == 0) {
              table.addPropertyChangeListener("tableCellEditor", new PropertyChangeListener() {
    
                @Override
                public void propertyChange(PropertyChangeEvent evt) {
                  SwingUtilities.invokeLater(new Runnable() {
    
                    @Override
                    public void run() {
                      if (table.isEditing()) {
                        table.getCellEditor().stopCellEditing();
                      }
                    }
                  });
                }
              });
            }
          }
        });
    
        chooser.showOpenDialog(null);
      }
    }
    db

    Edited by: Darryl Burke

    Edited(2) by: Darryl Burke -- shortened and improved the code

Answers

  • darrylburke
    darrylburke Member Posts: 18,007
    edited Oct 14, 2011 3:11AM Accepted Answer
    However, I can't figure out how "file name editing" works. Using Key Bindings I was able to find the binding between the F2 key and an Action named "editFileName". However, I can't find where this Action is actually created in the UI. I also can't figure out how clicking on a selected file name invokes this Action.
    I guess that would be in the proprietary <tt>sun.swing.FilePane</tt> class. EDIT: Also, disabling the <tt>Action</tt> showed that clicking doesn't invoke the <tt>Action</tt>. It's possible that both clicking and the <tt>Action</tt>'s <tt>actionPerformed(...)</tt> invoke the same method, which starts the editing.
    Does anybody know how to prevent the mouse click from allowing editing of the file name?
    Removing the associated <tt>MouseListener</tt> (in an unacceptably implementation dependent way) seems to do it. The detail view, which is an anonymous <tt>JTable</tt> subclass, is a bigger problem as the API doesn't provide a means to block editing, and removing the <tt>MouseListener</tt> also blocks cell selection events.

    Here's a working example I wouldn't recommend anyone to use in a real-world application. For other members reading this, the <tt>SwingUtils</tt> class is available here
    import darrylbu.util.SwingUtils;
    import java.awt.Container;
    import java.awt.event.*;
    import java.beans.PropertyChangeEvent;
    import java.beans.PropertyChangeListener;
    import javax.swing.*;
    
    public class ReadOnlyFileChooser {
    
      public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
    
          @Override
          public void run() {
            new ReadOnlyFileChooser().makeUI();
          }
        });
      }
    
      public void makeUI() {
        final JFileChooser chooser = new JFileChooser();
        InputMap inputMap = chooser.getInputMap(JFileChooser.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
        inputMap.put(KeyStroke.getKeyStroke("F2"), "none");
    
        final JList list = SwingUtils.getDescendantOfType(JList.class, chooser, "Enabled", true);
        final MouseListener mouseListener = list.getMouseListeners()[2];
        list.removeMouseListener(mouseListener);
        list.addMouseListener(new MouseAdapter() {
    
          @Override
          public void mouseClicked(MouseEvent e) {
            if (e.getClickCount() == 2) {
              mouseListener.mouseClicked(e);
            }
          }
        });
    
        final Container filePane = SwingUtilities.getAncestorOfClass(sun.swing.FilePane.class, list);
        filePane.addContainerListener(new ContainerAdapter() {
    
          @Override
          public void componentAdded(ContainerEvent e) {
            final JTable table = SwingUtils.getDescendantOfType(JTable.class, chooser, "Enabled", true);
            if (table != null && table.getPropertyChangeListeners("tableCellEditor").length == 0) {
              table.addPropertyChangeListener("tableCellEditor", new PropertyChangeListener() {
    
                @Override
                public void propertyChange(PropertyChangeEvent evt) {
                  SwingUtilities.invokeLater(new Runnable() {
    
                    @Override
                    public void run() {
                      if (table.isEditing()) {
                        table.getCellEditor().stopCellEditing();
                      }
                    }
                  });
                }
              });
            }
          }
        });
    
        chooser.showOpenDialog(null);
      }
    }
    db

    Edited by: Darryl Burke

    Edited(2) by: Darryl Burke -- shortened and improved the code
  • darrylburke
    darrylburke Member Posts: 18,007
    edited Oct 14, 2011 3:06AM
    Darryl Burke wrote:
    I guess that would be in the proprietary <tt>sun.swing.FilePane</tt> class.
    Yep. Line 1766
    http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/sun/swing/FilePane.java
    It's possible that both clicking and the <tt>Action</tt>'s <tt>actionPerformed(...)</tt> invoke the same method, which starts the editing.
    That too -- lines 488 and 1812

    db
  • kleopatra-JavaNet
    kleopatra-JavaNet Member Posts: 1,105 Gold Badge
    Rob, Darryl,

    Incredible that it's not a configurable property on the fileChooser :-( And nice digging from both of you :-)

    In view of the dirtiness of the results, I would opt for changing the UIManager property temporily, as the OP over at javaranch did see as an option: though the fact that the UIManager is queried once at construction time of the chooser is an implementation detail as well, so strictly speaking shouldn't be used in an ideal world, it's less heavyweight intrusion than fiddling with action properties and listeners.
        Boolean old = UIManager.getBoolean("FileChooser.readOnly");
        UIManager.put("FileChooser.readOnly", Boolean.TRUE);
        JFileChooser fc = new JFileChooser(".");
        UIManager.put("FileChooser.readOnly", old);
    Cheers
    Jeanette
    kleopatra-JavaNet
  • camickr
    camickr Member Posts: 24,931
    edited Oct 14, 2011 11:36AM
    Darryl, Jeannette
    I guess that would be in the proprietary sun.swing.FilePane class
    Thanks, its more complicated than I thought. I was only playing with the "List" view. I forgot about the "Details" view.
    Incredible that it's not a configurable property on the fileChooser
    That would seem to make more sense. Playing with UIManager properties scares people given that there is no guarantee that they are observed by all LAF's.

    I think we all agree that in this case the alternative is a worse solution :)

    One last question of a more general nature. Why do mouse events always seem to have separate code? Why can't the mouse event code look up the "editFileName" Action from the ActionMap and then invoke the Action?

    Edited by: camickr on Oct 14, 2011 11:31 AM
  • darrylburke
    darrylburke Member Posts: 18,007
    Why do mouse events always seem to have separate code? Why can't the mouse event code look up the "editFileName" Action from the ActionMap and then invoke the Action?
    Don't know, but wouldn't that entail constructing an <tt>ActionEvent</tt> to pass to the <tt>Action</tt>'s <tt>actionPerformed(...)</tt>? But it would have been nice to have Mouse Bindings on the lines of Key Bindings, with their own set of input maps.

    I really can't see why <tt>FilePane</tt> wasn't made part of the public API.

    db
  • camickr
    camickr Member Posts: 24,931
    but wouldn't that entail constructing an ActionEvent to pass to the Action's actionPerformed(...)?
    Still seems easier to me than to duplicate the code.
    Mouse Bindings on the lines of Key Bindings, with their own set of input maps.
    Yep, thats a formal API for what I was questioning. There should be an easy way to share Actions, whether. Invoked by the mouse or the keyboard.
  • 1051878
    1051878 Member Posts: 1

    camickr, you have posted a good sample for SyntaxDocument in http://www.discoverteenergy.com/files/SyntaxDocument.java which was posted in JTextPane Colour Highlighting https://forum.java.sun.com/message/6310383#6310383. Because I want get some help with this code but thread was in the archive and can not be replied,so I replied here. I know this is not a good way to ask irrelevant question about this topic here, is there any way to get in tough with you about SyntaxDocument issue?

  • camickr
    camickr Member Posts: 24,931

    SyntaxDocument was something I just played around with. I haven't looked at it or used in in 6-7 years. It was really just a suggestion on how you might do some highlighting. The code doesn't work very well when trying to handle multi-line comments.You can contact me at: Contact Us &amp;laquo; Java Tips Weblog, (I'm Rob). I might have some general advice for you but I won't be able to do any debugging of the actual code.

This discussion has been closed.