!7 import javax.swing.*; import javax.swing.border.*; import javax.swing.event.*; import javax.swing.tree.*; import javax.swing.table.*; import javax.swing.filechooser.FileSystemView; import javax.imageio.ImageIO; import java.util.Date; import java.util.List; import java.util.ArrayList; import java.io.*; import java.nio.channels.FileChannel; import java.net.URL; /** A basic File Browser. Requires 1.6+ for the Desktop & SwingWorker classes, amongst other minor things. Includes support classes FileTableModel & FileTreeCellRenderer. @TODO Bugs
  • Fix keyboard focus issues - especially when functions like rename/delete etc. are called that update nodes & file lists.
  • Needs more testing in general. @TODO Functionality
  • Double clicking a directory in the table, should update the tree
  • Move progress bar?
  • Add other file display modes (besides table) in CardLayout?
  • Menus + other cruft?
  • Implement history/back
  • Allow multiple selection
  • Add file search @author Andrew Thompson @version 2011-06-08 @see http://codereview.stackexchange.com/q/4446/7784 @license LGPL */ p-substance { FileBrowser.main(args); } sclass FileBrowser { /** Title of the application */ public static final String APP_TITLE = "FileBro"; /** Used to open/edit/print files. */ private Desktop desktop; /** Provides nice icons and names for files. */ private FileSystemView fileSystemView; /** currently selected File. */ private File currentFile; /** Main GUI container */ private JPanel gui; /** File-system tree. Built Lazily */ private JTree tree; private DefaultTreeModel treeModel; /** Directory listing */ private JTable table; private JProgressBar progressBar; /** Table model for File[]. */ private FileTableModel fileTableModel; private ListSelectionListener listSelectionListener; private boolean cellSizesSet = false; private int rowIconPadding = 6; /* File controls. */ private JButton openFile; private JButton printFile; private JButton editFile; /* File details. */ private JLabel fileName; private JTextField path; private JLabel date; private JLabel size; private JCheckBox readable; private JCheckBox writable; private JCheckBox executable; private JRadioButton isDirectory; private JRadioButton isFile; /* GUI options/containers for new File/Directory creation. Created lazily. */ private JPanel newFilePanel; private JRadioButton newTypeFile; private JTextField name; public Container getGui() { if (gui==null) { gui = new JPanel(new BorderLayout(3,3)); gui.setBorder(new EmptyBorder(5,5,5,5)); fileSystemView = FileSystemView.getFileSystemView(); desktop = Desktop.getDesktop(); JPanel detailView = new JPanel(new BorderLayout(3,3)); table = new JTable(); table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); table.setAutoCreateRowSorter(true); table.setShowVerticalLines(false); listSelectionListener = new ListSelectionListener() { @Override public void valueChanged(ListSelectionEvent lse) { int row = table.getSelectionModel().getLeadSelectionIndex(); setFileDetails( ((FileTableModel)table.getModel()).getFile(row) ); } }; table.getSelectionModel().addListSelectionListener(listSelectionListener); JScrollPane tableScroll = new JScrollPane(table); Dimension d = tableScroll.getPreferredSize(); tableScroll.setPreferredSize(new Dimension((int)d.getWidth(), (int)d.getHeight()/2)); detailView.add(tableScroll, BorderLayout.CENTER); // the File tree DefaultMutableTreeNode root = new DefaultMutableTreeNode(); treeModel = new DefaultTreeModel(root); TreeSelectionListener treeSelectionListener = new TreeSelectionListener() { public void valueChanged(TreeSelectionEvent tse){ DefaultMutableTreeNode node = (DefaultMutableTreeNode)tse.getPath().getLastPathComponent(); showChildren(node); setFileDetails((File)node.getUserObject()); } }; // show the file system roots. File[] roots = fileSystemView.getRoots(); for (File fileSystemRoot : roots) { DefaultMutableTreeNode node = new DefaultMutableTreeNode(fileSystemRoot); root.add( node ); File[] files = fileSystemView.getFiles(fileSystemRoot, true); for (File file : files) { if (file.isDirectory()) { node.add(new DefaultMutableTreeNode(file)); } } // } tree = new JTree(treeModel); tree.setRootVisible(false); tree.addTreeSelectionListener(treeSelectionListener); tree.setCellRenderer(new FileTreeCellRenderer()); tree.expandRow(0); JScrollPane treeScroll = new JScrollPane(tree); // as per trashgod tip tree.setVisibleRowCount(15); Dimension preferredSize = treeScroll.getPreferredSize(); Dimension widePreferred = new Dimension( 200, (int)preferredSize.getHeight()); treeScroll.setPreferredSize( widePreferred ); // details for a File JPanel fileMainDetails = new JPanel(new BorderLayout(4,2)); fileMainDetails.setBorder(new EmptyBorder(0,6,0,6)); JPanel fileDetailsLabels = new JPanel(new GridLayout(0,1,2,2)); fileMainDetails.add(fileDetailsLabels, BorderLayout.WEST); JPanel fileDetailsValues = new JPanel(new GridLayout(0,1,2,2)); fileMainDetails.add(fileDetailsValues, BorderLayout.CENTER); fileDetailsLabels.add(new JLabel("File", JLabel.TRAILING)); fileName = new JLabel(); fileDetailsValues.add(fileName); fileDetailsLabels.add(new JLabel("Path/name", JLabel.TRAILING)); path = new JTextField(5); path.setEditable(false); fileDetailsValues.add(path); fileDetailsLabels.add(new JLabel("Last Modified", JLabel.TRAILING)); date = new JLabel(); fileDetailsValues.add(date); fileDetailsLabels.add(new JLabel("File size", JLabel.TRAILING)); size = new JLabel(); fileDetailsValues.add(size); fileDetailsLabels.add(new JLabel("Type", JLabel.TRAILING)); JPanel flags = new JPanel(new FlowLayout(FlowLayout.LEADING,4,0)); isDirectory = new JRadioButton("Directory"); flags.add(isDirectory); isFile = new JRadioButton("File"); flags.add(isFile); fileDetailsValues.add(flags); JToolBar toolBar = new JToolBar(); // mnemonics stop working in a floated toolbar toolBar.setFloatable(false); JButton locateFile = new JButton("Locate"); locateFile.setMnemonic('l'); locateFile.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent ae) { try { System.out.println("Locate: " + currentFile.getParentFile()); desktop.open(currentFile.getParentFile()); } catch(Throwable t) { showThrowable(t); } gui.repaint(); } }); toolBar.add(locateFile); openFile = new JButton("Open"); openFile.setMnemonic('o'); openFile.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent ae) { try { System.out.println("Open: " + currentFile); desktop.open(currentFile); } catch(Throwable t) { showThrowable(t); } gui.repaint(); } }); toolBar.add(openFile); editFile = new JButton("Edit"); editFile.setMnemonic('e'); editFile.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent ae) { try { desktop.edit(currentFile); } catch(Throwable t) { showThrowable(t); } } }); toolBar.add(editFile); printFile = new JButton("Print"); printFile.setMnemonic('p'); printFile.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent ae) { try { desktop.print(currentFile); } catch(Throwable t) { showThrowable(t); } } }); toolBar.add(printFile); // Check the actions are supported on this platform! openFile.setEnabled(desktop.isSupported(Desktop.Action.OPEN)); editFile.setEnabled(desktop.isSupported(Desktop.Action.EDIT)); printFile.setEnabled(desktop.isSupported(Desktop.Action.PRINT)); flags.add(new JLabel(":: Flags")); readable = new JCheckBox("Read "); readable.setMnemonic('a'); flags.add(readable); writable = new JCheckBox("Write "); writable.setMnemonic('w'); flags.add(writable); executable = new JCheckBox("Execute"); executable.setMnemonic('x'); flags.add(executable); int count = fileDetailsLabels.getComponentCount(); for (int ii=0; ii14 as per camickr comment. width = (int)preferred.getWidth()+14; } tableColumn.setPreferredWidth(width); tableColumn.setMaxWidth(width); tableColumn.setMinWidth(width); } /** Add the files that are contained within the directory of this node. Thanks to Hovercraft Full Of Eels for the SwingWorker fix. */ private void showChildren(final DefaultMutableTreeNode node) { tree.setEnabled(false); progressBar.setVisible(true); progressBar.setIndeterminate(true); SwingWorker worker = new SwingWorker() { @Override public Void doInBackground() { File file = (File) node.getUserObject(); if (file.isDirectory()) { File[] files = fileSystemView.getFiles(file, true); //!! if (node.isLeaf()) { for (File child : files) { if (child.isDirectory()) { publish(child); } } } setTableData(files); } return null; } @Override protected void process(List chunks) { for (File child : chunks) { node.add(new DefaultMutableTreeNode(child)); } } @Override protected void done() { progressBar.setIndeterminate(false); progressBar.setVisible(false); tree.setEnabled(true); } }; worker.execute(); } /** Update the File details view with the details of this File. */ private void setFileDetails(File file) { currentFile = file; Icon icon = fileSystemView.getSystemIcon(file); fileName.setIcon(icon); fileName.setText(fileSystemView.getSystemDisplayName(file)); path.setText(file.getPath()); date.setText(new Date(file.lastModified()).toString()); size.setText(file.length() + " bytes"); readable.setSelected(file.canRead()); writable.setSelected(file.canWrite()); executable.setSelected(file.canExecute()); isDirectory.setSelected(file.isDirectory()); isFile.setSelected(file.isFile()); JFrame f = (JFrame)gui.getTopLevelAncestor(); if (f!=null) { f.setTitle( APP_TITLE + " :: " + fileSystemView.getSystemDisplayName(file) ); } gui.repaint(); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { /*try { // Significantly improves the look of the output in // terms of the file names returned by FileSystemView! UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch(Exception weTried) { }*/ JFrame f = new JFrame(APP_TITLE); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); FileBrowser FileBrowser = new FileBrowser(); f.setContentPane(FileBrowser.getGui()); try { URL urlBig = FileBrowser.getClass().getResource("fb-icon-32x32.png"); URL urlSmall = FileBrowser.getClass().getResource("fb-icon-16x16.png"); ArrayList images = new ArrayList(); images.add( ImageIO.read(urlBig) ); images.add( ImageIO.read(urlSmall) ); f.setIconImages(images); } catch(Exception weTried) {} f.pack(); f.setLocationByPlatform(true); f.setMinimumSize(f.getSize()); f.setVisible(true); FileBrowser.showRootFile(); } }); } } /** A TableModel to hold File[]. */ sclass FileTableModel extends AbstractTableModel { private File[] files; private FileSystemView fileSystemView = FileSystemView.getFileSystemView(); private String[] columns = { "Icon", "File", "Path/name", "Size", "Last Modified", "R", "W", "E", "D", "F", }; FileTableModel() { this(new File[0]); } FileTableModel(File[] files) { this.files = files; } public Object getValueAt(int row, int column) { File file = files[row]; switch (column) { case 0: return fileSystemView.getSystemIcon(file); case 1: return fileSystemView.getSystemDisplayName(file); case 2: return file.getPath(); case 3: return file.length(); case 4: return file.lastModified(); case 5: return file.canRead(); case 6: return file.canWrite(); case 7: return file.canExecute(); case 8: return file.isDirectory(); case 9: return file.isFile(); default: System.err.println("Logic Error"); } return ""; } public int getColumnCount() { return columns.length; } public Class getColumnClass(int column) { switch (column) { case 0: return ImageIcon.class; case 3: return Long.class; case 4: return Date.class; case 5: case 6: case 7: case 8: case 9: return Boolean.class; } return String.class; } public String getColumnName(int column) { return columns[column]; } public int getRowCount() { return files.length; } public File getFile(int row) { return files[row]; } public void setFiles(File[] files) { this.files = files; fireTableDataChanged(); } } /** A TreeCellRenderer for a File. */ sclass FileTreeCellRenderer extends DefaultTreeCellRenderer { private FileSystemView fileSystemView; private JLabel label; FileTreeCellRenderer() { label = new JLabel(); label.setOpaque(true); fileSystemView = FileSystemView.getFileSystemView(); } @Override public Component getTreeCellRendererComponent( JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) { DefaultMutableTreeNode node = (DefaultMutableTreeNode)value; File file = (File)node.getUserObject(); label.setIcon(fileSystemView.getSystemIcon(file)); label.setText(fileSystemView.getSystemDisplayName(file)); label.setToolTipText(file.getPath()); if (selected) { label.setBackground(backgroundSelectionColor); label.setForeground(textSelectionColor); } else { label.setBackground(backgroundNonSelectionColor); label.setForeground(textNonSelectionColor); } return label; } }