2

I wanted to make a tree from an existing directory, so that it shows all of the subfolders and files inside. I did it with this code:

File file = new File(path); TreeModel model = new FileTreeModel(file); JTree tree = new JTree(model);

But now it shows the full path instead of just the folder/file name. If you don't know what I mean, this might help: java tree Please help... I wanted to find the solution online but I didn't know how to describe the problem. :/

2xxx2
  • 45
  • 1
  • 9
  • Instead of using `File` directly, write your own class that wraps File, and then implement its `toString()` method. – JayC667 Apr 22 '16 at 22:23
  • @JayC667 Thanks for the advice, but I don't quite know how to do that... :/ Could you please, if you have the time, send me the code for that class? :) – 2xxx2 Apr 22 '16 at 22:26

1 Answers1

1
import java.io.File;
import java.util.ArrayList;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.WindowConstants;
import javax.swing.event.TreeModelEvent;
import javax.swing.event.TreeModelListener;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;

class FileTreeModel implements TreeModel {
    private final ArrayList<TreeModelListener>  mListeners  = new ArrayList<>();
    private final MyFile                        mFile;

    public FileTreeModel(final MyFile pFile) {
        mFile = pFile;
    }
    @Override public Object getRoot() {
        return mFile;
    }
    @Override public Object getChild(final Object pParent, final int pIndex) {
        return ((MyFile) pParent).listFiles()[pIndex];
    }
    @Override public int getChildCount(final Object pParent) {
        return ((MyFile) pParent).listFiles().length;
    }
    @Override public boolean isLeaf(final Object pNode) {
        return !((MyFile) pNode).isDirectory();
    }
    @Override public void valueForPathChanged(final TreePath pPath, final Object pNewValue) {
        final MyFile oldTmp = (MyFile) pPath.getLastPathComponent();
        final File oldFile = oldTmp.getFile();
        final String newName = (String) pNewValue;
        final File newFile = new File(oldFile.getParentFile(), newName);
        oldFile.renameTo(newFile);
        System.out.println("Renamed '" + oldFile + "' to '" + newFile + "'.");
        reload();
    }
    @Override public int getIndexOfChild(final Object pParent, final Object pChild) {
        final MyFile[] files = ((MyFile) pParent).listFiles();
        for (int i = 0; i < files.length; i++) {
            if (files[i] == pChild) return i;
        }
        return -1;
    }
    @Override public void addTreeModelListener(final TreeModelListener pL) {
        mListeners.add(pL);
    }
    @Override public void removeTreeModelListener(final TreeModelListener pL) {
        mListeners.remove(pL);
    }

    /**
     *  stolen from http://developer.classpath.org/doc/javax/swing/tree/DefaultTreeModel-source.html
     *
      * <p>
      * Invoke this method if you've modified the TreeNodes upon which this model
      * depends. The model will notify all of its listeners that the model has
      * changed. It will fire the events, necessary to update the layout caches and
      * repaint the tree. The tree will <i>not</i> be properly refreshed if you
      * call the JTree.repaint instead.
      * </p>
      * <p>
      * This method will refresh the information about whole tree from the root. If
      * only part of the tree should be refreshed, it is more effective to call
      * {@link #reload(TreeNode)}.
      * </p>
      */
    public void reload() {
        // Need to duplicate the code because the root can formally be
        // no an instance of the TreeNode.
        final int n = getChildCount(getRoot());
        final int[] childIdx = new int[n];
        final Object[] children = new Object[n];

        for (int i = 0; i < n; i++) {
            childIdx[i] = i;
            children[i] = getChild(getRoot(), i);
        }

        fireTreeStructureChanged(this, new Object[] { getRoot() }, childIdx, children);
    }

    /**
     * stolen from http://developer.classpath.org/doc/javax/swing/tree/DefaultTreeModel-source.html
     *
     * fireTreeStructureChanged
     *
     * @param source the node where the model has changed
     * @param path the path to the root node
     * @param childIndices the indices of the affected elements
     * @param children the affected elements
     */
    protected void fireTreeStructureChanged(final Object source, final Object[] path, final int[] childIndices, final Object[] children) {
        final TreeModelEvent event = new TreeModelEvent(source, path, childIndices, children);
        for (final TreeModelListener l : mListeners) {
            l.treeStructureChanged(event);
        }
    }
}

