Choosing a file in-pane with Swing
Asked Answered
M

2

2

I'm writing a GUI in Swing where I want to do a file chooser that is in the main window, looking something like the image below:

enter image description here

while there seem to be quite a few tutorials on how to write a popup file chooser, i don't see much information on how this type of chooser might be accomplished in swing.

also sorry if this has been asked before, i did a good bit of searching around and wan't able to find anything else..

Martsen answered 25/2, 2013 at 18:30 Comment(0)
R
2

JFileChooser actually extends JComponent, so you can use like any other component. Here is an example with two in-pane file choosers:

public class TestInPaneChoosers {
    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                buildFrame();
            }
        });
    }

    private static void buildFrame() {
        JFrame f = new JFrame("Test");
        f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

        f.setLayout(new FlowLayout());

        f.add(new JFileChooser());
        f.add(new JFileChooser());

        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }
}
Rind answered 25/2, 2013 at 18:34 Comment(2)
ah indeed, most excellent! however, this doesn't allow for the paned look i had in the image i posted previously. is this simply an impossibility with Swing/AWT?Martsen
It is not impossible, in Swing you can write your own components. However, this wouldn't be easy.Rind
B
6

PanelBrowser, shown below, is a basic prototype that functions similarly to the Mac OS X Finder column view illustrated in your question.

PanelBrowser

Update: Added horizontal scrolling and more file information.

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.io.File;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.DefaultListCellRenderer;
import javax.swing.DefaultListModel;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.filechooser.FileSystemView;

/**
 * @see https://mcmap.net/q/1019720/-choosing-a-file-in-pane-with-swing
 */
public class PanelBrowser extends Box {

    private static final Dimension SIZE = new Dimension(200, 300);
    private List<FilePanel> list = new ArrayList<FilePanel>();

    public PanelBrowser(File root) {
        super(BoxLayout.LINE_AXIS);
        setBackground(Color.red);
        FilePanel panel = new FilePanel(this, root);
        list.add(panel);
        this.add(panel);
    }

    private void update(FilePanel fp, File file) {
        int index = list.indexOf(fp);
        int i = list.size() - 1;
        while (i > index) {
            list.remove(i);
            this.remove(i);
            i--;
        }
        final FilePanel panel = new FilePanel(this, file);
        list.add(panel);
        this.add(panel);
        revalidate();
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                scrollRectToVisible(panel.getBounds());
            }
        });
    }

    private static class FilePanel extends Box {

        private static FileSystemView fsv = FileSystemView.getFileSystemView();
        private static DateFormat df = DateFormat.getDateTimeInstance(
            DateFormat.SHORT, DateFormat.DEFAULT);
        private PanelBrowser parent;
        private JList list;

        public FilePanel(PanelBrowser parent, File file) {
            super(BoxLayout.PAGE_AXIS);
            this.parent = parent;
            DefaultListModel model = new DefaultListModel();
            if (file.isFile()) {
                JLabel name = new JLabel(file.getName());
                name.setIcon(fsv.getSystemIcon(file));
                this.add(name);
                Date d = new Date(file.lastModified());
                JLabel mod = new JLabel("Date: " + df.format(d));
                this.add(mod);
                final String v = String.valueOf(file.length());
                JLabel length = new JLabel("Size: " + v);
                this.add(length);
            }
            if (file.isDirectory()) {
                for (File f : file.listFiles()) {
                    model.addElement(f);
                }
                list = new JList(model);
                list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
                list.setCellRenderer(new FileRenderer());
                list.addListSelectionListener(new SelectionHandler());
                this.add(new JScrollPane(list) {
                    @Override
                    public int getVerticalScrollBarPolicy() {
                        return JScrollPane.VERTICAL_SCROLLBAR_ALWAYS;
                    }
                });
            }
        }

        private static class FileRenderer extends DefaultListCellRenderer {

            @Override
            public Component getListCellRendererComponent(JList list, Object value,
                int index, boolean isSelected, boolean cellHasFocus) {
                JLabel label = (JLabel) super.getListCellRendererComponent(
                    list, value, index, isSelected, cellHasFocus);
                File f = (File) value;
                setText(f.getName());
                setIcon(fsv.getSystemIcon(f));
                return label;
            }
        }

        private class SelectionHandler implements ListSelectionListener {

            @Override
            public void valueChanged(ListSelectionEvent e) {
                if (!e.getValueIsAdjusting()) {
                    File f = (File) list.getSelectedValue();
                    parent.update(FilePanel.this, f);
                }
            }
        }

        @Override
        public Dimension getMinimumSize() {
            return new Dimension(SIZE);
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(SIZE);
        }

        @Override
        public Dimension getMaximumSize() {
            return new Dimension(SIZE.width, Short.MAX_VALUE);
        }
    }

    private static void display() {
        String path = System.getProperty("user.dir");
        PanelBrowser browser = new PanelBrowser(new File(path));
        JFrame f = new JFrame(path);
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.add(new JScrollPane(browser) {
            @Override
            public int getVerticalScrollBarPolicy() {
                return JScrollPane.VERTICAL_SCROLLBAR_NEVER;
            }
        });
        f.pack();
        f.setSize(4 * SIZE.width, SIZE.height);
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                display();
            }
        });
    }
}
Britnibrito answered 27/2, 2013 at 5:12 Comment(5)
Great code! As I said in the comments of my answer, it is not easy to write such a component, but it looks like it was easy for you :) There should be a repository of such components...Rind
@lbalazscs: It's only a prototype, lacking much of the functionality of JFileChooser.Britnibrito
It could have some more features (I would like to have the ability to sort the directories before the files, and image previews), but it would be a good start for an open source-component. You could put it at least on codereview.stackexchange.com, I don't think that many people would contribute in the comments of this answer, but on codereview there would be bigger chance.Rind
@lbalazscs: I'm an Outline guy! :-)Britnibrito
Well, Outline is a treetable, and this is a way of showing trees (with the details of a single node), so there is no conflict here :)Rind
R
2

JFileChooser actually extends JComponent, so you can use like any other component. Here is an example with two in-pane file choosers:

public class TestInPaneChoosers {
    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                buildFrame();
            }
        });
    }

    private static void buildFrame() {
        JFrame f = new JFrame("Test");
        f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

        f.setLayout(new FlowLayout());

        f.add(new JFileChooser());
        f.add(new JFileChooser());

        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }
}
Rind answered 25/2, 2013 at 18:34 Comment(2)
ah indeed, most excellent! however, this doesn't allow for the paned look i had in the image i posted previously. is this simply an impossibility with Swing/AWT?Martsen
It is not impossible, in Swing you can write your own components. However, this wouldn't be easy.Rind

© 2022 - 2024 — McMap. All rights reserved.