I found this Custom fileTree Component from github to implement it into my project, I'm using this JTree component to access and display file information like file name, file path, file type in a JTable. The project is about File Tagging, I've implemented all necessary features related to that! The only thing remaining is an issue that I need to rectify in fileTree Component
Here is the code of all the java classes:
Constants.java
package jtree;
public class Constants {
/**
* the name of the OS as given by the Java system property "os.name"
*/
public final static String osname = System.getProperty("os.name");
/**
* true if the program is running on OS X
*/
public final static boolean isOSX = osname.equalsIgnoreCase("Mac OS X");
/**
* true if the program is running on Linux
*/
public final static boolean isLinux = osname.equalsIgnoreCase("Linux");
/**
* true if the program is running on Solaris
*/
public final static boolean isSolaris = osname.equalsIgnoreCase("SunOS");
/**
* true if the program is running on Windows Vista
*/
public final static boolean isVista = osname.equalsIgnoreCase("Windows Vista");
/**
* true if the program is running on Windows
*/
public final static boolean isWindows = !(isOSX || isLinux || isSolaris);
}
FileTree.java
package jtree;
import java.awt.Component;
import java.io.File;
import java.util.Arrays;
import java.util.Comparator;
import javax.swing.JFileChooser;
import javax.swing.JTree;
import javax.swing.event.TreeExpansionEvent;
import javax.swing.event.TreeExpansionListener;
import javax.swing.filechooser.FileSystemView;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
public class FileTree extends JTree {
/** Creates a new instance of FileTree */
public FileTree() {
super(new DefaultTreeModel(new DefaultMutableTreeNode("root")));
fileTreeModel = (DefaultTreeModel)treeModel;
showHiddenFiles = false;
showFiles = true;
navigateOSXApps = false;
initComponents();
initListeners();
}
/**
* returns the data model used by the FileTree. This method returns the same value
* as <code>getModel()</code>, with the only exception being that this method
* returns a <code>DefaultTreeModel</code>
* @return the data model used by the <code>FileTree</code>
*/
public DefaultTreeModel getFileTreeModel() {
return fileTreeModel;
}
/**
* returns the selected file in the tree. If there are multiple selections in the
* tree, then it will return the <code>File</code> associated with the value
* returned from <code>getSelectionPath</code>. You can enable/disable mutliple
* selections by changing the mode of the <code>TreeSelectionModel</code>.
* @return the selected file in the tree
*/
public File getSelectedFile() {
TreePath treePath = getSelectionPath();
if (treePath == null)
return null;
DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode)treePath.getLastPathComponent();
FileTreeNode fileTreeNode = (FileTreeNode)treeNode.getUserObject();
return fileTreeNode.file;
}
/**
* returns an array of the files selected in the tree. To enable/disable multiple
* selections, you can change the selection mode in the
* <code>TreeSelectionModel</code>.
* @return an array of the files selected in the tree
*/
public File[] getSelectedFiles() {
TreePath[] treePaths = getSelectionPaths();
if (treePaths == null)
return null;
File [] files = new File[treePaths.length];
for (int i=0; i<treePaths.length; i++)
{
DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode)treePaths[i].getLastPathComponent();
FileTreeNode fileTreeNode = (FileTreeNode)treeNode.getUserObject();
files[i] = fileTreeNode.file;
}
return files;
}
/**
* initializes class members
*/
private void initComponents() {
if (Constants.isWindows)
fsv = FileSystemView.getFileSystemView();
initRoot();
setCellRenderer(new FileTreeCellRenderer());
setEditable(false);
}
/**
* sets up the listeners for the tree
*/
private void initListeners() {
addTreeExpansionListener(new TreeExpansionListener() {
public void treeCollapsed(TreeExpansionEvent event) {
}
public void treeExpanded(TreeExpansionEvent event) {
TreePath path = event.getPath();
DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) path.getLastPathComponent();
treeNode.removeAllChildren();
populateSubTree(treeNode);
fileTreeModel.nodeStructureChanged(treeNode);
}
});
FileTreeListener ftl = new FileTreeListener(this);
addMouseListener(ftl);
}
/**
* initializes the tree model
*/
private void initRoot() {
File[] roots = null;
if (Constants.isWindows)
roots = fsv.getRoots();
else
roots = File.listRoots();
if (roots.length == 1)
{
rootNode = new DefaultMutableTreeNode(new FileTreeNode(roots[0]));
populateSubTree(rootNode);
}
else if (roots.length > 1)
{
rootNode = new DefaultMutableTreeNode("Computer");
for (File root:roots)
rootNode.add(new DefaultMutableTreeNode(root));
}
else
rootNode = new DefaultMutableTreeNode("Error");
fileTreeModel.setRoot(rootNode);
}
/**
* returns true if deleting is allowed in the tree, false otherwise. The default
* value is false.
* @return true if deleting is allowed in the tree, false otherwise
*/
public boolean isDeleteEnabled() {
return allowDelete;
}
/**
* returns true if the user can navigate into OS X application bundles. false
* otherwise
* @return true if the user can navigate into OS X application bundles
*/
public boolean isNavigateOSXApps() {
return navigateOSXApps;
}
/**
* returns true if files will be shown in the tree, false otherwise. Default value
* is true.
* @return true if files will be shown in the tree, false otherwise
*/
public boolean isShowFiles() {
return showFiles;
}
/**
* returns true if the tree will show hidden files, false otherwise. Default value
* is false.
* @return true if the tree will show hidden files, false otherwise
*/
public boolean isShowHiddenFiles() {
return showHiddenFiles;
}
/**
* called whenever a node is expanded
* @param node the node to expand
*/
private void populateSubTree(DefaultMutableTreeNode node) {
Object userObject = node.getUserObject();
if (userObject instanceof FileTreeNode)
{
FileTreeNode fileTreeNode = (FileTreeNode)userObject;
File []files = fileTreeNode.file.listFiles();
// Windows displays directories before regular files, so we're going
// to sort the list of files such that directories appear first
if (Constants.isWindows)
{
Arrays.sort(files, new Comparator<File>() {
public int compare(File f1, File f2) {
boolean f1IsDir = f1.isDirectory();
boolean f2IsDir = f2.isDirectory();
if (f1IsDir == f2IsDir)
return f1.compareTo(f2);
if (f1IsDir && !f2IsDir)
return -1;
// here we assume that f1 is a file, and f2 is a directory
return 1;
}
});
}
else
Arrays.sort(files);
for (File file:files)
{
if (file.isFile() && !showFiles)
continue;
if (!showHiddenFiles && file.isHidden())
continue;
FileTreeNode subFile = new FileTreeNode(file);
DefaultMutableTreeNode subNode = new DefaultMutableTreeNode(subFile);
if (file.isDirectory())
{
if (!Constants.isOSX || navigateOSXApps || !file.getName().endsWith(".app"))
subNode.add(new DefaultMutableTreeNode("Fake"));
}
node.add(subNode);
}
}
}
/**
* Expands the tree to the <code>File</code> specified by the argument, and selects
* it as well. If the <code>currFile</code> does not exist or is null, calling this
* method will have no effect.
* @param currFile The file or directory to expand the tree to and select.
*/
public void setCurrentFile(File currFile) {
if (currFile == null || !currFile.exists())
return;
String path = currFile.getPath();
String [] pathParts = null;
if (Constants.isWindows)
pathParts = path.split("\\\\");
else
pathParts = path.split(File.separator);
if (Constants.isWindows)
{
int childCount = rootNode.getChildCount();
DefaultMutableTreeNode myComputer = null;
for (int i=0; i<childCount; i++)
{
FileTreeNode fileTreeNode =
(FileTreeNode)((DefaultMutableTreeNode)rootNode.getChildAt(i)).getUserObject();
if (fileTreeNode.file.getPath().equals(FileTreeNode.WINDOWS_MYCOMPUTER))
{
myComputer = (DefaultMutableTreeNode)rootNode.getChildAt(i);
TreePath treePath = new TreePath(myComputer.getPath());
expandPath(treePath);
break;
}
}
DefaultMutableTreeNode currNode = myComputer;
for (String part:pathParts)
{
childCount = currNode.getChildCount();
for (int i=0; i<childCount; i++)
{
DefaultMutableTreeNode childNode = (DefaultMutableTreeNode)currNode.getChildAt(i);
FileTreeNode fileTreeNode = (FileTreeNode)childNode.getUserObject();
String pathName = fileTreeNode.file.getName();
if (pathName.length() == 0)
pathName = fileTreeNode.file.getPath().substring(0, 2);
if (pathName.equals(part))
{
TreePath treePath = new TreePath(childNode.getPath());
expandPath(treePath);
selectionModel.setSelectionPath(treePath);
currNode = childNode;
break;
}
}
}
}
else
{
DefaultMutableTreeNode currNode = rootNode;
for (String part:pathParts)
{
int childCount = currNode.getChildCount();
for (int i=0; i<childCount; i++)
{
DefaultMutableTreeNode childNode = (DefaultMutableTreeNode)currNode.getChildAt(i);
FileTreeNode fileTreeNode = (FileTreeNode)childNode.getUserObject();
if (fileTreeNode.file.getName().equals(part))
{
TreePath treePath = new TreePath(childNode.getPath());
expandPath(treePath);
selectionModel.setSelectionPath(treePath);
currNode = childNode;
break;
}
}
}
}
}
/**
* Allow or disallow the user to delete files from the tree view.
* @param allowDelete <code>true</code> allows deleting of files/directories. <code>false</code> does
* not.
*/
public void setDeleteEnabled(boolean allowDelete) {
this.allowDelete = allowDelete;
}
/**
* Toggle the showing of files in the tree (as opposed to just directories)
* @param showFiles <code>true</code> shows files in the tree. <code>false</code> does not.
*/
public void setShowFiles(boolean showFiles) {
if (this.showFiles != showFiles)
{
this.showFiles = showFiles;
initRoot();
}
}
/**
* Allows or disallows the showing of hidden files and directories in the tree.
* @param showHiddenFiles <code>true</code> shows hidden files. <code>false</code> does not.
*/
public void setShowHiddenFiles(boolean showHiddenFiles) {
if (showHiddenFiles != this.showHiddenFiles)
{
this.showHiddenFiles = showHiddenFiles;
initRoot();
}
}
/**
* sets whether the user can navigate into OS X application bundles (.app). The
* default value is <code>false</code>
* @param navigateOSXApps if true, users will be able to navigate into OS X application bundles. set it
* to false to disallow navigating bundles.
*/
public void setNavigateOSXApps(boolean navigateOSXApps) {
this.navigateOSXApps = navigateOSXApps;
}
/**
* the root node of the <code>FileTree</code>
*/
protected DefaultMutableTreeNode rootNode;
/**
* the <code>TreeModel</code> for this object. The same value as the <code>JTree</code>
* treeModel member.
*/
protected DefaultTreeModel fileTreeModel;
/**
* just a filesystemview used to get icons for nodes in Windows
*/
protected FileSystemView fsv;
/**
* whether or not to show hidden files
*/
protected boolean showHiddenFiles;
/**
* whether or not to show files
*/
protected boolean showFiles;
/**
* whether to allow deleting of files
*/
protected boolean allowDelete;
/**
* allows/disallows navigating into OS X application bundles
*/
protected boolean navigateOSXApps;
/**
* A subclass of DefaultTreeCellRenderer that is responsible for rendering the
* nodes and their icons
*/
private class FileTreeCellRenderer extends DefaultTreeCellRenderer {
/**
* just a simple constructor
*/
public FileTreeCellRenderer() {
fileChooser = new JFileChooser();
}
/**
* returns a renderered node for the tree
* @param tree the tree to render the node for
* @param value the value of the node
* @param selected if the node is selected
* @param expanded if it's expanded
* @param leaf if its a leaf or not
* @param row the row number
* @param hasFocus if it has focus
* @return a renderered node for the tree
*/
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);
Object userObject = ((DefaultMutableTreeNode)value).getUserObject();
if (userObject instanceof FileTreeNode)
{
FileTreeNode fileTreeNode = (FileTreeNode)userObject;
if (!Constants.isWindows)
{
try { setIcon(fileChooser.getIcon(fileTreeNode.file)); }
catch (Exception e) { e.printStackTrace(); }
}
else
{
try { setIcon(fsv.getSystemIcon(fileTreeNode.file)); }
catch (Exception e) { e.printStackTrace(); }
}
}
return this;
}
/**
* used to obtain icons for non-Windows OSes
*/
private JFileChooser fileChooser;
}
}
FileTreeListener.Java
package jtree;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreePath;
public class FileTreeListener extends MouseAdapter {
/**
* Creates a new instance of FileTreeListener
* @param fileTree the <code>FileTree</code> to listen for
*/
public FileTreeListener(FileTree fileTree) {
if (fileTree == null)
throw new IllegalArgumentException("Null argument not allowed");
this.fileTree = fileTree;
}
/**
* Listens for right-clicks on the tree.
* @param e contains information about the mouse click event
*/
public void mousePressed(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON3)
rightClick(e.getX(), e.getY());
}
/**
*
* @param x the x coordinate of the mouse when it was pressed
* @param y the y coordinate of the mouse when it was pressed
*/
private void rightClick(int x, int y) {
TreePath treePath = fileTree.getPathForLocation(x, y);
if (treePath == null)
return;
if (!fileTree.isDeleteEnabled())
return;
JPopupMenu popup = new JPopupMenu();
popup.add(new DeleteFileAction(treePath));
popup.show(fileTree, x, y);
}
/**
* the <code>FileTree</code> to listen on
*/
private FileTree fileTree;
/**
* feature not implemented
*/
private class RenameAction extends AbstractAction {
public RenameAction(TreePath treePath) {
this.treePath = treePath;
putValue(Action.NAME, "Rename");
DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode)treePath.getLastPathComponent();
fileTreeNode = (FileTreeNode)treeNode.getUserObject();
if (!fileTreeNode.file.canWrite())
setEnabled(false);
}
@Override
public void actionPerformed(ActionEvent e) {
fileTree.startEditingAtPath(treePath);
}
private TreePath treePath;
private FileTreeNode fileTreeNode;
}
private class DeleteFileAction extends AbstractAction {
/**
* constructor for the action to delete a file or directory
* @param treePath the treepath of the node to act on
*/
public DeleteFileAction(TreePath treePath) {
this.treePath = treePath;
if (Constants.isOSX)
putValue(Action.NAME, "Move to Trash");
else
{
putValue(Action.NAME, "Delete");
putValue(Action.MNEMONIC_KEY, KeyEvent.VK_D);
}
DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode)treePath.getLastPathComponent();
fileTreeNode = (FileTreeNode)treeNode.getUserObject();
if (!fileTreeNode.file.canWrite())
setEnabled(false);
}
/**
* the action called when the user wants to delete a file or directory
* @param e information about the event that caused this method to be called
*/
public void actionPerformed(ActionEvent e) {
int choice = JOptionPane.showConfirmDialog(fileTree.getRootPane(),
"Are you sure you want to delete '" + fileTreeNode.file.getName()+"'?",
"Confirm delete",
JOptionPane.YES_NO_OPTION,
JOptionPane.QUESTION_MESSAGE);
if (choice == 1)
return; // they selected no
boolean success = false;
if (fileTreeNode.file.isDirectory())
success = deleteDirectory(fileTreeNode.file);
else
success = fileTreeNode.file.delete();
if (success)
{
fileTree.getFileTreeModel().removeNodeFromParent(
(DefaultMutableTreeNode)treePath.getLastPathComponent());
}
}
/**
* deletes a directory and its content
* @param dir The directory to delete
* @return true on success, false otherwise
*/
private boolean deleteDirectory(File dir) {
if (dir == null || !dir.exists() || !dir.isDirectory())
return false;
boolean success = true;
File [] list = dir.listFiles();
for (File file:list)
{
if (file.isDirectory())
{
if (!deleteDirectory(file))
success = false;
}
else
{
if (!file.delete())
success = false;
}
}
if (!dir.delete()) // finally delete the actual directory
success = false;
return success;
}
/**
* The <code>TreePath</code> to the node that will be deleted.
*/
private TreePath treePath;
/**
* The <code>FileTreeNode</code> stored inside the <code>DefaultMutableTreeNode</code>'s
* user object
*/
private FileTreeNode fileTreeNode;
}
}
FileTreeNode.java
package jtree;
import java.io.File;
public class FileTreeNode {
/**
* Creates a new instance of FileTreeNode
* @param file The <code>File</code> that will be represented by this class.
*/
public FileTreeNode(File file) {
if (file == null)
throw new IllegalArgumentException("Null file not allowed");
this.file = file;
}
/**
* returns the representation of this <code>File</code> best suited for use in
* the <code>FileTree</code>.
* @return the representation of this <code>File</code> as a <code>String</code>
*/
public String toString() {
String name = file.getName();
if (!Constants.isWindows)
return name;
if (name.length() == 0)
return file.getPath();
if (Constants.isVista)
{
if (name.equals(WINDOWS_MYCOMPUTER)){
return "Computer";
}
if ("::{F02C1A0D-BE21-4350-88B0-7367FC96EF3C}".equals(name)){
return "My Network";
}
return name;
}
if (name.equals(WINDOWS_MYCOMPUTER)){
return "This PC";
}
if ("::{F02C1A0D-BE21-4350-88B0-7367FC96EF3C}".equals(name)){
return "Network";
}
if ("::{031E4825-7B94-4DC3-B131-E946B44C8DD5}".equals(name)){
return "Library-MS";
}
return name;
}
/**
* the object being represented
*/
public File file;
/**
* the hex string that represents 'My Computer' in Windows
*/
public static final String WINDOWS_MYCOMPUTER = "::{20D04FE0-3AEA-1069-A2D8-08002B30309D}";
/**
* the hex string that represents 'My Network Places' in Win2k and XP
*/
public static final String WINDOWS_MYNETWORKPLACES = "::{208D2C60-3AEA-1069-A2D7-08002B30309D}";
/**
* the hex string that represents 'Network' in Vista
*/
public static final String WINDOWSVISTA_NETWORK = "::{F02C1A0D-BE21-4350-88B0-7367FC96EF3C}";
}
Main.java
package jtree;
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
public class Main {
/** Creates a new instance of Main */
public Main() {
}
public static void main(String[] args) {
JFrame jframe = new JFrame();
jframe.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
Container container = jframe.getContentPane();
container.setLayout(new BorderLayout());
FileTree fileTree = new FileTree();
fileTree.setShowHiddenFiles(false);
fileTree.setDeleteEnabled(true);
JScrollPane scrollPane = new JScrollPane(fileTree);
container.add(scrollPane, BorderLayout.CENTER);
jframe.setSize(300, 450);
jframe.setLocationByPlatform(true);
jframe.setVisible(true);
}
}
I've been trying to implement the Timer function for 3 or 5 seconds to update the JTree using the updateUI(), repaint(), revalidate() methods including the DefaultTreeModel. I'm still unable to keep the file JTree updated whenever a file is renamed, moved or deleted. Can someone please help me rectify this issue and guide me where to place the code snippet if written by you?