class MyFile {
    private final File mFile;

    public MyFile(final File pFile) {
        mFile = pFile;
    }

    public boolean isDirectory() {
        return mFile.isDirectory();
    }

    public MyFile[] listFiles() {
        final File[] files = mFile.listFiles();
        if (files == null) return null;
        if (files.length < 1) return new MyFile[0];

        final MyFile[] ret = new MyFile[files.length];
        for (int i = 0; i < ret.length; i++) {
            final File f = files[i];
            ret[i] = new MyFile(f);
        }
        return ret;
    }

    public File getFile() {
        return mFile;
    }

    @Override public String toString() {
        return mFile.getName();
    }
}

public class FileWrapperDeluxe {
    public static void main(final String[] args) {
        final JFrame f = new JFrame();
        f.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
        f.setBounds(100, 100, 400, 400);

        final File file = new File("E:\\");
        final MyFile mf = new MyFile(file);
        final TreeModel model = new FileTreeModel(mf);

        final JTree tree = new JTree(model);
        tree.setEditable(true);

        f.add(new JScrollPane(tree));
        f.setVisible(true);
    }
}
JayC667
  • 2,418
  • 2
  • 17
  • 31
  • Thats it, thanks! :D Now only one more thing, can I have my own icons for the folders and files in the tree? :) – 2xxx2 Apr 22 '16 at 22:59
  • 1
    https://docs.oracle.com/javase/tutorial/uiswing/components/tree.html and http://stackoverflow.com/questions/20691946/set-icon-to-each-node-in-jtree – JayC667 Apr 22 '16 at 23:04
  • Now I want to be able to rename files and folders, so I've done this: setEditable(true); But when i rename it and press enter, it changes back to the original name... What should I do? :/ – 2xxx2 Apr 24 '16 at 21:56
  • 1
    Change my example, add `tree.setEditable(true);`, then rename something, you'll see what to do then ;-) – JayC667 Apr 24 '16 at 22:26
  • I added that, nothing changed. I still don't know what to do... :( – 2xxx2 Apr 24 '16 at 22:31
  • 1
    The method `valueForPathChanged` of your `TreeModel` gets called. Check its parameters, they give you all the infos you need for renaming. – JayC667 Apr 24 '16 at 22:34
  • I made a part of it work. Now if i rename a folder in the tree, it really renames in in the windows explorer. But in the tree it still has the old name (and for some reason turns into a file instead of a folder). How do i refresh the tree model (or whatever I need to do)? – 2xxx2 Apr 24 '16 at 22:46
  • I tried a few things but nothing worked... There are 3 problems: 1) folders rename in windows explorer, but files don't. 2) nothing renames in the program's Tree. 3) if i try to rename a folder, it's icon changes to the file icon. Now I'm stuck again... Please help! :/ – 2xxx2 Apr 25 '16 at 15:01
  • 1
    Do u really want to change the actual file names, eg renaming real files and directories, or do you just want that to be in your application, showing a virtual file system? – JayC667 Apr 25 '16 at 23:34
  • I really want to change the names. So it changes on the computer, but ALSO in the application. – 2xxx2 Apr 26 '16 at 13:53
  • 1
    Okay check it out: I have changed my solution a little bit. Added the `reload()` and `fireTreeStructureChanged()` methods, plus changed the `valueForPathChanged()` method. Works like a charme, even though I did not include any checks. That's up to you now. Oh ah and BTW you could also work with the "DefaultTreeModel", makes updating and most other stuff a lot easier. Check solutions on google ;-) – JayC667 Apr 26 '16 at 21:23
  • Thanks, that worked and it's great. But I also want to add a few options to the tree (rename - done, delete, clone file/folder, create new...). Can you help me with that too, please? :D P.S. When I deleted a folder from the directory, in the tree, it didn't dissapear, but it turned into a node with the file's icon... :S – 2xxx2 Apr 26 '16 at 22:32
  • Usually I'm getting paid for that kind of stuff. Show some enthusiasm and get it done yourself. – JayC667 Apr 26 '16 at 23:23
  • Ok, sorry... But I'm only a beginner and I don't know how to do most of the stuff. :3 – 2xxx2 Apr 27 '16 at 12:09