Java tutorial
/* Core SWING Advanced Programming By Kim Topley ISBN: 0 13 083292 8 Publisher: Prentice Hall */ import java.awt.Dimension; import java.awt.Insets; import java.awt.Point; import java.awt.Rectangle; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.Transferable; import java.awt.dnd.Autoscroll; import java.awt.dnd.DnDConstants; import java.awt.dnd.DragGestureEvent; import java.awt.dnd.DragGestureListener; import java.awt.dnd.DragSource; import java.awt.dnd.DragSourceDragEvent; import java.awt.dnd.DragSourceDropEvent; import java.awt.dnd.DragSourceEvent; import java.awt.dnd.DragSourceListener; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; import java.io.FileNotFoundException; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.List; import javax.swing.JFrame; import javax.swing.JScrollBar; import javax.swing.JScrollPane; import javax.swing.JTree; import javax.swing.SwingUtilities; import javax.swing.Timer; import javax.swing.UIManager; import javax.swing.event.TreeExpansionEvent; import javax.swing.event.TreeExpansionListener; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreeModel; import javax.swing.tree.TreePath; public class FileTreeDragSource implements DragGestureListener, DragSourceListener { public FileTreeDragSource(FileTree tree) { this.tree = tree; // Use the default DragSource DragSource dragSource = DragSource.getDefaultDragSource(); // Create a DragGestureRecognizer and // register as the listener dragSource.createDefaultDragGestureRecognizer(tree, DnDConstants.ACTION_COPY_OR_MOVE, this); } // Implementation of DragGestureListener interface. public void dragGestureRecognized(DragGestureEvent dge) { // Get the mouse location and convert it to // a location within the tree. Point location = dge.getDragOrigin(); TreePath dragPath = tree.getPathForLocation(location.x, location.y); if (dragPath != null && tree.isPathSelected(dragPath)) { // Get the list of selected files and create a Transferable // The list of files and the is saved for use when // the drop completes. paths = tree.getSelectionPaths(); if (paths != null && paths.length > 0) { dragFiles = new File[paths.length]; for (int i = 0; i < paths.length; i++) { String pathName = tree.getPathName(paths[i]); dragFiles[i] = new File(pathName); } Transferable transferable = new FileListTransferable(dragFiles); dge.startDrag(null, transferable, this); } } } // Implementation of DragSourceListener interface public void dragEnter(DragSourceDragEvent dsde) { DnDUtils.debugPrintln( "Drag Source: dragEnter, drop action = " + DnDUtils.showActions(dsde.getDropAction())); } public void dragOver(DragSourceDragEvent dsde) { DnDUtils.debugPrintln("Drag Source: dragOver, drop action = " + DnDUtils.showActions(dsde.getDropAction())); } public void dragExit(DragSourceEvent dse) { DnDUtils.debugPrintln("Drag Source: dragExit"); } public void dropActionChanged(DragSourceDragEvent dsde) { DnDUtils.debugPrintln( "Drag Source: dropActionChanged, drop action = " + DnDUtils.showActions(dsde.getDropAction())); } public void dragDropEnd(DragSourceDropEvent dsde) { DnDUtils.debugPrintln("Drag Source: drop completed, drop action = " + DnDUtils.showActions(dsde.getDropAction()) + ", success: " + dsde.getDropSuccess()); // If the drop action was ACTION_MOVE, // the tree might need to be updated. if (dsde.getDropAction() == DnDConstants.ACTION_MOVE) { final File[] draggedFiles = dragFiles; final TreePath[] draggedPaths = paths; Timer tm = new Timer(200, new ActionListener() { public void actionPerformed(ActionEvent evt) { // Check whether each of the dragged files exists. // If it does not, we need to remove the node // that represents it from the tree. for (int i = 0; i < draggedFiles.length; i++) { if (draggedFiles[i].exists() == false) { // Remove this node DefaultMutableTreeNode node = (DefaultMutableTreeNode) draggedPaths[i] .getLastPathComponent(); ((DefaultTreeModel) tree.getModel()).removeNodeFromParent(node); } } } }); tm.setRepeats(false); tm.start(); } } public static void main(String[] args) { try { UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel"); } catch (Exception evt) { } JFrame f = new JFrame("Draggable File Tree"); try { FileTree tree = new FileTree("D:\\"); f.getContentPane().add(new JScrollPane(tree)); // Attach the drag source FileTreeDragSource dragSource = new FileTreeDragSource(tree); } catch (Exception e) { } f.pack(); f.setVisible(true); } protected FileTree tree; // The associated tree protected File[] dragFiles; // Dragged files protected TreePath[] paths; // Dragged paths } class FileTree extends JTree implements Autoscroll { public static final Insets defaultScrollInsets = new Insets(8, 8, 8, 8); protected Insets scrollInsets = defaultScrollInsets; public FileTree(String path) throws FileNotFoundException, SecurityException { super((TreeModel) null); // Create the JTree itself // Use horizontal and vertical lines putClientProperty("JTree.lineStyle", "Angled"); // Create the first node FileTreeNode rootNode = new FileTreeNode(null, path); // Populate the root node with its subdirectories boolean addedNodes = rootNode.populateDirectories(true); setModel(new DefaultTreeModel(rootNode)); // Listen for Tree Selection Events addTreeExpansionListener(new TreeExpansionHandler()); } // Returns the full pathname for a path, or null if not a known path public String getPathName(TreePath path) { Object o = path.getLastPathComponent(); if (o instanceof FileTreeNode) { return ((FileTreeNode) o).fullName; } return null; } // Adds a new node to the tree after construction. // Returns the inserted node, or null if the parent // directory has not been expanded. public FileTreeNode addNode(FileTreeNode parent, String name) { int index = parent.addNode(name); if (index != -1) { ((DefaultTreeModel) getModel()).nodesWereInserted(parent, new int[] { index }); return (FileTreeNode) parent.getChildAt(index); } // No node was created return null; } // Autoscrolling support public void setScrollInsets(Insets insets) { this.scrollInsets = insets; } public Insets getScrollInsets() { return scrollInsets; } // Implementation of Autoscroll interface public Insets getAutoscrollInsets() { Rectangle r = getVisibleRect(); Dimension size = getSize(); Insets i = new Insets(r.y + scrollInsets.top, r.x + scrollInsets.left, size.height - r.y - r.height + scrollInsets.bottom, size.width - r.x - r.width + scrollInsets.right); return i; } public void autoscroll(Point location) { JScrollPane scroller = (JScrollPane) SwingUtilities.getAncestorOfClass(JScrollPane.class, this); if (scroller != null) { JScrollBar hBar = scroller.getHorizontalScrollBar(); JScrollBar vBar = scroller.getVerticalScrollBar(); Rectangle r = getVisibleRect(); if (location.x <= r.x + scrollInsets.left) { // Need to scroll left hBar.setValue(hBar.getValue() - hBar.getUnitIncrement(-1)); } if (location.y <= r.y + scrollInsets.top) { // Need to scroll up vBar.setValue(vBar.getValue() - vBar.getUnitIncrement(-1)); } if (location.x >= r.x + r.width - scrollInsets.right) { // Need to scroll right hBar.setValue(hBar.getValue() + hBar.getUnitIncrement(1)); } if (location.y >= r.y + r.height - scrollInsets.bottom) { // Need to scroll down vBar.setValue(vBar.getValue() + vBar.getUnitIncrement(1)); } } } // Inner class that represents a node in this file system tree public static class FileTreeNode extends DefaultMutableTreeNode { public FileTreeNode(String parent, String name) throws SecurityException, FileNotFoundException { this.name = name; // See if this node exists and whether it is a directory fullName = parent == null ? name : parent + File.separator + name; File f = new File(fullName); if (f.exists() == false) { throw new FileNotFoundException("File " + fullName + " does not exist"); } isDir = f.isDirectory(); // Hack for Windows which doesn't consider a drive to be a // directory! if (isDir == false && f.isFile() == false) { isDir = true; } } // Override isLeaf to check whether this is a directory public boolean isLeaf() { return !isDir; } // Override getAllowsChildren to check whether this is a directory public boolean getAllowsChildren() { return isDir; } // Return whether this is a directory public boolean isDir() { return isDir; } // Get full path public String getFullName() { return fullName; } // For display purposes, we return our own name public String toString() { return name; } // If we are a directory, scan our contents and populate // with children. In addition, populate those children // if the "descend" flag is true. We only descend once, // to avoid recursing the whole subtree. // Returns true if some nodes were added boolean populateDirectories(boolean descend) { boolean addedNodes = false; // Do this only once if (populated == false) { File f; try { f = new File(fullName); } catch (SecurityException e) { populated = true; return false; } if (interim == true) { // We have had a quick look here before: // remove the dummy node that we added last time removeAllChildren(); interim = false; } String[] names = f.list(); // Get list of contents // Process the contents ArrayList list = new ArrayList(); for (int i = 0; i < names.length; i++) { String name = names[i]; File d = new File(fullName, name); try { FileTreeNode node = new FileTreeNode(fullName, name); list.add(node); if (descend && d.isDirectory()) { node.populateDirectories(false); } addedNodes = true; if (descend == false) { // Only add one node if not descending break; } } catch (Throwable t) { // Ignore phantoms or access problems } } if (addedNodes == true) { // Now sort the list of contained files and directories Object[] nodes = list.toArray(); Arrays.sort(nodes, new Comparator() { public boolean equals(Object o) { return false; } public int compare(Object o1, Object o2) { FileTreeNode node1 = (FileTreeNode) o1; FileTreeNode node2 = (FileTreeNode) o2; // Directories come first if (node1.isDir != node2.isDir) { return node1.isDir ? -1 : +1; } // Both directories or both files - // compare based on pathname return node1.fullName.compareTo(node2.fullName); } }); // Add sorted items as children of this node for (int j = 0; j < nodes.length; j++) { this.add((FileTreeNode) nodes[j]); } } // If we were scanning to get all subdirectories, // or if we found no content, there is no // reason to look at this directory again, so // set populated to true. Otherwise, we set interim // so that we look again in the future if we need to if (descend == true || addedNodes == false) { populated = true; } else { // Just set interim state interim = true; } } return addedNodes; } // Adding a new file or directory after // constructing the FileTree. Returns // the index of the inserted node. public int addNode(String name) { // If not populated yet, do nothing if (populated == true) { // Do not add a new node if // the required node is already there int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { FileTreeNode node = (FileTreeNode) getChildAt(i); if (node.name.equals(name)) { // Already exists - ensure // we repopulate if (node.isDir()) { node.interim = true; node.populated = false; } return -1; } } // Add a new node try { FileTreeNode node = new FileTreeNode(fullName, name); add(node); return childCount; } catch (Exception e) { } } return -1; } protected String name; // Name of this component protected String fullName; // Full pathname protected boolean populated;// true if we have been populated protected boolean interim; // true if we are in interim state protected boolean isDir; // true if this is a directory } // Inner class that handles Tree Expansion Events protected class TreeExpansionHandler implements TreeExpansionListener { public void treeExpanded(TreeExpansionEvent evt) { TreePath path = evt.getPath(); // The expanded path JTree tree = (JTree) evt.getSource(); // The tree // Get the last component of the path and // arrange to have it fully populated. FileTreeNode node = (FileTreeNode) path.getLastPathComponent(); if (node.populateDirectories(true)) { ((DefaultTreeModel) tree.getModel()).nodeStructureChanged(node); } } public void treeCollapsed(TreeExpansionEvent evt) { // Nothing to do } } } class FileListTransferable implements Transferable { public FileListTransferable(File[] files) { fileList = new ArrayList(); for (int i = 0; i < files.length; i++) { fileList.add(files[i]); } } // Implementation of the Transferable interface public DataFlavor[] getTransferDataFlavors() { return new DataFlavor[] { DataFlavor.javaFileListFlavor }; } public boolean isDataFlavorSupported(DataFlavor fl) { return fl.equals(DataFlavor.javaFileListFlavor); } public Object getTransferData(DataFlavor fl) { if (!isDataFlavorSupported(fl)) { return null; } return fileList; } List fileList; // The list of files } class DnDUtils { public static String showActions(int action) { String actions = ""; if ((action & (DnDConstants.ACTION_LINK | DnDConstants.ACTION_COPY_OR_MOVE)) == 0) { return "None"; } if ((action & DnDConstants.ACTION_COPY) != 0) { actions += "Copy "; } if ((action & DnDConstants.ACTION_MOVE) != 0) { actions += "Move "; } if ((action & DnDConstants.ACTION_LINK) != 0) { actions += "Link"; } return actions; } public static boolean isDebugEnabled() { return debugEnabled; } public static void debugPrintln(String s) { if (debugEnabled) { System.out.println(s); } } private static boolean debugEnabled = (System.getProperty("DnDExamples.debug") != null); }