8 Replies Latest reply: Nov 3, 2013 1:44 PM by camickr RSS

    How does the JFileChooser "readOnly" property work?

    camickr
      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
        • 1. Re: How does the JFileChooser "readOnly" property work?
          DarrylBurke
          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
          • 2. Re: How does the JFileChooser "readOnly" property work?
            DarrylBurke
            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
            • 3. Re: How does the JFileChooser "readOnly" property work?
              Kleopatra
              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
              • 4. Re: How does the JFileChooser "readOnly" property work?
                camickr
                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
                • 5. Re: How does the JFileChooser "readOnly" property work?
                  DarrylBurke
                  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
                  • 6. Re: How does the JFileChooser "readOnly" property work?
                    camickr
                    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.
                    • 7. Re: How does the JFileChooser "readOnly" property work?
                      ee07a93b-f53c-403f-856f-3499c321c8db

                      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?

                      • 8. Re: How does the JFileChooser "readOnly" property work?
                        camickr

                        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.