package edu.colostate.vchill.gui; import edu.colostate.vchill.Loader; import edu.colostate.vchill.ViewControl; import javax.swing.*; import javax.swing.event.*; import javax.swing.tree.DefaultTreeCellRenderer; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreePath; import javax.swing.tree.TreeSelectionModel; import java.awt.*; import java.awt.event.MouseEvent; import java.util.Collection; /** * This is a "file browser" that can be used to visualize directory structure * data / files * <p/> * NOTE: This currently uses a Control Message for ease of use. It may need * to be altered in order to be more dynamic about level, the nodes may need * to know about what type of data they are holding for multi directory access. * * @author Justin Carlson * @author Jochen Deyke * @author Alexander Deyke * @author jpont * @version 2010-08-30 * @created June 2, 2003 */ public class ViewFileBrowser extends JPanel { /** * */ private static final long serialVersionUID = -2063995646756926200L; public static final ViewControl vc = ViewControl.getInstance(); private final JTree tree; private final DefaultTreeModel model; private final ViewFileBrowserActions actions; private final ViewFileBrowserPopup popup; private static final ViewFileBrowser vfb = new ViewFileBrowser(); public static ViewFileBrowser getInstance() { return vfb; } /** * Private constructor prevents instantiation */ private ViewFileBrowser() { setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); this.model = new DefaultTreeModel(createNode("Connections")); createChildNodes((FileTreeNode) model.getRoot(), vc.getConnections(), true); this.tree = new JTree(this.model); this.actions = new ViewFileBrowserActions(this.tree); this.popup = new ViewFileBrowserPopup(this.actions); this.tree.addMouseListener(new MouseInputAdapter() { @Override public void mouseClicked(final MouseEvent e) { TreePath click = tree.getPathForLocation(e.getX(), e.getY()); switch (e.getButton()) { case MouseEvent.BUTTON1: //left click if (click == null) return; FileTreeNode node = (FileTreeNode) click.getLastPathComponent(); if (node == null || !node.isLeaf()) return; if (tree.isPathSelected(click) && e.getClickCount() < 2) return; tree.setSelectionPath(click); vc.setMessage(ViewFileBrowserActions.getControlMessage(click)); break; case MouseEvent.BUTTON3: //right click if (!tree.isPathSelected(click)) tree.setSelectionPath(click); popup.show(e.getComponent(), e.getX(), e.getY()); break; } } }); this.tree.addTreeSelectionListener(new TreeSelectionListener() { public void valueChanged(TreeSelectionEvent tse) { FileTreeNode node = (FileTreeNode) tree.getLastSelectedPathComponent(); if (node == null || !node.isLeaf()) return; vc.setMessage(ViewFileBrowserActions.getControlMessage(tse.getPath())); } }); this.tree.addTreeExpansionListener(new TreeExpansionListener() { public void treeExpanded(final TreeExpansionEvent tee) { TreePath path = tee.getPath(); FileTreeNode node = (FileTreeNode) path.getLastPathComponent(); Object pathObjects[] = path.getPath(); int pathLength = pathObjects.length; switch (pathLength) { case 1: createChildNodes(node, vc.getConnections(), true); break; case 2: vc.setCurrentURL((String) (((FileTreeNode) pathObjects[1]).getUserObject())); vc.setCurrentDirectory(""); createChildNodes(node, vc.getDirectory(), true); break; default: vc.setCurrentURL((String) (((FileTreeNode) pathObjects[1]).getUserObject())); String name = (String) (((FileTreeNode) pathObjects[pathLength - 1]).getUserObject()); if (name.endsWith(" DIR")) { //subdirectory - get contents vc.setCurrentDirectory(name); createChildNodes(node, vc.getDirectory(), true); } else { //file - get sweeps String dir = (String) (((FileTreeNode) pathObjects[pathLength - 2]).getUserObject()); vc.setCurrentDirectory(dir); vc.setCurrentFile(name); createChildNodes(node, vc.getSweeps(), false); } } } public void treeCollapsed(final TreeExpansionEvent tee) { } }); this.tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); //this.tree.setShowsRootHandles(true); this.tree.setShowsRootHandles(false); this.tree.setCellRenderer(new DefaultTreeCellRenderer() { /** * */ private static final long serialVersionUID = 4265159595747277418L; @Override public Component getTreeCellRendererComponent(final JTree tree, final Object value, final boolean sel, final boolean expanded, final boolean leaf, final int row, final boolean hasFocus) { super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus); FileTreeNode node = (FileTreeNode) value; Font current = getFont(); setFont(new Font(current.getFamily(), node.special ? Font.BOLD : Font.PLAIN, current.getSize())); switch (node.getLevel()) { case 0: setIcon(new ImageIcon(Loader.getResource("icons/top" + (expanded ? "Open" : "Closed") + ".gif"))); break; case 1: setIcon(new ImageIcon(Loader.getResource("icons/conn" + (expanded ? "Open" : "Closed") + ".gif"))); break; default: if (node.isLeaf()) { try { String[] parts = ((String) ((FileTreeNode) node.getParent()).getUserObject()).split(" "); setIcon(new ImageIcon(Loader.getResource("icons/sweep" + parts[parts.length - 1] + ".png"))); } catch (Exception e) { setIcon(new ImageIcon(Loader.getResource("icons/error.png"))); } } else { String name = (String) node.getUserObject(); if (name.endsWith(" DIR")) setIcon(new ImageIcon(Loader.getResource("icons/dir" + (expanded ? "Open" : "Closed") + ".gif"))); else setIcon(new ImageIcon(Loader.getResource("icons/file" + (expanded ? "Open" : "Closed") + ".gif"))); } } return this; } }); JScrollPane scrollPane = new JScrollPane(this.tree); add(scrollPane); } /** * @return a placeholder node */ private FileTreeNode loadingNode() { return createNode(FileTreeNode.LOADING); } /** * This method will create new child nodes for the specified parent. * Depending on the fill_with_loading variable these will be leaves or further nodes in the structure. * * @param parentNode The node that the information will be added to. * @param nodeNames A list of names for the new child nodes of <code>parentNode</code>. * @param fillWithLoading If true, this will cause the new children to have a * default String created in them to allow them to be changed to a valid leaf later. * When the tree is to end branching, this variable should be set to false in order * to create the end leaves. */ private void createChildNodes(final FileTreeNode parentNode, final Collection<String> nodeNames, final boolean fillWithLoading) { int numchildren = model.getChildCount(parentNode); if (!parentNode.complete) { if (nodeNames == null) { model.insertNodeInto(createNode("ERROR%20-%20Connection%20failed?"), parentNode, parentNode.getChildCount()); } else if (nodeNames.size() == 0) { model.insertNodeInto(createNode("Nothing%20to%20display"), parentNode, parentNode.getChildCount()); } else { for (String name : nodeNames) { FileTreeNode child = createNode(name); if (fillWithLoading) model.insertNodeInto(loadingNode(), child, 0); model.insertNodeInto(child, parentNode, parentNode.getChildCount()); } parentNode.complete = true; } for (int i = 0; i < numchildren; ++i) { model.removeNodeFromParent((FileTreeNode) model.getChild(parentNode, 0)); } model.nodeStructureChanged(parentNode); } } /** * This will simply create a new node, mostly here for the sake of cutting down on space use. * * @param nodeName name of the node to create * @return the new node */ private FileTreeNode createNode(final String nodeName) { return new FileTreeNode(nodeName); } public ViewFileBrowserActions getActions() { return this.actions; } }